Mongodb Aggregate using $group twice - mongodb

I have a bunch of documents in mongo with the following structure:
{
"_id" : "",
"number" : 2,
"colour" : {
"_id" : "",
"name" : "Green",
"hex" : "00ff00"
},
"position" : {
"_id" : "",
"name" : "Defence",
"type" : "position"
},
"ageGroup" : {
"_id" : "",
"name" : "Minor Peewee",
"type" : "age"
},
"companyId" : ""
}
I'm currently using Mongo's aggregate to group the documents by ageGroup.name which returns:
//Query
Jerseys.aggregate([
{$match: { companyId: { $in: companyId } } },
{$group: {_id: "$ageGroup.name", jerseys: { $push: "$$ROOT" }} }
]);
//returns
{
_id: "Minor Peewee",
jerseys: array[]
}
but I'd like it to also group by position.name within the age groups. ie:
{
_id: "Minor Peewee",
positions: array[]
}
//in positions array...
{
_id: "Defence",
jerseys: array[]
}
// or ageGroups->positions->jerseys if that makes more sense.
I've tried multiple groups but I don't think I'm setting them up correctly I always seem to get an array of _id's. I'm using Meteor as the server and I'm doing it within a meteor method.

You can use a composite aggregate _id in the first grouping stage.
Then, you can use one of those keys as the "main" _id of the final aggregate and $push the other into another array.
Jerseys.aggregate([
{
$match: { companyId: { $in: companyId } }
},
{
$group: { // each position and age group have an array of jerseys
_id: { position: "$position", ageGroup: "$ageGroup" },
jerseys: { $push: "$$ROOT" }
}
},
{
$group: { // for each age group, create an array of positions
_id: { ageGroup: "$_id.ageGroup" },
positions: { $push: { position: "$_id.position", jerseys:"$jerseys" } }
}
}
]);

Related

MongoDb sorting documents with multiple nested arrays

There is a document with nested arrays, just want to sort collection by customerId then productList.productId and then productList.itemList.id in ascending order. MongoDb version is 3.0.14.
I've tried so far this and the query doesn't sort the collection as expected:
db.file_old.find({}, {
customerId: 1,
"productList.productId": 1,
"productList.itemList.id": 1
})
.sort({
customerId: 1,
productList: 1,
"productList.itemList": 1
})
and try aggregate framework also like this:
db.file_old.aggregate([
{"$unwind": "$productList"} ,
{"$sort": {"customerId": 1, "productList.productId": 1}}
])
It work fine for two field but if try to adding "productList.itemList.id" doesn't work, like this:
db.file_old.aggregate([
{"$unwind": "$productList"} ,
{"$sort": {"customerId": 1, "productList.productId": 1, "productList.itemList.id": 1}}
])
Collection structure:
{
"_id" : ObjectId("5f33cc2a1e84082968132324"),
"customerId" : 2196,
"productList" : [
{
"productId" : 7531,
"itemList" : [
{
"id" : 144
},
{
"id" : 145
}
]
},
{
"productId" : 7534,
"itemList" : [
{
"id" : 1244
},
{
"id" : 1243
},
{
"id" : 1245
},
{
"id" : 1242
}
]
}
]
},{
"_id" : ObjectId("5f33cc2a1e84082968132326"),
"customerId" : 2201,
"productList" : [
{
"productId" : 101201,
"itemList" : [
{
"id" : 863
},
{
"id" : 865
},
{
"id" : 862
}
]
},
{
"productId" : 7537,
"itemList" : [
{
"id" : 982
},
{
"id" : 1002
},
{
"id" : 896
}
]
}
]
}
You can not sort directly array, first requires to unwind(deconstruct) and then sort will apply, lets see step by step,
productList
deconstruct array ($unwind)
itemList
deconstruct array ($unwind)
sort by id ($sort)
re-construct array ($group)
sort by productId ($sort)
re-construct productList ($group)
sort by customerId ($sort)
$unwind deconstruct productList array
db.collection.aggregate([
{ $unwind: "$productList" },
$unwind deconstruct productList.itemList array
{ $unwind: "$productList.itemList" },
$sort by productList.itemList.id ascending order
{ $sort: { "productList.itemList.id": 1 } },
$group by all 3 main level of ids and re-construct itemList array
{
$group: {
_id: {
_id: "$_id",
customerId: "$customerId",
productId: "$productList.productId"
},
itemList: { $push: "$productList.itemList" }
}
},
$sort by productId ascending order
{ $sort: { "_id.productId": 1 } },
$group by main 2 level of ids and re-construct productList array
{
$group: {
_id: {
_id: "$_id._id",
customerId: "$_id.customerId"
},
productList: {
$push: {
productId: "$_id.productId",
itemList: "$itemList"
}
}
}
},
$project to show required fields
{
$project: {
_id: "$_id._id",
customerId: "$_id.customerId",
productList: 1
}
},
$sort by customerId id
{ $sort: { customerId: 1 } }
])
Playground

