Mongo Db How to detect error in pipeline? - mongodb

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

Related

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

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"
}
}
])

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

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"
}
}
])

How to join and merge multiple collections in MongoDB

I have three collections which I want to merge in one response. I have tried with $lookup but it was not working. The sample schema's and expected response sample attached. The issue is that, it is not merging into one array.
Sample diagram:
Collection 1: product
db.collection("product").findOne({"_id": ObjectId(id), "uid":uid})
Response:{
"_id":"12345",
"uid":"537354",
"name":"toyota"
}
Collection 2: car_types
db.collection('car_types').find({"$or":[{"uid":uid,"car_type":"custom"},{"car_type":"default"}]})
Response: [
{
"_id":"9987",
"car_type":"default",
"title":"Sports"
},
{
"_id":"9988",
"uid":"537354",
"car_type":"custom",
"title":"Trucks"
}
]
Collection 3: car_images
db.collection("car_images").find({"car_id":car_id, "uid":uid})
Response:
[
{
"_id":"56433",
"uid":"537354",
"product_id":"12345",
"type_id":"9987",
"img_src":"cart.jpg"
},
{
"_id":"42453",
"uid":"537354",
"product_id":"12345",
"type_id":"9988",
"img_src":"mini.jpg"
}
]
Expected Response:
{
"product":{
"_id":"12345",
"uid":"537354",
"car_id":"9922",
"name":"toyota"
},
"car_types":[
{
"_id":"9987",
"car_type":"default",
"title":"Sports",
"img_src":"cart.jpg"
},
{
"_id":"9988",
"uid":"537354",
"car_type":"custom",
"title":"Trucks",
"img_src":"mini.jpg"
}
]
}
Try this below aggregation query which uses multiple-join-conditions-with-lookup:
db.product.aggregate([
/** lookup on `car_types` to pull in docs where if `uid are equal + car_type is custom` or `car_type is default` */
{
$lookup: {
from: "car_types",
let: { productUID: "$uid" }, /** Create a local variable */
pipeline: [
{
$match: {
$expr: {
"$or": [
{ $and: [ { $eq: [ "$uid", "$$productUID" ] }, { $eq: [ "$car_type", "custom" ] } ] },
{ $eq: [ "$car_type", "default" ] }
]
}
}
}
],
as: "car_types"
}
},
/** lookup on `car_images`, to pull in docs where if `type_id` exists in array of `car_types._id` or uid's are equal */
{
$lookup: {
from: "car_images",
let: { car_typesID: "$car_types._id", productUID: "$uid" },
pipeline: [
{
$match: { $expr: { $or: [ { $in: [ "$type_id", "$$car_typesID" ] }, { $eq: [ "$uid", "$$productUID" ] } ] } }
}
],
as: "car_images"
}
}
])
Test : mongoplayground

how to use $groupby and transform distinct value mongodb

How to transform the data using $if $else groupby condition MongoDB?
This playground should return two object who belongs to text with "tester 2" and "tester 3" also if I have multiple object in history collection it should also check with last object not will all object how it is possible
So condition should say if history's date is $gt then main collection should return nothing else return the matched criteria data.
db.main.aggregate([
{
$lookup: {
from: "history",
localField: "history_id",
foreignField: "history_id",
as: "History"
}
},
{
$unwind: "$History"
},
{
"$match": {
$expr: {
$cond: {
if: {
$eq: [
"5e4e74eb380054797d9db623",
"$History.user_id"
]
},
then: {
$and: [
{
$gt: [
"$date",
"$History.date"
]
},
{
$eq: [
"5e4e74eb380054797d9db623",
"$History.user_id"
]
}
]
},
else: {}
}
}
}
}
])
MongoPlayground
If I understand you correctly, it is what you are trying to do:
db.main.aggregate([
{
$lookup: {
from: "history",
let: {
main_history_id: "$history_id",
main_user_id: { $toString: "$sender_id" }
},
pipeline: [
{
$match: {
$expr: {
$and: [
{
$eq: [
"$history_id",
"$$main_history_id"
]
},
{
$eq: [
"$user_id",
"$$main_user_id"
]
}
]
}
}
}
],
as: "History"
}
},
{
$unwind: {
path: "$History",
preserveNullAndEmptyArrays: true
}
},
{
$sort: {
_id: 1,
"History.history_id": 1,
"History.date": 1
}
},
{
$group: {
_id: "$_id",
data: { $last: "$$ROOT" },
History: { $last: "$History" }
}
},
{
$replaceRoot: {
newRoot: {
$mergeObjects: [
"$data",
{ History: "$History" }
]
}
}
},
{
"$match": {
$expr: {
$or: [
{
$eq: [
{ $type: "$History.date" },
"missing"
]
},
{
$ne: [
"5e4e74eb380054797d9db623",
"$History.user_id"
]
},
{
$and: [
{
$eq: [
"5e4e74eb380054797d9db623",
"$History.user_id"
]
},
{
$gte: [
"$date",
"$History.date"
]
}
]
}
]
}
}
}
])
MongoPlayground