mongodb - find if value matches second element in array - mongodb

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

Related

Find MongoDB documents that are not contained across arrays

MongoDB Collection A contains documents with an array with some document ids of collection B:
Collection A:
{
some_ids_of_b: ["id1", ...]
}
Collection B:
{
_id: "id1"
},
{
_id: "id2"
},
...
How do I query all documents from B whose _ids are NOT in contained in the some_ids_of_b arrays of documents of A?
Simple lookup from collection B to A and filter to keep only those documents where you don't find any matches.
db.collb.aggregate([
{
"$lookup": {
"from": "colla",
"localField": "_id",
"foreignField": "someIdsOfB",
"as": "a"
}
},
{
$match: {
$expr: {
$eq: [{$size: "$a"}, 0]
}
}
}
])
Demo
One option is:
db.collectionB.aggregate([
{$lookup: {
from: "collectionA",
let: {my_id: "$_id"},
pipeline: [
{$match: {$and: [
{_id: collADocId},
{$expr: {$in: ["$$my_id", "$some_ids_of_b"]}}
]}},
{$project: {_id: 1}}
],
as: "some_ids_of_b"
}},
{$match: {"some_ids_of_b.0": {$exists: false}}},
{$unset: "some_ids_of_b"}
])
See how it works on the playground example
You can do it with Aggregation Framework:
$group and $addToSet - To get all $some_ids_of_b from all the documents in A collection.
$set with $reduce - To create an array with all unique values of the IDs from the B collection.
$lookup - To fetch the documents from the B collection, where the _id of the document is not present in the $b_ids array.
$project - To project data as expected output.
db.A.aggregate([
{
"$group": {
"_id": null,
"b_ids": {
"$addToSet": "$some_ids_of_b"
}
}
},
{
"$set": {
b_ids: {
$reduce: {
input: "$b_ids",
initialValue: [],
in: {
$setUnion: [
"$$value",
"$$this"
]
}
}
}
}
},
{
"$lookup": {
from: "B",
let: {
b_ids: "$b_ids"
},
pipeline: [
{
"$match": {
"$expr": {
$ne: [
{
"$in": [
"$_id",
"$$b_ids"
]
},
true
]
}
}
}
],
as: "data"
}
},
{
"$project": {
data: 1,
_id: 0
}
}
])
Working Example

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.

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

Mongodb if then condition under filter how to make

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

let in lookup whit objectId

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