MongoDB nested lookup with 3 levels - mongodb

I need to retrieve the entire single object hierarchy from the database as a JSON. Actually, the proposal about any other solution to achieve this result would be highly appreciated. I decided to use MongoDB with its $lookup support.
So I have three collections:
party
{ "_id" : "2", "name" : "party2" }
{ "_id" : "5", "name" : "party5" }
{ "_id" : "4", "name" : "party4" }
{ "_id" : "1", "name" : "party1" }
{ "_id" : "3", "name" : "party3" }
address
{ "_id" : "a3", "street" : "Address3", "party_id" : "2" }
{ "_id" : "a6", "street" : "Address6", "party_id" : "5" }
{ "_id" : "a1", "street" : "Address1", "party_id" : "1" }
{ "_id" : "a5", "street" : "Address5", "party_id" : "5" }
{ "_id" : "a2", "street" : "Address2", "party_id" : "1" }
{ "_id" : "a4", "street" : "Address4", "party_id" : "3" }
addressComment
{ "_id" : "ac2", "address_id" : "a1", "comment" : "Comment2" }
{ "_id" : "ac1", "address_id" : "a1", "comment" : "Comment1" }
{ "_id" : "ac5", "address_id" : "a5", "comment" : "Comment6" }
{ "_id" : "ac4", "address_id" : "a3", "comment" : "Comment4" }
{ "_id" : "ac3", "address_id" : "a2", "comment" : "Comment3" }
I need to retrieve all parties with all corresponding addresses and address comments as part of the record. My aggregation:
db.party.aggregate([{
$lookup: {
from: "address",
localField: "_id",
foreignField: "party_id",
as: "address"
}
},
{
$unwind: "$address"
},
{
$lookup: {
from: "addressComment",
localField: "address._id",
foreignField: "address_id",
as: "address.addressComment"
}
}])
The result is pretty weird. Some records are ok. But Party with _id: 4 is missing (there is no address for it). Also, there are two Party _id: 1 in the result set (but with different addresses):
{
"_id": "1",
"name": "party1",
"address": {
"_id": "2",
"street": "Address2",
"party_id": "1",
"addressComment": [{
"_id": "3",
"address_id": "2",
"comment": "Comment3"
}]
}
}{
"_id": "1",
"name": "party1",
"address": {
"_id": "1",
"street": "Address1",
"party_id": "1",
"addressComment": [{
"_id": "1",
"address_id": "1",
"comment": "Comment1"
},
{
"_id": "2",
"address_id": "1",
"comment": "Comment2"
}]
}
}{
"_id": "3",
"name": "party3",
"address": {
"_id": "4",
"street": "Address4",
"party_id": "3",
"addressComment": []
}
}{
"_id": "5",
"name": "party5",
"address": {
"_id": "5",
"street": "Address5",
"party_id": "5",
"addressComment": [{
"_id": "5",
"address_id": "5",
"comment": "Comment5"
}]
}
}{
"_id": "2",
"name": "party2",
"address": {
"_id": "3",
"street": "Address3",
"party_id": "2",
"addressComment": [{
"_id": "4",
"address_id": "3",
"comment": "Comment4"
}]
}
}
Please help me with this. I'm pretty new to MongoDB but I feel it can do what I need from it.

The cause of your 'troubles' is the second aggregation stage - { $unwind: "$address" }. It removes record for party with _id: 4 (because its address array is empty, as you mention) and produces two records for parties _id: 1 and _id: 5 (because each of them has two addresses).
To prevent removing of parties without addresses you should set preserveNullAndEmptyArrays option of $unwind stage to true.
To prevent duplicating of parties for its different addresses you should add $group aggregation stage to your pipeline. Also, use $project stage with $filter operator to exclude empty address records in output.
db.party.aggregate([{
$lookup: {
from: "address",
localField: "_id",
foreignField: "party_id",
as: "address"
}
}, {
$unwind: {
path: "$address",
preserveNullAndEmptyArrays: true
}
}, {
$lookup: {
from: "addressComment",
localField: "address._id",
foreignField: "address_id",
as: "address.addressComment",
}
}, {
$group: {
_id : "$_id",
name: { $first: "$name" },
address: { $push: "$address" }
}
}, {
$project: {
_id: 1,
name: 1,
address: {
$filter: { input: "$address", as: "a", cond: { $ifNull: ["$$a._id", false] } }
}
}
}]);