How can i retrieve all nested array of object element which has been matched by using "elementMatch" mongodb

We have array of object and every object contain array of object "Conversation".We need to get the count/elements where the field deliveryStatus is sent and userId is exits
[{
_id: "12312312"
conversation:[
{
"messageInformation" : {
"deliveryStatus" : "sent"
},
"userId" : 12
},
{
"messageInformation" : {
"deliveryStatus" : "sent",
}
}
]},
_id: "213123123123"
conversation:[
{
"messageInformation" : {
"deliveryStatus" : "sent"
},
"userId" : 33
},
{
"messageInformation" : {
"deliveryStatus" : "sent",
"userInfo" : [ ]
}
}
]}
]
I need the total count. I had try try elemMatch but it only return the first element
let counter = await ProjectConversation.find({
conversation: {
$elemMatch: {
userId :{$exists: true},
"messageInformation.deliveryStatus": deliveryStatus.Sent
}
}
}, {
_id: 0,
conversation.$: 1
});
It will be fine if I get the object and do the count by using javascript
You can use aggregate $unwind to deconstruct the array and use $count to count the number of documents that matches the $match property, eg :
db.collection.aggregate([{
$unwind: "$conversation"
},
{
$match: {
"conversation.messageInformation.deliveryStatus": "sent",
"conversation.userId": {
$exists: true
}
}
},
{
$count: "NumberOfMsgSent"
}
])
On Mongo Playground

mongodb: get top x rankings(count) of occurrences in documents

I have documents that have general geo location information. I am trying get the top x cities for example.
{
"_id" : ObjectId("593b6a7068c4281a3f7702c5"),
"clientID" : "1000000000",
"session_id" : "I9Ak2k1taOGHU0Z0000000000",
"location" : {
"country" : "United States",
"city" : "Seattle",
"postal" : "98105",
"traits" : null,
"local" : "America/Los_Angeles"
},
"dateTime" : ISODate("2017-06-09T21:12:56.819+0000"),
"action" : "PLAY",....
}
I am fairly new to mongo, coming from sql. I was hoping to get this done using aggregation & without doing it in code after I get a range of docs returned.
I figured out how to group these documents by session_id and such I now just need to add the instance of city count sorted descending and just display those cities.
my group query:
db.Playerstats.aggregate(
// Pipeline
[
// Stage 1 for tests
{
$match: {
clientID: "1000000000"
}
},
// Stage 2
{
$group: {
_id : "$session_id",
start: { $first: "$dateTime"},
stop: { $last: "$dateTime"},
eventID : { $addToSet: "$eventID"},
status : { $addToSet: "$eventStatus"},
browser: { $addToSet: "$browser.userAgent"},
OS : { $addToSet: "$browser.platform"},
city : { $addToSet: "location.city"},
p2pData : { $sum: "$p2p.totalVerifiedBytes"},
actions : { $addToSet: "$action"}
}
},
// Stage 3
{
$sort: {
startDate: -1
}
},
],
);
Thanks for any insight.
I just needed to add another grouping to the query for the cities and get that count. now I can add a limit and sorting to round it out.
{
$group: {
_id: "$city",
count: { $sum: 1 }
}
}

