How to filter with aggregation and lookup in mongoose? - mongodb

I am using the following code:
Appointment
.aggregate([
{
$lookup:
{
from: "users",
localField: "doctorId",
foreignField: "_id",
as: "appointment_book"
},
},
])
.then((task) => res.send(task))
.catch((error) => console.log(error));
});
and the output is:
[{
"_id": "5dfd17823a974b39d89857d0",
"userId": "5dfcff8d2c5be31b803313ee",
"doctorId": "5dfd00102c5be31b803313f0",
"dateOfAppointment": "22/12/2019",
"timeOfAppointment": "11",
"currentStatus": "Pending",
"remarks": "",
"__v": 0,
"appointment_book": [
{
"_id": "5dfd00102c5be31b803313f0",
"name": "doctor1",
"avatar": "http://localhost:4000/public/doctor-02.jpg",
"email": "doctor1#gmail.com",
"phone": "0234820",
"city": "Faridabad",
"password": "$2a$10$m/FZg5XG9tVt86FCRAc8fO0RWhcs9D.QLFIH7BQqP/wuOR7EX8OuG",
"problem": "Neurology",
"usertype": 1,
"saltSecret": "$2a$10$m/FZg5XG9tVt86FCRAc8fO",
"__v": 0
}
]
},
{
"_id": "5dfd17bf3a974b39d89857d2",
"userId": "5dfcffdb2c5be31b803313ef",
"doctorId": "5dfd00102c5be31b803313f0",
"dateOfAppointment": "23/12/2019",
"timeOfAppointment": "11",
"currentStatus": "Pending",
"remarks": "",
"__v": 0,
"appointment_book": [
{
"_id": "5dfd00102c5be31b803313f0",
"name": "doctor1",
"avatar": "http://localhost:4000/public/doctor-02.jpg",
"email": "doctor1#gmail.com",
"phone": "0234820",
"city": "Faridabad",
"password": "$2a$10$m/FZg5XG9tVt86FCRAc8fO0RWhcs9D.QLFIH7BQqP/wuOR7EX8OuG",
"problem": "Neurology",
"usertype": 1,
"saltSecret": "$2a$10$m/FZg5XG9tVt86FCRAc8fO",
"__v": 0
}
]
}
]
My requirement to get only "userid" : "5dfcffdb2c5be31b803313ef" in mongoose. How can I do this.
output should be:
{
"_id": "5dfd17bf3a974b39d89857d2",
"userId": "5dfcffdb2c5be31b803313ef",
"doctorId": "5dfd00102c5be31b803313f0",
....
"appointment_book": [
{
"_id": "5dfd00102c5be31b803313f0",
"name": "doctor1",
....
}
]
},
{
"_id": "5dfd17bf3a974b39d89857e3",
"userId": "5dfcffdb2c5be31b803313ef",
"doctorId": "5dfd00102c5be31b803313g1",
....
"appointment_book": [
{
"_id": "5dfd00102c5be31b803313ff",
"name": "doctor2",
....
}
]
},
]

You need to use $match as initial stage before $lookup stage in order to filter collection for the document needed & get referenced document/documents from other collection :
Appointment.aggregate([
{ $match: { "userId": "5dfcffdb2c5be31b803313ef" } },
{
$lookup:
{
from: "users",
localField: "doctorId",
foreignField: "_id",
as: "appointment_book"
},
}])
In case of userId being an ObjectId() :
const mongoose = require('mongoose');
const userId = mongoose.Types.ObjectId("5dfcffdb2c5be31b803313ef");
Appointment.aggregate([
{ $match: { "userId": userId } },
{
$lookup:
{
from: "users",
localField: "doctorId",
foreignField: "_id",
as: "appointment_book"
}
}])
Ref : $match

Related

MongoDB - join multiple collections