With the mongodb 3.6 and above $lookup syntax it is quite simple to join nested fields without using $unwind.
db.party.aggregate([
{ "$lookup": {
"from": "address",
"let": { "partyId": "$_id" },
"pipeline": [
{ "$match": { "$expr": { "$eq": ["$party_id", "$$partyId"] }}},
{ "$lookup": {
"from": "addressComment",
"let": { "addressId": "$_id" },
"pipeline": [
{ "$match": { "$expr": { "$eq": ["$address_id", "$$addressId"] }}}
],
"as": "address"
}}
],
"as": "address"
}},
{ "$unwind": "$address" }
])

Related

Stucking with nested array lookup in MongoDB

I'm trying to lookup query from nested array in mongodb and I'm getting stuck.
I have total threee collections.
(1) Channel (Parent)
(2) ChannelThreads (Children)
(3) Users
Channel Collection:
{
"_id" : ObjectId("61efcbdc1aa27f83da47c93f"),
"tags" : [],
"slug_history" : [
"iny1Xik"
],
"title" : "Pirate Chat",
"settingId" : ObjectId("61408586b719c8ce89f08674"),
"status" : "published",
"lockedPageContent" : "",
"slug" : "iny1Xik",
"createdAt" : ISODate("2022-01-25T10:07:24.144Z"),
"updatedAt" : ISODate("2022-01-25T10:07:24.144Z"),
"__v" : 0
}
Channel Thread Collection:
{
"_id" : ObjectId("61efcd5df82318884746eb80"),
"threadImage" : [],
"parentId" : null,
"channelId" : ObjectId("61efcbdc1aa27f83da47c93f"),
"authorId" : ObjectId("6177de8f8a5fd72a4f37b7db"),
"threadText" : "New Message",
"reactions" : [
{
"authors" : [
ObjectId("3687de8f8a5fd72a4f37b7bg")
],
"_id" : ObjectId("61ef856432753c196382c37d"),
"icon" : "&#128528"
}
],
"createdAt" : ISODate("2022-01-25T10:13:49.033Z"),
"updatedAt" : ISODate("2022-01-25T10:13:49.033Z"),
"__v" : 0
}
User Collection:
{
"_id" : ObjectId("6177de8f8a5fd72a4f37b7db"),
"image" : "",
"tags" : [],
"pushTokens" : [],
"lastLogin" : ISODate("2022-01-25T10:08:19.055Z"),
"firstName" : "dinesh",
"lastName" : "patel",
"email" : "dineshpatel#example.com",
"infusionSoftId" : "784589",
"role" : "user",
"__v" : 0,
"settings" : {
"commentNotification" : false,
"commentReplyNotification" : true
}
}
I'm trying to implement lookup for authors of thread reactions.
Expected Output:
{
"_id": ObjectId("61efcbdc1aa27f83da47c93f"),
"tags": [],
"slug_history": [
"iny1Xik"
],
"title": "Pirate Chat",
"settingId": ObjectId("61408586b719c8ce89f08674"),
"status": "published",
"lockedPageContent": "",
"slug": "iny1Xik",
"createdAt": ISODate("2022-01-25T10:07:24.144Z"),
"updatedAt": ISODate("2022-01-25T10:07:24.144Z"),
"__v": 0,
"threads": [
{
"_id": ObjectId("61efcd5df82318884746eb80"),
"threadImage": [],
"parentId": null,
"channelId": ObjectId("61efcbdc1aa27f83da47c93f"),
"authorId": {
"_id": ObjectId("6177de8f8a5fd72a4f37b7db"),
"image": "",
"tags": [],
"pushTokens": [],
"lastLogin": ISODate("2022-01-25T10:08:19.055Z"),
"firstName": "dinesh",
"lastName": "patel",
"email": "dineshpatel#example.com",
"infusionSoftId": "something",
"role": "user",
"__v": 0,
"settings": {
"commentNotification": false,
"commentReplyNotification": true
}
},
"threadText": "New Message",
"reactions": [
{
"authors": [
{
"_id": ObjectId("3687de8f8a5fd72a4f37b7bg"),
"image": "",
"tags": [],
"pushTokens": [],
"lastLogin": ISODate("2022-01-25T10:08:19.055Z"),
"firstName": "kayle",
"lastName": "hell",
"email": "kylehell#example.com",
"infusionSoftId": "8475151",
"role": "user",
"__v": 0,
"settings": {
"commentNotification": false,
"commentReplyNotification": true
}
}
],
"_id": ObjectId("61ef856432753c196382c37d"),
"icon": "&#128528"
}
],
"createdAt": ISODate("2022-01-25T10:13:49.033Z"),
"updatedAt": ISODate("2022-01-25T10:13:49.033Z"),
"__v": 0
}
]
}
How can write lookup query for reaction authors.
Thanks in advance!!
You can try nested lookup,
$lookup with channel thread collection, pass channel id in let
$match to match channelId condition
$lookup with user collection to get author info for authorId
$lookup with user collection to get reactions's authors info
$arrayElemAt to get first element from authorId
$map to iterate loop of reactions array, $filter to iterate loop of users and get matching author user info from users array,
$mergeObjects to merge authors and current object properties
$$REMOVE to remove users field because it is not needed now
db.channel.aggregate([
{
$lookup: {
from: "channelThread",
let: { channelId: "$_id" },
pipeline: [
{ $match: { $expr: { $eq: ["$$channelId", "$channelId"] } } },
{
$lookup: {
from: "user",
localField: "authorId",
foreignField: "_id",
as: "authorId"
}
},
{
$lookup: {
from: "user",
localField: "reactions.authors",
foreignField: "_id",
as: "users"
}
},
{
$addFields: {
authorId: { $arrayElemAt: ["$authorId", 0] },
reactions: {
$map: {
input: "$reactions",
as: "r",
in: {
$mergeObjects: [
"$$r",
{
authors: {
$filter: {
input: "$users",
cond: { $in: ["$$this._id", "$$r.authors"] }
}
}
}
]
}
}
},
users: "$$REMOVE"
}
}
],
as: "threads"
}
}
])
Playground

