Spring Mongodata Aggregation query - mongodb

I tried to implement an aggregation with the simple sum operation using spring-data-mongodb -> 1.10.11.RELEASE
I tried the following query
db.transaction.aggregate([{
$group: {
_id:"null",
netBalance: {
$sum: "$netBalance"
},
referalBalance: {
$sum: "$referalBalance"
}
}
}])
And the output on my terminal is
{ "_id" : "null", "netBalance" : 587432, "referalBalance" : 2940 }
When I tried same query, using spring mongodata
Aggregation aggregation =
Aggregation.newAggregation(
Aggregation.group("netBalance")
.sum("netBalance")
.as("netBalance"));
The result is not same as the terminal output, also I am not able to add a second field on the query. How can I modify the spring mongodata call to have the same query?

with spring you're grouping by netBalance instead of null in shell. To reproduce, just leave group param empty (null will throw an error)
Then you can apply sum on second field .
Try this code (not tested, but must work):
Aggregation aggregation =
Aggregation.newAggregation(
Aggregation.group()
.sum("netBalance").as("netBalance")
.sum("referalBalance").as("referalBalance"));

Related

Spring WebFlux + MongoDB: Tailable Cursor and Aggregation

I´m new with WebFlux and MongoDB. I´m trying to use aggregation in a capped collection with tailable cursor, but I´m nothing getting sucessful.
I´d like to execute this mongoDB query:
db.structures.aggregate(
[
{
$match: {
id: { $in: [8244, 8052]}
}
},
{ $sort: { id: 1, lastUpdate: 1} },
{
$group:
{
_id: {id: "$id"},
lastUpdate: { $last: "$lastUpdate" }
}
}
]
)
ReactiveMongoOperations gives me option to "tail" or "aggregation".
I´m able to execute aggregation:
MatchOperation match = new MatchOperation(Criteria.where("id").in(8244, 8052));
GroupOperation group = Aggregation.group("id", "$id").last("$lastUpdate").as("lastUpdate");
Aggregation aggregate = Aggregation.newAggregation(match, group);
Flux<Structure> result = mongoOperation.aggregate(aggregate,
"structures", Structure.class);
Or tail cursor
Query query = new Query();
query.addCriteria(Criteria.where("id").in(8244, 8052));
Flux<Structure> result = mongoOperation.tail(query, Structure.class);
Is it possible? Tail and Aggregation together?
Using aggregation was the way that I found to get only the last inserted document for each id.
Without aggregation I get:
query without aggregation
With aggregation:
query with aggregation
Tks in advance
The tailable cursor query creates a Flux that never completes (never emits onComplete event) and that Flux emits records as they are inserted in the database. Because of that fact I would think aggregations are not allowed by the database engine with the tailable cursor.
So the aggregation doesn't make sense in a way because on every newly inserted record the aggregation would need to be recomputed. Technically you can do a running aggregation where for every returned record you compute the wanted aggregate record and send it downstream.
One possible solution would be to do the aggregations programmatically on the returned "infinite" Flux:
mongoOperation.tail(query, Structure.class)
.groupBy(Structure::id) // create independent Fluxes based on id
.flatMap(groupedFlux ->
groupedFlux.scan((result, nextStructure) -> { // scan is like reduce but emits intermediate results
log.info("intermediate result is: {}", result);
if (result.getLastUpdate() > nextStructure.getLastUpdate()) {
return result;
} else {
result.setLastUpdate(nextStructure.getLastUpdate());
return result;
}
}));
On the other hand you should probably revisit your use case and what you need to accomplish here and see if something other than capped collection should be used or maybe the aggregation part is redundant (i.e. if newly inserted records always have the lastUpdate property larger then the previous record).

How to extract the creation date of _id and add it as a new field using the aggregation framework?

I have been trying for a while to extract the insertion date of a mongodb document and add it as a new field to the same document.
I'm trying to do it using the mongo and mongo shell aggregation framework without getting good results.
Here is my query
db.getCollection('my_collection').aggregate(
[
{
$match: {
MY QUERY CRITERIA
}
},
{
$addFields: { "insertTime": "$_id.getTimestamp()" }
}
]
)
I am trying to extracr insertion time from _id using the function getTimestamp() but for sure there is somtehing about aggregation framework syntax that I am missing because I can not do what I am trying to do in my query.
This works perfect:
ObjectId("5c34f746ccb26800019edd53").getTimestamp()
ISODate("2019-01-08T19:17:26Z")
But this does not work at all:
"$_id.getTimestamp()"
What I am missing?
Thanks in advance

