Mongo merge results - mongodb

Hello guys I have 3 collections that have dbref, where I wanted to mount a result similar to the one shown below but I couldn't get to something satifatorio using any aggregate can you indicate a better way?
Coolections:
data1 - Main data_reference1
referenced in data1 data_reference2
referenced in data_reference2
/* EXAMPLE RECORD data1 */
{
"_id" : ObjectId("604fafd443487df824aeca61"),
"name" : "doc1",
"reference_data1_list" : [
{
"data_reference" : ObjectId("604fb00643487df824aeca91"),
"user_add" : "USER01"
},
{
"data_reference" : ObjectId("604fb00d43487df824aeca9b"),
"user_add" : "USER01"
},
{
"data_reference" : ObjectId("604fb01743487df824aecaa7"),
"user_add" : "USER02"
}
]
}
/* EXAMPLE RECORDS data_reference1 */
/* 1 */
{
"_id" : ObjectId("604fb00643487df824aeca91"),
"reference_desc" : "Test Referenced Field 01",
"reference_data2" : ObjectId("604fb24743487df824aecd14")
}
/* 2 */
{
"_id" : ObjectId("604fb00d43487df824aeca9b"),
"reference_desc" : "Test Referenced Field 02",
"reference_data2" : ObjectId("604fb24743487df824aecd14")
}
/* 3 */
{
"_id" : ObjectId("604fb01743487df824aecaa7"),
"reference_desc" : "Test Referenced Field 03",
"reference_data2" : ObjectId("604fb25743487df824aecd27")
}
/* EXAMPLE RECORDS data_reference2 */
/* 1 */
{
"_id" : ObjectId("604fb24743487df824aecd14"),
"name" : "Student"
}
/* 2 */
{
"_id" : ObjectId("604fb25743487df824aecd27"),
"name" : "Theacher"
}
Desired result:
{
"_id": ObjectId("604fafd443487df824aeca61"),
"name": "doc1",
"reference_data1_list": [
{
"_id": ObjectId("604fb00643487df824aeca91"),
"reference_desc": "Test Referenced Field 01",
"reference_data2": {
"_id": ObjectId("604fb24743487df824aecd14"),
"name": "Student"
}
},
{
"_id": ObjectId("604fb00d43487df824aeca9b"),
"reference_desc": "Test Referenced Field 02",
"reference_data2": {
"_id": ObjectId("604fb24743487df824aecd14"),
"name": "Student"
}
},
{
"_id": "604fb01743487df824aecaa7",
"reference_desc": "Test Referenced Field 03",
"reference_data2": {
"_id": ObjectId("604fb25743487df824aecd27"),
"name": "Theacher"
}
}
]
}
I don't have much skill with the mongo and I didn't find any similar example to be able to use in the example.

Try this query:
db.data1.aggregate([
{ $unwind: "$reference_data1_list" },
{
$lookup: {
from: "data_reference1",
let: {
data_ref: "$reference_data1_list.data_reference",
user_add: "$reference_data1_list.user_add"
},
pipeline: [
{
$match: {
$expr: { $eq: ["$_id", "$$data_ref"] }
}
},
{
$addFields: { user_add: "$$user_add" }
}
],
as: "reference_data1_list"
}
},
{ $unwind: "$reference_data1_list" },
{
$lookup: {
from: "data_reference2",
localField: "reference_data1_list.reference_data2",
foreignField: "_id",
as: "reference_data1_list.reference_data2"
}
},
{ $unwind: "$reference_data1_list.reference_data2" },
{
$group: {
_id: "$_id",
name: { $first: "$name" },
reference_data1_list: { $push: "$reference_data1_list" }
}
}
]);
Output
{
"_id" : ObjectId("604fafd443487df824aeca61"),
"name" : "doc1",
"reference_data1_list" : [
{
"_id" : ObjectId("604fb00643487df824aeca91"),
"reference_desc" : "Test Referenced Field 01",
"reference_data2" : {
"_id" : ObjectId("604fb24743487df824aecd14"),
"name" : "Student"
},
"user_add" : "USER01"
},
{
"_id" : ObjectId("604fb00d43487df824aeca9b"),
"reference_desc" : "Test Referenced Field 02",
"reference_data2" : {
"_id" : ObjectId("604fb24743487df824aecd14"),
"name" : "Student"
},
"user_add" : "USER01"
},
{
"_id" : ObjectId("604fb01743487df824aecaa7"),
"reference_desc" : "Test Referenced Field 03",
"reference_data2" : {
"_id" : ObjectId("604fb25743487df824aecd27"),
"name" : "Theacher"
},
"user_add" : "USER02"
}
]
}

