Mongodb search multiple collections - node.js

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

Related

Mongodb and nodejs find and filter nested objects

Firstly this is my Workspaces collection
[
{
"WorkspaceId": "er890we8rw98ro9we8rjower",
"WorkspaceTitle": "My First Workspace",
"WorkspaceOwner": "user1",
"Pages": [
{
"PageId": "a1sd32as7d841a23sd",
"PageMembers": [
{
"MemberId": "user1",
"MemberName": "John",
"MemberAvatar": "https://example.com/jKLa29Wqy",
"MemberAccess": "can edit"
},
{
"MemberId": "user2",
"MemberName": "Margot",
"MemberAvatar": "https://example.com/wKKLSAqy",
"MemberAccess": "can edit"
},
{
"MemberId": "user3",
"MemberName": "Silvia",
"MemberAvatar": "https://example.com/wKKLSAqy",
"MemberAccess": "can edit"
},
...
]
}
]
}
]
I wanr to print specific nested objects from document. I need to get a specific document inside my collection like this.
{
"WorkspaceId": "er890we8rw98ro9we8rjower",
"WorkspaceTitle": "My First Workspace",
"WorkspaceOwner": "user1",
"Pages": [
{
"PageId": "a1sd32as7d841a23sd",
"PageMembers": [
{
"MemberId": "user1",
"MemberName": "John",
"MemberAvatar": "https://example.com/jKLa29Wqy",
"MemberAccess": "can edit"
}
]
}
]
}
But when I run this script, my api prints all of collection. How can I achieve this?
Below is the query I am using:
db.findOne({ $or: [{ "WorkspaceOwner": member_id }, { "Pages.PageMembers.MemberId": member_id }] }, function (err, result) {
res.status(200).json(result);
});
You can achieve by using unwind and filter aggregation.
db.collection.aggregate([
{
"$unwind": "$Pages"
},
{
$addFields: {
"Pages.PageMembers": {
$filter: {
input: "$Pages.PageMembers",
as: "temp",
cond: {
$in: [
"$$temp.MemberId",
[
"user1"
]
]
}
}
}
}
}
])
Playground

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

Can't join collections with ids in mongodb

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

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

Aggregate and get all fields

I can now do something like this:
{
"$match": {
"$and": [
{
"$or": [
{ "to": system.mongoose.Types.ObjectId(userId) },
{ "from": system.mongoose.Types.ObjectId(userId) }
]
}
]
}
},
{
"$group": {
"_id": "$conversationId",
"from": { "$first": "$from" },
"to": { "$first": "$to" },
"content": { "$first": "$content" },
"createDate": {"$first": "$createDate"}
}
}
And get just first result for each conversationId but how can I get all records?

Resources