MongoDB project with lookup - mongodb

I want to get other collection's field using $lookup.
For example, this is my users collection:
{ "_id" : ObjectId("601c4f89049f9d2cc47eece2"), "username" : "Ahmad", "email" : "aaa#aaa.com", "password" : "$2a$08$fAT1G.db9LjXRV7bOPKNzuosWZ3QGJs2rdHIGmPbWtusDA0Qko47e", "tokens" : [ { "_id" : ObjectId("601d4de52472f01ef4552e77"), "token" : "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6IjYwMWM0Zjg5MDQ5ZjlkMmNjNDdlZWNlMiIsImlhdCI6MTYxMjUzMzIyMSwiZXhwIjoxNjEzMTM4MDIxfQ.lEt5mIQkjVCRFvDJsHoK1rwdN-WELSVWMFW3p8itZBU" }, { "_id" : ObjectId("601c4f89049f9d2cc47eece3"), "token" : "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6IjYwMWM0Zjg5MDQ5ZjlkMmNjNDdlZWNlMiIsImlhdCI6MTYxMjQ2ODEwNSwiZXhwIjoxNjEzMDcyOTA1fQ.WwKCenn7Qh3C1DhJT7H1Kcqu4ZRl0Z7SYJxBV6v87S8" } ], "createdAt" : ISODate("2021-02-04T19:48:25.024Z"),
"updatedAt" : ISODate("2021-02-05T13:53:41.730Z"), "__v" : 0 }
{ "_id" : ObjectId("601c954a59477b1d38e8a69e"), "username" : "fred", "email" : "isded#gmail.com", "password" : "$2a$08$nZ2kusIoSdwgjBpcZX4WTe2sO3tnieUcVIDGXiUCofLSvkdSdTRFa", "tokens" : [ { "_id" : ObjectId("601c954a59477b1d38e8a69f"), "token" : "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6IjYwMWM5NTRhNTk0NzdiMWQzOGU4YTY5ZSIsImlhdCI6MTYxMjQ4NTk2MiwiZXhwIjoxNjEzMDkwNzYyfQ.bHkhfBlgswerB25za2y3YODk-cbRgGhlcrtr-zaIG9M" } ], "createdAt" : ISODate("2021-02-05T00:46:02.518Z"), "updatedAt" : ISODate("2021-02-05T00:46:02.518Z"), "__v" : 0 }
And here posts collection:
{ "_id" : ObjectId("601c5190939a1e294c688267"), "title" : "abdulila", "content" : "wqd", "category" : "Health", "owner" : ObjectId("601c4f89049f9d2cc47eece2"), "comments" : [ {
"_id" : ObjectId("601c532a5b746f1ad081c57e"), "content" : "Marchgisio", "owner" : ObjectId("601c4f89049f9d2cc47eece2"), "createdAt" : ISODate("2021-02-04T20:03:54.055Z"), "updatedAt" : ISODate("2021-02-04T20:03:54.055Z") }, { "_id" : ObjectId("601c53445b746f1ad081c57f"), "content" : "Balotelli", "owner" : ObjectId("601c4f89049f9d2cc47eece2"), "createdAt" : ISODate("2021-02-04T20:04:20.967Z"), "updatedAt" : ISODate("2021-02-04T20:04:20.967Z") } ], "createdAt" : ISODate("2021-02-04T19:57:04.902Z"), "updatedAt" : ISODate("2021-02-04T20:04:20.968Z"), "__v" : 2 }
{ "_id" : ObjectId("601c958259477b1d38e8a6a0"), "title" : "ohh mighty spurs", "content" : "harry kane", "category" : "Sports", "owner" : ObjectId("601c954a59477b1d38e8a69e"), "comments" : [ { "_id" : ObjectId("601c958f59477b1d38e8a6a1"), "content" : "eorje", "owner" : ObjectId("601c954a59477b1d38e8a69e"), "createdAt" : ISODate("2021-02-05T00:47:11.461Z"), "updatedAt" : ISODate("2021-02-05T00:47:11.461Z") }, { "_id" : ObjectId("601c95cac3b0131328e29344"), "content" : "qwe", "owner" : ObjectId("601c954a59477b1d38e8a69e"), "createdAt" : ISODate("2021-02-05T00:48:10.207Z"), "updatedAt" : ISODate("2021-02-05T00:48:10.207Z") }, { "_id" : ObjectId("601c95e3af31ab1608abd23f"), "content" : "wef", "owner" : ObjectId("601c954a59477b1d38e8a69e"), "createdAt" : ISODate("2021-02-05T00:48:35.168Z"), "updatedAt" : ISODate("2021-02-05T00:48:35.168Z") } ], "createdAt" : ISODate("2021-02-05T00:46:58.962Z"), "updatedAt" : ISODate("2021-02-05T00:48:35.168Z"), "__v" : 3 }
{ "_id" : ObjectId("601d210fbf41fd262c9172c4"), "title" : "bobo", "content" : "hiv is a lie and in god name we will kill all the jews in balastin XD UwU\n", "category" : "Movies", "owner" : ObjectId("601c954a59477b1d38e8a69e"), "comments" : [ ], "createdAt" : ISODate("2021-02-05T10:42:23.773Z"), "updatedAt" : ISODate("2021-02-05T10:42:23.773Z"), "__v"
: 0 }
{ "_id" : ObjectId("601d4a2f1f169d3d9cbfcbfe"), "title" : "Abdallah", "content" : "dfsv", "category" : "Health", "owner" : ObjectId("601c954a59477b1d38e8a69e"), "comments" : [ { "_id" : ObjectId("601d50d80678f626105f943e"), "content" : "dvd", "owner" : ObjectId("601c4f89049f9d2cc47eece2"), "createdAt" : ISODate("2021-02-05T14:06:16.941Z"), "updatedAt"
: ISODate("2021-02-05T14:06:16.941Z") }, { "_id" : ObjectId("601d531fcc80c13fb832af40"), "content" : "svsf", "owner" : ObjectId("601c4f89049f9d2cc47eece2"), "createdAt" : ISODate("2021-02-05T14:15:59.873Z"), "updatedAt" : ISODate("2021-02-05T14:15:59.873Z") } ], "createdAt" : ISODate("2021-02-05T13:37:51.787Z"), "updatedAt" : ISODate("2021-02-05T14:15:59.874Z"), "__v" : 2 }
My purpose is to get all comments of 1 specific post. Then, for each comment - I want its content and owner username.
This is my try:
Post.aggregate([
{
$match: { _id: mongoose.Types.ObjectId(req.query.postID) },
},
{
$limit: 1,
},
{
$project: {
_id: 0,
'comments.owner': 1,
'comments.content': 1,
},
},
]);
So this gives me back the content and the owner id. But I want the owner username which presents in the users collection. How could I achieve this?

