【发布时间】:2018-04-27 19:08:06
【问题描述】:
我已经实现了一个系统,其中有两种价格:
- 特定客户的特定价格(clientId、productId)
- 许多客户的共同价格(feeId、productId)
我的架构是灵活的,即有具有 clientId (1) 的价格和具有 feeId (2) 的价格。但它们都必须有一个 productId。
只有一个 Price 具有相同的 productId 和相同的 clientId。
var schema = new Schema({
feeId: {type: Schema.Types.ObjectId, ref: 'Fee'},
clientId: {type: Schema.Types.ObjectId, ref: 'Client'},
productId: {type: Schema.Types.ObjectId, ref: 'Product', required:true},
price: {type: Number, required: true}
});
我的目标是获取特定客户和产品系列的价格,查询应该返回客户的价格(价格匹配 clientId),如果有的话,以及属于该系列的产品的价格(有feeId和没有clientId的价格,匹配familyId)。
现在我有这个:
Price.aggregate([
{
$lookup:
{
'from': 'products',
'localField': 'productId',
'foreignField': '_id',
'as': 'product'
}
},
{
$unwind: '$product'
},
{
$match: {
$and: [
{
"product.family": new mongoose.Types.ObjectId(req.params.familyId)
},
{
$or: [
{"clientId": new mongoose.Types.ObjectId(req.params.clientId)},
{"feeId": {$exists: true, $ne: null}}
]
}
]
}
},
])
通过该查询,我得到以下结果:
{
"title": "Precio",
"body": [
{
"_id": "5a0974c7347eff02c784e5bd",
"price": 8,
"productId": "5a0970f9347eff02c784e5b1",
"clientId": "59f350dfb8634f659680299e",
"product": {
"_id": "5a0970f9347eff02c784e5b1",
"erpId": "12345",
"name": "Entrecot Entero",
"description": "Pieza entera",
"weight": null,
"photoURL": "https://okelan.s3.amazonaws.com/x14-FILETES-ENTRECOT-250gr_PREMIUM-e1491499466438-n6oowu8z2p0d877vgp4eioezbckou4u636uwvc35mo.jpg.pagespeed.ic.l38ZGRUwf6.jpg",
"familyId": "59e721e8b8634f65967eabf4"
},
"client": {
"_id": "59f350dfb8634f659680299e",
"contactName": "Pablo 2",
"NIF": "12345678Z",
"email": "pablogm.grao@gmail.com",
"password": "$2a$10$gXn.G/q4ar3wbjSyBCu0XOVud0HL5l3d.2WXNab4cCfB3uToncQLm",
"orders": [],
"erpId": "14123",
"IBAN": "ES6621000418401234567891",
"photoURL": "https://okelan.s3.amazonaws.com/logo.png",
"company": "Cárnicas Paco",
"deliveryMode": "Prueba",
"paymentMode": "Prueba",
"active": true,
"feeId": "59e78d76b8634f65967ec1b3",
"phone": "620859192",
"fee": "59e78d76b8634f65967ec1b3",
"shippingAddresses": [
{
"province": "Asturias",
"city": "Grado",
"postalCode": 33820,
"street": "Calle Asturias, 14 3°DCHA",
"_id": "5a097de74c468c17af16d18e"
},
{
"province": "adfasd",
"city": "adsfasd",
"postalCode": 23423,
"street": "asdfasd",
"_id": "5a097de74c468c17af16d18d"
}
],
"billingAddress": {
"province": "adfasd",
"city": "adsfasd",
"postalCode": 23423,
"street": "asdfasd"
}
}
},
{
"_id": "5a0c0d91e5127378570f13b0",
"price": 14,
"productId": "5a09ac68e640a63e0520301f",
"clientId": "59f350dfb8634f659680299e",
"product": {
"_id": "5a09ac68e640a63e0520301f",
"erpId": "2355",
"name": "Prueba",
"description": "25",
"weight": 25,
"photoURL": "https://okelan.s3.amazonaws.com/camera.jpg",
"familyId": "59e721e8b8634f65967eabf4"
},
"client": {
"_id": "59f350dfb8634f659680299e",
"contactName": "Pablo 2",
"NIF": "12345678Z",
"email": "pablogm.grao@gmail.com",
"password": "$2a$10$gXn.G/q4ar3wbjSyBCu0XOVud0HL5l3d.2WXNab4cCfB3uToncQLm",
"orders": [],
"erpId": "14123",
"IBAN": "ES6621000418401234567891",
"photoURL": "https://okelan.s3.amazonaws.com/logo.png",
"company": "Cárnicas Paco",
"deliveryMode": "Prueba",
"paymentMode": "Prueba",
"active": true,
"feeId": "59e78d76b8634f65967ec1b3",
"phone": "620859192",
"fee": "59e78d76b8634f65967ec1b3",
"shippingAddresses": [
{
"province": "Asturias",
"city": "Grado",
"postalCode": 33820,
"street": "Calle Asturias, 14 3°DCHA",
"_id": "5a097de74c468c17af16d18e"
},
{
"province": "adfasd",
"city": "adsfasd",
"postalCode": 23423,
"street": "asdfasd",
"_id": "5a097de74c468c17af16d18d"
}
],
"billingAddress": {
"province": "adfasd",
"city": "adsfasd",
"postalCode": 23423,
"street": "asdfasd"
}
}
}
]
}
这里可以找到一些源文件:https://pastebin.com/qBDqKa5y
是否可以使用聚合运算符在一个查询中完成?
感谢@dnickless 提供最终解决方案:
Price.aggregate([
{
$lookup:
{
'from': 'products',
'localField': 'productId',
'foreignField': '_id',
'as': 'product'
}
},
{
$unwind: '$product'
},
{
$lookup:
{
'from': 'clients',
'localField': 'clientId',
'foreignField': '_id',
'as': 'client'
}
},
{
$unwind: '$client'
},
{
$match: {
$and: [
{
"product.familyId": new mongoose.Types.ObjectId(req.params.familyId)
},
{
$or: [
{"clientId": new mongoose.Types.ObjectId(req.params.clientId)},
{"feeId": new mongoose.Types.ObjectId(req.body.feeId)}
]
}
]
}
},
{
$sort:
{
"clientId":
-1
}
},
{
$group: {
_id: {
"productId":
"$productId"
}
,
"doc":
{
$first: "$$ROOT"
}
}
},
{
$project: { // restore target structure
"_id": "$doc._id", // you may or may not need this field
"price": "$doc.price",
"productId": "$_id.productId",
"clientId": "$doc.clientId",
"feeId": "$doc.feeId",
"promoPrice": "$doc.promoPrice"
}
},
{
$lookup:
{
'from': 'products',
'localField': 'productId',
'foreignField': '_id',
'as': 'product'
}
},
{
$unwind: '$product'
},
{
$lookup:
{
'from': 'clients',
'localField': 'clientId',
'foreignField': '_id',
'as': 'client'
}
},
{
$unwind: '$client'
}
])
【问题讨论】:
-
能否请您发布一些示例文件?
-
@dnickless 我已经发布了一些所需文件的示例
-
一些源文件是我们需要的,而不是你现在得到的结果。
-
另外,肯定有问题。您的最后一个
$match阶段只会返回具有特定值的“clientId”的文档,而不是示例中的第一个和最后一个文档......?! -
@dnickless 这里是:pastebin.com/qBDqKa5y
标签: node.js mongodb express mongoose aggregation-framework