mongodb aggregate,groupby list in object - mongodb

I am looking at performing a group by upon a given value within a nest object. For example, my document structure is as follows:(Sorry about the limited data)
"_id" : "92623ba7-4ca5-46c7-8d76-c4bc8387ea00",
"Status" : 2.0,
"UploadDate" : ISODate("2018-10-30T12:01:19.619Z"),
"UpdateDate" : ISODate("2018-10-30T12:01:19.619Z"),
"Request" : "abc123",
"ShowCaseHtml" : "",
"PageResult" : [ ],
"ProductFilter" : {
"_id" : "9430fb88-2deb-4508-8422-dd67c3a35205",
"Status" : 2,
"UploadDate" : ISODate("2018-11-05T10:52:37.122Z"),
"UpdateDate" : ISODate("2018-11-05T10:52:37.122Z"),
"ProductPageType" : 0,
"Categories" : [ ],
"PriceRanges": [ ],
"Brands" : [ ],
"Sellers": [ ],
"StarRatings" : [ ],
},
"BreadCrumbs"[ ]
Result of my query returns only a part of object, what i need is full object but filterd version, here is my query
db.getCollection('dbName').aggregate(
{$match: {"Request": "abc123"}},
{$project :
{"PageResult":1,"ProductFilter":1,"BreadCrumbs":1,"ShowCaseHtml":1}},
{$unwind: "$PageResult"},
{$sort:{'PageResult.MarketPlaceProductPrice.ProductPrice':1}},
{$skip: 2},
{$limit: 3},
{$group: {"_id": "$_id", "PageResult": {$push: "$PageResult"}}},
{$project :
{"PageResult.MarketPlaceProductPrice.ProductPrice":1,"_id":0}}
)
Result of the query is like,
{
"PageResult" : [
{
"MarketPlaceProductPrice" : {
"ProductPrice" : 1519.00
}
},
{
"MarketPlaceProductPrice" : {
"ProductPrice" : 2749.00
}
},
{
"MarketPlaceProductPrice" : {
"ProductPrice" : 3359.00
}
}
]
}
i need the ProductFilter,BreadCrumbs and ShowCaseHtml areas too, that is why i specified the
{$project :
{"PageResult":1,"ProductFilter":1,"BreadCrumbs":1,"ShowCaseHtml":1}}
but i only have filtered PageResult area, any ideas ?

Using this query solved my problem
db.getCollection('dbName').aggregate(
{$match: {"Request":"abc123"}},
{$project : {"PageResult":1,"ProductFilter":1,"BreadCrumbs":1,"ShowCaseHtml":1}},
{$unwind: "$PageResult"},
{$sort:{'PageResult.MarketPlaceProductPrice.ProductPrice':1}},
{$skip: 2},
{$limit: 3},
{$group: {"_id": {"Request":"$Request","ProductFilter":"$ProductFilter","BreadCrumbs":"$BreadCrumbs","ShowCaseHtml":"$ShowCaseHtml"},"PageResult": {$push : "$PageResult"}
}}
)

Related

How can i access N th level of Sub Document in MongoDB