Finding all documents which share the same value in an array

Consider I have the following data below:
{
"id":123,
"name":"apple",
"codes":["ABC", "DEF", "EFG"]
}
{
"id":234,
"name":"pineapple",
"codes":["DEF"]
}
{
"id":345,
"name":"banana",
"codes":["HIJ","KLM"]
}
If I didn't want to search by a specific code, is there a way to find all fruits in my mongodb collection which shares the same code?
db.collection.aggregate([
{ $unwind: '$codes' },
{ $group: { _id: '$codes', count: {$sum:1}, fruits: {$push: '$name'}}},
{ $match: {'count': {$gt:1}}},
{ $group:{_id:null, total:{$sum:1}, data:{$push:{fruits: '$fruits', code:'$_id'}}}}
])
result:
{ "_id" : null, "total" : 1, "data" : [ { "fruits" : [ "apple", "pineapple" ], "code" : "DEF" } ] }

How can I change the whole schema of mongodb with existing data?

I'm trying to change database mysql to mongodb,
Wanting to change whole data schema.
And I succeed to migrate data.
But I want to change this mongodb documents schema from
{
"_id" : ObjectId("581c45b1a5245ca984000009"),
"memb_id" : "ME20160520041223736026",
"story_category_id" : "S001000",
"story_pet_category" : "D",
"temp_text" : "THIS IS DATA TO CHANGE",
"story_status" : "D",
"regtime" : 1463988822,
"modifytime" : 1468573528,
"story_contents" : [
{
"story_image_url" : "/uploads/2016/05/23/compression/728870e82c47901a70509cfdc2bc3b56.jpg"
},
{
"story_image_url" : "/uploads/2016/05/23/compression/fd1552e1ea5380fc0cdae793336a8d67.jpg"
},
{
"story_image_url" : "/uploads/2016/05/23/compression/2aaff6a75db4c8bfa1a0133234d3e6fe.jpg"
}
]
}
to
{
"_id" : ObjectId("581c45b1a5245ca984000009"),
"memb_id" : "ME20160520041223736026",
"story_category_id" : "S001000",
"story_pet_category" : "D",
"story_status" : "D",
"regtime" : 1463988822,
"modifytime" : 1468573528,
"story_contents" : [
{
"story_text":"THIS IS DATA TO CHANGE",
"story_image_url":""
}
{
"story_text":"",
"story_image_url" : "/uploads/2016/05/23/compression/728870e82c47901a70509cfdc2bc3b56.jpg"
},
{
"story_text":"",
"story_image_url" : "/uploads/2016/05/23/compression/fd1552e1ea5380fc0cdae793336a8d67.jpg"
},
{
"story_text":"",
"story_image_url" : "/uploads/2016/05/23/compression/2aaff6a75db4c8bfa1a0133234d3e6fe.jpg"
}
]
}
Exactly what I want is Pushing that "temp_text" in "story_contents" Array, Changing "story_contents"'s schema like this. (Whole 6423 documents)
How can I write mongodb query..?
You can go with an aggregation that will add story_text field/value to all items from story_contents array :
db.collection.aggregate([{
$unwind: "$story_contents"
}, {
$group: {
_id: "$_id",
memb_id: { $first: "$memb_id" },
story_category_id: { $first: "$story_category_id" },
story_pet_category: { $first: "$story_pet_category" },
story_status: { $first: "$story_status" },
regtime: { $first: "$regtime" },
modifytime: { $first: "$modifytime" },
story_contents: {
$push: { temp_text: "$temp_text", story_image_url: "$story_contents.story_image_url" }
}
}
}, {
$out: "collection2"
}])
1 $unwind to convert story_contents array to JSON object
1 $group to regroup all the items by _id with construction of the new array story_contents with additional temp_text field
1 $out to write the result into a new collection named collection2