Mongodb Aggregation to count element pairs and individual elements - mongodb

I have following data:
{ "id" : 1, "lsPairs" :[{"location" : "L0", "service" : "S0" }]}
{ "id" : 2, "lsPairs" :[{"location" : "L0", "service" : "S0" },{"location" : "L1", "service" : "S1"}]}
{ "id" : 3, "lsPairs" :[{"location" : "L0", "service" : "S0" },{"location" : "L1", "service" : "S1"}, {"location" : "L2", "service" : "S2"}]}
{ "id" : 4, "lsPairs" :[{"location" : "L0", "service" : "S0" },{"location" : "L1", "service" : "S1"},{"location" : "L2", "service" : "S2"}, {"location" : "L3", "service" : "S3"}]}`
I want to get location count, service count and (location,service) pair count
{ "_id" : "L3" , "count" : 1}
{ "_id" : "L2" , "count" : 2}
{ "_id" : "L1" , "count" : 3}
{ "_id" : "L0" , "count" : 4}
{ "_id" : "S3" , "count" : 1}
{ "_id" : "S2" , "count" : 2}
{ "_id" : "S1" , "count" : 3}
{ "_id" : "S0" , "count" : 4}
{ "_id" : { "loc" : "L2" , "srv" : "S2"} , "count" : 2}
{ "_id" : { "loc" : "L1" , "srv" : "S1"} , "count" : 3}
{ "_id" : { "loc" : "L3" , "srv" : "S3"} , "count" : 1}
{ "_id" : { "loc" : "L0" , "srv" : "S0"} , "count" : 4}`
Now I run group function three times, group different id.
Any idea for using one group to get these result?

You will need to deconstruct the array with $unwind then $group the documents.
collection.aggregate([
{ $unwind: "$lsPairs" },
{ $group: {
_id: {
"loc": "$lsPairs.location",
"srv": "$lsPairs.service"
},
"count": { $sum: 1 }
}}
])
Output
{ "_id" : { "loc" : "L3", "srv" : "S3" }, "count" : 1 }
{ "_id" : { "loc" : "L2", "srv" : "S2" }, "count" : 2 }
{ "_id" : { "loc" : "L1", "srv" : "S1" }, "count" : 3 }
{ "_id" : { "loc" : "L0", "srv" : "S0" }, "count" : 4 }

Keep the first round location-service pair to a collection and reused it.
db.locservice.aggregate([ {$unwind:"$lsPairs"},
{$group:{_id:"$lsPairs",count: { $sum: 1}}},
{$sort:{_id:1}},
{$out:"lsp"} ])
Take location from temp collection and group it.
db.lsp.aggregate([{$project:{_id:0, loc:"$_id.location", count:1}},
{$group:{_id:"$loc", cnt:{$sum:"$count"}}}, {$sort:{_id:1}} ])
Take service from temp collection and group it.
db.lsp.aggregate([{$project:{_id:0, srv:"$_id.service", count:1}},
{$group:{_id:"$srv", cnt:{$sum:"$count"}}}, {$sort:{_id:1}} ])

The following I add location and service to array, can I group two array same time
db.locservice.aggregate([ {$unwind:"$lsPairs"},
{$group:{_id:"$lsPairs",count: { $sum: 1},
locs:{$push:{item:"$lsPairs.location"}},
srvs:{$push:{item:"$lsPairs.service"}}}},
{$project:{count:1, locs:1, srvs:1}} ])
{ "_id" : { "location" : "L3", "service" : "S3" }, "count" : 1, "locs" : [ { "item" : "L3" } ], "srvs" : [ { "item" : "S3" } ] }
{ "_id" : { "location" : "L2", "service" : "S2" }, "count" : 2, "locs" : [ { "item" : "L2" }, { "item" : "L2" } ], "srvs" : [ { "item" : "S2" }, { "item" : "S2" } ] }
{ "_id" : { "location" : "L1", "service" : "S1" }, "count" : 3, "locs" : [ { "item" : "L1" }, { "item" : "L1" }, { "item" : "L1" } ], "srvs" : [ { "item" : "S1" }, { "item" : "S1" }, { "item" : "S1" } ] }
{ "_id" : { "location" : "L0", "service" : "S0" }, "count" : 4, "locs" : [ { "item" : "L0" }, { "item" : "L0" }, { "item" : "L0" }, { "item" : "L0" } ], "srvs" : [ { "item" : "S0" }, { "item" : "S0" }, { "item" : "S0" }, { "item" : "S0" } ] }