{
"_id" : ObjectId("5b4d815ad85250f4e502d142"),
"company_Name" : "Rishabh Instruments",
"spaces" : [
{
"_id" : "11",
"name" : "Trishala",
"spaces" : [
{
"_id" : "111",
"name" : "ESL"
},
{
"_id" : "112",
"name" : "IS"
}
]
},
{
"_id" : "12",
"name" : "Riddhi",
"spaces" : [
{}
]
},
{
"name" : "XYZ",
"spaces" : [
{}
]
}
]
}
This is my Document structure in Mongo DB and it may have the sub documents upto N th level so for now I want to access the document named with ESL and IS. so how can i do that i am able to go upto 2nd level with below query so
db.space.aggregate([
{$match: {company_Name: 'Rishabh Instruments'}},
{$unwind: '$spaces'},
{$match: {'spaces.name': 'Trishala'}}
// {$unwind: '$spaces'},
// {$match: {'spaces.name': 'ESL'}}
])
but if i uncomment those two lines then it doesn't return any thing
so can anyone guide me or give any hint.
I tried the below solution and its working as expected i can go to N th level by chaining the key as in my case key is spaces
db.space.aggregate([
{$match: {company_Name: 'Rishabh Instruments'}},
{$unwind: '$spaces'},
{$match: {'spaces.name': 'Trishala'}},
{$unwind: '$spaces.spaces'},
{$match: {'spaces.spaces.name': 'ESL'}}
])
Try this aggregate query with projection to match the sample output you had provided :
db.space.aggregate([
{$match: {company_Name: 'Rishabh Instruments'}},
{$unwind: '$spaces'},
{$match: {'spaces.name': 'Trishala'}},
{$unwind: '$spaces.spaces'},
{$match: {$or : [{'spaces.spaces.name': 'ESL'},{'spaces.spaces.name': 'IS'}]}},
{$project : {_id : 0, _id :'$spaces.spaces._id', name:'$spaces.spaces.name'}}
])
Output :
{ "_id" : "111", "name" : "ESL" }
{ "_id" : "112", "name" : "IS" }

MongoDB: $mod operator in aggregation pipeline

