How to implement `lookup` in 2nd level in mongoDB? [duplicate] - node.js

This question already has an answer here:
$lookup multiple levels without $unwind?
(1 answer)
Closed 2 years ago.
While trying to use lookup in 2nd level, getting an empty array instead of an object.
I have 3 collections,
1. registration
2. bus
3. operator
Here, registration contains busId, and the bus contains the operatorId.
I first populate the bus using the busId of registration.
Then with the populated bus try to populate the operator.
Documents of registration collection:
{ "_id": "1", "busId": "1" }
{ "_id": "2", "busId": "2" }
Documents of bus collection:
{ "_id": "1", "operatorId": "1", "name": "bus 01" }
{ "_id": "2", "operatorId": "2", "name": "bus 02" }
Documents of operator collection:
{ "_id": "1", "name": "operator 01" }
{ "_id": "2", "name": "operator 02" }
My expected result:
[
{
_id: '1',
busId: '1',
busInfo: { _id: '1', operatorId: '1', name: 'bus 01' },
operatorInfo: { _id: '1', name: 'operator 01' }
},
{
_id: '2',
busId: '2',
busInfo: { _id: '2', operatorId: '2', name: 'bus 02' },
operatorInfo: { '_id': '2', name: 'operator 02' }
}
]
What I tried this far:
I am using the following aggregation:
db.collection('registration').aggregate([
{
$lookup: {
from: 'bus',
localField: 'busId',
foreignField: '_id',
as: 'busInfo'
}
},
{ $unwind: '$busInfo' },
{
$lookup: {
from: 'operator',
localField: 'busInfo.operatorId',
foreignField: '_id',
as: 'operatorInfo'
}
}
]);
And getting the empty operator list.
[
{
_id: '1',
busId: '1',
busInfo: { _id: '1', operatorId: '1', name: 'bus 01' },
operatorInfo: []
},
{
_id: '2',
busId: '2',
busInfo: { _id: '2', operatorId: '2', name: 'bus 02' },
operatorInfo: []
}
]
This result has the empty operatorInfo Array. But I need the operatorInfo object.
The first lookup is working fine. But after getting bus from the busId, I need to get the operator details of the operatorId. operatorId is inside the bus.

you should try
localField: 'busInfo.operatorId instead of
localField: 'busInfo.operator

Related

How can I make lookup on in with productId and categoryId

How can make I lookup on in with productId and categoryId.
Below is the sample of JSON
"data": {
"status": 1,
"offerId": "634017ad34c7b3545bd47681",
"offerType": "buyOneGetOne",
"categoryId": [
"62de9b22dcafd44290ab03c1",
"62dfe695ec4d69cfeab0265d"
],
"productId": [
"633170230e88e2d859d15ee6",
"62de9d102b2b72f80eda7585",
"62de9d1e2b2b72f80eda7588"
],
"title": "Buy One Get One Deal ",
"description": "buy one get more",
"amountType": "amount",
"amount": 12341,
"startDate": "2022-10-04T06:43:39.000Z",
"endDate": "2022-10-08T06:43:39.000Z"
}
I'm going to assume that you are using ObjectId as _id in your categories and products collection. If I understood your question your document as the following minimal structure:
_id: '1',
data: {
...
}
So, you are looking for something like:
db.nameOfYourCollectionWithData.aggregate([{
$addFields: {
'data.categoryId': {
$map: {
input: '$data.categoryId',
'in': {
$toObjectId: '$$this'
}
}
},
'data.productId': {
$map: {
input: '$data.productId',
'in': {
$toObjectId: '$$this'
}
}
}
}
}, {
$lookup: {
from: 'categories',
localField: 'data.categoryId',
foreignField: '_id',
as: 'data.categoryId'
}
}, {
$lookup: {
from: 'products',
localField: 'data.productId',
foreignField: '_id',
as: 'data.productId'
}
}])
First, you have to convert your array ids into ObjectId. After do this, you can perform the lookup stages directly. If your document is 'data', just remove any reference to data in the aggregate pipeline.
If your _id in categories and products are string instead of ObjectId, just remove the $addFields stage, because is no longer neccessary.