Mongodb aggregate lookup three collections

Learning MongoDB for the past two days and I am trying to aggregate three collections but unable to achieve it
Below are the three collection maintaining in the database
t_credentials
{
"_id" : "619ca68b624c41e408348406",
"title" : "Company ID"
}
t_groups
{
"_id" : "61a253da88ca12a37218898d",
"group_name" : "Gold"
}
t_user_credentials
{
"_id" : "619ca88a624c41e408348424",
"credential_id" : "619ca68b624c41e408348406",
"group_id" : "61a253da88ca12a37218898d",
"identifiers" : {
"first_name" : "Lee",
"middle_name" : "Min",
"last_name" : "Ho"
},
"created_at" : "2021-12-01T17:20:49.000Z"
}
Here I am trying to achieve the output in the below format:
Expected Output
[{
"_id" : "619ca88a624c41e408348424",
"first_name" : ,
"middle_name" : ,
"last_name" : ,
"credential" : {
"_id:" : "619ca68b624c41e408348406",
"title" : "Company ID"
},
"group" : {
"_id" : "61a253da88ca12a37218898d",
"group_name" : "Gold"
},
"created_at" : "2021-12-01T17:20:49.000Z"
}]
But, I am getting the fields only from t_user_credentials but not able to get like in the above format
Query
db.t_user_credentials.aggregate([
{
$lookup: {
from: "t_credentials",
localField: "_id",
foreignField: "credential_id",
as: "credentials"
}
},
{
$unwind: {
path:'$credentials',
preserveNullAndEmptyArrays: true
}
},
{
$lookup: {
from: "t_groups",
localField: "_id",
foreignField: "group_id",
as: "groups"
}
},
{
$unwind: {
path: '$groups',
preserveNullAndEmptyArrays: true
}
},
{
$project: {
last_name: "$identifiers.last_name",
first_name: "$identifiers.first_name",
middle_name: "$identifiers.middle_name",
"credentials.title": 1,
created_at: 1,
group_id: 1
}
}
])
Can any one help me to solve this issue, it will be very helpful for me.
This query uses $replaceWith to merge the identifiers sub-document into the $$ROOT document. We also use $unset to remove fields we are no longer interested in. Before all of that we make sure to unwind our credential and group fields.
You can check out a live demo of this query here
Consider the following:
Database
db={
"t_credentials": [
{
"_id": "619ca68b624c41e408348406",
"title": "Company ID"
}
],
"t_groups": [
{
"_id": "61a253da88ca12a37218898d",
"group_name": "Gold"
}
],
"t_user_credentials": [
{
"_id": "619ca88a624c41e408348424",
"credential_id": "619ca68b624c41e408348406",
"group_id": "61a253da88ca12a37218898d",
"identifiers": {
"first_name": "Lee",
"middle_name": "Min",
"last_name": "Ho"
},
"created_at": "2021-12-01T17:20:49.000Z"
}
]
}
Query
db.t_user_credentials.aggregate([
{
"$lookup": {
"from": "t_credentials",
"localField": "credential_id",
"foreignField": "_id",
"as": "credential"
}
},
{
"$lookup": {
"from": "t_groups",
"localField": "group_id",
"foreignField": "_id",
"as": "group"
}
},
{
$unwind: "$group",
},
{
$unwind: "$credential"
},
{
$replaceWith: {
$mergeObjects: [
"$$ROOT",
"$identifiers"
]
}
},
{
$unset: [
"group_id",
"credential_id",
"identifiers"
]
}
])
Result
[
{
"_id": "619ca88a624c41e408348424",
"created_at": "2021-12-01T17:20:49.000Z",
"credential": {
"_id": "619ca68b624c41e408348406",
"title": "Company ID"
},
"first_name": "Lee",
"group": {
"_id": "61a253da88ca12a37218898d",
"group_name": "Gold"
},
"last_name": "Ho",
"middle_name": "Min"
}
]

