let in lookup whit objectId - mongodb

I want to use some ObjectId in my let but i have the same error every time :
Failed to execute script.
Error: invalid object id: length
Details:
#(shell):6:36
My query :
db.projects.aggregate([
{$lookup: {
from: 'pointValueChangements',
let: {id: '$_id'},
pipeline: [
{ $match: { projectId: new ObjectId('$$id'), date: {$lte: new Date()}}},
{ $sort: { date: -1}},
{ $limit: 1},
],
as: 'pointValue',
}},
{$project: {pointValue: 1}}
])
If i replace new ObjectId('$$id') by '$$id' the query work but the result is not good.
If i replace '$$id' by ObjectId(someMongoId) all is ok (but is not good because this id match only for one row...)

Try this query
db.projects.aggregate([
{$lookup: {
from: 'pointValueChangements',
let: {id: '$_id'},
pipeline: [
{ $match: {
$expr:
{
$and:
[
{ $eq: ['$projectId', '$$id'] },
{ date: {$lte: new Date() }},
],
},
}},
{ $sort: { date: -1}},
{ $limit: 1},
],
as: 'pointValue',
}},
{$project: {pointValue: 1}}
])

Related

mongodb - find if value matches second element in array

Trying to return this document from a collection by checking if a variable (userId) matches the second element in users array.
I created a playground. Expected result is the user document xyz as thats the only user who liked rrr and the user rrr has not liked back - https://mongoplayground.net/p/WI7hqR7SIMh
Expected result:
[
{
"count": 1,
"users": [
{
"_id": "xyz",
"group": 1,
"name": "xyyy"
}
]
}
]
My query is below where variable userId is xw5vk1s and is the second element in above array. The two conditions I am checking are like: true and userId = second element is users array
const users = await db
.collection('users')
.aggregate([
{
$lookup: {
from: "likes",
let: {userId: "$_id"},
pipeline: [{$match: {$expr: {$and: [{like: true, "users.1": userId} ]}}}],
as: "valid"
}
},
{$match: {
"valid.0": {$exists: true},
}
},
{$unset: ["valid"]},
{$group: {_id: 0, users: {$push: "$$ROOT"}, count: {$sum: 1}}}
])
The query is not working.
One option is using two $lookups:
First find the pairs for partnerships that "likes" rrr
$match only the unpaired
Get the user data and format the response
db.partnership.aggregate([
{$match: {$expr: {$eq: [{$last: "$users"}, "rrr"]}}},
{$lookup: {
from: "partnership",
let: {likes: {$last: "$users"}, me: {$first: "$users"}},
pipeline: [
{$match: {
$expr: {$and: [
{$eq: [{$last: "$users"}, "$$me"]},
{$eq: [{$first: "$users"}, "$$likes"]}
]
}
}
}
],
as: "paired"
}
},
{$match: {"paired.0": {$exists: false}}},
{$project: {_id: 0, user: {$first: "$users"}}},
{$lookup: {
from: "users",
localField: "user",
foreignField: "_id",
as: "user"
}},
{$project: {user: {$first: "$user"}}},
{$replaceRoot: {newRoot: "$user"}}
])
See how it works on the playground example
** I hope this will solve your problem **
https://mongoplayground.net/p/8Ax_NaRhfHs
[
{
$addFields: {
secondItem: {
$arrayElemAt: [
"$users",
1
]
}
}
},
{
$match: {
$and: [
{
users: {
$in: [
"$users",
"xw5vk1s"
]
}
},
{
$expr: {
$eq: [
"$secondItem",
"xw5vk1s"
]
}
}
]
}
}
]

Mongodb - count from a sub collection inside aggregate lookup

I have the following mongo db query that works:
.collection('commentsPosts')
.aggregate(
[
{$match:{
commentedAt: {
$lte: from,
},
...(postId && { postId }),
}
},
{$lookup:
{
from:"users",
localField:"commentedBy",
foreignField:"_id",
as:"commenterDetails"
// NEED TO LOOK UP "posts" collection here to find number of posts by commenterDetails.userId = posterId in posts table
},
}
]
)
I need to go one more level down to TO LOOK UP count of posts to find number of posts where commenterDetails.userId = posterId in posts collection
I am looking for a way to add the working query below to the query above.
const userPostCount = await req.db.collection('posts').countDocuments({
creatorId: userId,
});
You can use nested $lookups like this: https://mongoplayground.net/p/_il8aiilLh5
db.comments.aggregate([
{$lookup:
{
from:"users",
let: {userId: "$commentedBy"},
as:"commenterDetails",
pipeline: [
{$match: {$expr: {
$eq: ["$$userId", "$_id"],
}}},
{$lookup: {
from: "posts",
as: "posts",
let: {posterId: "$_id"},
pipeline: [
{$match: {$expr: {
$eq: ["$posterId", "$$posterId"]
}}},
{$count: "total"},
]
}},
{$addFields: {
posts: {$arrayElemAt: ['$posts', 0]}
}},
]
},
},
{$addFields: {
commenterDetails: {$arrayElemAt: ['$commenterDetails', 0]}
}}
])

Aggregate function two match with lookup mongodb

