I have a collection peopleColl containing records with people data. Each record is uniquely indexed by id and has a managers field of type array.
Example:
{
id: 123,
managers: [456, 789]
},
{
id: 321,
managers: [555, 789]
}
I want to write a single query to find all people with the same manager, for several ids (managers). So given [456, 555, 789] the desired output would be:
{
456: 1,
555: 1,
789: 2
}
I can do it (slowly) in a for-loop in Python as follows:
idToCount = {id: peopleColl.count({"managers": id}) for id in ids}
Edit: I am primarily interested in solutions <= MongoDB 3.4
You can try below aggregation in mongodb 3.4.4 and above
db.collection.aggregate([
{ "$unwind": "$managers" },
{ "$group": { "_id": "$managers", "count": { "$sum": 1 }}},
{ "$group": {
"_id": null,
"data": {
"$push": {
"k": { "$toLower": "$_id" },
"v": "$count"
}
}
}},
{ "$replaceRoot": { "newRoot": { "$arrayToObject": "$data" }}}
])
Output
[
{
"456": 1,
"555": 1,
"789": 2
}
]
You can try below pipeline.
db.collection.aggregate([
{ "$unwind": "$managers" },
{ "$group": { "_id": "$managers", "count": { "$sum": 1 }}}
])
Output:
{'_id': 456, 'count': 1},
{'_id': 555, 'count': 1},
{'_id': 789, 'count': 2}
So you can loop through and create the Id-Count mapping
result = db.collection.aggregate([
{ "$unwind": "$managers" },
{ "$group": { "_id": "$managers", "count": { "$sum": 1 }}}
])
iD_Count = {}
result.forEach(function(d, i) {
iD_Count[d._id] = d.count;
})
iD_Count:
{
456: 1,
555: 1,
789: 2
}
You can try below aggregation in 3.6.
db.colname.aggregate([
{"$unwind":"$managers"},
{"$group":{"_id":"$managers","count":{"$sum":1}}},
{"$group":{
"_id":null,
"managerandcount":{"$mergeObjects":{"$arrayToObject":[[["$_id","$count"]]]}}
}},
{"$replaceRoot":{"newRoot":"$managerandcount"}}
])
Related
I have a mongodb collection:
{
_id: ... ,
userId: ...,
gadgets: 3
}
I need to find all users who have at least 5 records in the collection. For every such user, I then need to sort the number of gadgets in descending order and get the 5th value. And, I am still at mongodb 4.4 and so, can't do $sortArray.
For example, if user has records with 14, 10, 7, 7, 5 gadgets (in any order), result should be:
{
userId: ....
qualifyingGadgets: 5
}
I don't have a MongoDB server at version 4.4 to test, but I think you'll have all these aggregation operations available.
db.users.aggregate([
{
"$group": {
"_id": "$userId",
"gadgets": {"$push": "$gadgets"}
}
},
{
"$match": {
"$expr": {
"$gte": [{"$size": "$gadgets"}, 5]
}
}
},
{
"$unwind": "$gadgets"
},
{
"$sort": {"gadgets": -1}
},
{
"$group": {
"_id": "$_id",
"gadgets": {"$push": "$gadgets"}
}
},
{
"$project": {
"_id": 0,
"userId": "$_id",
"qualifyingGadgets": {"$arrayElemAt": ["$gadgets", 4]}
}
}
])
Try it on mongoplayground.net.
Below is my collection
[{'_id': ObjectId('603e9cc2784fa0d80d8672cd'),
'name': 'balaji',
'items': [{'price': 1, 'price_range': 'A'},
{'price': 6, 'price_range': 'B'},
{'price': 4, 'price_range': 'C'}]}]
So in the above collection, we can see only one record and it contains an array with name items and this array contains objects with price and price_range attributes, may I know how to get the sum of all the prices in this array please, I tried with below query and it did not work
aggregation_string = [{"$match":{"name": "balaji"
}},{ "$group": {
"_id": None,
"count": { "$sum": "$items.price" }
}}]
db.sample_collection1.aggregate(aggregation_string)
and I am getting count as 0. Can someone please help me here.
In your example since you don't need to group the objects you can simply project the sum this way :
db.collection.aggregate([
{
"$match": {
"name": "balaji"
}
},
{
"$project": {
"name": 1,
"priceTotal": {
"$sum": "$items.price"
}
}
},
])
It should works from mongoDB 3.2 and I think it's the best way.
But if you absolutely need to use the $group, you have to do it this way:
db.collection.aggregate([
{
"$match": {
"name": "balaji"
}
},
{
"$group": {
"_id": null,
"count": {
"$sum": {
"$sum": "$items.price"
}
}
}
}
])
It was your $sum query that was incomplete.
Or with the unwind operator to avoid doing twice the $sum :
db.collection.aggregate([
{
"$match": {
"name": "balaji"
}
},
{
"$unwind": "$items",
},
{
"$group": {
"_id": null,
"count": {
"$sum": "$items.price"
}
}
}
])
Please, I need a help for aggregate the status (2 and 3) count from year in a MongoDb nested document.
My Json:
[
{
"_id":1,
"name":"aaa",
"calendars":[
{
"year":2012,
"status":2
},
{
"year":2013,
"status":1
},
{
"year":2014,
"status":3
}
]
},
{
"_id":2,
"name":"bbb",
"calendars":[
{
"year":2012,
"status":1
},
{
"year":2013,
"status":1
},
{
"year":2014,
"status":2
}
]
}
]
This is my mongodb code:
db.mycol.aggregate([{"$match": {"calendars.status": {"$in": [2, 3]}}}, {"$unwind": "$calendars"},
{"$group": {_id: {"year": "$calendars.year"},
total: {"$sum": 1}
}},
{"$project": {
"year": "$_id.year",
"total": "$total", "_id": 0}},
])
And I need the result:
year total
2012 1
2013 0
2014 2
Thanks
I will first unwind the array object and match accordingly,
db.test.aggregate([
{
"$unwind": "$calendars"
},
{
"$match": {
"calendars.status": {
"$in": [
2,
3
]
}
}
},
{
"$group": {
_id: {
"year": "$calendars.year"
},
total: {
"$sum": 1
}
}
},
{
"$project": {
"year": "$_id.year",
"total": "$total",
"_id": 0
}
},
])
Now i get this table(it can't be more than two kinds of {A,B,C} to appear in the same data at the same time.):
{_id:1,A:a}
{_id:2,B:b}
{_id:3,C:a}
{_id:4,A:a}
{_id:5,A:b}
{_id:6,A:c}
{_id:7,C:a}
How to get this result?
a:4
b:2
c:1
you can get this result with mongodb aggregation framework,
first, you'll need to add all the value in a single field, and the perform a $group on that field:
db.collection.aggregate([{
"$project": {
"v": ["$A", "$B", "$C"]
}
}, {
"$unwind": "$v"
}, {
"$match": {
"v": {
"$ne": null
}
}
}, {
"$group": {
"_id": "$v",
"count": {
"$sum": 1
}
}
}])
result:
[
{
"_id": "c",
"count": 1
},
{
"_id": "b",
"count": 2
},
{
"_id": "a",
"count": 4
}
]
you can try it here: mongoplayground.net/p/rGHUPWsw2ee
I have a collection of documents with the following structure:
{
_id: 1,
array: [
{value: 10 },
{value: 11 },
{value: 12 }
]
}
I want make an aggregate query on the collection:
get the proportion of each item. (i.e. for example the proportion of item 1 would be value of item 1 divided by the sum of the values of all three items.
Note: I want to do this within a single query.
The basic idea here is to $unwind the array, $group the document and then apply to each array member. This works better for MongoDB 2.6 or greater due to the $map operator:
db.collection.aggregate([
{ "$unwind": "$array" },
{ "$group": {
"_id": "$_id",
"array": { "$push": "$array" },
"total": { "$sum": "$array.value" }
}},
{ "$project": {
"array": {
"$map": {
"input": "$array",
"as": "el",
"in": {
"value": "$$el.value",
"prop": {
"$divide": [ "$$el.value", "$total" ]
}
}
}
}
}}
])
Or with earlier versions:
db.collection.aggregate([
{ "$unwind": "$array" },
{ "$group": {
"_id": "$_id",
"array": { "$push": "$array" },
"total": { "$sum": "$array.value" }
}},
{ "$unwind": "$array" },
{ "$group": {
"_id": "$_id",
"array": {
"$push": {
"value": "$array.value",
"prop": {
"$divide": [ "$array.value", "$total" ]
}
}
}
}}
])
In either case, if you are not actually "aggregating" anything beyond the document, it is far more efficient to do this calculation in client code. The $unwind here can get very costly due to what it does.
Also if you just stored the "total" as another element, then the simple $project is all that you need, which comes at very little cost by itself. Keeping a total on updates is just simple usage of the $inc operator as you $push new elements to the array.
Here is the aggregation pipeline you need:
[
{$unwind: '$array'},
{
$group: {
_id: '$_id',
array: {$push: '$array'},
sum: {$sum: '$array.value'}
}
},
{$unwind: '$array'},
{
$project: {
_id: 1,
'array.value': 1,
'array.proportion': {
$divide: ['$array.value', '$sum']
}
}
}
]