Mongodb if then condition under filter how to make - mongodb

db.main.aggregate([
{
$lookup: {
from: "history",
localField: "history_id",
foreignField: "history_id",
as: "History"
}
},
{
$project: {
"History": {
$filter: {
input: "$History",
as: "his",
if: {
$eq: [
"5e4a8d2d3952132a08ae5764",
"$$his.user_id"
]
},
then: {
cond: {
$and: [
{
$lt: [
"$$his.date",
"$date"
]
},
{
$eq: [
"5e4a8d2d3952132a08ae5764",
"$$his.user_id"
]
}
]
}
},
else: {}
}
},
data: 1,
history_id: 1,
sender_id: 1,
text: 1,
date: 1
}
},
{
$unwind: "$History"
}
])
MongoPlayground
Play with if condition under filter getting error. My purpose is specified user_id match with history's user_id then condition work other wise not.
How to fix it please guide or alternative approaches are welcome.

After discussion in chat, it seems the overall problem was how to select documents from the main collection based on 2 criteria of the related history documents:
db.main.aggregate([
{$lookup: {
from: "history",
localField: "history_id",
foreignField: "history_id",
as: "History"
}},
{$match: {
$expr: {
$eq: [
false,
{$reduce: {
input: "$History",
initialValue: false,
in: {
$or: [
"$$value",
{$and: [
{$eq: [
"$$this.user_id",
ObjectId("5e4a8d2d3952132a08ae5764")
]},
{$gte: [
"$$this.date",
"$date"
]}
]}
]
}
}}
]
}
}}
])
Playground

Related

MongoDB : Add match to nested pipeline lookup

I have this BD :
db={
"dashboard": [
{
"_id": "dashboard1",
"name": "test",
"user": 1
}
],
"templatefolders": [
{
"dashboardId": "dashboard1",
"folderId": "folder123",
"name": "folder",
"region": "XXX"
},
{
"dashboardId": "dashboard1",
"folderId": "folder123",
"name": "folder1",
"region": "YYY"
}
],
"folders": [
{
"_id": "folder123",
"name": "test1"
}
]
}
I have this query :
db.dashboard.aggregate([
{
$lookup: {
from: "templatefolders",
let: {
"boardId": "$_id"
},
pipeline: [
{
$match: {
$expr: {
$eq: [
"$dashboardId",
"$$boardId"
]
}
}
},
{
$lookup: {
from: "folders",
let: {
"folderId": "$folderId"
},
pipeline: [
{
$match: {
$expr: {
$eq: [
"$_id",
"$$folderId"
]
}
}
},
],
as: "joinFolder"
},
},
],
as: "joinDashboard"
}
}
])
I want to fetch all dashbords where dashbord.templateFolder.name=="folder" OR dashbord.templateFolder.folders.name=="test1"
Please, how i can achieve this ?
How i can add many conditions in the nested pipeline lookup, where one condition is related to another condition.
One option is use $lookup and then $match.
In your case:
db.templatefolders.aggregate([
{$lookup: {
from: "folders",
let: {"folderId": "$folderId"},
pipeline: [
{$match: {
$expr: {$and: [
{$eq: ["$_id", "$$folderId"]},
{$eq: ["$name", "test1"]}
]}
}}
],
as: "folder"
}
},
{$match: {$or: [{"folder.0": {$exists: true}}, {name: "folder"}]}},
{$lookup: {
from: "dashboard",
as: "dashboard",
localField: "dashboardId",
foreignField: "_id"
}},
{$project: {_id: 0, dashboard: {$first: "$dashboard"}}},
{$replaceRoot: {newRoot: "$dashboard"}}
])
See how it works on the playground example
You did not define your requested result, but you can use group if you have duplicates and you want to remove them.

Mongo db - How to get two counts from same collection with a single lookup?