Related

How to $lookup in nested array of objects

I have to two collections one is tours and other is destinations so in tours have i have an array of locations which has destination object with an id and that id is belongs to another destinations collection but the thing is i am not be able to lookup details of destination in the array of locations. every tried many query search here too. but not getting expected result.
Tours :
{
"_id" : ObjectId("5f3122f4d8d57e3b9650e5b4"),
"title" : "tour 1",
"locations" : [
{
"destination" : {
"id" : "5ec5ae9037ea99f20a79071a"
},
"services" : {
"hotel" : true
}
},
{
"destination" : {
"id" : "5ec5ae8e37ea99f20a78ef8c"
},
"services" : {
"hotel" : true
}
}
]
}
{
"_id" : ObjectId("5f2d65e68bc6e9155310d147"),
"title" : "tour 2",
"locations" : [
{
"destination" : {
"id" : "5ecf994435c3a6025d5bf126"
},
"services" : {
"hotel" : true
}
}
]
}
{
"_id" : ObjectId("5f2d66398bc6e9155310d161"),
"title" : "tour 3",
"locations" : [
{
"destination" : {
"id" : "5ec5ae8e37ea99f20a78ef8d"
},
"services" : {
"hotel" : true
}
}
]
}
Destinations :
{
"_id" : ObjectId("5ec5ae9037ea99f20a79071a"),
"name" : "dest 1",
"country" : "country name"
}
{
"_id" : ObjectId("5ec5ae8e37ea99f20a78ef8c"),
"name" : "dest 2",
"country" : "country name"
}
{
"_id" : ObjectId("5ec5ae8e37ea99f20a78ef8d"),
"name" : "dest 3",
"country" : "country name"
}
{
"_id" : ObjectId("5ecf994435c3a6025d5bf126"),
"name" : "dest 4",
"country" : "country name"
}
Expected result :
{
"_id" : ObjectId("5f3122f4d8d57e3b9650e5b4"),
"title" : "tour 1",
"locations" : [
{
"destination" : {
"id" : "5ec5ae9037ea99f20a79071a",
"name" : "dest 1",
"country" : "country name"
},
"services" : {
"hotel" : true
}
},
{
"destination" : {
"id" : "5ec5ae8e37ea99f20a78ef8c",
"name" : "dest 2",
"country" : "country name"
},
"services" : {
"hotel" : true
}
}
]
},
{
"_id" : ObjectId("5f2d65e68bc6e9155310d147"),
"title" : "tour 2",
"locations" : [
{
"destination" : {
"id" : "5ecf994435c3a6025d5bf126",
"name" : "dest 4",
"country" : "country name"
},
"services" : {
"hotel" : true
}
}
]
},
{
"_id" : ObjectId("5f2d66398bc6e9155310d161"),
"title" : "tour 3",
"locations" : [
{
"destination" : {
"id" : "5ec5ae8e37ea99f20a78ef8d",
"name" : "dest 3",
"country" : "country name"
},
"services" : {
"hotel" : true
}
}
]
}
Tried query :
db.tours.aggregate([
{
"$addFields": {
"locations": {
"$map": {
"input": "$locations",
"in": {
"$mergeObjects": [
"$$this",
{
"dest_oid": {
"$toObjectId": "$$this.destination.id"
}
}
]
}
}
}
}
},
{ "$unwind": "$locations" },
{ "$lookup": {
"from": "destinations",
"localField": "locations.dest_oid",
"foreignField": "_id",
"as": "locations.dest",
}},
{ "$unwind": "$locations.dest" },
{ "$group": {
"_id": "$_id",
"locations": { "$push": "$locations" }
}}
])
even i have tried this
MongoDB $lookup on nested document
Quick fixes,
$unwind locations array put first because need to convert id to object id
db.tours.aggregate([
{ $unwind: "$locations" },
you skip this part if you have already converted string id t object id
$addFields replace locations.destination.id to object id filtered your logic to short, here no need $map and $mergeObjects options
{
$addFields: {
"locations.destination.id": {
$toObjectId: "$locations.destination.id"
}
}
},
$lookup that you have already did, but change as locations.destination
{
$lookup: {
from: "destinations",
as: "locations.destination",
localField: "locations.destination.id",
foreignField: "_id"
}
},
$unwind locations.destination because its array and we need object
{
$unwind: {
path: "$locations.destination"
}
},
$group that you have already did, few changes, push first destination and services in locations and add first title
{
$group: {
_id: "$_id",
locations: { $push: "$locations" },
title: { $first: "$title" }
}
}
])
Playground: https://mongoplayground.net/p/yaTCij7NRUj
If you can convert/update the string destination.id in the Tours collection to ObjectId if they are not already. Following query should work.
Query:
db.Tours.aggregate([
{
$unwind: "$locations",
},
{
$lookup: {
from: "Destinations",
localField: "locations.destination.id",
foreignField: "_id",
as: "destination",
},
},
{
$project: {
title: "$title",
locations: {
destination: {
$arrayElemAt: ["$destination", 0],
},
services: "$locations.services",
},
},
},
{
$group: {
_id: "$_id",
title: {
$first: "$title",
},
locations: {
$push: "$locations",
},
},
},
]);
Playground Link