I want to join (left) multiple collection:
Step1:
db.user.insertOne({
user_id: "id_user_1",
})
Step2: (add first document to each colection - id_user_1)
db.est_zone.insertOne({
zone: {
temp: "tempo1",
},
user_id: "id_user_1"
})
db.est_tax.insertOne({
tax: {
fam: "fam1",
},
user_id: "id_user_1"
})
Step3: (add second document to each colection - id_user_1)
db.est_zone.insertOne({
zone: {
temp: "tempo2",
},
user_id: "id_user_1"
})
db.est_tax.insertOne({
tax: {
fam: "fam2",
},
user_id: "id_user_1"
})
So, when i use $lookup (aggregate) with just one collection, it work like what i expect:
db.user.aggregate([
{
$match: {
user_id: "id_user_1"
}},
{
$lookup: {
from: "est_zone",
localField: "user_id",
foreignField: "user_id",
as: "est_zone"
}},
{$unwind: "$est_zone"},
])
output:
[
{
"_id": {
"$oid": "63db1a68ef527b03093e78d4"
},
"user_id": "id_user_1",
"est_zone": {
"_id": {
"$oid": "63db1a68ef527b03093e78d5"
},
"zone": {
"temp": "tempo1"
},
"user_id": "id_user_1"
}
},
{
"_id": {
"$oid": "63db1a68ef527b03093e78d4"
},
"user_id": "id_user_1",
"est_zone": {
"_id": {
"$oid": "63db1ad58c3dc65723ce0ac9"
},
"zone": {
"temp": "tempo2"
},
"user_id": "id_user_1"
}
}
]
tempo1 and tempo2 are in one document.
i expect fam1 in tempo1 document
and fam2 in tempo2 document
but, when i use lookup for join the "est_tax" it doesnt works like i want (even if use $unwind):
db.user.aggregate([
{
$match: {
user_id: "id_user_1"
}},
{
$lookup: {
from: "est_zone",
localField: "user_id",
foreignField: "user_id",
as: "est_zone"
}},
{$unwind: "$est_zone"},
{
$lookup: {
from: "est_tax",
localField: "user_id",
foreignField: "user_id",
as: "est_tax"
}},
])
output:
[
{
"_id": {
"$oid": "63db1a68ef527b03093e78d4"
},
"user_id": "id_user_1",
"est_zone": {
"_id": {
"$oid": "63db1a68ef527b03093e78d5"
},
"zone": {
"temp": "tempo1"
},
"user_id": "id_user_1"
},
"est_tax": [
{
"_id": {
"$oid": "63db1a68ef527b03093e78d6"
},
"tax": {
"fam": "fam1"
},
"user_id": "id_user_1"
},
{
"_id": {
"$oid": "63db1ad58c3dc65723ce0aca"
},
"tax": {
"fam": "fam2"
},
"user_id": "id_user_1"
}
]
},
{
"_id": {
"$oid": "63db1a68ef527b03093e78d4"
},
"user_id": "id_user_1",
"est_zone": {
"_id": {
"$oid": "63db1ad58c3dc65723ce0ac9"
},
"zone": {
"temp": "tempo2"
},
"user_id": "id_user_1"
},
"est_tax": [
{
"_id": {
"$oid": "63db1a68ef527b03093e78d6"
},
"tax": {
"fam": "fam1"
},
"user_id": "id_user_1"
},
{
"_id": {
"$oid": "63db1ad58c3dc65723ce0aca"
},
"tax": {
"fam": "fam2"
},
"user_id": "id_user_1"
}
]
}
]
What i expect:
[
{
"_id": {
"$oid": "63db1a68ef527b03093e78d4"
},
"user_id": "id_user_1",
"est_zone": {
"_id": {
"$oid": "63db1a68ef527b03093e78d5"
},
"zone": {
"temp": "tempo1"
},
"user_id": "id_user_1"
},
"est_tax": [
{
"_id": {
"$oid": "63db1a68ef527b03093e78d6"
},
"tax": {
"fam": "fam1"
},
"user_id": "id_user_1"
},
]
},
{
"_id": {
"$oid": "63db1a68ef527b03093e78d4"
},
"user_id": "id_user_1",
"est_zone": {
"_id": {
"$oid": "63db1ad58c3dc65723ce0ac9"
},
"zone": {
"temp": "tempo2"
},
"user_id": "id_user_1"
},
"est_tax": [
{
"_id": {
"$oid": "63db1ad58c3dc65723ce0aca"
},
"tax": {
"fam": "fam2"
},
"user_id": "id_user_1"
}
]
}
]
on the output:
first document must have only tempo1 and fam1
second document mst have only tempo2 and fam2
i new in mongoDB and still thinking like SQL,
I am open to suggestions, i read there's no problem with duplicate data (mb in user, idk)
but, i need to keep separate "est_zone" and "est_tax"
Thx for yout time!

Cannot get parent data by mongoDB aggregate graphLookup

