In MongoDB, I need to be able to unwind nested an array in a document inside an array inside the main document.
{
"_id" : ObjectId("5808d700536d1a3d69f4cf51"),
"last_name" : "Maity",
"xiith_mark" : 58,
"id" : "3539488",
"first_name" : "Harshavardhan",
"course_name" : "BE/B.Tech",
"institute_name_string" : "Abhayapuri College, P.O. Abhayapuri",
"profile_percentage" : 45,
"xiith_mark_type" : "Percentage",
"xth_mark_type" : "Percentage",
"date_of_birth" : "14-April-1993",
"xth_mark" : 30,
"last_login" : 1470827224,
"percentage" : 55,
"job_details" : [
{
"status" : NumberLong(6),
"applied_date" : NumberLong(1470831441),
"job_id" : NumberLong(92928),
"contact_viwed_status" : 0,
"label_name" : [
"shortlisted",
"rejected"
],
"questionnaire_status" : 0,
"batch_id" : NumberLong(6),
"call_letter" : NumberLong(812)
},
{
"status" : NumberLong(6),
"applied_date" : NumberLong(1470831441),
"job_id" : NumberLong(92928),
"contact_viwed_status" : 0,
"label_name" : [
"shortlisted",
"rejected"
],
"questionnaire_status" : 0,
"batch_id" : NumberLong(6),
"call_letter" : NumberLong(812)
}
],
"branch_name" : "Applied Electronics",
"candidate_state_name" : "West Bengal",
"candidate_city_name_string" : "Kolkata",
"10" : 10,
"12" : 12,
"skills" : "",
"gender" : "Male",
"fw_id" : "FW15884830",
"cgpa" : 0,
"picture_path" : "",
"hq_passout_year" : 2019
}
Based on the record above I need to count the job labels (job_details.label_name).
I have tried the following query:
db.response.aggregate(
{"$match":type_match},
{"$unwind": "$job_details" },
{"$group":
{
"_id":"$job_details.label_name",
"count": {"$sum": 1 }
}
}
])
The output is:
{
"count": 2,
"_id": [
"shortlisted",
"rejected"
]
}
But I want the output to be:
[
{
"count": 1,
"_id": "shortlisted"
},
{
"count": 1,
"_id": "rejected"
}
]
How can I get this output?
In unwind stage, field should be an array field. If not array field, it treats it as array of 1 element.
From the docs:
Changed in version 3.2: $unwind stage no longer errors on non-array operands. If the operand does not resolve to an array but is not missing, null, or an empty array, $unwind treats the operand as a single element array.
Answer to your query:
db.response.aggregate([
{
$project:
{
"job_details.label_name":1,
_id:0
}
},
{
$unwind:"$job_details.label_name"
},
{
$group:
{
_id:"$job_details.label_name",
count:{$sum:1}
}
}
])
Refer Shell Output
Related
I've populated a database in Mongodb with the following data in a collection named people.
{ "_id" : 0, "name" : "Bernice Pope", "age" : 69, "date" : "2017-10-04T18:35:44.011Z" }
{ "_id" : 1, "name" : "Eric Malone", "age" : 57, "date" : "2017-10-04T18:35:44.014Z" }
{ "_id" : 2, "name" : "Blanche Miller", "age" : 35, "date" : "2017-10-4T18:35:44.015Z" }
{ "_id" : 3, "name" : "Sue Perez", "age" : 64, "date" : "2017-10-04T18:35:44.016Z" }
{ "_id" : 4, "name" : "Ryan White", "age" : 39, "date" : "2017-10-04T18:35:44.019Z"}
{ "_id" : 5, "name" : "Grace Payne", "age" : 56, "date" : "2017-10-04T18:35:44.020Z" }
{ "_id" : 6, "name" : "Jessie Yates", "age" : 53, "date" : "2017-10-04T18:35:44.020Z"}
{ "_id" : 7, "name" : "Herbert Mason", "age" : 37, "date" : "2017-10-4T18:35:44.020Z" }
{ "_id" : 8, "name" : "Jesse Jordan", "age" : 47, "date" : "2017-10-04T18:35:44.020Z"}
{ "_id" : 9, "name" : "Hulda Fuller", "age" : 25, "date" : "2017-10-04T18:35:44.020Z"}
Based on the people collection, I need to create a view named PeopleNames which likes below:
{ "LengthOfName": 8, "names" : [ "Sue Perez" ]}
{ "LengthOfName" : 9, "names" : [ "Ryan White" ]}
{ "LengthOfName" : 10, "names" : [ "Eric Malone", "Grace Payne" ]}
{ "LengthOfName" : 11, "names" : [ "Bernice Pope", "Jessie Yates", "Jesse Jordan", "Hulda Fuller" ]}
{ "LengthOfName" : 12, "names" : [ "Herbert Mason" ]}
{ "LengthOfName" : 13, "names" : [ "Blanche Miller"]}
LengthOfName is the total number of the characters in the last name and first name.
My approach is to first add the names into an array, the use $split operator to split the names and use $strLenCP to count the characters.
db.people.aggregate([
{
$project : {
name : 1,
name_array : [{$push : { $split : {$name : " "}}}]
}
}
,{
$unwind : "$name_array"
},{
$project : {
name : 1,
nameLength : {$strLenCP : $name_array}
}
},{
$group :{
_id : "$nameLength",
nameLength: 1
}
}])
But I am receiving error saying that my "$name_array" is undefined Any ideas?
The $push aggregation operator is only available in the $group stage.
You can group the documents by the name length (after trimming the spaces in the name using $replaceAll) and use $push to add the names to the names array. You can then add a $project stage to add the LengthOfName field to the documents and finally add a $sort stage to sort the documents by the LengthOfName field.
db.collection.aggregate([
{
$group: {
_id: {
$strLenCP: {
$replaceAll: {
input: "$name",
find: " ",
replacement: ""
}
}
},
names: {
$push: "$name"
}
}
},
{
$project: {
_id: 0,
LengthOfName: "$_id",
names: "$names",
}
},
{
$sort: {
LengthOfName: 1,
}
}
])
MongoPlayground
I have below collection structure and I want to find minimum score for each student.
>db.students.findOne()
{
"_id" : 0,
"name" : "aimee Zank",
"scores" : [
{
"type" : "exam",
"score" : 1.463179736705023
},
{
"type" : "quiz",
"score" : 11.78273309957772
},
{
"type" : "homework",
"score" : 6.676176060654615
},
{
"type" : "homework",
"score" : 35.8740349954354
}
]
}
I use below aggregate command
db.students.aggregate([
{
$group: {_id: "$_id" , min: {$min: '$scores.score'}}
}
])
below is the output:
{ "_id" : 199, "min" : [ 82.11742562118049, 49.61295450928224, 28.86823689842918, 5.861613903793295 ] }
{ "_id" : 198, "min" : [ 11.9075674046519, 20.51879961777022, 55.85952928204192, 64.85650354990375 ] }
{ "_id" : 95, "min" : [ 8.58858127638702, 88.40377630359677, 25.71387474240768, 23.73786528217532 ] }
{ "_id" : 11, "min" : [ 78.42617835651868, 82.58372817930675, 87.49924733328717, 15.81264595052612 ] }
{ "_id" : 94, "min" : [ 6.867644836612586, 63.4908039680606, 85.41865347441522, 26.82623527074511 ] }
it returns all scores for each student instead of the minimum one. What wrong with my query command? I am using mongo 3.4.
After some searching, I found that the solution is to add $unwind on scores.score. The complete command is:
stus = db.students.aggregate([
{
"$unwind": "$scores"
},
{
$group: {_id: "$_id" , minScore: {$min: '$scores.score'}}
}
])
This is one of many similar objects in shopping list collection. How do I do a query to get the list of only the "name" of people buying more than 2 "Noodles"?
Please help me figure this out, thanks in advance.
I assume this should have the $gt operator but I am not sure how to execute it correctly.
{
"_id" : ObjectId("591422529f75f9119575c1d8"),
"name" : "Hisham",
"age" : 20,
"address" : {
"house" : "HomeName",
"street" : "Fairyland",
"city" : "Faketon",
"pincode" : 000000
},
"itemlist" : [
{
"iname" : "Soap",
"quantity" : 2,
"price" : 10,
"rate" : 20,
"itemID" : "1"
},
{
"iname" : "Mirror",
"quantity" : 1,
"price" : 600,
"rate" : 600,
"itemID" : "4"
},
{
"iname" : "Noodles",
"quantity" : 4,
"price" : 50,
"rate" : 200,
"itemID" : "5"
},
{
"iname" : "Plug",
"quantity" : 2,
"price" : 50,
"rate" : 100,
"itemID" : "6"
}
]
}
you can achieve this with the aggregation framework like this :
db.collection.aggregate([
{
$unwind:"$itemlist"
},
{
$match:{
"itemlist.iname":"Noodles"
}
},
{
$group:{
_id:"$itemlist.iname",
name:{
$first:"$name"
},
count:{
$sum:1
}
}
},
{
$match:{
count:{
$gte:2
}
}
}
])
How it works:
unwind the itemlist array with $unwind
keep only Noodles item
count occurence of Noodles using $group
keep only document where count >= 2
You can select the all documents that match your criteria using the $elemMatch operator in the $match. From there all you need is a $group stage.
db.collection.aggregate([
{ "$match": {
"itemlist": {
"$elemMatch": {
"quantity": { "$gt": 2 },
"iname": "Noodles"
}
}
}},
{ "$group": { "_id": null, "names": { "$push": "$name" } } }
])
I wanted to group by cart.name and find the sum of cart.qty in mongodb. Below is sample document
{
"_id" : ObjectId("581323379ae5e607645cb485"),
"cust" : {
"name" : "Customer 1",
"dob" : "09/04/1989",
"mob" : 999999999,
"loc" : "Karimangalam",
"aadhar" : {
}
},
"cart" : [
{
"name" : "Casual Shirt",
"qty" : 1,
"mrp" : 585,
"discperc" : 10,
"fit" : null,
"size" : "L"
},
{
"name" : "Casual Shirt",
"qty" : 1,
"mrp" : 500,
"discperc" : 0,
"fit" : null,
"size" : "L"
},
{
"name" : "Cotton Pant",
"qty" : 1,
"mrp" : 850,
"discperc" : 0,
"fit" : null,
"size" : "34"
},
{
"name" : "Cotton Pant",
"qty" : 1,
"mrp" : 1051,
"discperc" : 10,
"fit" : null,
"size" : "34"
}
],
"summary" : {
"bill" : 2822.4,
"qty" : 4,
"mrp" : 2986,
"received" : "2800",
"balance" : -22.40000000000009
},
"createdAt" : ISODate("2016-10-28T10:06:47.367Z"),
"updatedAt" : ISODate("2016-10-28T10:06:47.367Z")
}
There are many document like this. I want the output as below distinct product name (cart.name) and its total qty
{Casual Shirt , 30},
{Cotton Pant , 10},
{T-Shirt , 15},
{Lower , 12}
Here is my query trying to group by cart.name and sum qty
db.order.aggregate( [
{ $unwind: "$cart" },
{ $group: {
_id: "$cart.name",
totalQTY: { $sum:"$cart.qty"},
count: { $sum: 1 }
}
}
] )
but it displays wrong totalQty values for each product name. I checked manually.
Please give me the correct query.
> db.collection.aggregate([
... { $unwind: "$cart" },
... { $group: { "_id": "$cart.name", totalQTY: { $sum: "$cart.qty" }, count: { $sum: 1 } } }
... ])
I get the following result:
{ "_id" : "Cotton Pant", "totalQTY" : 2, "count" : 2 }
{ "_id" : "Casual Shirt", "totalQTY" : 11, "count" : 2 }
I'm not sure what you're looking for, it looks like your aggregation pipeline is correct. (Note I changed the Casual Shirt Quantity to be 10 and 1 respectively)
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”
}
)