Mongodb $lookup inside $addfield?

I have a collection named users, and this is how one specific user will look like:
{
_id: 'Object ID',
name: 'String',
cart: [
{
product_id: 'Product object ID',
quantity: 'Number',
},
...
],
}
I want my desired results to look like this:
{
_id: 'Object ID',
name: 'String',
cart: [
{
product_id: 'Product object ID',
quantity: 'Number',
product_details: {
'all the details of the product from Products collection which matches the product_id',
},
},
...
],
}
I tried adding addFields into lookup but it's getting too complicated and doesn't work as desired. What's the best way to aggregate this?
You can achieve this in several different ways, here's what I consider to be the most simple:
db.users.aggregate([
{
"$lookup": {
"from": "products",
let: {
cart: "$cart"
},
pipeline: [
{
$match: {
$expr: {
$in: [
"$_id",
"$$cart.product_id"
]
}
}
},
{
$replaceRoot: {
newRoot: {
"$mergeObjects": [
"$$ROOT",
{
"$arrayElemAt": [
{
$filter: {
input: "$$cart",
cond: {
$eq: [
"$_id",
"$$this.product_id"
]
}
}
},
0
]
}
]
}
}
}
],
"as": "cart"
}
}
])
Mongo Playground

MongoDB : Push or merge one lookup's result in other lookup's result

I have 3 collection namely constants, activities, applications with mentioned properties.
Now, querying constants collection with activities and activities with applications with matching Id's. I am getting correct results. But now activity_types are shown at per data level.
But expecting the output should be at per item level inside data whichever is matching with item. Because activities are matching for Item and it should be shown in per item level not at data level. I tried with $push and $group but not getting expected results.
Constants
{
_id: id
value : {
categories: [
{
id: 001,
title: "test 1"
},
{
id: 002,
title: "test 2"
},
{
id: 003,
title: "test 3"
}
]
}
}
Activity
{
propert1: "",
propert2: "",
config: {
agenda_item_category_ids: [ObjectId(001), ObjectId(002)]
},
activity_type_id: ObjectId(123)
}
{
propert1: "",
propert2: "",
activity_type_id: ObjectId(456)
config: {
agenda_item_category_ids: [ObjectId(002)]
}
}
applications
{
_id: ObjectId(123),
prop1: "",
prop2: ""
}
{
_id: ObjectId(456),
prop1: "",
prop2: ""
}
Current query
const results = await Constants.aggregate([
{
$match: query,
},
{
$unwind: {
path: '$value.categories',
preserveNullAndEmptyArrays: true,
},
},
{
$lookup: {
from: 'activity',
localField: 'value.categories.id',
foreignField: 'config.agenda_item_category_ids',
as: 'data',
},
},
{
$lookup: {
from: 'applications',
localField: 'items.activity_type_id',
foreignField: '_id',
as: 'activity_type',
},
},
{
$project: {
_id: 0,
category_id: '$value.categories.id',
title: '$value.categories.title',
description: '$value.categories.description',
icon_src: '$value.categories.icon_src',
data: 1,
activity_type: 1,
},
},
]);
Current output
[
{
data: [
{item1},
{item2}
],
activity_type,
title
_id
},
{
data: [
{item1},
{item2}
],
activity_type,
title
_id
}
]
Expected output
[
{
data: [
{
item1,
activity_type
},
{
item2,
activity_type
}
],
title
_id
},
]
Tried method
{
"_id": "$_id",
"activity_type": {
"$push": "$activity_type"
}
}

How can I filter the object in the List with Mongoose?

