Can't join collections with ids in mongodb - node.js

I am trying to get all requests of a user from the request collection based on request status. I am trying to lookup the collection but it doesn't work. Is there any solution to work it out.
Here is my code:
Users.aggregate([
{
$lookup: {
from: 'requests',
let: {userId: '$userId', status: '$status'},
pipeline: [
{
$match: {
$expr: {
$and: [
{ $eq: ['$_id', '$$userId'] },
{ $eq: ['$$status', 1] }
]
},
}
}
],
as: 'requests'
}
}
]).exec()
I think { $eq: ['$_id', '$$userId'] } is not working. I tried using $toObjectId but still same result.
Here is test data for users:
{
"_id": {
"$oid": "5f1c0112ad207a13308a3fea"
},
"createDate": {
"$date": "2020-07-25T09:52:58.678Z"
},
"userRole": 10,
"status": 1,
"fullName": "Test Name",
"email": "test.name#mailinator.com",
"password": "$2b$10$HQN//qFTQKW8tBnf7G0OV.Uta0zNbxd1hPlGVwvLp5CVIf49Y5PNW",
"__v": 0,
"profileImage": "1595957619296.jpg"
}
And test request data:
{
"_id": {
"$oid": "5f2178c578153018ca5d79e8"
},
"request": "This is a demo request.",
"userId": {
"$oid": "5f1c0112ad207a13308a3fea"
},
"createDate": {
"$date": "2020-07-28T18:30:00.000Z"
},
"status": 1
}

There are few fixes in your query,
$match your user document status is 1, if you don't want then your can exclude,
db.users.aggregate([
{
$match: {
status: 1
}
},
$lookup with requests
let userId it is from user collection so add user collection _id so its corrected,
no need to create status variable because we already checked condition for in above
{
$lookup: {
from: "requests",
let: {
userId: "$_id"
},
pipeline: [
{
$match: {
$expr: {
$and: [
$eq check first is requests collection $userId and second is that we have created variable above in let and use $$userId because its reference to main collection users let variables
{
$eq: [
"$userId",
"$$userId"
]
},
second $eq check $status is 1 in requests collection, and you did it in user collection we we have already used in above $match condition
{
$eq: [
"$status",
1
]
}
]
}
}
}
],
as: "requests"
}
}
])
Have divided in parts for explanation purpose you can merge it as they are in sequence,
Working Playground: https://mongoplayground.net/p/THU6HeyqMN4

Related

Lookup based on match result