Following is the data
[
{
"_id": {
"$oid": "6364f2eee35fc06fa06afb5f"
},
"type": "subbranch",
"parent_id": "635116c18fe4294398842ebb",
"org_name": "Pune - Delhi"
},
{
"_id": {
"$oid": "635116c18fe4294398842ebb"
},
"type": "branch",
"org_name": "Delhi Branch",
"parent_id": "0"
}
]
query which i have written is as follows
// req.params.id is 6364f2eee35fc06fa06afb5f
let id = mongoose.Types.ObjectId(req.params.id);
let data = await organisation.aggregate([
{
$addFields: { "_idString": { "$toString": "$_id" }}
},
{
$graphLookup: {
from: "organisations",
startWith: "$parent_id",
connectFromField: "parent_id",
connectToField: "_idString",
as: "parent"
}
},
{
$match: {_id: id}
},
]);
but in output i get as follow
[
{
"_id": "6364f2eee35fc06fa06afb5f",
"type": "subbranch",
"parent_id": "635116c18fe4294398842ebb",
"org_name": "Pune - Delhi",
"_idString": "6364f2eee35fc06fa06afb5f",
"parent": [
]
}
]
i am getting empty parent array but expected output is array with parent data in it.
any suggestion would be appreciated.
Remember connectFromField expected or extracted from current aggregated collection while connectToField is connected to from orginal collection
DEMO ON https://mongoplayground.net/p/vYDdOgNt9bW
The aggregate query be like
db.collection.aggregate([
{
$addFields: {
"parent_id": {
$convert: {
input: "$parent_id",
to: "objectId",
onError: "$parent_id",
}
}
}
},
{
$graphLookup: {
from: "collection",
startWith: "$parent_id",
connectFromField: "parent_id",
connectToField: "_id",
as: "parent"
}
}
])
Outputs
[
{
"_id": ObjectId("6364f2eee35fc06fa06afb5f"),
"org_name": "Pune - Delhi",
"parent": [
{
"_id": ObjectId("635116c18fe4294398842ebb"),
"org_name": "Delhi Branch",
"parent_id": "0",
"type": "branch"
}
],
"parent_id": ObjectId("635116c18fe4294398842ebb"),
"type": "subbranch"
},
{
"_id": ObjectId("635116c18fe4294398842ebb"),
"org_name": "Delhi Branch",
"parent": [],
"parent_id": "0",
"type": "branch"
}
]

Join multiple collections in MongoDB

Greetings amigo i have one question related joining multiple collection in MongoDb
i have collection schema something like below
Posts Collection
{
"type": "POST_TYPE",
"_id": "63241dffb0f6770c23663230",
"user_id": "63241dffb0f6770c23663230",
"post_id": "63241dffb0f6770c23663230",
"likes": 50
}
Post Types: 1. Event
{
"date": "2022-09-16T07:07:18.242+00:00",
"_id": "63241dffb0f6770c23663230",
"user_id": "63241dffb0f6770c23663230",
"venue": "Some Place",
"lat": "null",
"long": "null",
}
Post Types: 2. Poll
{
"created_date": "2022-09-16T07:07:18.242+00:00",
"_id": "63241dffb0f6770c23663230",
"user_id": "63241dffb0f6770c23663230",
"question": "Question??????",
"poll_opt1": "Yes",
"poll_opt2": "No",
"poll_opt1_count": "5",
"poll_opt2_count": "2"
}
now i have to join Post collection with respective collection e.g.
"post_id" to Event::_id or Poll::_id with condition to Post::type
i have tried aggregation but it does not gave expected output.
i am trying to get output something like below
[
{
"type": "event",
"_id": "63241dffb0f6770c23663230",
"user_id": "63241dffb0f6770c23663230",
"post_id": {
"date": "2022-09-16T07:07:18.242+00:00",
"_id": "63241dffb0f6770c23663230",
"user_id": "63241dffb0f6770c23663230",
"venue": "Some Place",
"lat": "null",
"long": "null"
},
"likes": 50
},
{
"type": "poll",
"_id": "63241dffb0f6770c23663230",
"user_id": "63241dffb0f6770c23663230",
"post_id": {
"created_date": "2022-09-16T07:07:18.242+00:00",
"_id": "63241dffb0f6770c23663230",
"user_id": "63241dffb0f6770c23663230",
"question": "Question??????",
"poll_opt1": "Yes",
"poll_opt2": "No",
"poll_opt1_count": "5",
"poll_opt2_count": "2"
},
"likes": 50
}
]
is there any efficient way to achieve this or better MongoDb schema to manage these types of records?
You can try something like this, using $facet:
db.posts.aggregate([
{
"$facet": {
"eventPosts": [
{
"$match": {
type: "event"
},
},
{
"$lookup": {
"from": "events",
"localField": "post_id",
"foreignField": "_id",
"as": "post_id"
}
}
],
"pollPosts": [
{
"$match": {
type: "poll"
},
},
{
"$lookup": {
"from": "poll",
"localField": "post_id",
"foreignField": "_id",
"as": "post_id"
}
}
]
}
},
{
"$addFields": {
"doc": {
"$concatArrays": [
"$pollPosts",
"$eventPosts"
]
}
}
},
{
"$unwind": "$doc"
},
{
"$replaceRoot": {
"newRoot": "$doc"
}
},
{
"$addFields": {
"post_id": {
"$cond": {
"if": {
"$eq": [
{
"$size": "$post_id"
},
0
]
},
"then": {},
"else": {
"$arrayElemAt": [
"$post_id",
0
]
}
}
}
}
}
])
We do the following, in the query:
Perform two $lookups for the different post_type within $facet. This unfortunately will increase, with the different values of post_type.
Then we combine all the arrays obtained from $facet, using $concatArray.
Then we unwind the concatenated array, and bring the nested document to the root using $replaceRoot.
Finally, for post_id we pick the first array element if it exists, to match the desired output.
Playground link.