I have a working query below:
{
$lookup: {
from: "watchList",
as: "watchList",
let: {
postId: "$_id",
userId: userCurrent
},
pipeline: [
{
$match: {
$and: [
{ $expr: {$eq: ["$postId","$$postId"]} },
{ $expr: {$eq: ["$$userId", "$user"]} },
]
}
},
{
$count: "watchCount"
},
]
}
},
The above query gives me a count of two conditions combined in AND:
{ $expr: {$eq: ["$postId","$$postId"]} },
{ $expr: {$eq: ["$$userId", "$user"]} },
In addition, is it possible to get the count of ONLY { $expr: {$eq: ["$postId","$$postId"]} } without doing another lookup ?
So there will be two counts -
Count 1:
AND condition for
{ $expr: {$eq: ["$postId","$$postId"]} },
{ $expr: {$eq: ["$$userId", "$user"]} },
Count 2:
Single condition
{ $expr: {$eq: ["$postId","$$postId"]} }
You can do something like this,
$group first match count to firstCount and second match count to secondCount
{
$lookup: {
from: "watchList",
as: "watchList",
let: {
postId: "$_id",
userId: userCurrent
},
pipeline: [
{
$group: {
_id: null,
firstCount: {
$sum: {
$cond: [
{
$and: [
{ $eq: ["$postId", "$$postId"] },
{ $eq: ["$$userId", "$user"] }
]
},
1,
0
]
}
},
secondCount: {
$sum: {
$cond: [{ $eq: ["$postId", "$$postId"] }, 1, 0]
}
}
}
}
]
}
}

getting problem with condition in mongodb

See my condition does work fine for my history's first collection but for second it does not work either If I replace my specified id with 5e4a8d2d3952132a08ae5724 that exists in my second object of history's collection return me all data which is not correct because it's date is greater then main collection's date so it should return me nothing please suggest how to fix it.
db.main.aggregate([
{
$lookup: {
from: "history",
localField: "history_id",
foreignField: "history_id",
as: "History"
}
},
{
$unwind: "$History"
},
{
"$match": {
$expr: {
$cond: {
if: {
$eq: [
"5e4a8d2d3952132a08ae5764",
"$History.user_id"
]
},
then: {
$and: [
{
$gt: [
"$date",
"$History.date"
]
},
{
$eq: [
"5e4a8d2d3952132a08ae5764",
"$History.user_id"
]
}
]
},
else: {}
}
}
}
}
])
when I put two object into history collection it does not work either MongoPlayground
Here is working playground with single history object https://mongoplayground.net/p/hrNofrq1c3S
The reason, why your query (which is posted in the OP) is not working because, you have not specified anything in else part. So, the better approach will be with '$filter' operator.
You can try the below:
db.main.aggregate([
{
$lookup: {
from: "history",
localField: "history_id",
foreignField: "history_id",
as: "History"
}
},
{
$project: {
"History": {
$filter: {
input: "$History",
as: "his",
cond: {
$and: [
{
$lt: [
"$$his.date",
"$date"
]
},
{
$eq: [
"5e4a8d2d3952132a08ae5764",
"$$his.user_id"
]
}
]
}
}
},
data: 1,
history_id: 1,
sender_id: 1,
text: 1,
date: 1
}
},
{
$unwind: "$History"
}
])
MongoPlayGroundLink

mongodb aggregation conditional $lookup pipeline performance