I have collection name Services:
[
{
"_id": "61dad1d21aa077c61b7bc2aa",
"name": "HomeMaintenance",
"subServices": [
"61dacb86cb94917c1edcea8f",
"61dad5812881410ba441c401"
],
},
{
"_id": "61dad60b2881410ba441c40e",
"name": "HomeMaintenance",
"subServices": [],
}
]
in another hand I have a subServices Collection like this :
[
{
"_id": "61dacb86cb94917c1edcea8f",
"name": "something",
"title": "something else",
"imageUrl": "",
"__v": 0,
"service": "61dad1d21aa077c61b7bc2aa"
},
{
"_id": "61dad5812881410ba441c401",
"name": "Plumbing",
"title": "Plumbing",
"imageUrl": "",
"__v": 0,
"service": "61dad1d21aa077c61b7bc2aa"
}
]
I came up with a solution with two queries like this
const requestedService = (serviceId)=>{
return servicesModel.findById(id);
};
const ids= requestedService.subServices
const subServicesList = (ids) => {
return subServicesModel.find({
_id: {
$in: ids,
},
});
};
which works perfectly fine, I was wondering is there any way to do these queries with one aggregation pipeline with lookup stage, first find the main services from service collection and then from subServices collection find that subServices of service
something like this
const result = await servicesModel.aggregate([
{
$match: { _id: ObjectId(id) },
},
{
$lookup: {
from: "sub_services",
let: { pid: "$_id" },
pipeline: [
{
$match: {
$expr: {
$in: ["$$pid" //>> id in sub_services modal , //>> "array which we get from match" ],
},
},
},
],
as: "subServices",
},
},
]);
The let is used for declaring the variable from the left document.
Specifies variables to use in the pipeline stages. Use the variable expressions to access the fields from the joined collection's documents that are input to the pipeline.
db.services.aggregate([
{
$match: {
_id: ObjectId(id)
},
},
{
$lookup: {
from: "sub_services",
let: {
subServices: "$subServices"
},
pipeline: [
{
$match: {
$expr: {
$in: [
"$_id",
"$$subServices"
]
},
},
},
],
as: "subServices",
},
},
])
Sample Mongo Playground

Aggregate with multiple lookup and pipeline return only the last element

I try below code, it's working to lookup value from other collection. But why it only return the last element.
If I omitted the unwind function, It does return all result from the model, but the second lookup will not working as the first lookup return arrays.
My objective is to look up folder that include the model id which represented in templatefolders collection.
const result = await this.dashboardModel
.aggregate([{ $match: filter }])
.lookup({
from: 'templatefolders',
as: 'template',
let: { id: '$_id' },
pipeline: [
{
$match: {
$expr: {
$and: [
{
$eq: ['$dashboardId', '$$id'],
},
{
$eq: ['$deletedAt', null],
},
],
},
},
},
{
$project: {
_id: 1,
folderId: 1,
},
},
],
})
.unwind('template')
.lookup({
from: 'folders',
as: 'folder',
let: { folderId: '$template.folderId' },
pipeline: [
{
$match: {
$expr: {
$and: [
{
$eq: ['$_id', '$$folderId'],
},
{
$eq: ['$deletedAt', null],
},
],
},
},
},
{
$project: {
_id: 1,
name: 1,
},
},
],
})
.unwind('folder')
.exec();
return result;
Result
{
"data": [
{
...(parent field)
"template": {
"_id": "60ab22b03b39e40012b7cc4a",
"folderId": "60ab080b3b39e40012b7cc41"
},
"folder": {
"_id": "60ab080b3b39e40012b7cc41",
"name": "Folder 1"
}
}
],
"meta": {},
"success": true,
"message": "Succesfully get list"
}
I came from Front end background. I hope my question is not a silly one.
Thanks!
EDIT:
dashboard: [{
_id: dashboard1
}]
templatefolders: [{
dashboardId: dashboard1,
folderId: folder123
}]
folders: [{
_id: folder123
}]
You can use $lookup to join collections
$lookup to join two collections .Lookup doc
$unwind to deconstruct the array. Unwind doc
$group to reconstruct the array which we already deconstructed Group doc
Here is the code
db.dashboard.aggregate([
{
"$lookup": {
"from": "templatefolders",
"localField": "_id",
"foreignField": "dashboardId",
"as": "joinDashboard"
}
},
{
"$unwind": "$joinDashboard"
},
{
"$lookup": {
"from": "folders",
"localField": "joinDashboard.folderId",
"foreignField": "_id",
"as": "joinDashboard.joinFolder"
}
},
{
"$group": {
"_id": "$_id",
"joinDashboard": {
"$push": "$joinDashboard"
}
}
}
])
Working Mongo playground

Mongodb search multiple collections

Basically I am searching messages.
I have 2 collections:
Users
Messages
Users:
[
{
"_id": "Xuibgsadbgsi35Gsdf",
"fullName": "User A"
},
{
"_id": "Afstg34tg4536gh",
"fullName": "User B"
},
{
"_id": "KHJDFhfs7dfgsvdfwsef",
"fullName": "User C"
}
]
Messages:
[
{
"_id": "YONgsa793423bD",
"groupId": "Phsdfyg92345sgb7651",
"senderId": "Xuibgsadbgsi35Gsdf",
"message": "Hello there!"
},
{
"_id": "sdgDFGbaofh135df",
"groupId": "Phsdfyg92345sgb7651",
"senderId": "KHJDFhfs7dfgsvdfwsef",
"message": "Hello #Xuibgsadbgsi35Gsdf"
},
{
"_id": "sdgDFGbaofh135df",
"groupId": "Phsdfyg92345sgb7651",
"senderId": "KHJDFhfs7dfgsvdfwsef",
"message": "Hello"
}
]
Now here I want to search: User A, so I should get those message in which User A is involved in any way, either he is a sender or he is mentioned in some message text.
How can I query this scenario?
You can do this
db.users.aggregate([
{
$match: {
fullName: "User A"
}
},
{
"$lookup": {
"from": "messages",
let: {
id: "$_id" //This is what you need to match in the messages collection, kind of variable
},
"pipeline": [
{
$match: {
$or: [
{
$expr: { //Matching in sender id
$eq: [
"$senderId",
"$$id"
]
}
},
{
$expr: {
"$regexMatch": { //matching in messages
"input": "$message",
"regex": "$$id",
}
}
}
]
}
}
],
"as": "senders"
}
},
{
$match: {
$expr: {
"$gt": [//Filtering only the matched results
{
"$size": "$senders"
},
0
]
}
}
}
])
To add filtering, you can add a match stage before lookup as below
{
$match: {
fullName: "User A"
}
}
Note, mongo is case sensitive db. Updated sample

MongoDB Aggregation of a user and his groups

I have 2 collections, "user" and "group" and I wanna return an object of a user and his groups.
The user is something like
{
"_id": "5dea1ad0df42010984d1036f",
// more info
}
and the group
{
"_id": "5dea1ad0df42010984d1566a",
"members": [
{
"_id": "5dea1ad0df42010984d1036f",
"fullName": "Example",
"img":"https://example.com",
},
// more objects like that
]
}
Still didn't find a proper way of doing it, I'd be happy if someone can guide me/explain!
You can use $lookup with custom pipeline:
db.user.aggregate([
{
$lookup: {
from: "group",
let: { user_id: "$_id" },
pipeline: [
{ $match: { $expr: { $in: [ "$$user_id", "$members._id" ] } } }
],
as: "groups"
}
}
])
Mongo Playground

Mongoose Aggregate with Lookup

I have a simple two collections like below :
assignments:
[
{
"_id": "593eff62630a1c35781fa325",
"topic_id": 301,
"user_id": "59385ef6d2d80c00d9bdef97"
},
{
"_id": "593eff62630a1c35781fa326",
"topic_id": 301,
"user_id": "59385ef6d2d80c00d9bdef97"
}
]
and users collection:
[
{
"_id": "59385ef6d2d80c00d9bdef97",
"name": "XX"
},
{
"_id": "59385b547e8918009444a3ac",
"name": "YY"
}
]
and my intent is, an aggregate query by user_id on assignment collection, and also I would like to include user.name in that group collection. I tried below:
Assignment.aggregate([{
$match: {
"topic_id": "301"
}
},
{
$group: {
_id: "$user_id",
count: {
$sum: 1
}
}
},
{
$lookup: {
"from": "kullanicilar",
"localField": "user_id",
"foreignField": "_id",
"as": "user"
}
},
{
$project: {
"user": "$user",
"count": "$count",
"_id": "$_id"
}
},
But the problem is that user array is always blank.
[ { _id: '59385ef6d2d80c00d9bdef97', count: 1000, user: [] } ]
I want something like :
[ { _id: '59385ef6d2d80c00d9bdef97', count: 1000, user: [_id:"59385ef6d2d80c00d9bdef97",name:"XX"] } ]

Resources