db.setting.aggregate([
{
$match: {
status: true,
deleted_at: 0,
_id: {
$in: [
ObjectId("5c4ee7eea4affa32face874b"),
ObjectId("5ebf891245aa27c290672325")
]
}
}
},
{
$lookup: {
from: "site",
localField: "_id",
foreignField: "admin_id",
as: "data"
}
},
{
$project: {
name: 1,
status: 1,
numberOfRecord: {
$size: "$data"
}
}
},
{
$sort: {
numberOfRecord: 1
}
}
])
I would like to fetch record and number of record which are greater then equal to 2020-01-01 tried to add below code but doesn't get success.
How can I make this correct please guide thanks in advance. Here is playground https://mongoplayground.net/p/GU8WbTVqo2I
{
$match: {
"data.createdAt": {
$gte: new Date("2020-01-01")
}
}
},
Output should be
[
{
"_id": ObjectId("5ebf891245aa27c290672325"),
"name": "Menz",
"numberOfRecord": 0,
"status": true
},
{
"_id": ObjectId("5c4ee7eea4affa32face874b"),
"name": "Dave",
"numberOfRecord": 1, // instead 2 bcoz this is only gte "2020-01-01"
"status": true
}
]
You can use $unwind to split up the array, but using $match will completely eliminate documents that don't have any matching documents, so you would need to used $group with a conditional count, perhaps:
db.setting.aggregate([
{$match: {
status: true,
deleted_at: 0,
_id: {
$in: [
ObjectId("5c4ee7eea4affa32face874b"),
ObjectId("5ebf891245aa27c290672325")
]
}
}},
{$lookup: {
from: "site",
localField: "_id",
foreignField: "admin_id",
as: "data"
}},
{$unwind: {
path: "$data",
preserveNullAndEmptyArrays: true
}},
{$group: {
_id: "$_id",
name: {$first: "$name"},
status: {$first: "$status"},
numberOfRecord: {
$sum:{
$cond:{
if:{
$gte:[
"$data.createdAt",
new Date("2020-01-01")
]
},
then: 1,
else: 0
}
}
}
}},
{$sort: { numberOfRecord: 1 }}
])
Playground

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.

Group count 2 item object array mongodb

I have the following array:
[
{
“counter”: 123456,
“user”: “USER1”,
“last”: “USER1”
},
{
“counter”: 123,
“user”: “USER1”,
“last”: “USER2”
},
{
“counter”: 111,
“user”: “USER2”,
“last”: “USER2”
},
{
“counter”: 1122,
“user”: “USER2”,
“last”: “USER2”
},
{
“counter”: 112233,
“user”: “USER1”,
“last”: “USER2”
},
]
I'm doing the following query on mongodb:
{$group: {
_id: “$user”,
total: {$sum: 1},
last: {$sum: {$cond: [
{$eq: ['$last’, '$user']}, 1, 0
]}}
}}
I would like to get the following result:
[
{
_id: “USER1”
total: 3,
last: 1
},
{
_id: “USER2”
total: 2,
last: 4
}
]
But I get this:
[
{
_id: “USER1”
total: 3,
last: 1
},
{
_id: “USER2”
total: 2,
last: 2
}
]
When I make the group I can not count the last item satisfactorily
How can I get the expected result? Thank you for your help.
You have only 2 "USER2" then can't $sum 4 $last on that _id.
try using compose _id if you need by index in user.
db.teste.aggregate([
{$group:{
_id: {user:"$user",last:"$last"},
total: {$sum:1},
last: {
$sum: {
$cond:[ {$eq: ["$last", user]}, 1, 0]
}
}
}}
])
If you need to add string or other occurrences in different fields. Query is:
db.teste.aggregate([
{$group:{
_id: "$user"
}},
{$lookup:{
from: "teste",
let: { indice: "$_id" },
pipeline: [
{$group:{
_id: null,
user:{$sum:{$cond:[
{$eq:["$user", "$$indice"]}, 1, 0
]}},
last:{$sum:{$cond:[
{$eq:["$last", "$$indice"]}, 1, 0
]}}
}},
{$project:{
_id: 0
}}
],
as: "res"
}}
])
From zero to hero:
//First we'll extract only what we need find, on this case distinct users
db.teste.aggregate([
{$group:{
_id: "$user"
}}
])
//Here we will get the indexes of the search and reinsert in our filter using $let (nodejs var)
db.teste.aggregate([
{$group:{
_id: "$user"
}},
{$lookup:{
from: "teste",
let: { indice: "$_id" },
pipeline: [],
as: "res"
}}
])
//Let's counter the total of reinserted elements
db.teste.aggregate([
{$group:{
_id: "$user"
}},
{$lookup:{
from: "teste",
let: { indice: "$_id" },
pipeline: [
{$group:{
_id: null,
total:{$sum:1}
}}
],
as: "res"
}}
])
//Now let's test a true condition
db.teste.aggregate([
{$group:{
_id: "$user"
}},
{$lookup:{
from: "teste",
let: { indice: "$_id" },
pipeline: [
{$group:{
_id: null,
user:{$sum:{$cond:[
{$eq:[true, true]}, 1, 0
]}}
}}
],
as: "res"
}}
])
//cond tested let's extract what we want
db.teste.aggregate([
{$group:{
_id: "$user"
}},
{$lookup:{
from: "teste",
let: { indice: "$_id" },
pipeline: [
{$group:{
_id: null,
user:{$sum:{$cond:[
{$eq:["$user", "$$indice"]}, 1, 0
]}},
last:{$sum:{$cond:[
{$eq:["$last", "$$indice"]}, 1, 0
]}}
}}
],
as: "res"
}}
])
//Let's take the _id of the sub-colection because we do not need it
db.teste.aggregate([
{$group:{
_id: "$user"
}},
{$lookup:{
from: "teste",
let: { indice: "$_id" },
pipeline: [
{$group:{
_id: null,
user:{$sum:{$cond:[
{$eq:["$user", "$$indice"]}, 1, 0
]}},
last:{$sum:{$cond:[
{$eq:["$last", "$$indice"]}, 1, 0
]}}
}},
{$project:{
_id: 0
}}
],
as: "res"
}}
])