Related

How to find Immediate Child of a node in mongo db

I have following Collection
Location Collection
[
{id : 1 name : 'l1' , 'location' : pune , parentLocation : Maharashstra},
{id : 2 name : 'l2' , 'location' : nashik , parentLocation : Maharashstra},
{id : 3 name : 'l3' , 'location' : mumbai , parentLocation : Maharashstra},
{id : 4 name : 'l4' , 'location' : Maharashstra , parentLocation : India},
{id : 5 name : 'l5' , 'location' : India , parentLocation : null}
]
Is any query we throw and get immediate node of location using above data.
Example.
When I said India it should be return
India
|---Maharashtra
|---Pune
|---..
|---Nashik
|---Mumbai
Thank you
Using Aggregation framework we can get this desired result.
The below query gives us the result and in this query I have used $lookup, $match, $project
db.location.aggregate([
{
$lookup: {
from: "location",
localField: "location",
foreignField: "parentLocation",
as:"Result"
}
},
{$match:{ "Result": {$exists:true, $ne:[]}, parentLocation: {$ne: null} }},
{$project :{ parentLocation:1, location:1, "Result.location":1}},
{$match: {location: "Maharashstra"}}
])
Documents in my collection
{ "_id" : ObjectId("5b83e4860c35ef57411a575b"), "id" : 1, "name" : "l1", "location" : "pune", "parentLocation" : "Maharashstra" }
{ "_id" : ObjectId("5b83e4860c35ef57411a575c"), "id" : 2, "name" : "l2", "location" : "nashik", "parentLocation" : "Maharashstra" }
{ "_id" : ObjectId("5b83e4860c35ef57411a575d"), "id" : 3, "name" : "l3", "location" : "mumbai", "parentLocation" : "Maharashstra" }
{ "_id" : ObjectId("5b83e4860c35ef57411a575e"), "id" : 4, "name" : "l4", "location" : "Maharashstra", "parentLocation" : "India" }
{ "_id" : ObjectId("5b83e4860c35ef57411a575f"), "id" : 5, "name" : "l5", "location" : "India", "parentLocation" : null }
{ "_id" : ObjectId("5b83fec90c35ef57411a5760"), "id" : 6, "name" : "l6", "location" : "Chennai", "parentLocation" : "Tamilnadu" }
{ "_id" : ObjectId("5b83fec90c35ef57411a5761"), "id" : 7, "name" : "l7", "location" : "Trichy", "parentLocation" : "Tamilnadu" }
{ "_id" : ObjectId("5b83fec90c35ef57411a5762"), "id" : 8, "name" : "l8", "location" : "Alapuzha", "parentLocation" : "Kerala" }
{ "_id" : ObjectId("5b83fec90c35ef57411a5763"), "id" : 9, "name" : "l9", "location" : "Kerala", "parentLocation" : "India" }
{ "_id" : ObjectId("5b83fec90c35ef57411a5764"), "id" : 10, "name" : "l10", "location" : "Tamilnadu", "parentLocation" : "India" }
After executing the above query, the result is
{
"_id" : ObjectId("5b83e4860c35ef57411a575e"),
"location" : "Maharashstra",
"parentLocation" : "India",
"Result" : [
{
"location" : "pune"
},
{
"location" : "nashik"
},
{
"location" : "mumbai"
}
]
}
If we cut down the last $match in our query, then it will return the complete the grouping of states.
db.location.aggregate([
{
$lookup: {
from: "location",
localField: "location",
foreignField: "parentLocation",
as:"Result"
}
},
{$match:{ "Result": {$exists:true, $ne:[]}, parentLocation: {$ne: null} }},
{$project :{ parentLocation:1, location:1, "Result.location":1}}
])
The result of the above query is
{
"_id" : ObjectId("5b83e4860c35ef57411a575e"),
"location" : "Maharashstra",
"parentLocation" : "India",
"Result" : [
{
"location" : "pune"
},
{
"location" : "nashik"
},
{
"location" : "mumbai"
}
]
}
{
"_id" : ObjectId("5b83fec90c35ef57411a5763"),
"location" : "Kerala",
"parentLocation" : "India",
"Result" : [
{
"location" : "Alapuzha"
}
]
}
{
"_id" : ObjectId("5b83fec90c35ef57411a5764"),
"location" : "Tamilnadu",
"parentLocation" : "India",
"Result" : [
{
"location" : "Chennai"
},
{
"location" : "Trichy"
}
]
}