$elemMatch and $eq equivalent in spring data mongodb

I have a following query for my mongodb. How to translate it to the equivalent code in spring data mongodb:
db.getCollection('account').find({
colorList: {$elemMatch: {
$eq:"577b"
}
}
})
one of the account collection is shown as below:
{
"_id" : ObjectId("133b6ca05e7c058819ab6e6c"),
"fleetList" : [
"577b",
"123b"
]
}
Instead of using $elemMatch and $eq, you can use $in for your query too. This query makes exactly that your query does:
db.account.find({ "colorList": { $in: ["577b"] } });
And for spring-data-mongodb method for this query is:
List<Account> findByColorListIn(List<String> colorIds); //In your case colorIds list has one element only.
If you want to stick with your query:
#Query("{'colorList': {\$elemMatch: {\$eq: ?0}}}")
List<Account> findByColorList(String colorId)

Aggregate not behaving in Meteor as in Mongo

This is the query that I'm trying to run. If I run it on the Mongo console I get
meteor:PRIMARY> db.keywords_o2m.aggregate({$match:{keyword:{$in:['sql']}}},{$unwind:'$synonym'},{$group:{_id:0,kw:{$addToSet:'$synonym'}}});
{ "_id" : 0, "kw" : [ "database" ] }
However, if I copypaste it and try to run it on Meteor calling Meteor.call('getAllKeywordSynonyms',kw,function(err,data){...}); with this code
if(Meteor.isServer){
Meteor.methods({
'getAllKeywordSynonyms':function(keyword){
console.log("keywordO2M aggregate");
console.log(keywordO2M.aggregate({$match:{keyword:{$in:['sql']}}},{$unwind:'$synonym'},{$group:{_id:0,kw:{$addToSet:'$synonym'}}}));
}
)};
}
I get
I20151220-12:49:38.197(-8)? keywordO2M aggregate
I20151220-12:49:38.197(-8)? [ { _id: 5676fe5a17aeddb799dc4ef8,
I20151220-12:49:38.197(-8)? keyword: 'sql',
I20151220-12:49:38.197(-8)? synonym: 'database' } ]
It looks like it ran the $match and ignored the $unwind and $group.
I've tried using meteorhacks:aggregate and monbro:mongod-mapreduce-aggregation, but no difference.
What am I doing wrong?
Meteor is not as forgiving with notation.
With the aggregation function, the pipeline stages need to all be passed as a single parameter in an array, so the correct syntax would be
console.log(keywordO2M.aggregate([{$match:{keyword:{$in:['sql']}}},{$unwind:'$synonym'},{$group:{_id:0,kw:{$addToSet:'$synonym'}}}]));
Note the square brackets so that only one parameter gets passed to aggregate.

Mongodb aggregation in mongo command prompt

I have the following code based upon this question
How to efficiently perform "distinct" with multiple keys?:
collection = db.products;
result = collection.aggregate(
[
{"$group": { "_id": { "P1 Connection": "$p1c", "P1 Size": "$p1s" } } },
{"$match" : {"parentGUID":ObjectId("5509b246c519ce4b900138a3")}}
]
)
printjson(result);
The printjson statement only prints a bunch of code, and not an object. I also tried result() but that got the following error:
> result()
2015-10-29T10:31:14.892-0400 TypeError: Property 'result' of object #<Object> is not a function
How do I get the results of this aggregation? It looks like it may be possible to do this if I put my code in a file and run that, but I am having a hard time believing that there is no quick and dirty way to run this query in the mongodb command prompt.
Move the $match pipeline step to the very beginning, this will filter the documents that get into the pipeline and the $group pipeline stage will then run the pipeline with the correct documents. Since MongoDB 2.6 adds support for returning a cursor for the aggregate() method, you would need to iterate over the cursor using the forEach() method and access the documents, as in the following example:
var pipeline = [
{"$match" : {"parentGUID":ObjectId("5509b246c519ce4b900138a3")}},
{"$group": { "_id": { "P1 Connection": "$p1c", "P1 Size": "$p1s" } } }
];
var results = db.products.aggregate( pipeline );
results.forEach(printjson);