i made aggregation query like below. this query works great and generate data that i want. what i want to ask is aggregation like this is good in performace especially using pipeline in conditional $lookup like this?
ReactiveAggregate(this, Product, [
{$match: {slug}},
{$lookup: {
from: 'suppliers',
let: {supplierId: '$supplier_id'},
pipeline: [
{
$match: {
$expr: {
$eq: ['$_id', '$$supplierId']
}
}
},
{
$lookup: {
from: 'addresses',
let: {supplierAddress: '$address'},
pipeline: [
{
$match: {
$expr: {
$eq: ['$_id', '$$supplierAddress']
}
}
}
],
as: 'address'
}
},
{$unwind: {path: '$address', preserveNullAndEmptyArrays: false}},
{
$lookup: {
from: 'supplier_assets',
let: {supplierId: '$_id'},
pipeline: [
{
$match: {
$expr: {
$and: [
{$eq: ['$meta.supplier_id', '$$supplierId']},
{$eq: ['$meta.type', 'logo']}
]
}
}
}
],
as: 'logo'
}
},
{$unwind: {path: '$logo', preserveNullAndEmptyArrays: false}},
{
$lookup: {
from: 'products',
let: {supplierId: '$_id'},
pipeline: [
{
$match: {
$expr: {
$and: [
{$eq: ['$supplier_id', '$$supplierId']},
{$eq: ['$isApproved', true]}
]
}
},
},
{
$limit: 5
},
{$project: {
name: 1,
slug: 1,
supplier_id: 1,
variantMaster: {
$filter: {
input: '$variants',
as: 'variantMaster',
cond: {$eq: ['$$variantMaster.is_master', true]}
}
}
}
},
{$unwind: {path: '$variantMaster', preserveNullAndEmptyArrays: true}},
{
$lookup: {
from: 'product_assets',
let: {variantMasterId: '$variantMaster._id'},
pipeline: [
{
$match: {
$expr: {
$or: [
{
$and: [
{$eq: ['$meta.variant_id', '$$variantMasterId']},
{$eq: ['$meta.is_primary', true]}
]
},
{
$eq: ['$meta.variant_id', '$$variantMasterId'],
}
]
}
}
},
],
as: 'images'
}
},
{$lookup: {
from: 'product_sale',
let: {productId: '$_id'},
pipeline: [
{
$match: {
$expr: {
$and: [
{$eq: ['$product_id', '$$productId']},
{$gt: ['$end_date', new Date()]},
{$lte: ['$start_date', new Date()]},
{$eq: ['$active', true]},
{$eq: ['$approved', true]},
]
}
}
},
],
as: 'sale'
}},
{
$project:{
name: 1,
slug: 1,
supplierName: '$supplier.name',
price: '$variantMaster.price',
sale: {$arrayElemAt: ['$sale', 0]},
image: {$arrayElemAt: ['$images', 0]}
}
}
],
as: 'relatedProducts'
}
},
{
$project: {
name: 1,
url: 1,
relatedProducts: 1,
logo: 1,
city: '$address.city',
state: '$address.state',
}
}
],
as: 'supplier'
}},
{
$unwind: '$supplier'
},
{$project: {
name: 1,
slug: 1,
supplier: 1,
variants: 1,
description: 1,
variantMaster: {
$filter: {
input: '$variants',
as: 'variantMaster',
cond: {$eq: ['$$variantMaster.is_master', true]}
}
}
}
},
{
$unwind: '$variantMaster'
},
{
$lookup: {
from: 'product_assets',
localField: 'variantMaster._id',
foreignField: 'meta.variant_id',
as: 'images'
}
},
{
$lookup: {
from: 'product_sale',
let: {productId: '$_id'},
pipeline: [
{
$match: {
$expr: {
$and: [
{$eq: ['$product_id', '$$productId']},
{$gt: ['$end_date', new Date()]},
{$lte: ['$start_date', new Date()]},
{$eq: ['$active', true]},
{$eq: ['$approved', true]},
]
}
}
},
],
as: 'sale'
},
},
{
$unwind: {path: '$sale', preserveNullAndEmptyArrays: true}
},
{
$lookup: {
from: 'stock',
localField: 'variantMaster.stock',
foreignField: '_id',
as: 'stock'
}
},
{
$unwind: {path: '$stock', preserveNullAndEmptyArrays: true}
},
{
$lookup: {
from: 'product_review',
let: {productId: '$_id'},
pipeline: [
{
$match: {
$expr: {$eq: ['$product_id', '$$productId']}
},
},
{
$lookup: {
from: 'user_data',
let: {reviewProfileId: '$user_profile_id'},
pipeline: [
{
$match: {
$expr: {$eq: ['$_id', '$$reviewProfileId']}
}
},
{
$lookup: {
from: 'avatar',
let: {profileId: '$_id'},
pipeline: [
{
$match: {
$expr: {$eq: ['$meta.profile_id', '$$profileId']}
}
}
],
as: 'avatar'
}
},
{
$project: {
user_id: 1,
avatar: {$arrayElemAt: ['$avatar', 0]},
}
}
],
as: 'user'
}
},
{$unwind: {path: '$user', preserveNullAndEmptyArrays: true}},
{
$project: {
user: 1,
rating: 1,
review: 1,
user_fullname: 1,
postedAt: 1,
owner_reply: 1,
repliedAt: 1,
}
}
],
as: 'reviews'
}
},
{
$lookup: {
from: 'favorited_products',
let: {productId: '$_id'},
pipeline: [
{
$match: {
$expr: {
$and: [
{$eq: ['$product_id', '$$productId']},
{$eq: ['$user_id', this.userId]}
]
}
}
}
],
as: 'favorite'
}
},
{
$unwind: {path: '$favorite', preserveNullAndEmptyArrays: true}
},
{
$project: {
name: 1,
images: 1,
description: 1,
price: '$variantMaster.price',
variantMaster: 1,
supplier: 1,
is_ready_stock: 1,
is_custom: 1,
is_culinary: 1,
slug: 1,
reviews: 1,
sale: 1,
variants: 1,
stock: 1,
favorite: 1,
}
}
])
here is the log i got from mongo shell
"executionStats" : {
"executionSuccess" : true,
"nReturned" : 1,
"executionTimeMillis" : 5,
"totalKeysExamined" : 1,
"totalDocsExamined" : 1,
// rest..
sure it's fast because only 1 data was returned, but what if 100 or 1000 data. I'm still learning mongodb. please share pros and cons of doing aggregation query like this in mongodb. what is good approach when making aggregation query? i know i can refer to this page for optimize aggregation pipeline
Thanks.

How to perform nested lookup in mongo query?

This is my aggregation query
db.user.aggregate([
{ $addFields: { user_id: { $toString: "$_id" } } },
{
$lookup: {
from: "walletData",
let: { id: "$user_id" },
pipeline: [
{
$match: {
$expr: {
$and: [
{
$eq: ["$userId", "$$id"]
},
{
$gt: ["$lastBalance", 0]
}
]
}
}
}
],
as: "balance"
}
}
])
I get the desired output from this result but need to join one more collection in this query. How can i achieve that?
For example consider these collections:
user : {
"_id": ObjectId("xyz")
}
walletData:{
"userId": "xyz",
"lastBalance": 5
}
AnotherWalletdata:{
"userId": "xyz",
"lastBalance": 6
}
I got the result after joining first two tables how do i join the third table only if the balance of the second table(walletData) is greater than zero?
Expected Output :
{"id":"xyz",
"walletdataBal":5,
"AnotherWalletDataBal":6
}
You can join any number of collections by using only $lookup and $unwind one after another followed by Conditional Projection for whatever that's required at last. Below is the well-tested and working solution for the same :
db.user.aggregate([
{$lookup: {from: "walletData", localField: "_id", foreignField: "userId", as: "walletDataBal"}},
{$unwind: "$walletDataBal"},
{$lookup: {from: "anotherwalletData", localField: "_id", foreignField: "userId", as: "anotherWalletDataBal"}},
{$unwind: "$anotherWalletDataBal"},
{$project: {"id": "$_id", "_id": 0, walletDataBal: "$walletDataBal.lastBalance",
anotherWalletDataBal: {$cond:
{if: { $gt: [ "$walletDataBal.lastBalance", 0 ] },
then: "$anotherWalletDataBal.lastBalance",
else: "$$REMOVE" }}}
]).pretty();
You can add another $lookup stage to achieve the output
db.user.aggregate([
{ "$addFields": { "user_id": { "$toString": "$_id" } } },
{ "$lookup": {
"from": "walletData",
"let": { "id": "$user_id" },
"pipeline": [
{ "$match": {
"$expr": {
"$and": [
{ "$eq": ["$userId", "$$id"] },
{ "$gt": ["$lastBalance", 0] }
]
}
}}
],
"as": "balance"
}},
{ "$lookup": {
"from": "anotherWalletData",
"let": { "id": "$user_id" },
"pipeline": [
{ "$match": {
"$expr": {
"$and": [
{ "$eq": ["$userId", "$$id"] },
{ "$gt": ["$lastBalance", 0] }
]
}
}}
],
"as": "anotherWalletData"
}},
{ "$project": {
"walletdataBal": { "$arrayElemAt": ["$balance.lastBalance", 0] },
"anotherwalletdataBal": {
"$arrayElemAt": ["$anotherWalletData.lastBalance", 0]
}
}}
])
{ $unwind: "$balance" },
{ $lookup: {
from: "walletData",
localField: "balance.userId",
foreignField: "userId",
as:"anotherwalletData"
}}
])
I solved my query, I had to apply unwind after the above lookup.