Nested array count without unwind?

Is it possible to count a nested array without using $unwind in MongoDB? If possible then help me out. My json file is like this:
{
"_id" : ObjectId("57307906f051147d5317984e"),
"user" : [
{
"firstName" : "chetan",
"lastName" : "kumar",
"age" : 23,
"like" : [
{
"bookname" : "mongo"
},
{
"bookname" : "php"
},
{
"bookname" : "java"
}
]
},
{
"firstName" : "nepolean",
"lastName" : "dang",
"age" : 26,
"like" : [
{
"bookname" : "mongo"
},
{
"bookname" : "php"
}
]
},
{
"firstName" : "Raj",
"lastname" : "kumar",
"age" : 26,
"like" : [
{
"bookname" : "mongo"
}
]
}
],
"sales" : [
{
"firstName" : "ashu",
"lastName" : "jha",
"age" : 27
}
]
}
If I use $unwind then I can easily find out the result like this:
db.userdetail.aggregate([
{"$unwind": "$user"},
{"$project": {
"likecount": { "$size": "$user.like" }
}}
]).pretty()
{ "_id" : ObjectId("57307906f051147d5317984e"), "likecount" : 3 }
{ "_id" : ObjectId("57307906f051147d5317984e"), "likecount" : 2 }
{ "_id" : ObjectId("57307906f051147d5317984e"), "likecount" : 1 }

MongoDB Aggregation - return default value for documents that don't match query

I'm having trouble figuring out the right aggregation pipe operations to return the results I need.
I have a collection similar to the following :-
{
"_id" : "writer1",
"Name" : "writer1",
"Website" : "website1",
"Reviews" : [
{
"Film" : {
"Name" : "Jurassic Park",
"Genre" : "Action"
},
"Score" : 4
},
{
"Technology" : {
"Name" : "Mad Max",
"Genre" : "Action"
},
"Score" : 5
}
]
}
{
"_id" : "writer2",
"Name" : "writer2",
"Website" : "website1",
"Reviews" : [
{
"Technology" : {
"Name" : "Mad Max",
"Genre" : "Action"
},
"Score" : 5
}
]
}
And this is my aggregation so far : -
db.writers.aggregate([
{ "$unwind" : "$Reviews" },
{ "$match" : { "Reviews.Film.Name" : "Jurassic Park" } },
{ "$group" : { "_id" : "$Website" , "score" : { "$avg" : "$Reviews.Score" },
writers :{ $push: { name:"$Name", score:"$Reviews.Score" } }
}}
])
This returns only writers who have a review of the matching film and also only websites that have at least 1 writer who has reviewed the film,
however, I need to return all websites containing a list of their all writers, with a score of 0 if they haven't written a review for the specified film.
so, I am currently getting : -
{ "_id" : "website1", "score" : 4, "writers" : [ { "name" : "writer1", "score" : 4 } ] }
When I actually need : -
{ "_id" : "website1", "score" : 2, "writers" : [ { "name" : "writer1", "score" : 4 },{ "name" :"writer2", "score" : 0 } ] }
Can anyone point me in the right direction?
Cheers

MongoDB groupby query

