英文:
Query different product prices where the reference id is the same in mongoose and nodejs
问题
I've translated the content you provided:
我有一个名为Products的模型的文档,其结构如下:
{
"_id" : ObjectId("646ebcd4c24c4fd5d050c5be"),
"name" : "PRODUCT TEST",
}
我还有另一个名为Prices的模型的文档,其结构如下:
请注意,上述文档中的id与以下文档中的"productId"字段相匹配。
{
"_id" : ObjectId("646f8be5c86433269b82fc54"),
"unitMeasurementId" : ObjectId("646eca3e2fb2ca526a322523"),
"price" : 100,
"productId" : ObjectId("646ebcd4c24c4fd5d050c5be")
}
{
"_id" : ObjectId("646f8be5c86433269b82fc55"),
"unitMeasurementId" : ObjectId("646eca3e2fb2ca526a322524"),
"price" : 200,
"productId" : ObjectId("646ebcd4c24c4fd5d050c5be")
}
{
"_id" : ObjectId("646f8be5c86433269b82fc56"),
"unitMeasurementId" : ObjectId("646eca3e2fb2ca526a322525"),
"price" : 300,
"productId" : ObjectId("646ebcd4c24c4fd5d050c5be")
}
最后,我有一个名为Units of Measure的模型的文档,其结构如下:
请注意,上述文档中的"unitMeasurementId"字段与以下文档中的"_id"字段(分别)匹配。
{
"_id" : ObjectId("646eca3e2fb2ca526a322523"),
"name" : "UNIT"
}
{
"_id" : ObjectId("646eca3e2fb2ca526a322524"),
"name" : "DOZEN"
}
{
"_id" : ObjectId("646eca3e2fb2ca526a322525"),
"name" : "HUNDRED"
}
我尝试查询Price模型中的所有文档,其中"productId"字段与上面显示的第一个文档(产品模型文档)的"_id"匹配,并按Price模型中的文档顺序显示它们,即首先具有"unitMeasurementId"等于id:"646eca3e2fb2ca526a322523"(单位为"UNIT")的价格,然后具有"unitMeasurementId"等于id:"646eca3e2fb2ca526a322524 "(单位为"DOZEN")的第二价格,最后具有"unitMeasurementId"等于id:"646eca3e2fb2ca526a322525"(单位为"HUNDRED")的价格。
然而,会有一些"Products"模型的文档没有以前单位度量的价格。例如,某个产品仅具有两个单位度量的价格,单位和百分比,因此它没有具有单位度量为"DOZEN"的记录,在这种情况下,我希望在查询时填充该空间为NULL。
最终,我想要获得的结果如下:
"products": [
{
"_id": "646ebcd4c24c4fd5d050c5be",
"name": "PRODUCT TEST",
"prices": [
{
"_id": "646f8be5c86433269b82fc54",
"unitMeasurementId": {
"_id": "646eca3e2fb2ca526a322523",
"name": "UNIT",
},
"price": 100,
"productId" : ObjectId("646ebcd4c24c4fd5d050c5be")
},
{
"_id": "646f8be5c86433269b82fc55",
"unitMeasurementId": {
"_id": "646eca3e2fb2ca526a322524",
"name": "DOZEN",
},
"price": 200,
"productId" : ObjectId("646ebcd4c24c4fd5d050c5be")
},
{
"_id": "646f8be5c86433269b82fc56",
"unitMeasurementId": {
"_id": "646eca3e2fb2ca526a322525",
"name": "HUNDRED",
},
"price": 300,
"productId" : ObjectId("646ebcd4c24c4fd5d050c5be")
}
]
},
{
"_id": "646ebcd4c24c4fd5d050c5bf",
"name": "PRODUCT TEST 2",
"prices": [
{
"_id": "646f8be5c86433269b82fc57",
"unitMeasurementId": {
"_id": "646eca3e2fb2ca526a322523",
"name": "UNIT",
},
"price": 400,
"productId" : ObjectId("646ebcd4c24c4fd5d050c5be")
},
null <----- 在没有该单位度量的价格的情况下放置null
{
"_id": "646f8be5c86433269b82fc59",
"unitMeasurementId": {
"_id": "646eca3e2fb2ca526a322525",
"name": "HUNDRED",
},
"price": 500,
"productId" : ObjectId("646ebcd4c24c4fd5d050c5be")
}
]
},
]
在没有该单位度量的价格的情况下放置null
我已经将内容翻译为中文,供您参考。
英文:
I have documents (of a model called Products) with the following structure:
{
"_id" : ObjectId("646ebcd4c24c4fd5d050c5be"),
"name" : "PRODUCT TEST",
}
And I have other documents (of a model called Prices) with the following structure:
Note that the id in the above document matches the "productId" field in the following documents.
{
"_id" : ObjectId("646f8be5c86433269b82fc54"),
"unitMeasurementId" : ObjectId("646eca3e2fb2ca526a322523"),
"price" : 100,
"productId" : ObjectId("646ebcd4c24c4fd5d050c5be")
}
{
"_id" : ObjectId("646f8be5c86433269b82fc55"),
"unitMeasurementId" : ObjectId("646eca3e2fb2ca526a322524"),
"price" : 200,
"productId" : ObjectId("646ebcd4c24c4fd5d050c5be")
}
{
"_id" : ObjectId("646f8be5c86433269b82fc56"),
"unitMeasurementId" : ObjectId("646eca3e2fb2ca526a322525"),
"price" : 300,
"productId" : ObjectId("646ebcd4c24c4fd5d050c5be")
}
Lastly, I have these documents (of a model called Units of Measure) with the following structure:
Note that the "unitMeasurementId" field of the above documents matches the "_id" field (respectively) of the following documents.
{
"_id" : ObjectId("646eca3e2fb2ca526a322523"),
"name" : "UNIT"
}
{
"_id" : ObjectId("646eca3e2fb2ca526a322524"),
"name" : "DOZEN"
}
{
"_id" : ObjectId("646eca3e2fb2ca526a322525"),
"name" : "HUNDRED"
}
What I'm trying to do is query, in the Price model, all the documents where the "productId" field matches the "_id" of the first document displayed above (the product model document) and display them in the order of the documents in the Price model. model "Units of measure", that is, first the price that has the "unitMeasurementId" equal to the id: "646eca3e2fb2ca526a322523" (unit of measure "UNIT"), then the second price with the "unitMeasurementId" equal to the id: "646eca3e2fb2ca526a322524 " (unit of measure "DOZEN") and finally the price that has the "unitMeasurementId" equal to the id: "646eca3e2fb2ca526a322525" (unit of measure "HUNDRED").
However, there will be documents of the "Products" model that do not have a price with a previous unit of measure. For example, a product only has two prices with two units of measure, Unit and Hundred, so it does not have a record with the Unit of measure dozen, in that case, I would like to fill that space when querying, with NULL.
In the end, the result I want to get is the following:
"products": [
{
"_id": "646ebcd4c24c4fd5d050c5be",
"name": "PRODUCT TEST",
"prices": [
{
"_id": "646f8be5c86433269b82fc54",
"unitMeasurementId": {
"_id": "646eca3e2fb2ca526a322523",
"name": "UNIT",
},
"price": 100,
"productId" : ObjectId("646ebcd4c24c4fd5d050c5be")
},
{
"_id": "646f8be5c86433269b82fc55",
"unitMeasurementId": {
"_id": "646eca3e2fb2ca526a322524",
"name": "DOZEN",
},
"price": 200,
"productId" : ObjectId("646ebcd4c24c4fd5d050c5be")
},
{
"_id": "646f8be5c86433269b82fc56",
"unitMeasurementId": {
"_id": "646eca3e2fb2ca526a322525",
"name": "HUNDRED",
},
"price": 300,
"productId" : ObjectId("646ebcd4c24c4fd5d050c5be")
}
]
},
{
"_id": "646ebcd4c24c4fd5d050c5bf",
"name": "PRODUCT TEST 2",
"prices": [
{
"_id": "646f8be5c86433269b82fc57",
"unitMeasurementId": {
"_id": "646eca3e2fb2ca526a322523",
"name": "UNIT",
},
"price": 400,
"productId" : ObjectId("646ebcd4c24c4fd5d050c5be")
},
null <----- Place null in case there is no price with that unit of measure
{
"_id": "646f8be5c86433269b82fc59",
"unitMeasurementId": {
"_id": "646eca3e2fb2ca526a322525",
"name": "HUNDRED",
},
"price": 500,
"productId" : ObjectId("646ebcd4c24c4fd5d050c5be")
}
]
},
]
Place null in case there is no price with that unit of measure
I have tried the following, using the FOR statement, however, as the documents increase, the response time is higher, so I would like to know how to do it with pure queries with mongoose:
const getPriceDetails = async (req, res) => {
try {
const productsIds = await PriceDetails.find({}).distinct('productId');
const products = await Product.find({ _id: { $in: productsIds } }, { _id: true, name: true}).lean();
const unitMeasurements = await UnitMeasurement.find({}).lean();
for (const product of products) {
const prices = await Prices.find({ productId: product._id, active: true }, { price: true, unitMeasurementId: true }).sort({ productId: 1 }).populate('unitMeasurementId');
const c = unitMeasurements.map((i) => prices.find((j) => {
return j.unitMeasurementId.name === i.name
}));
product.prices = c;
}
res.send({ products });
} catch (e) {
serverError(e, res);
}
}
I attach an image of the frontend and how I handle said information:
答案1
得分: 1
MongoDB中的高级连接应使用mongoDB聚合管道来完成。
这比在正常代码中手动执行连接操作(就像您在getPriceDetails
函数中所做的那样)性能更好。
以下是一个返回您想要的数据的聚合管道示例:
{
"aggregate": "products",
"pipeline": [
{
"$lookup": {
"from": "prices",
"let": {
"i": "$_id"
},
"pipeline": [
{
"$match": {
"$expr": {
"$eq": [
"$productId",
"$$i"
]
}
}
},
{
"$project": {
"unitMeasurementId": 1,
"price": 1
}
}
],
"as": "prices"
}
},
{
"$unwind": {
"path": "$prices"
}
},
{
"$lookup": {
"from": "units",
"localField": "prices.unitMeasurementId",
"foreignField": "_id",
"as": "prices.unit"
}
},
{
"$unwind": {
"path": "$prices.unit"
}
},
{
"$project": {
"name": 1,
"prices._id": 1,
"prices.price": 1,
"prices.unit": {
"name": "$prices.unit.name",
"_id": "$prices.unit._id"
}
}
},
{
"$group": {
"_id": {
"name": "$name",
"_id": "$_id"
},
"prices": {
"$push": "$prices"
}
}
},
{
"$project": {
"_id": "$_id._id",
"name": "$_id.name",
"prices": "$prices"
}
},
{
"$limit": 10
}
]
}
您需要根据需要适应参数limit和skip,以便不会一次获取所有DB数据,而是只获取所需的数据。
英文:
Advanced joins inside mongoDB should be made using the mongoDB aggregation pipeline.
These perform better than doing the joins manually inside your normal code (as you did in your function getPriceDetails
).
Here is a aggregation pipeline which returns the data you want:
<!-- begin snippet: js hide: false console: true babel: false -->
<!-- language: lang-js -->
{
"aggregate": "products",
"pipeline": [
{
"$lookup": {
"from": "prices",
"let": {
"i": "$_id"
},
"pipeline": [
{
"$match": {
"$expr": {
"$eq": [
"$productId",
"$$i"
]
}
}
},
{
"$project": {
"unitMeasurementId": 1,
"price": 1
}
}
],
"as": "prices"
}
},
{
"$unwind": {
"path": "$prices"
}
},
{
"$lookup": {
"from": "units",
"localField": "prices.unitMeasurementId",
"foreignField": "_id",
"as": "prices.unit"
}
},
{
"$unwind": {
"path": "$prices.unit"
}
},
{
"$project": {
"name": 1,
"prices._id": 1,
"prices.price": 1,
"prices.unit": {
"name": "$prices.unit.name",
"_id": "$prices.unit._id"
}
}
},
{
"$group": {
"_id": {
"name": "$name",
"_id": "$_id"
},
"prices": {
"$push": "$prices"
}
}
},
{
"$project": {
"_id": "$_id._id",
"name": "$_id.name",
"prices": "$prices"
}
},
{
"$limit": 10
}
]
}
<!-- end snippet -->
You will need to adapt the params limit and skip so you don't fetch all DB data at once for every request, but only what is needed.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论