Mongodb aggregation in mongo command prompt - mongodb

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);

Related

Mongodb aggregate $count

I would like to count the number of documents returned by an aggregation.
I'm sure my initial aggregation works, because I use it later in my programm. To do so I created a pipeline variable (here called pipelineTest, ask me if you want to see it in detail, but it's quite long, that's why I don't give the lines here).
To count the number of documents returned, I push my pipeline with :
{$count: "totalCount"}
Now I would like to get (or log) totalCount value. What should I do ?
Here is the aggregation :
pipelineTest.push({$count: "totalCount"});
cursorTest = collection.aggregate(pipelineTest, options)
console.log(cursorTest.?)
Thanks for your help, I read lot and lot doc about aggregation and I still don't understand how to read the result of an aggregation...
Assuming you're using async/await syntax - you need to await on the result of the aggregation.
You can convert the cursor to an array, get the first element of that array and access totalCount.
pipelineTest.push({$count: "totalCount"});
cursorTest = await collection.aggregate(pipelineTest, options).toArray();
console.log(cursorTest[0].totalCount);
Aggregation
db.mycollection.aggregate([
{
$count: "totalCount"
}
])
Result
[ { totalCount: 3 } ]
Your Particulars
Try the following:
pipelineTest.push({$count: "totalCount"});
cursorTest = collection.aggregate(pipelineTest, options)
console.log(cursorTest.totalCount)

Spring Mongodata Aggregation query

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"));

runCommand vs aggregate method to do aggregation

To run aggregation query it is possible to use either of these:
db.collectionName.aggregate(query1);
OR
db.runCommand(query2)
But I noticed something bizarre this morning. While this:
db.runCommand(
{
"aggregate":"collectionName",
allowDiskUse: true,
"pipeline":[
{
"$match":{
"field":param
}
}
]
});
fails with error:
{
"ok" : 0.0,
"errmsg" : "aggregation result exceeds maximum document size (16MB)",
"code" : 16389,
"codeName" : "Location16389"
}
This:
db.collectionName.aggregate([
{
$match: {
field: param
}
}
])
is working (gives the expected aggregation result).
How is this possible?
Well the difference is of course that the .aggregate() method returns a "cursor", where as the options you are providing to runCommand() you are not. This actually was the legacy form which returned the response as a single BSON document with all it's limitations. Cursors on the other hand do not have the limitation.
Of course you can use the runCommand() method to "make your own cursor" with the shell, since after-all that is exactly what the .aggregate() method is doing "under the covers". The same goes for all drivers, which essentially invoke the database command for everything.
With the shell, you can transform your request like this:
var cmdRes = db.runReadCommand({
"aggregate": "collectionName",
"allowDiskUse": true,
"pipeline":[
{
"$match":{
"field":param
}
}
],
"cursor": { "batchSize": 25 }
});
var cursor = new DBCommandCursor(db, cmdRes);
cursor.next(); // will actually iterate the cursor
If you really want to dig into it then type in db.collectionName.aggregate without the parenthesis () so you actually print the function definition. This will show you some other function calls and you can dig further into them and eventually see what is effectively the lines shown above, amongst a lot of other stuff.
But the way you ran it, it's a "single BSON Document" response. Run it the way shown here, and you get the same "cursor" response.

Within a mongodb $match, how to test for field MATCHING , rather than field EQUALLING

Can anyone tell me how to add a $match stage to an aggregation pipeline to filter for where a field MATCHES a query, (and may have other data in it too), rather than limiting results to entries where the field EQUALS the query?
The query specification...
var query = {hello:"world"};
...can be used to retrieve the following documents using the find() operation of MongoDb's native node driver, where the query 'map' is interpreted as a match...
{hello:"world"}
{hello:"world", extra:"data"}
...like...
collection.find(query);
The same query map can also be interpreted as a match when used with $elemMatch to retrieve documents with matching entries contained in arrays like these documents...
{
greetings:[
{hello:"world"},
]
}
{
greetings:[
{hello:"world", extra:"data"},
]
}
{
greetings:[
{hello:"world"},
{aloha:"mars"},
]
}
...using an invocation like [PIPELINE1] ...
collection.aggregate([
{$match:{greetings:{$elemMatch:query}}},
]).toArray()
However, trying to get a list of the matching greetings with unwind [PIPELINE2] ...
collection.aggregate([
{$match:{greetings:{$elemMatch:query}}},
{$unwind:"$greetings"},
]).toArray()
...produces all the array entries inside the documents with any matching entries, including the entries which don't match (simplified result)...
[
{greetings:{hello:"world"}},
{greetings:{hello:"world", extra:"data"}},
{greetings:{hello:"world"}},
{greetings:{aloha:"mars"}},
]
I have been trying to add a second match stage, but I was surprised to find that it limited results only to those where the greetings field EQUALS the query, rather than where it MATCHES the query [PIPELINE3].
collection.aggregate([
{$match:{greetings:{$elemMatch:query}}},
{$unwind:"$greetings"},
{$match:{greetings:query}},
]).toArray()
Unfortunately PIPELINE3 produces only the following entries, excluding the matching hello world entry with the extra:"data", since that entry is not strictly 'equal' to the query (simplified result)...
[
{greetings:{hello:"world"}},
{greetings:{hello:"world"}},
]
...where what I need as the result is rather...
[
{greetings:{hello:"world"}},
{greetings:{hello:"world"}},
{greetings:{"hello":"world","extra":"data"}
]
How can I add a second $match stage to PIPELINE2, to filter for where the greetings field MATCHES the query, (and may have other data in it too), rather than limiting results to entries where the greetings field EQUALS the query?
What you're seeing in the results is correct. Your approach is a bit wrong. If you want the results you're expecting, then you should use this approach:
collection.aggregate([
{$match:{greetings:{$elemMatch:query}}},
{$unwind:"$greetings"},
{$match:{"greetings.hello":"world"}},
]).toArray()
With this, you should get the following output:
[
{greetings:{hello:"world"}},
{greetings:{hello:"world"}},
{greetings:{"hello":"world","extra":"data"}
]
Whenever you're using aggregation in MongoDB and want to create an aggregation pipeline that yields documents you expect, you should always start your query with the first stage. And then eventually add stages to monitor the outputs from subsequent stages.
The output of your $unwind stage would be:
[{
greetings:{hello:"world"}
},
{
greetings:{hello:"world", extra:"data"}
},
{
greetings:{hello:"world"}
},
{
greetings:{aloha:"mars"}
}]
Now if we include the third stage that you used, then it would match for greetings key that have a value {hello:"world"} and with that exact value, it would find only two documents in the pipeline. So you would only be getting:
{ "greetings" : { "hello" : "world" } }
{ "greetings" : { "hello" : "world" } }

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.