Mongodb $lookup with nested document

I am trying to create a join using mongo's lookup. I have these three collections.
orderTracking
{
_id: ObejctId("59fb7815b3b8429f4750b0df"),
itemName : "Hamam Soap",
TrackLocation: [{locationId: 1, at:"2017-10-11"},
{locationId: 2,at:"2017-10-13"}],
userId : 12,
price: 20
}
locationType
{
_id: ObejctId("59b2111345cb72345a35fefd"),
locationId : 1
productTypeName: "Warehouse"
},{
_id: ObejctId("59af8ce445cb72345a35feea"),
locationId : 2
productTypeName: "On Transit"
}
User
{
_id: ObejctId("59a504eb6171b554c02292a9"),
"user ID":12,
"userName" : "Shahabaz Shafi",
"dateOfBirth" : "1992-01-01",
"addres": {
"country" : "India",
"state" : "Karnataka",
"city" : "Bengaluru"
}
}
and trying to flatten this to this kind of output.
{
"userName" : "Shahabaz Shafi",
"userId":12,
"dateOfBirth" : "1992-01-01",
"country" : "India",
"state" : "Karnataka",
"city" : "Bengaluru"
"locationType" : [ {productTypeName: "Warehouse",at:"2017-10-11"}, {productTypeName: "On Transit",at:"2017-10-13"}]
}
Edit: 15-11-2018 Updated output
Made some changes to the output columns
{
"userName":"Shahabaz Shafi",
"userId":12,
"dateOfBirth":"1992-01-01",
"country":"India",
"state":"Karnataka",
"city":"Bengaluru",
"items":[
{
"itemName":"Hamam Soap",
"userId":12,
"price":20,
"TrackLocation":[
{
"locationId":1,
"at":"2017-10-11",
"productTypeName":"Warehouse"
},
{
"locationId":2,
"at":"2017-10-13",
"productTypeName":"On Transit"
}
]
}
]
}
How do I approach this ?
PS : I am also using compass
You can use below aggregation with mongodb 3.6 and above
db.User.aggregate([
{ "$lookup": {
"from": "orderTracking",
"let": { "userId": "$userId" },
"pipeline": [
{ "$match": { "$expr": { "$eq": ["$userId", "$$userId"] }}},
{ "$unwind": "$TrackLocation" },
{ "$lookup": {
"from": "locationType",
"let": { "location": "$TrackLocation.locationId" },
"pipeline": [
{ "$match": { "$expr": { "$eq": ["$locationId", "$$location"] }}}
],
"as": "locationType"
}},
{ "$project": {
"_id": 0,
"productTypeName": { "$arrayElemAt": ["$locationType.productTypeName", 0] },
"at": "$TrackLocation.at"
}}
],
"as": "locationType"
}},
{ "$replaceRoot": { "newRoot": { "$mergeObjects": ["$addres", "$$ROOT"] }}},
{ "$project": { "addres": 0 }}
])
Output
[
{
"_id": ObjectId("59a504eb6171b554c02292a9"),
"city": "Bengaluru",
"country": "India",
"dateOfBirth": "1992-01-01",
"locationType": [
{
"at": "2017-10-11",
"productTypeName": "Warehouse"
},
{
"at": "2017-10-13",
"productTypeName": "On Transit"
}
],
"state": "Karnataka",
"userId": 12,
"userName": "Shahabaz Shafi"
}
]