MongoDB aggregation lookup objects in specific date range

I have some users and orders made by them:
db={
orders: [
{
"_id": "wJNEiSYwBd5ozGtLX",
"orderId": 52713,
"retailerId": 1320,
"createdAt": ISODate("2020-01-31T04:34:13.790Z"),
"status": "closed"
},
{
"_id": "wJNEiSYwBd5ozGtLX2",
"orderId": 52714,
"retailerId": 1320,
"createdAt": ISODate("2021-03-31T04:34:13.790Z"),
"status": "closed"
}
],
users: [
{
"_id": "2gSznevqwkGTxLRvL",
"createdAt": ISODate("2018-04-10T08:33:13.455Z"),
"username": "retailer#gmail.com",
"info": {
"retailerId": 1320,
},
"settings": {},
"status": "active",
}
]
}
If I try to aggregate orders into users:
db.users.aggregate([
{
"$lookup": {
"from": "orders",
"localField": "info.retailerId",
"foreignField": "retailerId",
"as": "orders"
}
},
])
I can get all orders merged into users like this:
[
{
"_id": "2gSznevqwkGTxLRvL",
"createdAt": ISODate("2018-04-10T08:33:13.455Z"),
"info": {
"retailerId": 1320
},
"orders": [
{
"_id": "wJNEiSYwBd5ozGtLX",
"createdAt": ISODate("2020-01-31T04:34:13.79Z"),
"orderId": 52713,
"retailerId": 1320,
"status": "closed"
},
{
"_id": "wJNEiSYwBd5ozGtLX2",
"createdAt": ISODate("2021-03-31T04:34:13.79Z"),
"orderId": 52714,
"retailerId": 1320,
"status": "closed"
}
],
"settings": {},
"status": "active",
"username": "retailer#gmail.com"
}
]
But I want to only merge orders from last month, not all orders into users.
How can I specify the date range?
https://mongoplayground.net/p/mZlDHQf2thN
Demo - https://mongoplayground.net/p/-hjGipqQPJq
https://docs.mongodb.com/manual/reference/operator/aggregation/lookup/#join-conditions-and-uncorrelated-sub-queries
To perform uncorrelated subqueries between two collections as well as allow other join conditions besides a single equality match, the $lookup stage has the following syntax:
Use $lookup in the pipeline style -
db.users.aggregate([
{
"$lookup": {
"from": "orders",
"let": { "rId": "$info.retailerId" },
"pipeline": [
{
$match: {
$expr: { $eq: [ "$retailerId","$$rId" ] },
createdAt: { $gte: ISODate("2021-03-01T00:00:00.000Z"), $lt: ISODate("2021-04-01T00:00:00.000Z") } // write date range query here
}
}
],
"as": "orders"
}
}
])

get nested model reference from mongodb

I have code below to get bookmarked blog of user
Question -> I want to also need the user object with selected field
Bookmarkblog.aggregate([{
$lookup:
{
from: "blogs",
localField: "blog",
foreignField: "_id",
as: "blog"
}
}, {
$match: { "user": mongoose.Types.ObjectId(req.headers.userid) }
}])
current output
[
{
"_id": "5a82ce0d80f62b18bc5ac5bf",
"blog": [
{
"_id": "5a696a31f78861dd29bec5bd",
"user": "59bbbe071b4a358f43dc0cbf",
"category": "59c3b5022ad1854d496cfb8b",
"image": [
"57904805-imac_pro_large.jpg"
],
"tags": [
"Mac"
],
"content": "",
"title": "iMac Pro, the most powerful Mac ever",
"created": "2018-01-25T05:25:05.025Z",
"__v": 0
}
],
"user": "59e5a62427543c54603050de",
"created": "2018-02-13T11:37:49.071Z",
"__v": 0
}
]