How can I filter the object in the List with Mongoose?
However, I have to reach the Scenario ID in all products at once without sending repeated queries.
There is an error in findAll, it does not allow me to enter the Array.
let getItems = await secondModel.findOne({
item_scenarios.scenario_id: '1'
});
Model making is like this.
item_scenarios: [
{
_id: 6050545060f40107076f2755,
scenario_id: '1',
scenario_tree_point: [Array]
},
{
_id: 6050546160f40107076f2756,
scenario_id: '1',
scenario_tree_point: [Array]
},
{
_id: 6050549b60f40107076f2757,
scenario_id: '1',
scenario_tree_point: [Array]
},
{
_id: 6050698e140a110a11365aad,
scenario_id: '604f3376dd79d8118a6990fe',
scenario_tree_point: [Array]
}
],
Error : SyntaxError: Unexpected token '.'
The error message is referring to the dot in item_scenarios.scenario_id. They are not valid in object keys in JS. Wrapping the key in quotes should fix the error:
let getItems = await secondModel.findOne({
'item_scenarios.scenario_id': '1'
});
findOne return the whole document when your condition are met. So use aggregate
db.collection.aggregate([
{
$project: {
item_scenarios: {
$filter: {
input: "$item_scenarios",
as: "scenario",
cond: {
$eq: [
"$$scenario.scenario_id",
"1"
]
}
}
}
}
}
])
output:
[
{
"_id": ObjectId("5a934e000102030405000000"),
"item_scenarios": [
{
"_id": "6050545060f40107076f2755",
"scenario_id": "1",
"scenario_tree_point": []
},
{
"_id": "6050546160f40107076f2756",
"scenario_id": "1",
"scenario_tree_point": []
},
{
"_id": "6050549b60f40107076f2757",
"scenario_id": "1",
"scenario_tree_point": []
}
]
}
]
mongo playground: https://mongoplayground.net/p/OWyOD1HdKFE

MongoDB: Aggregation, group by several fields

I have a collection with documents, of that structure
{
type: 'a',
date: '2014-01-04'
},
{
type: 'b',
date: '2014-01-04'
},
{
type: 'b',
date: '2014-01-04
},
{
type: 'c',
date: '2014-01-03'
},
{
type: 'a',
date: '2014-01-03'
}
I want to aggregate that data by date and type (group by date and count by type):
{
date: '2014-01-04': {
'a': 1,
'b': 2
},
date: '2014-01'03': {
'a': 1,
'c': 1
}
}
I have aggregate function, like this
db.items.aggregate([
{
$match: { user: user },
},
{
$group: { _id: {date: '$date'}, count: {$sum: 1}, services: {$push: '$type'}}
}
], function (err, results) {
But doing that I still need to reduce results by services.
Can this be done with one aggregation query?
You can of course group by more than one field:
{ $group: { _id: { date: '$date', services: '$services' } }
But that is not what you want it seems. You can not every easily convert data to keys, unless you can do that all by hand. The following query would be an option:
db.test.aggregate( [
{ $group: {
'_id' : { date: '$date' },
a: { $sum: {
$cond: [ { $eq: [ '$type', 'a' ] }, 1, 0 ]
} },
b: { $sum: {
$cond: [ { $eq: [ '$type', 'b' ] }, 1, 0 ]
} },
c: { $sum: {
$cond: [ { $eq: [ '$type', 'c' ] }, 1, 0 ]
} },
} },
{ $project: {
_id: 0,
date: '$_id.date',
a: '$a',
b: '$b',
c: '$c',
} }
] );
You will need to manually add a line for each new type.
By assuming you have fixed number of types, you can solve it as follows :
db.collection.aggregate(
{$group : {_id : "$date",
a:{$sum:{$cond:[{$eq:['$type','a']},1,0]}},
b:{$sum:{$cond:[{$eq:['$type','b']},1,0]}},
c:{$sum:{$cond:[{$eq:['$type','c']},1,0]}}
}},
{$project : {_id : 0, date : "$_id", a: "$a", b : "$b", c : "$c"}}
)

Resources