Mongodb aggregate three collections

Learning MongoDB for the past two days and I am trying to aggregate three collections but unable to achieve it
Below are the four collection maintaining in the database
university
{
"_id" : "5834ecf7432d92675bde9d82",
"name": "NIFT"
}
college
{
"_id" : "5834ecf7432d92675bde9d83",
"name": "NIFT Hyderabad",
"university_id":"5834ecf7432d92675bde9d82"
}
departments
{
"_id" : "5834ecf7432d92675bde9d84",
"department_name": "Fashion Technology",
"college_id" : "5834ecf7432d92675bde9d83"
},
{
"_id" : "5834ecf7432d92675bde9d85",
"department_name": "Merchandising",
"college_id" : "5834ecf7432d92675bde9d83"
}
Sections
{
"_id" : "5834ecf7432d92675bde9d86",
"section_name": "A",
"students" : "56",
"department_id":"5834ecf7432d92675bde9d84"
},
{
"_id" : "5834ecf7432d92675bde9d87",
"section_name": "B",
"students" : "60",
"department_id":"5834ecf7432d92675bde9d84"
},
{
"_id" : "5834ecf7432d92675bde9d86",
"section_name": "A",
"students" : "55",
"department_id":"5834ecf7432d92675bde9d85"
},
{
"_id" : "5834ecf7432d92675bde9d87",
"section_name": "B",
"students" : "44",
"department_id":"5834ecf7432d92675bde9d85"
}
Here I am trying to achieve the output in the below format
Expected Output
[{
"_id": "5834ecf7432d92675bde9d83",
"name": "NIFT Hyderabad",
"university_id": "5834ecf7432d92675bde9d82",
"departments": [{
"_id": "5834ecf7432d92675bde9d84",
"department_name": "CSE",
"college_id": "5834ecf7432d92675bde9d83",
"sections": [{
"_id": "5834ecf7432d92675bde9d86",
"section_name": "A",
"students": "56",
"department_id": "5834ecf7432d92675bde9d84"
}, {
"_id": "5834ecf7432d92675bde9d87",
"section_name": "B",
"students": "60",
"department_id": "5834ecf7432d92675bde9d84"
}]
},
{
"_id": "5834ecf7432d92675bde9d85",
"department_name": "Mechanical",
"college_id": "5834ecf7432d92675bde9d83",
"sections": [{
"_id": "5834ecf7432d92675bde9d86",
"section_name": "A",
"students": "55",
"department_id": "5834ecf7432d92675bde9d85"
},
{
"_id": "5834ecf7432d92675bde9d87",
"section_name": "B",
"students": "44",
"department_id": "5834ecf7432d92675bde9d85"
}
]
}
]
}]
But, I am getting department and sections in separate arrays for college but not able to get like in the above format
Query
db.college.aggregate([
{"$match": { "university_id": "5834ecf7432d92675bde9d82" } },
{"$lookup": {
"localField": "_id",
"from": "departments",
"foreignField": "college_id",
"as": "departments"
}},
{"$unwind":"$departments"},
{$group : {_id : "$_id", departments : {$push : "$departments" }}},
{"$lookup": {
"localField": "departments._id",
"from": "sections",
"foreignField": "department_id",
"as": "sections"}
}
])
Can any one help me to solve this issue, it will be very helpful for me.
You can try below aggregation query.
The below query pushes the sections into department when they are joined and $group to push department to create the final structure.
db.college.aggregate([
{
"$match": {
"university_id": "5834ecf7432d92675bde9d82"
}
},
{
"$lookup": {
"localField": "_id",
"from": "departments",
"foreignField": "college_id",
"as": "departments"
}
},
{
"$unwind": {
"path": "$departments",
"preserveNullAndEmptyArrays": true
}
},
{
"$lookup": {
"localField": "departments._id",
"from": "sections",
"foreignField": "department_id",
"as": "departments.sections"
}
},
{
"$group": {
"_id": "$_id",
"name": {
"$first": "$name"
},
"university_id": {
"$first": "$university_id"
},
"departments": {
"$push": "$departments"
}
}
}
])