I have colletions containing records like
{ "type" : "me", "tid" : "1" }
{ "type" : "me", "tid" : "1" }
{ "type" : "me", "tid" : "1" }
{ "type" : "you", "tid" : "1" }
{ "type" : "you", "tid" : "1" }
{ "type" : "me", "tid" : "2" }
{ "type" : "me", "tid" : "2"}
{ "type" : "you", "tid" : "2"}
{ "type" : "you", "tid" : "2" }
{ "type" : "you", "tid" : "2"}
I have want result like below
[
{"tid" : "1","me" : 3,"you": 2},
{"tid" : "2","me" : 2,"you": 3}
]
I have tried group and; aggregate queries doesn't get required result format.
below is the group query.
db.coll.group({
key: {tid : 1,type:1},
cond: { tid : { "$in" : [ "1","2"]} },
reduce: function (curr,result) {
result.total = result.total + 1
},
initial: { total : 0}
})
it result is like
[
{"tid" : "1", "type" : "me" ,"total": 3 },
{"tid" : "1","type" : "you" ,"total": 2 },
{"tid" : "2", "type" : "me" ,"total": 2 },
{"tid" : "2","type" : "you" ,"total": 3 }
]
following is aggregate query
db.coll.aggregate([
{$match : { "tid" : {"$in" : ["1","2"]}}},
{$group : { _id : {tid : "$tid",type : "$type"},total : {"$sum" : 1}}}
])
gives following result
{
"result" :
[
{"_id" : {"tid" : "1","type" : "me"},"total" : 3},
{"_id" : {"tid" : "2","type" : "me" },"total" : 2},
{"_id" : {"tid" : "2","type" : "you"},"total" : 3}
]
"ok" : 1
}
it is possible to obtain I specified result or I have to do some manipulation in my code.
Thanks
If you change your aggregation to this:
db.so.aggregate([
{ $match : { "tid" : { "$in" : ["1", "2"] } } },
{ $group : {
_id : { tid : "$tid", type : "$type" },
total : { "$sum" : 1 }
} },
{ $group : {
_id : "$_id.tid",
values: { $push: { type: "$_id.type", total: '$total' } }
} }
])
Then your output is:
{
"result" : [
{
"_id" : "1",
"values" : [
{ "type" : "you", "total" : 2 },
{ "type" : "me", "total" : 3 }
]
},
{
"_id" : "2",
"values" : [
{ "type" : "me", "total" : 2 },
{ "type" : "you", "total" : 3 }
]
}
],
"ok" : 1
}
Although that is not the same as what you want, it is going to be the closest that you can get. And in your application, you can easily pull out the values in the same was as with what you would like to get out of it.
Just keep in mind, that in general you can not promote a value (you, me) to a key — unless your key is of a limited set (3-4 items max).

Do $sort works for sub array document

I have a collection which has a field of array kind. I want to sort on the basis of a field of sub-array but Mongo is not sorting the data.
My collection is:
{
"_id" : ObjectId("51f1fcc08188d3117c6da351"),
"cust_id" : "abc123",
"ord_date" : ISODate("2012-10-03T18:30:00Z"),
"status" : "A",
"price" : 25,
"items" : [{
"sku" : "ggg",
"qty" : 7,
"price" : 2.5
}, {
"sku" : "ppp",
"qty" : 5,
"price" : 2.5
}]
}
My Query is:
db.orders.aggregate([
{ "$unwind" : "$items"} ,
{ "$match" : { }} ,
{ "$group" : { "items" : { "$addToSet" : { "sku" : "$items.sku"}} , "_id" : { }}} ,
{ "$sort" : { "items.sku" : 1}} ,
{ "$project" : { "_id" : 0 , "items" : 1}}
])
Result is:
"result" : [
{
"items" : [
{
"sku" : "ppp"
},
{
"sku" : "ggg"
}
]
}
],
"ok" : 1
}
Whereas "sku":"ggg" should come first when it is ascending.
You weant to do the sort BEFORE you regroup:
db.orders.aggregate([
{ "$unwind" : "$items"} ,
{ "$sort" : { "items.sku" : 1}},
{ "$match" : { }} ,
{ "$group" : { "items" : { "$push" : { "sku" : "$items.sku"}} , "_id" : null}} ,
{ "$project" : { "_id" : 0 , "items" : 1}}
])