MongoDB aggregate $lookup with _ID's from array - mongodb

I have two collections in MongoDB: "carts" and another "products"
Carts:
[{
"_id": {
"$oid": "62af0fefebc0b42a875c7df1"
},
"uuid": "6ca05ae0-522a-4db3-b380-2d2330ee1e27",
"cartitems": [
"62a0b24680cc2891148daf7b",
"62a7339d91d01868921afa0a",
"62a72f7191d01868921afa08",
"62a7330291d01868921afa09"
],
"created": "2022-06-19T14:00:47.846958537+02:00[Europe/Brussels]",
"lastupdate": "2022-06-19T14:01:06.15165564+02:00[Europe/Brussels]"
},
{...},
{...}]
products:
[{
"_id": {
"$oid": "62a0b24680cc2891148daf7b"
},
"name": "Product1",
"created": "2022-06-11T09:41:54.461308647+02:00[Europe/Brussels]",
"category": "Workshops",
"pricein": "28900",
"lastupdate": "2022-06-17T16:09:53.385655474+02:00[Europe/Brussels]"
},
{...},
{...}]
I would like to use Aggregate:
db.carts.aggregate([
{ $match: { uuid: "6ca05ae0-522a-4db3-b380-2d2330ee1e27" } },
{
$lookup: {
from: "products",
localField: "cartitems",
foreignField: "_id",
as: "output"
}
}
])
This is not working because localField: "cartitems" should be converted:
"$addFields": {
"convertedIdStr": {
"$toString": "$cartitems"
}
But I don't manage to convert because cartitems is an array.
Any help would be great, Thanks a lot!

Use $lookup with pipeline. In $lookup pipeline stage, add $match stage by filtering the (converted to string) product's _id is in cartitems (array) variable.
db.carts.aggregate([
{
$match: {
uuid: "6ca05ae0-522a-4db3-b380-2d2330ee1e27"
}
},
{
$lookup: {
from: "products",
let: {
cartitems: "$cartitems"
},
pipeline: [
{
$match: {
$expr: {
$in: [
{
$toString: "$_id"
},
"$$cartitems"
]
}
}
}
],
as: "output"
}
}
])
Sample Mongo Playground

Related

How to Group By and Count values inside multi layered array in mongoDB?

I have very complicated Document of MongoDB
For example: Order Document >>>
{
"_id": "62cdbae0421b250009acc329",
"cartitems": "62cdbaaf74c9c80009f5a4b2",
},
{
"_id": "62d27e192b254600099ae680",
"cartitems": "62d27d9d91568c0009866d23",
}
and cart Document >>>
{
"_id": "62cdbaaf74c9c80009f5a4b2",
"cartItems": [
{
"productCode": [
"NCK-1"
],
"Price": "56",
},
{
"productCode": [
"NCK-2"
],
"Price": "56",
}
],
},
{
"_id": "62d27d9d91568c0009866d23",
"cartItems": [
{
"productCode": [
"NCK-3"
],
"Price": "56",
},
{
"productCode": [
"NCK-1"
],
"Price": "56",
}
],
},
I want to join Order Document Order.cartitems with Cart._id and groupby ProductCode and Count Product Code and Sum Price i.e In total the result must look like
NCK-1 112
NCK-2 56
NCK-3 56
I tried the following code >>>
Order.aggregate([
{
$lookup: {
from: Cart.collection.name,
localField: 'cartitems',
foreignField: '_id',
as: 'cartitems',
},
},
{ $unwind: '$cartitems' },
{
$group: {
_id: '$cartitems.cartItems.productCode',
count: { $sum: '$cartitems.cartItems.Price' },
},
},
]);
I could not find the solution please guide me in solving this.
$lookup
$unwind
$unwind - Deconstruct the cartitems.cartItems array into multiple documents.
$group - Since there is only one item in cartitems.cartItems.productCode, can consider using $first to take the first value of the array. And you need to cast cartitems.cartItems.Price to number type before sum.
db.order.aggregate([
{
$lookup: {
from: "cart",
localField: "cartitems",
foreignField: "_id",
as: "cartitems"
}
},
{
$unwind: "$cartitems"
},
{
$unwind: "$cartitems.cartItems"
},
{
$group: {
_id: {
$first: "$cartitems.cartItems.productCode"
},
count: {
$sum: {
$toInt: "$cartitems.cartItems.Price"
}
}
}
}
])
Sample Mongo Playground

Request to check all array elements

I have a problem with MongoDB syntax.
I have two documents:
alley(the "tree" field is the ID of the tree):
{
"_id": {"$oid": "62572d82cc40164fef7f1a56"},
"name": "good alley",
"tree": [
{"$oid": "626976eb4b93122bc617d701"},
{"$oid": "626976eb4b93122bc617d702"}
]
},
.......
tree:
{
"_id": {"$oid": "626976eb4b93122bc617d701"},
"dateInstall": {"$date": "2021-02-27T00:00:00.000Z"},
"species": [
{"$oid": "62585a63edfc726a4ff24fb8"}
]
},
.......
I need to write a query "an alley where trees were not planted last year"
My Code
db.alley.aggregate([
{
$lookup: {
from: "tree",
localField: "tree",
foreignField: "_id",
as: "tree"
}
},
{
$match: {{$not:{$and:[
{"tree.dateInstall": {$gt: new ISODate("2020-12-31")}},
{"tree.dateInstall": {$lt: new ISODate("2022-01-01")}}
]
}}}
}
]);
You should first $unwind trees in alleys so you can properly $lookup the trees in tree collection. Use pipeline inside lookup to query only trees planted last year. Finally $group trees into alleys again and use $match to filter out those alleys without trees.
db.getCollection("alley").aggregate([
{
$unwind: "$tree",
},
{
$lookup: {
from: "tree",
let: { tree: "$tree" },
pipeline: [
{
$match: {
$expr: {
$and: [
{ $eq: [ "$$tree", "$_id" ] },
{ $gt: [ "$dateInstall", new ISODate("2020-12-31") ] },
{ $lt: [ "$dateInstall", new ISODate("2022-01-01") ] },
]
}
}
}
],
localField: "tree",
foreignField: "_id",
as: "tree"
}
},
{
$group: {
_id: { id: "$_id", name: "$name" },
trees: { $addToSet: { $first: "$tree" } }
}
},
{
$match: {
trees: { $size: 0 }
}
}
])

Aggregate not working as expected in mongoose

Here's a lookup query that i'm using
{
$lookup: {
from: 'weeks',
localField: 'weeks',
foreignField: '_id',
as: 'weeks'
}
}
Result with this query
"weeks": [
{
"_id": "619e87d7b1bd6501c7aae286",
"name": "week-1",
"description": "Commodo in o.",
"course": "619e87d7b1bd6501c7aae281",
"days": [
"619e87dab1bd6501c7aae2a8",
"619e87dab1bd6501c7aae2a9",
"619e87dab1bd6501c7aae2aa",
"619e87dab1bd6501c7aae2ab",
"619e87dab1bd6501c7aae2ac",
"619e87dab1bd6501c7aae2ad"
],
"isCopy": false,
"__v": 0
},
{
"_id": "619e87d7b1bd6501c7aae287",
"name": "week-2",
"description": "Irure e.",
"course": "619e87d7b1bd6501c7aae281",
"days": [
"619e87dab1bd6501c7aae2db",
"619e87dab1bd6501c7aae2dc",
"619e87dab1bd6501c7aae2dd",
"619e87dab1bd6501c7aae2de",
"619e87dab1bd6501c7aae2df",
"619e87dab1bd6501c7aae2e0"
],
"isCopy": false,
"__v": 0
},]
In the above lookup localField weeks is an array of object Id's.
When i execute this code it works as expected, but i want to use the same functionality with pipeline.
Here's the code i wrote
{
$lookup: {
from: "weeks",
let: { wks: "$weeks" },
pipeline: [
{
$match: {
_id: {
$in: ["$$wks"]
}
}
}
],
as: "weeks"
}
}
Result with this query `weeks:[]`
When i run this, i don't get anything in output, the reason for that in my opinion is that weeks array is being interpreted as a string instead of object id.
How do i fix it now ...
Is this what you need ? Check my mongoplayground below, if not, make your own sample data and share a new link with me.
db.collection.aggregate([
{
$lookup: {
from: "weeks",
let: {
wks: "$weeks"
},
pipeline: [
{
$match: {
$expr: {
$in: [
"$_id",
"$$wks"
]
}
}
}
],
as: "weeks"
}
}
])
mongoplayground

How to do an aggregation and lookup on double nested array in MongoDB?

I have three collections named issue, category and article, they are defined as follows:
// issue
{
"_id": "612775fbd237d9769a9fc3e4",
"title": "Weekly Issue1",
"data": [
{
"categoryId": ObjectId("61272e7dd237d9769a9fc3d9"),
"articles": [ ObjectId("61272f29d237d9769a9fc3da"), ...]
},
...
]
}
]
}
// category
{
"_id" : "61272e7dd237d9769a9fc3d9",
"name" : "News"
}
// article
{
"_id" : "61272f29d237d9769a9fc3da",
"title" : "Some News",
"url" : "https://www.google.com"
}
I would like to do an aggregation on issue and hope to get JSON results like this:
{
"id": "612775fbd237d9769a9fc3e4",
"tittle": "Weekly Issue1",
"data": [
{
"categoryId": "61272e7dd237d9769a9fc3d9",
"categoryName": "News"
"articles": [ {id: "61272f29d237d9769a9fc3da", title:"Some News", url:"https://www.google.com"}, ...]
},
...
]
}
]
}
My question is how to write the aggregation script? I am just a newbie to MongoDB, I have no idea about it now.
db.issue.aggregate([])
Thanks :-)
$unwind deconstruct data array
$lookup with article collection
$lookup with category collection
$addFields to edit categoryName field, $arrayElemAt to get first element from result of category
$group by _id and reconstruct the data array and return required fields by $first operator
db.issue.aggregate([
{ $unwind: "$data" },
{
$lookup: {
from: "article", // replace your original collection name
localField: "data.articles",
foreignField: "_id",
as: "data.articles"
}
},
{
$lookup: {
from: "category", // replace your original collection name
localField: "data.categoryId",
foreignField: "_id",
as: "data.categoryName"
}
},
{
$addFields: {
"data.categoryName": { $arrayElemAt: ["$data.categoryName.name", 0] }
}
},
{
$group: {
_id: "$_id",
title: { $first: "$title" },
data: { $push: "$data" }
}
}
])
Playground
use this aggregate
I think you could use $project instead of $addField but in test, I got some issue, and use addFields
[
{
'$lookup': {
'from': 'category',
'localField': 'data.0.categoryId',
'foreignField': '_id',
'as': 'dataCategory'
}
}, {
'$lookup': {
'from': 'article',
'localField': 'data.0.articles',
'foreignField': '_id',
'as': 'dataArticles'
}
}, {
'$unwind': {
'path': '$dataCategory'
}
}, {
'$addFields': {
'data': [
{
'categoryId': '$dataCategory._id',
'categoryName': '$dataCategory.name',
'articles': '$dataArticles'
}
]
}
}, {
'$project': {
'data': 1,
'tittle': 1
}
}
]
Because you have a nested array, you can do also a nested $lookup
with the first $lookup we add the category information
with the second nested $lookup we add the articles info
(the middle $addFields is to keep from the joined-category only its own articles)
Test code here
db.issue.aggregate([
{
"$lookup": {
"from": "category",
"let": {"data": "$data"},
"pipeline": [
{"$match": {"$expr": {"$in": ["$_id","$$data.categoryId"]}}},
{
"$addFields": {
"articles": {
"$arrayElemAt": [
{
"$filter": {
"input": "$$data",
"as": "d",
"cond": {"$eq": ["$$d.categoryId","$_id"]
}
}
},0]}}
},
{
"$lookup": {
"from": "article",
"localField": "articles.articles",
"foreignField": "_id",
"as": "articles"
}
}
],
"as": "data"
}
}
])