mongo $unwind and $group

I have two collections. One of which I wish to add a reference to the other and have it populated on return.
Here is an example json I am trying to achieve as the result:
{
"title": "Some Title",
"uid": "some-title",
"created_at": "1412159926",
"updated_at": "1412159926",
"id": "1",
"metadata": {
"date": "2016-10-17",
"description": "a description"
},
"tags": [
{
"name": "Tag 1",
"uid": "tag-1"
},
{
"name": "Tag 2",
"uid": "tag-2"
},
{
"name": "Tag 3",
"uid": "tag-3"
}
]
}
Here is the mongo query I have which gets my close, but it nests the original body of the item within the _id object.
db.tracks.aggregate([{
$unwind: "$tags"
}, {
$lookup: {
from: "tags",
localField: "tags",
foreignField: "_id",
as: "tags"
}
}, {
$unwind: "$tags"
}, {
$group: {
"_id": {
"title": "$title",
"uid": "$uid",
"metadata": "$metadata"
},
"tags": {
"$push": "$tags"
}
}
}])
So the result is this:
{
"_id" : {
"title" : "Some Title",
"uid" : "some-title",
"metadata" : {
"date" : "2016-10-17",
"description" : "a description"
}
},
"tags" : [
{
"_id" : ObjectId("580499d06fe29ce7093fb53a"),
"name" : "Tag 1",
"uid" : "tag-1"
},
{
"_id" : ObjectId("580499d06fe29ce7093fb53b"),
"name" : "Tag 2",
"uid" : "tag-2"
}
]
}
Is there a way to achieve the desired output? Also is there a way to not have to define in the $group all the fields which I wish to return, I would like to return the original Object but with the referenced documents in the tags array.
Since you had initially pivoted your original documents on the tags array field which means the documents will be denormalized, your $group pipeline should
use the _id field as its _id key and access the other fields using the $first or $last operator.
The group pipeline operator is similar to the SQL's GROUP BY clause. In SQL, you can't use GROUP BY unless you use any of the aggregation functions. The same way, we have to use an aggregation function in MongoDB as well, so unfortunately there is no other way of not having to define in the $group pipeline all the fields which you wish to return apart from using the $first or $last operator on each field:
db.tracks.aggregate([
{ "$unwind": "$tags" },
{
"$lookup": {
"from": "tags",
"localField": "tags",
"foreignField": "_id",
"as": "resultingArray"
}
},
{ "$unwind": "$resultingArray" },
{
"$group": {
"_id": "$_id",
"title": { "$first": "$title" },
"uid": { "$first": "$uid" },
"created_at": { "$first": "$created_at" },
"updated_at": { "$first": "$updated_at" },
"id": { "$first": "$id" },
"metadata": { "$first": "$metadata" },
"tags": { "$push": "$resultingArray" }
}
}
])
One trick I always use whenever I want to debug a pipeline that's giving unexpected results is to run the aggregation with just the first pipeline operator. If that gives the expected result, add the next.
In the answer above, you'd first try aggregating just the $unwind; if that works, add the $lookup. This can help you narrow down which operator is causing issues. In this case, you could run the pipeline with just the first three steps since you believe the $group is the one causing issues and then inspect the resulting documents from that pipeline:
db.tracks.aggregate([
{ "$unwind": "$tags" },
{
"$lookup": {
"from": "tags",
"localField": "tags",
"foreignField": "_id",
"as": "resultingArray"
}
},
{ "$unwind": "$resultingArray" }
])
which yields the output
/* 1 */
{
"_id" : ObjectId("5804a6c900ce8cbd028523d9"),
"title" : "Some Title",
"uid" : "some-title",
"created_at" : "1412159926",
"updated_at" : "1412159926",
"id" : "1",
"metadata" : {
"date" : "2016-10-17",
"description" : "a description"
},
"resultingArray" : {
"name" : "Tag 1",
"uid" : "tag-1"
}
}
/* 2 */
{
"_id" : ObjectId("5804a6c900ce8cbd028523d9"),
"title" : "Some Title",
"uid" : "some-title",
"created_at" : "1412159926",
"updated_at" : "1412159926",
"id" : "1",
"metadata" : {
"date" : "2016-10-17",
"description" : "a description"
},
"resultingArray" : {
"name" : "Tag 2",
"uid" : "tag-2"
}
}
/* 3 */
{
"_id" : ObjectId("5804a6c900ce8cbd028523d9"),
"title" : "Some Title",
"uid" : "some-title",
"created_at" : "1412159926",
"updated_at" : "1412159926",
"id" : "1",
"metadata" : {
"date" : "2016-10-17",
"description" : "a description"
},
"resultingArray" : {
"name" : "Tag 3",
"uid" : "tag-3"
}
}
From inspection you will see that for each input document, the last pipeline outputs 3 documents where 3 is the number of array elements in the computed field resultingArray and they all have a common _id and the other fields with the exception of the resultingArray field which is different, thus you get your desired results by adding a pipeline that groups the documents by the _id field and subsequently getting the other fields with $first or $last operator, as in the given solution:
db.tracks.aggregate([
{ "$unwind": "$tags" },
{
"$lookup": {
"from": "tags",
"localField": "tags",
"foreignField": "_id",
"as": "resultingArray"
}
},
{ "$unwind": "$resultingArray" },
{
"$group": {
"_id": "$_id",
"title": { "$first": "$title" },
"uid": { "$first": "$uid" },
"created_at": { "$first": "$created_at" },
"updated_at": { "$first": "$updated_at" },
"id": { "$first": "$id" },
"metadata": { "$first": "$metadata" },
"tags": { "$push": "$resultingArray" }
}
}
])