MongoDB lookup when foreign field is an array of ids

I have two collections, fruit and salesman . I want my query to return all fruit with comma separated salesman.
Salesman document have array of fruit id
fruit document have id, name ,........
salesman table have id,name, fruits[apple_id,mango_id.......],...
db.getCollection('fruit').aggregate([{ "$unwind": "$fruits" }, { "$lookup": {
"from": "salesman",
"localField": "fruits",
"foreignField": "_id",
"as": "fruitObjects"
}},
{ "$unwind": "$fruitObjects" } ])
even this query is not giving result of $fruitObjects..?
Fruit Document
{
"_id" : ObjectId("5b101caddcab7850a4ba32eb"),
"name" : "Mango"
}
{
"_id" : ObjectId("5b101caddcab7850a4ba32ec"),
"name" : "Pears"
}
{
"_id" : ObjectId("5b101caddcab7850a4ba32de"),
"name" : "apple"
}
{
"_id" : ObjectId("5b101caddcab7850a4ba32fe"),
"name" : "guava"
}
Salesman document
{
"_id" : ObjectId("5b101caddcab7850a4ba3257"),
"name" : "xyz",
"fruits":["5b101caddcab7850a4ba32ec","5b101caddcab7850a4ba32de","5b101caddcab7850a4ba32fe"]
}
{
"_id" : ObjectId("5b101caddcab7850a4ba3258"),
"name" : "abc",
"fruits":["5b101caddcab7850a4ba32eb","5b101caddcab7850a4ba32de"]
}
{
"_id" : ObjectId("5b101caddcab7850a4ba3259"),
"name" : "def",
"fruits":["5b101caddcab7850a4ba32ec"]
}
{
"_id" : ObjectId("5b101caddcab7850a4ba3260"),
"name" : "zxc",
"fruits":["5b101caddcab7850a4ba32ec","5b101caddcab7850a4ba32de","5b101caddcab7850a4ba32eb"]
}
``````````````````````````
Yes #barrypicker is correct - types of fields didn't match which is resulting in empty on salesman, Please try to store both of same type. Meanwhile you can actually convert one to other type - while querying each time, Also you need not to do $unwind, Please try below query :
Query 1:
db.fruit.aggregate([
{
$lookup:
{
from: "salesman",
let: { fruitName: { $toString: '$_id' } },
pipeline: [
{
$match:
{
$expr:
{ $in: ["$$fruitName", "$fruits"] }
}
}, { $project: { name: 1, _id: 0 } }
],
as: "salesman"
}
}])
Result for Query 1:
/* 1 */
{
"_id" : ObjectId("5b101caddcab7850a4ba32eb"),
"name" : "Mango",
"salesman" : [
{
"name" : "abc"
},
{
"name" : "zxc"
}
]
}
/* 2 */
{
"_id" : ObjectId("5b101caddcab7850a4ba32ec"),
"name" : "Pears",
"salesman" : [
{
"name" : "def"
},
{
"name" : "zxc"
}
]
}
/* 3 */
{
"_id" : ObjectId("5b101caddcab7850a4ba32de"),
"name" : "apple",
"salesman" : [
{
"name" : "abc"
},
{
"name" : "zxc"
}
]
}
/* 4 */
{
"_id" : ObjectId("5b101caddcab7850a4ba32fe"),
"name" : "guava",
"salesman" : []
}
Or if you want those names to be in an array :
Query 2 :
db.fruit.aggregate([
{
$lookup:
{
from: "salesman",
let: { fruitName: { $toString: '$_id' } },
pipeline: [
{
$match:
{
$expr:
{ $in: ["$$fruitName", "$fruits"] }
}
}, { $project: { name: 1, _id: 0 } }
],
as: "salesmanList"
}
}, {
$project: {
name: 1, salesman: {
$map:
{
input: "$salesmanList",
as: "each",
in: '$$each.name'
}
}
}
}])
Result for Query 2:
/* 1 */
{
"_id" : ObjectId("5b101caddcab7850a4ba32eb"),
"name" : "Mango",
"salesman" : [
"abc",
"zxc"
]
}
/* 2 */
{
"_id" : ObjectId("5b101caddcab7850a4ba32ec"),
"name" : "Pears",
"salesman" : [
"def",
"zxc"
]
}
/* 3 */
{
"_id" : ObjectId("5b101caddcab7850a4ba32de"),
"name" : "apple",
"salesman" : [
"abc",
"zxc"
]
}
/* 4 */
{
"_id" : ObjectId("5b101caddcab7850a4ba32fe"),
"name" : "guava",
"salesman" : []
}
fruit collection :
/* 1 */
{
"_id" : ObjectId("5b101caddcab7850a4ba32eb"),
"name" : "Mango"
}
/* 2 */
{
"_id" : ObjectId("5b101caddcab7850a4ba32ec"),
"name" : "Pears"
}
/* 3 */
{
"_id" : ObjectId("5b101caddcab7850a4ba32de"),
"name" : "apple"
}
/* 4 */
{
"_id" : ObjectId("5b101caddcab7850a4ba32fe"),
"name" : "guava"
}
salesman collection :
/* 1 */
{
"_id" : ObjectId("5b101caddcab7850a4ba3258"),
"name" : "abc",
"fruits" : [
"5b101caddcab7850a4ba32eb",
"5b101caddcab7850a4ba32de"
]
}
/* 2 */
{
"_id" : ObjectId("5b101caddcab7850a4ba3259"),
"name" : "def",
"fruits" : [
"5b101caddcab7850a4ba32ec"
]
}
/* 3 */
{
"_id" : ObjectId("5b101caddcab7850a4ba3260"),
"name" : "zxc",
"fruits" : [
"5b101caddcab7850a4ba32ec",
"5b101caddcab7850a4ba32de",
"5b101caddcab7850a4ba32eb"
]
}
If you want the entire object from salesman then you could remove { $project: { name: 1, _id: 0 } } in $lookup.
Ref : $lookup, $map
Does this help?
db.salesman.aggregate([
{ $unwind : "$fruits" },
{ $addFields : { "fruitObjectId": { $toObjectId: "$fruits" } } },
{ $lookup : {
"from" : "fruit",
"localField" : "fruitObjectId",
"foreignField" : "_id",
"as" : "fruitObjects"
}
}
])
Or, perhaps from the opposite perspective?
db.fruit.aggregate([
{ $project : {
_id : { $toString : "$_id" },
name : 1
}
},
{ $lookup : {
"from" : "salesman",
"localField" : "_id",
"foreignField" : "fruits",
"as" : "fruitObjects"
}
}
])

MongoDB Keep path where a criteria is met

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)

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'.