I came up with this pipeline. I'm sure it cloud be improve.
The steps are (commented in the pipeline)
Split comments array so we could concentrate on each one
Lookup for the user info
The lookup brings an array of one element, so I unwind it.
Replace the owner object id with the owner username.
Group comments
Project the needed fields
I test this directly on mongodb with one of your post.
[
{
$match: { _id: ObjectId("601d4a2f1f169d3d9cbfcbfe") },
},
{
$unwind: '$comments' //split comments array so we could concentrate on each one
},
{
$lookup: {
from: 'user',
localField: 'comments.owner', // lookup for the user info
foreignField: '_id',
as: 'owner'
}
},
{
$unwind: '$owner' // the lookup brings an array of one element, so I unwind it.
},
{
$addFields: {
'comments.owner': "$owner.username" //replace the owner object id with the owner username
}
},
{
$group: {
_id: '_id', // group comments
'comments': {$push: '$comments'}
}
},
{
$project:{
_id: 0, // project the needed fields
'comments.content':1,
'comments.owner': 1,
}
}
])

Related

How to find and update an array in the object of array document using mongoose

A query to how to update the items array of object by
finding the breed dachshund
update the name to new-dog
updatedAt to the current time
[{
"_id" : ObjectId("60053b74aa72f132cb75b8b6"),
"clientId" : "test",
"items" : [
{
"_id" : ObjectId("60053b74aa72f132cb75b8b7"),
"name" : "tommy",
"breed" : "dachshund",
"createdAt" : 1610955636,
"updatedAt" : 1610955636
},
{
"_id" : ObjectId("60053b74aa72f132cb75b8b8"),
"name" : "mickey",
"breed" : "husky",
"createdAt" : 1610955636,
"updatedAt" : 1610955636
},
{
"_id" : ObjectId("60053b74aa72f132cb75b8b9"),
"name" : "whiskey",
"breed" : "dachshund",
"createdAt" : 1610955636,
"updatedAt" : 1610955636
},
{
"_id" : ObjectId("60053b74aa72f132cb75b8ba"),
"name" : "milo",
"breed" : "bulldog",
"createdAt" : 1610955636,
"updatedAt" : 1610955636
},
{
"_id" : ObjectId("60053b74aa72f132cb75b8bb"),
"name" : "pooh",
"breed" : "poodle",
"createdAt" : 1610955636,
"updatedAt" : 1610955636
}
],
"shopname" : "myshopify"
}]
const curDatetime = moment(moment();
model.updateOne(
{_id: ObjectId("60053b74aa72f132cb75b8b6")},
{
arrayFilters: [
{'el.breed': 'dachshund'}
]
},
{
$set: {
'items.$[el].name': 'new-dog',
'items.$[el].updatedAt': curDatetime
}
}, "multi": true)

Mongodb subdocument fields union

I need to join two collection from mongodb. First of all I have an aggregation like this:
db.messages.aggregate([
{
$lookup: {
from :"channels",
localField: "channels",
foreignField: "_id",
as: "merged_channels"
}
}
]).pretty()
after this aggragation my documents looks like this:
{
"_id" : "21ca6117-1f14-4613-9407-db7f3a011142",
"author" : "03072fad-a8fd-53f3-b25f-abbfaf15b055",
"title" : "test2",
"body" : "test2",
"channels" : [
"8008d5a8-eb3b-4e55-98c5-a60fd7275bd2",
"8008d5a8-eb3b-4e55-98c5-a60fd7275bd3"
],
"comments" : [
{
"author" : "03072fad-a8fd-53f3-b25f-abbfaf15b055",
"body" : "comment1",
"created_at" : ISODate("2018-03-15T07:08:10.018Z")
},
{
"author" : "03072fad-a8fd-53f3-b25f-abbfaf15b055",
"body" : "testbody",
"created_at" : ISODate("2018-03-15T07:08:09.366Z")
}
],
"created_at" : ISODate("2018-03-15T07:08:09.018Z"),
"updated_at" : ISODate("2018-03-15T07:08:09.366Z"),
"deleted_at" : null,
"merged_channels" : [
{
"_id" : "8008d5a8-eb3b-4e55-98c5-a60fd7275bd2",
"author" : "03072fad-a8fd-53f3-b25f-abbfaf15b055",
"name" : "chan2",
"members" : [
{
"id" : "8008d5a8-eb3b-4e55-98c5-a60fd7275cb5",
"type" : "group"
}
],
"created_at" : ISODate("2018-03-15T07:08:08.872Z"),
"updated_at" : ISODate("2018-03-15T07:08:08.872Z"),
"deleted_at" : null
},
{
"_id" : "8008d5a8-eb3b-4e55-98c5-a60fd7275bd3",
"author" : "8008d5a8-eb3b-4e55-98c5-a60fd7275fg4",
"name" : "chan3",
"members" : [
{
"id" : "8008d5a8-eb3b-4e55-98c5-a60fd7275cb5",
"type" : "group"
},
{
"id" : "8008d5a8-eb3b-4e55-98c5-a60fd7275cb6",
"type" : "user"
},
{
"id" : "8008d5a8-eb3b-4e55-98c5-a60fd7275cb7",
"type" : "role"
}
],
"created_at" : ISODate("2018-03-15T07:08:08.872Z"),
"updated_at" : ISODate("2018-03-15T07:08:09.358Z"),
"deleted_at" : null
}
]
}
I want to take members fields from chan2 and chan3, union them and put into the root (message) document. And then remove merged_channels field. Remove the merged field is not a problem, but have no idea how to extract fields from subobject and merge them.
How would I achieve this?
You can use below aggregation in 3.4.
$reduce to $concatArrays and project with exclusion to drop the merged_channels field.
db.col.aggregate({
"$addFields":{
"merged_arrays":{
"$reduce":{
"input":"$merged_channels",
"initialValue":[],
"in":{"$concatArrays":["$$value", "$$this.members"]}
}
}
},
{"$project":{"merged_channels":0}}
})

Add additional properties to the output json

There is a collection of User
{
"_id" : ObjectId("5a0d45ca8af3a91847b7cf95"),
"updatedAt" : ISODate("2017-11-16T09:34:14.651Z"),
"createdAt" : ISODate("2017-11-16T08:01:14.119Z"),
"name" : "John",
"email" : "test1#gmail.com",
"groupsFavorite" : [
ObjectId("5a0d45db8af3a91847b7cf96")
],
"groups" : [
ObjectId("5a0d45db8af3a91847b7cf96"),
ObjectId("5a0d45e18af3a91847b7cf97")
],
"__v" : 3
}
There is a collection of Groups
/* 1 */
{
"_id" : ObjectId("5a0d45db8af3a91847b7cf96"),
"updatedAt" : ISODate("2017-11-16T08:01:31.815Z"),
"createdAt" : ISODate("2017-11-16T08:01:31.815Z"),
"userId" : ObjectId("5a0d45ca8af3a91847b7cf95"),
"title" : "New title",
"slug" : "new-title-1",
"description" : "Lorem",
"__v" : 0
}
/* 2 */
{
"_id" : ObjectId("5a0d45e18af3a91847b7cf97"),
"updatedAt" : ISODate("2017-11-16T08:01:37.005Z"),
"createdAt" : ISODate("2017-11-16T08:01:37.005Z"),
"userId" : ObjectId("5a0d45ca8af3a91847b7cf95"),
"title" : "New title",
"slug" : "new-title-2",
"description" : "Lorem",
"__v" : 0
}
/* 3 */
{
"_id" : ObjectId("5a0d5cb0cd59342da943d55a"),
"updatedAt" : ISODate("2017-11-16T09:38:56.912Z"),
"createdAt" : ISODate("2017-11-16T09:38:56.912Z"),
"userId" : ObjectId("5a0d5c48cd59342da943d559"),
"title" : "New title",
"slug" : "new-title-3",
"description" : "Lorem",
"__v" : 0
}
There is a method that returns groups of a particular user.
It is necessary to add these data to the new property of favorite: true or false for building a table.
For example:
{
"_id" :"5a0d45db8af3a91847b7cf96",
"updatedAt" : "2017-11-16T08:01:31.815Z",
"createdAt" : "2017-11-16T08:01:31.815Z",
"userId" : "5a0d45ca8af3a91847b7cf95",
"title" : "New title",
"slug" : "new-title-1",
"description" : "Lorem",
"favorite": "true"
},
{
"_id" : "5a0d45e18af3a91847b7cf97",
"updatedAt" : "2017-11-16T08:01:37.005Z",
"createdAt" : "2017-11-16T08:01:37.005Z",
"userId" : "5a0d45ca8af3a91847b7cf95",
"title" : "New title",
"slug" : "new-title-2",
"description" : "Lorem",
"favorite": "false"
},
{
"_id" : "5a0d5cb0cd59342da943d55a",
"updatedAt" : "2017-11-16T09:38:56.912Z",
"createdAt" : "2017-11-16T09:38:56.912Z",
"userId" : "5a0d5c48cd59342da943d559",
"title" : "New title",
"slug" : "new-title-3",
"description" : "Lorem",
"favorite": "false"
}
You can use $lookup operator for checking the _id and userId fields in "User" collection. And also for determining it is in or not, you can use $eq operator.
db.getCollection('Groups').aggregate([
{
$lookup:
{
from: "User",
localField: "_id",
foreignField: "groupsFavorite",
as: "FavoriteByGrp"
}
}
,{
$lookup:
{
from: "User",
localField: "userId",
foreignField: "_id",
as: "FavoriteByUsr"
}
}
,{
"$project":
{
_id:1,
updatedAt:1,
createdAt:1,
userId:1,
title:1,
slug:1,
description:1,
favorite:
{
"$cond":
{
if: { "$eq": [ "$FavoriteByGrp._id", "$FavoriteByUsr._id" ] },
then: "true",
else: "false"
}
}
}
}
])
Result:
/* 1 */
{
"_id" : ObjectId("5a0d45db8af3a91847b7cf96"),
"updatedAt" : ISODate("2017-11-16T08:01:31.815Z"),
"createdAt" : ISODate("2017-11-16T08:01:31.815Z"),
"userId" : ObjectId("5a0d45ca8af3a91847b7cf95"),
"title" : "New title",
"slug" : "new-title-1",
"description" : "Lorem",
"favorite" : "true"
}
/* 2 */
{
"_id" : ObjectId("5a0d45e18af3a91847b7cf97"),
"updatedAt" : ISODate("2017-11-16T08:01:37.005Z"),
"createdAt" : ISODate("2017-11-16T08:01:37.005Z"),
"userId" : ObjectId("5a0d45ca8af3a91847b7cf95"),
"title" : "New title",
"slug" : "new-title-2",
"description" : "Lorem",
"favorite" : "false"
}
/* 3 */
{
"_id" : ObjectId("5a0d5cb0cd59342da943d55a"),
"updatedAt" : ISODate("2017-11-16T09:38:56.912Z"),
"createdAt" : ISODate("2017-11-16T09:38:56.912Z"),
"userId" : ObjectId("5a0d5c48cd59342da943d559"),
"title" : "New title",
"slug" : "new-title-3",
"description" : "Lorem",
"favorite" : "false"
}

MongoDB $lookup on array of object

I have 2 collections structured as below. I have tried $lookup to get the result but I am not getting any result because of my local and foreign fields are in array of object.
Below is my structure:
{
"_id" : ObjectId("5795a3531d3f3afc19caefef"),
"name" : "category1",
"updatedAt" : "1469431592786",
"resources" : [
{
"_id" : ObjectId("5791be003fa3bedc15d3adde"),
"title" : "resource1",
"availability" : false
},
{
"_id" : ObjectId("5795a3771d3f3afc19caeff0"),
"title" : "resource2",
"availability" : true
}
]
}
Above "categories" schema have resources array of object. this resource _id is stored in bookings collection in following way:
"booking":
{
"_id" : ObjectId("57960aa8000ca7a46b7ef683"),
"name" : "1469491200000",
"__v" : 0,
"schedule" : [
{
"resourceId" : ObjectId("5791be003fa3bedc15d3adde"),
"userId" : ObjectId("5791be003fa3bedc15d3adcve"),
"title" : "grofep",
"_id" : ObjectId("57960aa8f9f9951c1fc923b1")
},
{
"resourceId" : ObjectId("5791be003fa3bedc15d3bddz"),
"userId" : ObjectId("5791be003fa3bedc15d3adcve"),
"title" : "mr3",
"_id" : ObjectId("57960aa8f9f9951c1fc923b2")
},
{
"resourceId" : ObjectId("5791be003fa3bedc15d3adde"),
"userId" : ObjectId("5791be003fa3bedc15d3adcve"),
"title" : "grofep23",
"_id" : ObjectId("57960aa8f9f9951c1fc923b3")
}
]
}
Now I want to get all the schedule of booking collection with their resource information.I want to fetch resources from categories table on the basis of booking schedule.
Desired output:
[
{
"name" : "1469491200000",
"resourceId" : ObjectId("5791be003fa3bedc15d3adde"),
"resourceTitle":"title",
"availability":false,
"bookings": [
{
"userId" : ObjectId("5791be003fa3bedc15d3adcve"),
"title" : "grofep",
"_id" : ObjectId("57960aa8f9f9951c1fc923b1")
},
{
"userId" : ObjectId("5791be003fa3bedc15d3adcve"),
"title" : "grofep23",
"_id" : ObjectId("57960aa8f9f9951c1fc923b3")
}
]
},
{
"name" : "1469491200000",
"resourceId" : ObjectId("5791be003fa3bedc15d3bddz"),
"resourceTitle":"mr3",
"availability":false,
"bookings": [
{
"userId" : ObjectId("5791be003fa3bedc15d3adcve"),
"title" : "mr3",
"_id" : ObjectId("57960aa8f9f9951c1fc923b2")
}
]
}
]
Help me to get this desired result.
Thanks.

Get document based on multiple criteria of embedded collection

I have the following document, I need to search for multiple items from the embedded collection"items".
Here's an example of a single SKU
db.sku.findOne()
{
"_id" : NumberLong(1192),
"description" : "Uploaded via CSV",
"items" : [
{
"_id" : NumberLong(2),
"category" : DBRef("category", NumberLong(1)),
"description" : "840 tag visual",
"name" : "840 Visual Mini Round",
"version" : NumberLong(0)
},
{
"_id" : NumberLong(7),
"category" : DBRef("category", NumberLong(2)),
"description" : "Maxi",
"name" : "Maxi",
"version" : NumberLong(0)
},
{
"_id" : NumberLong(11),
"category" : DBRef("category", NumberLong(3)),
"description" : "Button",
"name" : "Button",
"version" : NumberLong(0)
},
{
"_id" : NumberLong(16),
"category" : DBRef("category", NumberLong(4)),
"customizationFields" : [
{
"_class" : "CustomizationField",
"_id" : NumberLong(1),
"displayText" : "Custom Print 1",
"fieldName" : "customPrint1",
"listOrder" : 1,
"maxInputLength" : 12,
"required" : false,
"version" : NumberLong(0)
},
{
"_class" : "CustomizationField",
"_id" : NumberLong(2),
"displayText" : "Custom Print 2",
"fieldName" : "customPrint2",
"listOrder" : 2,
"maxInputLength" : 17,
"required" : false,
"version" : NumberLong(0)
}
],
"description" : "2 custom lines of farm print",
"name" : "Custom 2",
"version" : NumberLong(2)
},
{
"_id" : NumberLong(20),
"category" : DBRef("category", NumberLong(5)),
"description" : "Color Red",
"name" : "Red",
"version" : NumberLong(0)
}
],
"skuCode" : "NF-USDA-XC2/SM-BC-R",
"version" : 0,
"webCowOptions" : "840miniwithcust2"
}
There are repeat items.id throughout the embedded collection. Each Sku is made up of multiple items, all combinations are unique, but one item will be part of many Skus.
I'm struggling with the query structure to get what I'm looking for.
Here are a few things I have tried:
db.sku.find({'items._id':2},{'items._id':7})
That one only returns items with the id of 7
db.sku.find({items:{$all:[{_id:5}]}})
That one doesn't return anything, but it came up when looking for solutions. I found about it in the MongoDB manual
Here's an example of a expected result:
sku:{ "_id" : NumberLong(1013),
"items" : [ { "_id" : NumberLong(5) },
{ "_id" : NumberLong(7) },
{ "_id" : NumberLong(12) },
{ "_id" : NumberLong(16) },
{ "_id" :NumberLong(2) } ] },
sku:
{ "_id" : NumberLong(1014),
"items" : [ { "_id" : NumberLong(5) },
{ "_id" : NumberLong(7) },
{ "_id" : NumberLong(2) },
{ "_id" : NumberLong(16) },
{ "_id" :NumberLong(24) } ] },
sku:
{ "_id" : NumberLong(1015),
"items" : [ { "_id" : NumberLong(5) },
{ "_id" : NumberLong(7) },
{ "_id" : NumberLong(12) },
{ "_id" : NumberLong(2) },
{ "_id" :NumberLong(5) } ] }
Each Sku that comes back has both a item of id:7, and id:2, with any other items they have.
To further clarify, my purpose is to determine how many remaining combinations exist after entering the first couple of items.
Basically a customer will start specifying items, and we'll weed it down to the remaining valid combinations. So Sku.items[0].id=5 can only be combined with items[1].id=7 or items[1].id=10 …. Then items[1].id=7 can only be combined with items[2].id=20 … and so forth
The goal was to simplify my rules for purchase, and drive it all from the Sku codes. I don't know if I dug a deeper hole instead.
Thank you,
On the part of extracting the sku with item IDs 2 and 7, when I recall correctly, you have to use $elemMatch:
db.sku.find({'items' :{ '$all' :[{ '$elemMatch':{ '_id' : 2 }},{'$elemMatch': { '_id' : 7 }}]}} )
which selects all sku where there is each an item with _id 2 and 7.
You can use aggregation pipelines
db.sku.aggregate([
{"$unwind": "$sku.items"},
{"$group": {"_id": "$_id", "items": {"$addToSet":{"_id": "$items._id"}}}},
{"$match": {"items._id": {$all:[2,7]}}}
])