How do I convert my object id to string so that I can compare it with string in $eq.
mongo version - 4.0
db.user.aggregate([{
$lookup:{
from: "sometable",
let:{user:["$_id"]},
pipeline:[{
$match: {
$expr: {
$and:[
{
$eq: [ "$userId", "$$user"]
},
{
$gt: [ "$lastBalance", 0]
}
]
}
}
}
],
as: "response"
},
}])
You can use $toString,
$eq: [ "$userId", {$toString: "$$user"}]
You can also try this:
$eq: [ {$toObjectId: "$$userId", "$user"}]
I don't know why but objectId comparisons are quicker it seems
Related
In my NodeJs application where I am using MongoDb database and I have two collections products and market_companies both collections have a field name hub_id and dimensions here dimensions is an object field.I have created the aggregation pipeline where in both the collections I am comparing hub_id and dimensions field but here the thing is hub_id in products collection is an integer but in market_companies its an string due to which I am not getting desired output.
I want to know how can I convert hub_id to integer from string in market_companies collection.
Below is my code:
db.products.aggregate([
{
$lookup: {
from: "market_companies",
let: {
hubId: "$hubId",
dimensions: "$dimensions"
},
as: "companies",
pipeline: [
{
$match: {
$expr: {
$and: [
{
$eq: [
"$hubId", // How to convert this into Integer
"$$hubId"
]
},
{
$setEquals: [
{
"$objectToArray": {
$ifNull: [
"$dimensions",
{}
]
}
},
{
"$objectToArray": {
$ifNull: [
"$$dimensions",
{}
]
}
}
]
}
]
}
}
},
]
}
}
])
Someone let me know any help appreciated.
You can do it with $toInt operator:
{
$eq: [
{ $toInt: "$hubId" },
"$$hubId"
]
},
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
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]
}
}
}
}
]
}
}
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
How to transform the data using $if $else condition MongoDB
mongoPlayground
I am getting worst digging into $if $expr condition
This playground should return nothing because date is $gt then main collection but it does return me data.
So condition should say if history's date is $gt then main collection should return nothing else return the matched criteria data.
the first history record only is used, as it has the same _id as the history_id in the main collection
so if you check the first history document
{
"_id": ObjectId("5e4e755b380054797d9db627"),
"user_id": "5e4e74eb380054797d9db625",
"history_id": ObjectId("5e4e755b380054797d9db627"),
"date": 1587650683433,
"__v": 1
}
and your query
db.main.aggregate([
{
$lookup: {
from: "history",
localField: "history_id",
foreignField: "_id",
as: "History"
}
},
{
$unwind: "$History"
},
{
"$match": {
$expr: {
$cond: {
if: {
$eq: [
"5e4e74eb380054797d9db623",
"$History.user_id"
]
},
then: {
$and: [
{
$gt: [
"$date",
"$History.date"
]
},
{ // also, this part has no meaning as the same condition is used in the if part
$eq: [
"5e4e74eb380054797d9db623",
"$History.user_id"
]
}
]
},
else: {}
}
}
}
}
])
the if condition is not satisfied, as the user_id in history model "5e4e74eb380054797d9db625" does not equal to the specified id "5e4e74eb380054797d9db623"
so, it goes to the else part which is getting all the documents in the main collection
note: I've added some comment in the query, could you check it too?
If I am not wrong I guess this is what you're expecting :-
db.main.aggregate([
{
$lookup: {
from: "history",
localField: "history_id",
foreignField: "_id",
as: "History"
}
},
{
$unwind: "$History"
},
{
"$match": {
$expr: {
$and: [
{
$eq: [
"$_id",
"$History.user_id"
]
},
{
$gt: [
"$date",
"$Histoy.date"
]
}
]
}
}
}
])
Well, few modification might be needed to fulfill your need.