mongodb - Multiple LET and AS assignments to use within the same pipeline - mongodb

How to perform multiple let and as and use them within the same pipeline. I have the following query but assignment defaults to userTo. Both userFrom and userTo are from users collection
{
$lookup: {
from: "users",
let: {
userFrom: "$from",
},
as: "userFrom",
let: {
userTo: "$to",
},
as: "userTo",
pipeline: [
{
$match: {
$expr: {
$or: [
{
$eq: [
"$$userFrom",
"$_id"
]
},
{
$eq: [
"$$userTo",
"$_id"
]
}
]
}
}
},

You are not using them correctly. as specifies the name of the output array of the $lookup. There is only 1 result set per lookup so only 1 as would be needed.
let specifies the variables you want to use in the sub-pipeline. You can put them in an object if you want to use multiple variables.
Your code should look like this:
db.collection.aggregate([
{
$lookup: {
from: "users",
let: {
userFrom: "$from",
userTo: "$to"
},
pipeline: [
{
$match: {
$expr: {
$or: [
{
$eq: [
"$$userFrom",
"$_id"
]
},
{
$eq: [
"$$userTo",
"$_id"
]
}
]
}
}
}
],
as: "userLookup"
}
}
])

Related

Check for non-existing field in lookup pipeline

How to check if a field is not existing inside lookup expression? The answers in similar questions are not helpful (e.g. {$eq : null} or $exists:true).
For example, I want to lookup inventory only if disabled is not existing.
db.orders.aggregate([
{
$lookup: {
from: "inventory",
let: {item: "$item"},
pipeline: [
{
$match: {
$expr: {
$and: [
{
$eq: ["$sku", "$$item" ]
},
{
$eq: [ "$disabled", null ]
}
]
}
}
},
],
as: "inv"
}
}
])
A playground sample is here
You can use $exists outside the $expr:
db.orders.aggregate([
{
$lookup: {
from: "inventory",
let: {item: "$item"},
pipeline: [
{
$match: {
$and: [
{$expr: {$eq: ["$sku", "$$item"]}},
{disabled: {$exists: false}}
]
}
}
],
as: "inv"
}
}
])
See how it works on the playground example
you could use:
{ $match: { someField: { $exists: true } } }
before the look up, to filter out the documents that you do not want to look up

Mongo Db How to detect error in pipeline?

I need to join two collection with two conditions.
products:
{
...
sku: "4234",
organizationId: ObjectId("asdasdasd);
...
};
order_history:
{
...
itemSku: "4234",
organizationId: ObjectId("asdasdasd);
...
}
I chose pipeline approach:
$lookup: {
from: 'order_history',
let: { foreign_sku: "$itemSku", foreign_organizationId: "$organizationId" },
pipeline: [
{
$match: {
$expr: {
$and: [
{ $eq: ["$organizationId", "$$foreign_organizationId"] },
{ $eq: ["$sku", "$$foreign_sku" ] }
]
}
}
}
],
as: 'order_history'
}
I wrote it on base of Mongo Documentation, but my conditions are ignored.
I have scalar multiplication in result. Where is my mistake?
I already mention in the comment and the code is below
db.products.aggregate([
{
$lookup: {
from: "order_history",
let: {
foreign_sku: "$sku",
foreign_organizationId: "$organizationId"
},
pipeline: [
{
$match: {
$expr: {
$and: [
{
$eq: [
"$organizationId",
"$$foreign_organizationId"
]
},
{
$eq: [
"$itemSku",
"$$foreign_sku"
]
}
]
}
}
}
],
as: "order_history"
}
}
])
Working Mongo playground

MongoDB aggregating data

when I am running code from Mongodb docs to use pipeline in the $lookup
I am getting an error that let is not supported. How to use let: { <var_1>: , …, <var_n>: } inside of $lookup.
Error:
Code:
db.orders.aggregate([
{
$lookup:
{
from: "warehouses",
let: { order_item: "$item", order_qty: "$ordered" },
pipeline: [
{ $match:
{ $expr:
{ $and:
[
{ $eq: [ "$stock_item", "$$order_item" ] },
{ $gte: [ "$instock", "$$order_qty" ] }
]
}
}
},
{ $project: { stock_item: 0, _id: 0 } }
],
as: "stockdata"
}
}
])

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

$match in $lookup result

I have next mongo code:
db.users.aggregate([
{
$match: {
$and: [
{ UserName: { $eq: 'administrator' } },
{ 'Company.CompanyName': { $eq: 'test' } }
]
}
},
{
$lookup: {
from: "companies",
localField: "CompanyID",
foreignField: "CompanyID",
as: "Company"
}
},
])
The $lookup part of the code working great. I got next result:
But if I add $match to the code, it brings nothing.
I found that the problem is in the second match: { 'Company.CompanyName': { $eq: 'test' } }, but I can not realize what is wrong with it.
Any ideas?
UPDATE:
I had also tried $unwind on the $lookup result, but no luck:
db.users.aggregate([
{
$match: {
$and: [
{ UserName: { $eq: 'administrator' } },
{ 'Company.CompanyName': { $eq: 'edt5' } }
]
}
},
{ unwind: '$Company' },
{
$lookup: {
from: 'companies',
localField: 'CompanyID',
foreignField: 'CompanyID',
as: 'Company'
}
},
])
With MongoDB 3.4, you can run an aggregation pipeline that uses the $addFields pipeline and a $filter operator to only return the Company array with elements that match the given condition. You can then wrap the $filter expression with the $arrayElemAt operator to return a single document which in essence incorporates the $unwind functionality by flattening the array.
Follow this example to understand the above concept:
db.users.aggregate([
{ "$match": { "UserName": "administrator" } },
{
"$lookup": {
"from": 'companies',
"localField": 'CompanyID',
"foreignField": 'CompanyID',
"as": 'Company'
}
},
{
"$addFields": {
"Company": {
"$arrayElemAt": [
{
"$filter": {
"input": "$Company",
"as": "comp",
"cond": {
"$eq": [ "$$comp.CompanyName", "edt5" ]
}
}
}, 0
]
}
}
}
])
Below answer is for mongoDB 3.6 or later.
Given that:
You have a collection users with a field CompanyID and a collection of companies with a field CompanyID
you want to lookup Companies on Users by matching CompanyID, where additionally:
each User must match condition: User.UserName equals administrator
each Company on User must match condition: CompanyName equals edt5
The following query will work for you:
db.users.aggregate([
{ $match: { UserName: 'administrator' } },
{
$lookup: {
from: 'companies',
as: 'Company',
let: { CompanyID: '$CompanyID' },
pipeline: [
{
$match: {
$expr: {
$and: [
{ $eq: ['$CompanyID', '$$CompanyID'] },
{ $eq: ['$CompanyName', 'edt5'] },
]
}
}
}
]
}
},
])
Explanation:
This is the way to perform left join queries with conditions more complex than simple foreign / local field equality match.
Instead of using localField and foreignField, you use:
let option where you can map local fields to variables,
pipeline option where you can specify aggregation Array.
In pipeline you can use $match filter, with $expr, where you can reuse variables defined earlier in let.
More info on $lookup
Nice tutorial
here is code for fitering array inside lookup.
const userId = req.userData.userId;
const limit = parseInt(req.params.limit);
const page = parseInt(req.params.page);
Collection.aggregate([
{ $match: {} },
{ $sort: { count: -1 } },
{ $skip: limit * page },
{ $limit: limit },
{
$lookup: {
from: Preference.collection.name,
let: { keywordId: "$_id" },
pipeline: [
{
$match: {
$expr: {
$and: [
{ $eq: ["$keyword", "$$keywordId"] },
{
$eq: ["$user", mongoose.Types.ObjectId(userId)],
},
],
},
},
},
],
as: "keywordData",
},
},
{
$project: {
_id: 0,
id: "$_id",
count: 1,
for: 1,
against: 1,
created_at: 1,
updated_at: 1,
keyword: 1,
selected: {
$cond: {
if: {
$eq: [{ $size: "$keywordData" }, 0],
},
then: false,
else: true,
},
},
},
}])