I have a restaurants collection that contains 3772 documents and I am trying to calculate the total number of documents that contain a score in first element of the grades array that's a multiple of 7 using the aggregation framework.
Query:
db.restaurants.aggregate([
{$project: {remainder: {$mod: ["$grades.0.score", 7]},
restaurant_id: 1,
name: 1,
grades: 1
}
},
{$match: {remainder: {$eq: 0}}},
{$group: {_id: null, total: {$sum: 1}}}
])
However, I am getting an error message that's caused by the use of the $mod operator in the $project pipeline stage. The error message is the following:
$mod only supports numeric types, not Array and NumberDouble
However, both $grades.0.score and 7 are integers, right? What should I change to make this query work as intended?
Example document:
{
"_id" : ObjectId("57290430139a4a37132c9e93"),
"address" : {
"building" : "469",
"coord" : [
-73.961704,
40.662942
],
"street" : "Flatbush Avenue",
"zipcode" : "11225"
},
"borough" : "Brooklyn",
"cuisine" : "Hamburgers",
"grades" : [
{
"date" : ISODate("2014-12-30T00:00:00Z"),
"grade" : "A",
"score" : 8
},
{
"date" : ISODate("2014-07-01T00:00:00Z"),
"grade" : "B",
"score" : 23
},
{
"date" : ISODate("2013-04-30T00:00:00Z"),
"grade" : "A",
"score" : 12
},
],
"name" : "Wendy'S",
"restaurant_id" : "30112340"
}
instead of $grades.0.score
put $grades[0].score
in your query.
the above is wrong. see below the correct form. As you want to filter by grades whose first score is a multiple of 7, you aggregation should start like this.
db.restaurants.aggregate([{$match: {"grades.0.score": {$mod: [7, 0]}}},{$group: {_id: null, total: {$sum: 1}}}])
I changed the grade.0.score to 7 and ran the command to check it is working or not, it seems it is working as you wanted.
> db.restaurants.find().pretty();
{
"_id" : 0,
"address" : {
"building" : "469",
"coord" : [
-73.961704,
40.662942
],
"street" : "Flatbush Avenue",
"zipcode" : "11225"
},
"borough" : "Brooklyn",
"cuisine" : "Hamburgers",
"grades" : [
{
"date" : ISODate("2014-12-30T00:00:00Z"),
"grade" : "A",
"score" : 7
},
{
"date" : ISODate("2014-07-01T00:00:00Z"),
"grade" : "B",
"score" : 23
},
{
"date" : ISODate("2013-04-30T00:00:00Z"),
"grade" : "A",
"score" : 12
}
],
"name" : "Wendy'S",
"restaurant_id" : "30112340"
> db.restaurants.aggregate([{$match: {"grades.0.score": {$mod: [7, 0]}}},{$group:{_id:null,count:{$sum:1}}} ])
{ "_id" : null, "count" : 1 }
First: why doesn't it work? Try:
db.restaurants.aggregate([
{$project: {
score0: "$grades.0.score",
restaurant_id: 1,
name: 1
}
}
])
You'll see that score0 returns [0 elements] so it does output an array hence the error message.
Based on this other question Get first element in array and return using Aggregate? (Mongodb), here is a solution to your problem:
db.restaurants.aggregate([
{$unwind: "$grades"},
{$group:{"_id":"$_id","grade0":{$first:"$grades"}}},
{$project: {
remainder: {$mod: ["$grade0.score", 7]},
restaurant_id: 1,
name: 1,
grade0: 1,
}
},
{$match: {remainder: {$eq: 0}}},
{$group: {_id: null, total: {$sum: 1}}}
])

Mongodb aggregation - first create item list and get intersect of items with rating details

I asked the question before. The question
{
"_id" : ObjectId("5539d45ee3cd0e48e99c3fa6"),
"userId" : 1,
"movieId" : 6,
"rating" : 2.0000000000000000,
"timestamp" : 9.80731e+008
}
{
"_id" : ObjectId("5539d45ee3cd0e48e99c1fa7"),
"userId" : 1,
"movieId" : 22,
"rating" : 3.0000000000000000,
"timestamp" : 9.80731e+008
},
{
"_id" : ObjectId("5539d45ee3cd0e48e99c1fa8"),
"userId" : 1,
"movieId" : 32,
"rating" : 2.0000000000000000,
"timestamp" : 9.80732e+008
},
{
"_id" : ObjectId("5539d45ee3cd0e48e99c1fa9"),
"userId" : 2,
"movieId" : 32,
"rating" : 4.0000000000000000,
"timestamp" : 9.80732e+008
},
{
"_id" : ObjectId("5539d45ee3cd0e48e99c1fa3"),
"userId" : 2,
"movieId" : 6,
"rating" : 5.0000000000000000,
"timestamp" : 9.80731e+008
}
Then needed to get the common(intersect) items for given two users (like userId:1 and userId:2) like [6,32].
But now i need to get that with ratings of each of them like [ {"movieId":6,"user1_rating" : 2,"user2_rating" : 4},{"movieId":32,"user1_rating" : 2,"user2_rating" : 5} ]
How can i get that?
I tried to do with
db.collection.aggregate([
{$match: {"$or":[{"userId":2},{"userId":1}]}},
{$group: {_id: "$movieId", users: {$push: {"userId":"$userId","rating":"$rating"}}}},
{$project: { movieId: "$_id", _id: 0,rating:"$users.rating", allUsersIncluded: { $setIsSubset: [ [1,2], "$users.userId"]}}},
{$match: { allUsersIncluded: true }},
{$group: { _id: null, movies: {$push: {"movie":"$movieId","Rating":"$rating"}}}}
])
But I get [ {"movie":6,0 : 2,1 : 4},{"movie":32,0 : 2,1 : 5} ]
Finally i achieved my target.The answer is
db.collection.aggregate([
{$match: {"$or":[{"userId":2},{"userId":1}]}},
{$group: {_id: "$movieId", users: {$addToSet: {"userId":"$userId","rating":"$rating"}}}},
{$project: { movieId: "$_id", _id: 0,user:"$users", allUsersIncluded: { $setIsSubset: [ [1,2], "$users.userId"]}}},
{$match: { allUsersIncluded: true }},
{$group: { _id: null, movies: {$addToSet: {"movie":"$movieId","user":"$user"}}}}
])

MongoDB aggregate $match and $group with $sum

i have a collection with documents like this:
{
"Company" : "4433",
"Descripcion" : "trabajo",
"Referencia" : "11817",
"HoraImportado" : "15:54",
"ImportedOd" : "2014-05-20T13:54:28.493Z",
"Items" : [],
"Notes" : [
{
"_id" : ObjectId("537b5ea4c61b1d1743f43420"),
"NoteDateTime" : "2014-05-20T13:54:44.418Z",
"Description" : "nota",
"IsForTechnician" : true,
"Username" : "admin"
},
{
"_id" : ObjectId("537c4a549e956f77ab8c7c38"),
"NoteDateTime" : ISODate("2014-05-21T06:40:20.299Z"),
"Description" : "ok",
"IsForTechnician" : true,
"Username" : "admin"
}
],
"OrderState" : "Review",
"SiniestroDe" : "Emergencia",
"Technicians" : [
{
"TechnicianId" : ObjectId("53465f9d519c94680327965d"),
"Name" : "Administrator",
"AssignedOn" : ISODate("2014-05-20T13:54:44.373Z"),
"RemovedOn" : null
}
],
"TechniciansHistory" : [
{
"TechnicianId" : ObjectId("53465f9d519c94680327965d"),
"Name" : "Administrator",
"AssignedOn" : ISODate("2014-05-20T13:54:44.373Z"),
"RemovedOn" : null
},
{
"Name" : "Nuevo",
"AssignedOn" : ISODate("2014-05-20T13:54:44.373Z"),
"RemovedOn" : null,
"TechnicianId" : ObjectId("5383577a994be8b9a9e3f01e")
}
],
"Telefonos" : "615554006",
"_id" : ObjectId("537b5ea4c61b1d1743f4341f"),
"works" : [
{
"code" : "A001",
"name" : "Cambiar bombilla",
"orderId" : "537b5ea4c61b1d1743f4341f",
"price" : "11",
"ID" : 33,
"lazyLoaded" : true,
"status" : 0,
"Date" : ISODate("2014-05-21T06:40:20.299Z"),
"TechnicianId" : "53465f9d519c94680327965d",
"_id" : ObjectId("537c4a549e956f77ab8c7c39")
},
{
"code" : "A001",
"name" : "Cambiar bombilla",
"orderId" : "537b5ea4c61b1d1743f4341f",
"price" : "11",
"ID" : 34,
"lazyLoaded" : true,
"status" : 0,
"Date" : ISODate("2014-05-21T06:40:20.299Z"),
"TechnicianId" : "53465f9d519c94680327965d",
"_id" : ObjectId("537c4a549e956f77ab8c7c3a")
}
]
}
Now i want to get the works for a selected TechnicianId array, group by TechnicianId and get the sum of the works.price for each technician.+
I try with this:
db.orders.aggregate([
{ $match: { 'works.TechnicianId': {$in:['53465f9d519c94680327965d']}}},
{ $group: { _id: "$works.TechnicianId",total:{$sum:'$works.price'}}},
])
And this is the result:
{
"result" : [
{
"_id" : [
"53465f9d519c94680327965d",
"53465f9d519c94680327965d"
],
"total" : 0
}
],
"ok" : 1
}
The total its the $sum but its 0 but should be 44.
Try adding unwind,
db.orders.aggregate([
{ $match: { 'works.TechnicianId': {$in:['53465f9d519c94680327965d']}}},
{ $unwind: "$works" },
{ $group: { _id: "$works.TechnicianId",total:{$sum:'$works.price'}}},
])
Look here for more info : http://docs.mongodb.org/manual/reference/operator/aggregation/unwind/
The price value is a string. $sum only operates on Numbers.
I've checked this by running the following:
db.foo.insert({"cost": "1"})
db.foo.insert({"cost": "2"})
db.foo.insert({"cost": "3"})
db.foo.insert({"cost": 4})
db.foo.insert({"cost": 5})
db.foo.aggregate([{$group: {_id: null, cost: {$sum: "$cost"}}}])
{ "result" : [ { "_id" : null, "cost" : 9 } ], "ok" : 1 }
According to this answer, you can't cast values in normal Mongo queries, so you can't change the string to a number inline.
You should either update all values to a Number datatype or use map-reduce. I'd go for the former.
If the value is a string to prevent floating point errors, consider multiplying by 100 to store the value in cents: "10.50" --> 1050
As Lalit Agarwal indicated, you'll also need to unwind the array of works. Example of what happens if you don't:
db.bar.insert({"works": [{price: 10}]})
db.bar.insert({"works": [{price: 20}, {price: 30}]})
db.bar.insert({"works": [{price: 40}, {price: 50}]})
db.bar.aggregate([
{$group: {_id: null, total: {$sum: "$works.price"} }}
])
{ "result" : [ { "_id" : null, "total" : 0 } ], "ok" : 1 }
db.bar.aggregate([
{$unwind: "$works"},
{$group: {_id: null, total: {$sum: "$works.price"} }}
])
{ "result" : [ { "_id" : null, "total" : 150 } ], "ok" : 1 }
What $unwind does is make 5 documents out of the initial 3, all with a single value in the works field. It then groups and sums them.
db.inventory.insert(
{
item: “ABC1”,
details: {
model: “14Q3”,
manufacturer: “XYZ Company”
},
stock: [ { size: “S”, qty: 25 }, { size: “M”, qty: 50 } ],
category: “clothing”
}
)

MongoDB double $group aggregation

I have several documents that looks like this:
{
"_id" : ObjectId("50b59cd75bed76f46522c34e"),
"player_id" : 0,
"league_id" : 2,
"results" : [
{ "discipline" : "football",
"score" : 25.15
},
{
"discipline" : "basketball",
"score" : 21.24
},
{
"discipline" : "cycling",
"score" : 68.19
},]
}
I try to aggregate this data. First unwind results array, then leave only "football" and "cycling", next count average result. This part I did, and it is working.
My code:
db.grades.aggregate(
{$unwind:"$results"},
{$match: {$or: [{"results.discipline":"football"},{"results.discipline":"cycling"} ]}},
{$group:{_id:{player_id:"$player_id",league_id:"$league_id"}, 'average':{$avg:"$results.score"}}},
)
Then I try to aggregate by league_id, it means, average players results in specific leagues, add to code above:
{$group:{_id:"$_id.league_id",aver_league:{$avg:$average}}}
And now code looks like:
db.grades.aggregate(
{$unwind:"$results"},
{$match: {$or: [{"results.discipline":"football"},{"results.discipline":"cycling"} ]}},
{$group:{_id:{player_id:"$player_id",league_id:"$league_id"}, 'average':{$avg:"$results.score"}}},
{$group:{_id:"$_id.league_id",aver_league:{$avg:$average}}}
)
Console displays: JavaScript execution failed: ReferenceError: $average is not defined. What is wrong? Where did I make a mistake? Is it possible to aggregate by _id.league_id?
Try this pipeline:
[
{$unwind:"$results"},
{$match: {"results.discipline":{$in:["football", "basketball"]}}},
{$group{_id:{player_id:"$player_id",league_id:"$league_id"}, 'average':{$avg:"$results.score"}}}
]
it works for me with your doc:
{
"result" : [
{
"_id" : {
"player_id" : 0,
"league_id" : 2
},
"average" : 23.195
}
],
"ok" : 1
}
UPD. If you want to group again, by league_id:
[{$unwind:"$results"},
{$match: {"results.discipline":{$in:["football", "basketball"]}}},
{$group:{_id:{player_id:"$player_id",league_id:"$league_id"}, 'average':{$avg:"$results.score"} }},
{$group:{_id:"$_id.league_id", 'average':{$avg:"$average"} }} ]
{ "result" : [ { "_id" : 2, "average" : 23.195 } ], "ok" : 1 }