MongoDB Keep path where a criteria is met - mongodb

I'm new to MongoDB.
In the find query I'm using the following structure:
db.report.find({'accountList.transactionList.description': /.*aear.*/i})
However, accountList contains multiple values, and so does transaction list, the exact query would be:
db.report.find({'accountList[0].transactionList[4].description': /.*aear.*/i})
The problem is that accountList has multiple accounts, and only one of them has the value 'aear' in the description. When I'm executing the query it returns me both accounts, and I'd like to keep only the account where aear is in its description. Also, this MUST be iterable over many files, since it file has different transactionLists, therefore in some documents aear will not appear at all, and in others it might appear multiple types, always in different positions. I believe something must be done in projection, but setting it like this doesn't work:
.projection({"accountList.id":1,"accountList.transactionList.description":1})
Here's the output:
"accountList" : [
{
"id" : "1",
"type" : "xD",
"currency" : "EUR",
"transactionList" : [
{
"onDate" : ISODate("2019-08-25T21:00:00.000-03:00"),
"description" : "aear"
},
{
"onDate" : ISODate("2019-08-25T21:00:00.000-03:00"),
"description" : "bb"
},
{
"onDate" : ISODate("2019-08-25T21:00:00.000-03:00"),
"description" : "cc"
}
]
},
{
"id" : "2",
"type" : "xD",
"currency" : "USD",
"transactionList" : [
{
"onDate" : ISODate("2019-08-15T21:00:00.000-03:00"),
"description" : "aa",
},
{
"onDate" : ISODate("2019-08-14T21:00:00.000-03:00"),
"description" : "ee"
}
]
}
]
And I'd like something like this, where I''m only getting the path to where the condition is met:
"accountList" : [
{
"id" : "1",
"type" : "xD",
"currency" : "EUR",
"transactionList" : [
{
"onDate" : ISODate("2019-08-25T21:00:00.000-03:00"),
"description" : "aear"
},

To accomplish that you need to use aggregate. I believe this code will work in your case:
db.report.aggregate([
{ "$match": { "accountList.transactionList.description": { $regex: "aear", $options: "i"} } },
{ "$unwind": "$accountList" },
{ "$unwind": "$accountList.transactionList" },
{ "$match": { "accountList.transactionList.description": { $regex: "aear", $options: "i"} } },
{ "$group": {
"_id": {
"_id": "$_id",
"accountListId": "$accountList.id",
"accountListType": "$accountList.type",
"accountListCurrency": "$accountList.currency",
},
"transactionList": { "$push": "$accountList.transactionList" }
}},
{ "$group": {
"_id": "$_id._id",
"accountList": {
"$push": {
"id": "$_id.accountListId",
"type": "$_id.accountListType",
"currency": "$_id.accountListCurrency",
"transactionList": "$transactionList"
}
}
}}
])

Updating my answer as this question got updated with new required o/p :
Answer for New Question :
If you've only one transaction matching to given criteria /.*aear.*/i, let's say description is unique across accountList array of report document(exact for provided sample):
db.report.aggregate([{
$match: {
'accountList.transactionList.description': /.*aear.*/i
}
},{ $unwind: '$accountList' },{ $unwind: '$accountList.transactionList' },{$match :{ 'accountList.transactionList.description': /.*aear.*/i}}, { $project: { 'accountList': 1, _id: 0 } }])
But, if you've multiple descriptions (across multiple objects in accountsList array of a report document) matches to given criteria in accountList :
db.report.aggregate([{
$match: {
'accountList.transactionList.description': /.*aear.*/i
}
}, { $unwind: '$accountList' }, { $unwind: '$accountList.transactionList' }, { $match: { 'accountList.transactionList.description': /.*aear.*/i } },
{ $group: { _id: '$_id', accountList: { $push: '$accountList' }, data: { $first: '$$ROOT' } } }
, { $addFields: { 'data.accountList': '$accountList' } }, { $replaceRoot: { 'newRoot': '$data' } }, { $project: { 'accountList': 1, _id: 0 } }
])
Output :
/* 1 */
{
"accountList" : [
{
"id" : "1100",
"type" : "xD",
"currency" : "EUR",
"transactionList" : {
"onDate" : ISODate("2019-08-26T00:00:00.000Z"),
"description" : "aear"
}
},
{
"id" : "1200",
"type" : "xD",
"currency" : "USD",
"transactionList" : {
"onDate" : ISODate("2019-08-16T00:00:00.000Z"),
"description" : "aear"
}
}
]
}
/* 2 */
{
"accountList" : [
{
"id" : "1",
"type" : "xD",
"currency" : "EUR",
"transactionList" : {
"onDate" : ISODate("2019-08-26T00:00:00.000Z"),
"description" : "aear"
}
}
]
}
If in case you've multiple matching descriptions in transaction array & also in other objects of accounts array (this will work for all above scenarios as well but it might not be needed as per requirement, it can be bulky, Check document#3 in Output for clarification) :
db.report.aggregate([
{ "$match": { "accountList.transactionList.description": /.*aear.*/i } },
{ "$unwind": "$accountList" },
{ "$unwind": "$accountList.transactionList" },
{ "$match": { "accountList.transactionList.description": /.*aear.*/i } },
{
"$group": {
"_id": {
"docId": "$_id",
"accountsListObjId": "$accountList.id"
},
"transactionList": { "$push": "$accountList.transactionList" },
"accountList": { "$first": '$accountList' }
}
}
, { $addFields: { 'accountList.transactionList': '$transactionList' } },
{
"$group": {
"_id": "$_id.docId",
"accountList": { $push: '$accountList' }
}
}, { $project: { 'accountList': 1, _id: 0 } }
])
Output :
/* 1 */
{
"accountList" : [
{
"id" : "1100",
"type" : "xD",
"currency" : "EUR",
"transactionList" : [
{
"onDate" : ISODate("2019-08-26T00:00:00.000Z"),
"description" : "aear"
}
]
},
{
"id" : "1200",
"type" : "xD",
"currency" : "USD",
"transactionList" : [
{
"onDate" : ISODate("2019-08-16T00:00:00.000Z"),
"description" : "aear"
}
]
}
]
}
/* 2 */
{
"accountList" : [
{
"id" : "1",
"type" : "xD",
"currency" : "EUR",
"transactionList" : [
{
"onDate" : ISODate("2019-08-26T00:00:00.000Z"),
"description" : "aear"
}
]
}
]
}
/* 3 */
{
"accountList" : [
{
"id" : "00",
"type" : "xD",
"currency" : "EUR",
"transactionList" : [
{
"onDate" : ISODate("2019-08-26T00:00:00.000Z"),
"description" : "aear"
},
{
"onDate" : ISODate("2019-08-26T00:00:00.000Z"),
"description" : "aear"
}
]
},
{
"id" : "100",
"type" : "xD",
"currency" : "USD",
"transactionList" : [
{
"onDate" : ISODate("2019-08-16T00:00:00.000Z"),
"description" : "aear"
}
]
}
]
}
If you're looking for exact text, you can do this as well(cause regex is not allowed in cond) :
db.report.aggregate([
{
$match: {
'accountList.transactionList.description': 'aear'
}
}, { $unwind: '$accountList' }, {
$addFields: {
'accountList.transactionList': {
$filter: {
input: '$accountList.transactionList',
as: 'eachTransaction',
cond: { $eq: ["$$eachTransaction.description", 'aear'] }
}
}
}
}, { $match: { 'accountList.transactionList': { $ne: [] } } }, { $group: { _id: '$_id', accountList: { $push: '$accountList' }, data: { $first: '$$ROOT' } } }
, { $addFields: { 'data.accountList': '$accountList' } }, { $replaceRoot: { 'newRoot': '$data' } }, { $project: { 'accountList': 1, _id: 0 } }])
Output : Same as above.
Answer for Old Question :
Ok you've two options here, Please try these :
If you've only one object in accountList which does matches with the given filter then you can simply do this:
db.report.find({'accountList.transactionList.description': /.*aear.*/i}, {'accountList.$': 1})
Output :
/* 1 */
{
"_id" : ObjectId("5d6435145a0d22d3c86df0c7"),
"accountList" : [
{
"id" : "4474",
"transactionList" : [
{
"description" : "aear"
},
{
"description" : "koe"
}
]
}
]
}
/* 2 */
{
"_id" : ObjectId("5d6435145a0d22d3c86df0d7"),
"accountList" : [
{
"id" : "4400",
"transactionList" : [
{
"description" : "aear"
},
{
"description" : "koe"
}
]
}
]
}
/* 3 */
{
"_id" : ObjectId("5d6435145a0d22d3c86df077"),
"accountList" : [
{
"id" : "0000",
"transactionList" : [
{
"description" : "aear"
},
{
"description" : "koe"
}
]
}
]
}
/* 4 */
{
"_id" : ObjectId("5d6435145a0d22d3c86df1c7"),
"accountList" : [
{
"id" : "0101",
"transactionList" : [
{
"description" : "aear"
},
{
"description" : "koe"
}
]
}
]
}
Downside of above .find () query is it would get only first matching object in accountList, If you've multiple matching objects for given filter in accountList then you need to use aggregation (this aggregation query can be used for earlier scenario as well, Please check output for diff) :
db.report.aggregate([
{
$match: {
"accountList.transactionList.description": /.*aear.*/i
}
},
{ $unwind: "$accountList" },
{
$match: {
"accountList.transactionList.description": /.*aear.*/i
}
}, { $group: { _id: '$_id', accountList: { $push: '$accountList' }, doc: { $first: '$$ROOT' } } }, { $addFields: { 'doc.accountList': '$accountList' } },
{ $replaceRoot: { 'newRoot': '$doc' } }
])
Output :
// This first object is best example where you need aggregation
/* 1 */
{
"_id" : ObjectId("5d6435145a0d22d3c86df1c7"),
"accountList" : [
{
"id" : "0101",
"transactionList" : [
{
"description" : "aear"
},
{
"description" : "koe"
}
]
},
{
"id" : "1111",
"transactionList" : [
{
"description" : "aear"
},
{
"description" : "koe"
}
]
}
]
}
/* 2 */
{
"_id" : ObjectId("5d6435145a0d22d3c86df0d7"),
"accountList" : [
{
"id" : "4400",
"transactionList" : [
{
"description" : "aear"
},
{
"description" : "koe"
}
]
}
]
}
/* 3 */
{
"_id" : ObjectId("5d6435145a0d22d3c86df077"),
"accountList" : [
{
"id" : "0000",
"transactionList" : [
{
"description" : "aear"
},
{
"description" : "koe"
}
]
}
]
}
/* 4 */
{
"_id" : ObjectId("5d6435145a0d22d3c86df0c7"),
"accountList" : [
{
"id" : "4474",
"transactionList" : [
{
"description" : "aear"
},
{
"description" : "koe"
}
]
}
]
}

Try this query:
db.report.find({'accountList[0].transactionList[4].description': { $regex: /.*aear.*/i} })
OR - Which will return only the first matching document:
db.report.find({'accountList[0].transactionList[4].description': /.*aear.*/i}).limit(1)

Related

Mongo find query only returns one result

Hello I have the following data structure :
[
{
"name": "a name",
"project": [
{
companyName: "a name",
contactPerson: [
{
work_email: "test#test.com"
}
]
},
{
companyName: "a name1",
contactPerson: [
{
work_email: "test1#test.com"
}
]
},
{
companyName: "a name2",
contactPerson: [
{
work_email: "test2#test.com"
}
]
},
{
companyName: "a name3",
contactPerson: [
{
work_email: "test#test.com"
}
]
},
]
}
]
With this query i want to find all projects that have the email test#test.com :
db.collection.find({
"project.contactPerson.work_email": "test#test.com"
},
{
"project.$": 1
})
It only returns the first result it finds and then it just stops. but in my data i have two projects with that email and i want to find both. here's a playground you can use to further help me if you can. Thanks in advance and much appreciated : https://mongoplayground.net/p/4Mpp7kHi98u
The positional $ operator limits the contents of an to return either:
The first element that matches the query condition on the array.
The first element if no query condition is specified for the array
(Starting in MongoDB 4.4). Ref
You can do something like following,
[
{
"$unwind": "$project"
},
{
$addFields: {
"project.contactPerson": {
$filter: {
input: "$project.contactPerson",
cond: {
$eq: [
"$$this.work_email",
"test#test.com"
]
}
}
}
}
},
{
$match: {
$expr: {
$ne: [
"$project.contactPerson",
[]
]
}
}
},
{
$group: {
_id: "$_id",
name: {
$first: "$name"
},
project: {
"$addToSet": "$project"
}
}
}
]
Working Mongo playground
db.collection.aggregate([
{
$unwind: "$project"
},
{
$match: {
"project.contactPerson.work_email": "test#test.com"
}
},
{
"$group": {
"_id": "$_id",
"name": {
"$first": "$name"
},
"project": {
"$push": {
"companyName": "$project.companyName",
"contactPersion": "$project.contactPerson"
}
}
}
}
])
//step1(problem statement related find):, find shows all projects even one of the array element of contact is matched, use an aggregate function to display specific email ids
> db.test3.find({ "project.contact.email": "abc2#email.com" }).pretty();
{
"_id" : ObjectId("5f43fdc153e34ac6967fe8ce"),
"name" : "Pega Contractors",
"project" : [
{
"pname" : "pname1",
"contact" : [
{
"email" : "xyz1#email.com"
}
]
},
{
"pname" : "pname2",
"contact" : [
{
"email" : "abc2#email.com"
}
]
},
{
"pname" : "pname3",
"contact" : [
{
"email" : "xyz1#email.com"
}
]
}
]
}
--
//aggregate option:
//Step1: data preparation
> db.test3.find().pretty();
{
"_id" : ObjectId("5f43fdc153e34ac6967fe8ce"),
"name" : "Pega Contractors",
"project" : [
{
"pname" : "pname1",
"contact" : [
{
"email" : "xyz1#email.com"
}
]
},
{
"pname" : "pname2",
"contact" : [
{
"email" : "abc2#email.com"
}
]
},
{
"pname" : "pname3",
"contact" : [
{
"email" : "xyz1#email.com"
}
]
}
]
}
>
//step2: aggregate and unwind project for the next step pipeline input
> db.test3.aggregate([ {$unwind: "$project"}]);
{ "_id" : ObjectId("5f43fdc153e34ac6967fe8ce"), "name" : "Pega Contractors", "project" : { "pname" : "pname1", "contact" : [ { "email" : "xyz1#email.com" } ] } }
{ "_id" : ObjectId("5f43fdc153e34ac6967fe8ce"), "name" : "Pega Contractors", "project" : { "pname" : "pname2", "contact" : [ { "email" : "abc2#email.com" } ] } }
{ "_id" : ObjectId("5f43fdc153e34ac6967fe8ce"), "name" : "Pega Contractors", "project" : { "pname" : "pname3", "contact" : [ { "email" : "xyz1#email.com" } ] } }
//step3: Desired outcome, i.e display data specific to email
> db.test3.aggregate([
... {$unwind: "$project"},
... {$match: {"project.contact.email":"xyz1#email.com"}}
... ]);
{ "_id" : ObjectId("5f43fdc153e34ac6967fe8ce"), "name" : "Pega Contractors", "project" : { "pname" : "pname1", "contact" : [ { "email" : "xyz1#email.com" } ] } }
{ "_id" : ObjectId("5f43fdc153e34ac6967fe8ce"), "name" : "Pega Contractors", "project" : { "pname" : "pname3", "contact" : [ { "email" : "xyz1#email.com" } ] } }
> db.test3.aggregate([ {$unwind: "$project"}, {$match: {"project.contact.email":"acb2#email.com"}} ]);
> db.test3.aggregate([ {$unwind: "$project"}, {$match: {"project.contact.email":"abc2#email.com"}} ]);
{ "_id" : ObjectId("5f43fdc153e34ac6967fe8ce"), "name" : "Pega Contractors", "project" : { "pname" : "pname2", "contact" : [ { "email" : "abc2#email.com" } ] } }
>

Mongodb - Array indexed by string in $addToSet operator

Suppose we have these two documents:
{
"_id" : ObjectId("5f3cdd1d0fefeba343ff3093"),
"country" : "C1",
"time" : "1994",
"value" : NumberInt(100),
"type" : "type1",
"origin" : "O1"
}
{
"_id" : ObjectId("5f3cdd1d0fefeba343ff3094"),
"country" : "C1",
"time" : "1994",
"value" : NumberInt(200),
"type" : "type1",
"origin" : "O2"
}
I want to retrieve the aggregation with the origin array indexed by strings (the value of "type"); expected output:
{
"_id" : {
"country" : "C1",
"time" : "1994"
},
"TOT" : NumberInt(300),
"count" : 2.0,
"origin" : [
"O1": NumberInt(100),
"O2": NumberInt(200)
]
}
Here we have the type of the "origin" array as {[key: string]: number}.
With the following query, the origin array is instead indexed by numbers:
use local;
db.getCollection("test_collection").aggregate(
[
{
"$match" : {
"type" : {
"$in" : [
"type1"
]
}
}
},
{
"$group" : {
"_id" : {
"country" : "$country",
"time" : "$time"
},
"TOT" : {
"$sum" : "$value"
},
"count" : {
"$sum" : 1.0
},
"origin" : {
"$addToSet" : "$value"
}
}
}
],
{
"allowDiskUse" : false
}
);
You can try $arrayToObject after adding in origin,
use local;
db.getCollection("test_collection").aggregate([
{ "$match": { "type": { "$in": ["type1"] } } },
{
"$group": {
"_id": {
"country": "$country",
"time": "$time"
},
"TOT": { "$sum": "$value" },
"count": { "$sum": 1.0 },
// add object like this
"origin": {
"$addToSet": {
k: "$origin",
v: "$value"
}
}
}
},
// add this
{ $addFields: { origin: { $arrayToObject: "$origin" } } }
],
{ "allowDiskUse": false }
)
Playground

How to $setDifference in array & Object using Mongo DB

UserDetails
{
"_id" : "5c23536f807caa1bec00e79b",
"UID" : "1",
"name" : "A",
},
{
"_id" : "5c23536f807caa1bec00e78b",
"UID" : "2",
"name" : "B",
},
{
"_id" : "5c23536f807caa1bec00e90",
"UID" : "3",
"name" : "C"
}
UserProducts
{
"_id" : "5c23536f807caa1bec00e79c",
"UPID" : "100",
"UID" : "1",
"status" : "A"
},
{
"_id" : "5c23536f807caa1bec00e79c",
"UPID" : "200",
"UID" : "2",
"status" : "A"
},
{
"_id" : "5c23536f807caa1bec00e52c",
"UPID" : "300",
"UID" : "3",
"status" : "A"
}
Groups
{
"_id" : "5bb20d7556db6915846da55f",
"members" : {
"regularStudent" : [
"200" // UPID
],
}
},
{
"_id" : "5bb20d7556db69158468878",
"members" : {
"regularStudent" : {
"0" : "100" // UPID
}
}
}
Step 1
I have to take UID from UserDetails check with UserProducts then take UPID from UserProducts
Step 2
we have to check this UPID mapped to Groups collection or not ?.
members.regularStudent we are mapped UPID
Step 3
Suppose UPID not mapped means i want to print the UPID from from UserProducts
I have tried but couldn't complete this, kindly help me out on this.
Expected Output:
["300"]
Note: Expected Output is ["300"] , because UserProducts having UPID 100 & 200 but Groups collection mapped only 100& 200.
My Code
var queryResult = db.UserDetails.aggregate(
{
$lookup: {
from: "UserProducts",
localField: "UID",
foreignField: "UID",
as: "userProduct"
}
},
{ $unwind: "$userProduct" },
{ "$match": { "userProduct.status": "A" } },
{
"$project": { "_id" : 0, "userProduct.UPID" : 1 }
},
{
$group: {
_id: null,
userProductUPIDs: { $addToSet: "$userProduct.UPID" }
}
});
let userProductUPIDs = queryResult.toArray()[0].userProductUPIDs;
db.Groups.aggregate([
{
$unwind: "$members.regularStudent"
},
{
$group: {
_id: null,
UPIDs: { $addToSet: "$members.regularStudent" }
}
},
{
$project: {
members: {
$setDifference: [ userProductUPIDs , "$UPIDs" ]
},
_id : 0
}
}
])
My Output
{
"members" : [
"300",
"100"
]
}
You need to fix that second aggregation and get all UPIDs as an array. To achieve that you can use $cond and based on $type either return an array or use $objectToArray to run the conversion, try:
db.Groups.aggregate([
{
$project: {
students: {
$cond: [
{ $eq: [ { $type: "$members.regularStudent" }, "array" ] },
"$members.regularStudent",
{ $map: { input: { "$objectToArray": "$members.regularStudent" }, as: "x", in: "$$x.v" } }
]
}
}
},
{
$unwind: "$students"
},
{
$group: {
_id: null,
UPIDs: { $addToSet: "$students" }
}
},
{
$project: {
members: {
$setDifference: [ userProductUPIDs , "$UPIDs" ]
},
_id : 0
}
}
])

Issue retrieving subdocuments from MongoDB

I have the following dataset:
{
"_id" : ObjectId("59668a22734d1d48cf34de08"),
"name" : "Nobody Cares",
"menus" : [
{
"_id" : "menu_123",
"name" : "Weekend Menu",
"description" : "A menu for the weekend",
"groups" : [
{
"name" : "Spirits",
"has_mixers" : true,
"sizes" : [
"Single",
"Double"
],
"categories" : [
{
"name" : "Vodka",
"description" : "Maybe not necessary?",
"drinks" : [
{
"_id" : "drink_123",
"name" : "Absolut",
"description" : "Fancy ass vodka",
"sizes" : [
{
"_id" : "size_123",
"size" : "Single",
"price" : 300
}
]
}
]
}
]
}
],
"mixers" : [
{
"_id" : "mixer_1",
"name" : "Coca Cola",
"price" : 150
},
{
"_id" : "mixer_2",
"name" : "Lemonade",
"price" : 120
}
]
}
]
}
And I'm attempting to retrieve a single drink from that dataset, I'm using the following aggregate query:
db.getCollection('places').aggregate([
{ $match : {"menus.groups.categories.drinks._id" : "drink_123"} },
{ $unwind: "$menus" },
{ $project: { "_id": 1, "menus": { "groups": { "categories": { "drinks": { "name": 1 } } } } } }
])
However, it's returning the full structure of the dataset along with the correct data.
So instead of:
{
"_id": "drink_123",
"name": "Absolut"
}
I get:
{
"_id": ObjectId("59668a22734d1d48cf34de08"),
"menus": {
"groups": {
"categories": {
"drinks": { "name": "Absolut" }
}
}
}
}
For example. Any ideas how to just retrieve the subdocument?
If you need to retain the deeply nested model then this call will produce the desired output:
db.getCollection('places').aggregate([
{ $match : {"menus.groups.categories.drinks._id" : "drink_123"} },
{ $project: {"_id": '$menus.groups.categories.drinks._id', name: '$menus.groups.categories.drinks.name'}},
{ $unwind: "$name" },
{ $unwind: "$name" },
{ $unwind: "$name" },
{ $unwind: "$name" },
{ $unwind: "$_id" },
{ $unwind: "$_id" },
{ $unwind: "$_id" },
{ $unwind: "$_id" }
])
The numerous unwinds are the result of the deep nesting of the drinks subdocuments.
Though, FWIW, this sort of query does perhaps suggest that the model isn't 'read friendly'.

Aggregate using $group twice

I've read SO and questions like this one. However I'm not able to build the query I want...
Let's say I have the following data structure:
{
"CAUG" : "id1",
"action" : "actionA",
"date" : ISODate("2017-01-01"),
"hp" : 16
}
{
"CAUG" : "id1",
"action" : "actionB",
"date" : ISODate("2017-01-01"),
"hp" : 17
}
{
"CAUG" : "id1",
"action" : "actionC",
"date" : ISODate("2017-02-10"),
"hp" : 18
}
{
"CAUG" : "id2",
"action" : "actionX",
"date" : ISODate("2018-01-01"),
"hp" : 20
}...
The desired output is something like (not sure about brackets and other stuff...):
{
"CAUG" : "id1",
"timeline" : [
ISODate ("2017-01-01) {
{ "action" : "ActionA", hp : "..." }
{ "action" : "ActionB", hp : "..." }
},
ISODate ("2017-02-10) {
{ "action" : "ActionC", hp : "..." }
}
]
}
{
"CAUG" : "id2",
"timeline" : [
ISODate ("2018-01-01) {
{ "action" : "ActionX", hp : "..." }
}
]
}
At this time my (very limited) query is:
(I've tried many things like composite _id, but I'm alway stucked at some point).
db.aggregate(
[
{ $match: { something } },
{ $project: { something } },
{ $group: {
_id: '$CAUG',
"timeline": { "$push": "$$ROOT" }
}
}
]
)
The problem is I do not know how to do another $group inside timeline array... I'm stucked with the output below... Any clue please? Have a nice weekend.
{
"_id" : "1",
"timeline" : [
{
"CAUG" : "ca220491-ug43816",
"action" : "actionA",
"date" : ISODate("2016-12-21T23:00:00.000+0000")
},
{
"CAUG" : "ca220491-ug43816",
"action" : "actionB",
"date" : ISODate("2016-12-21T23:00:00.000+0000")
},
{
"CAUG" : "ca220491-ug43816",
"action" : "actionC",
"date" : ISODate("2017-02-21T23:00:00.000+0000")
}
]
}
Try running the following aggregate operation:
db.collection.aggregate([
{
"$group": {
"_id": {
"CAUG": "$CAUG",
"date": {
"$dateToString": {
"format": "%Y-%m-%d",
"date": "$date"
}
}
},
"docs": {
"$push": {
"action" : "$action",
"hp" : "$hp"
}
}
}
},
{
"$group": {
"_id": "$_id.CAUG",
"timeline": {
"$push": {
"date": "$_id.date",
"docs": "$docs"
}
}
}
}
])
which gives the sample output
/* 1 */
{
"_id" : "id1",
"timeline" : [
{
"date" : "2017-02-10",
"docs" : [
{
"action" : "actionC",
"hp" : 18.0
}
]
},
{
"date" : "2017-01-01",
"docs" : [
{
"action" : "actionA",
"hp" : 16.0
},
{
"action" : "actionB",
"hp" : 17.0
}
]
}
]
}
/* 2 */
{
"_id" : "id2",
"timeline" : [
{
"date" : "2018-01-01",
"docs" : [
{
"action" : "actionX",
"hp" : 20.0
}
]
}
]
}