How to perform lookup in aggregation when foreignField is in array?

I have two collections:
// users
{
_id: "5cc7c8773861275845167f7a",
name: "John",
accounts: [
{
"_id": "5cc7c8773861275845167f76",
"name": "Name1",
},
{
"_id": "5cc7c8773861275845167f77",
"name": "Name2",
}
]
}
// transactions
{
"_id": "5cc7c8773861275845167f75",
"_account": "5cc7c8773861275845167f76",
}
Using lookup I want to populate _account field in transactions collection with respective element from users.accounts array.
So, I want the final result as:
{
"_id": "5cc7c8773861275845167f75",
"_account": {
"_id": "5cc7c8773861275845167f76",
"name": "Name1",
},
}
I have already tried using this code:
db.transactions.aggregate([
{
$lookup:
{
from: "users.accounts",
localField: "_account",
foreignField: "_id",
as: "account"
}
}
])
In the result account array comes as empty.
What is the correct way to do it ?
You can use below aggregation with mongodb 3.6 and above
db.transactions.aggregate([
{ "$lookup": {
"from": "users",
"let": { "account": "$_account" },
"pipeline": [
{ "$match": { "$expr": { "$in": ["$$account", "$accounts._id"] } } },
{ "$unwind": "$accounts" },
{ "$match": { "$expr": { "$eq": ["$$account", "$accounts._id"] } } }
],
"as": "_account"
}},
{ '$unwind': '$_account' }
])
Try with this
I think case 1 is better.
1)-
db.getCollection('transactions').aggregate([
{
$lookup:{
from:"user",
localField:"_account",
foreignField:"accounts._id",
as:"trans"
}
},
{
$unwind:{
path:"$trans",
preserveNullAndEmptyArrays:true
}
},
{
$unwind:{
path:"$trans.accounts",
preserveNullAndEmptyArrays:true
}
},
{$match: {$expr: {$eq: ["$trans.accounts._id", "$_account"]}}},
{$project:{
_id:"$_id",
_account:"$trans.accounts"
}}
])
2)-
db.getCollection('users').aggregate([
{
$unwind:{
path:"$accounts",
preserveNullAndEmptyArrays:true
}
},
{
$lookup:
{
from: "transactions",
localField: "accounts._id",
foreignField: "_account",
as: "trans"
}
},
{$unwind:"$trans"},
{
$project:{
_id:"$trans._id",
_account:"$accounts"
}
}
])