在猫鼬中,我有一个交易集合。每个交易都有一个类似这样的项目列表:
var transactionItemSchema = new mongoose.Schema({
productId: String,
quantity: Number,
price: Number
});
var transactionSchema = new mongoose.Schema({
details: String,
items: [transactionItemSchema ],
}, {
timestamps: true
});
我需要通过乘以价格*数量并四舍五入两位小数来计算每个项目的总价值,但是我还需要通过对交易中所有项目的总和求和来获得交易总和。因此,例如,如果我在mongo中有此交易记录:
[{
details: 'First Transaction',
items: [{
price: 5.2,
quantity: 2
}, {
price: 4,
quantity: 3
}]
}, {
details: 'First Transaction',
items: [{
price: 0.333,
quantity: 3
}]
}]
在进行交易时返回如下信息:
[{
total: 22.40,
details: 'First Transaction',
items: [{
price: 5.2,
quantity: 2,
total: 10.40
}, {
price: 4,
quantity: 3,
total: 12.00
}]
}, {
total: 1.00,
details: 'Second Transaction',
items: [{
price: 0.333,
quantity: 3,
total: 1.00
}]
}]
有没有办法通过猫鼬的一些聚集来实现这一目标?
答案 0 :(得分:1)
假设模型是调用Transaction
:
Transaction.aggregate([
{ "$addFields": {
"items": {
"$map": {
"input": "$items",
"in": {
"$mergeObjects": [
"$$this",
{ "total": { "$round": [{ "$multiply": [ "$$this.price", "$$this.quantity" ] }, 2] } }
]
}
}
}
}}
])
或者没有$mergeObjects
:
Transaction.aggregate([
{ "$addFields": {
"total": {
"$sum": {
"$map": {
"input": "$items",
"in": {
"$round": [{ "$multiply": [ "$$this.price", "$$this.quantity" ] }, 2]
}
}
}
},
"items": {
"$map": {
"input": "$items",
"in": {
"price": "$$this.price",
"quantity": "$$this.quantity",
"total": { "$round": [{ "$multiply": [ "$$this.price", "$$this.quantity" ] }, 2] }
}
}
}
}}
])
$map
运算符实际上用于数组转换,其中您提供input
数组和一个表达式,以应用于定义对象输出的每个数组元素对于每个元素。在这里,$multiply
与两个参数一起应用于“相乘”以获得结果。
$mergeObjects
是可选,用作获取每个元素(price
和quantity
)的现有对象属性并将其包含在输出中的一种方法宾语。另一种方法是手动为每个元素在输出对象中指定属性,如图所示。
当然,对于 total总计,基本上会提供相同的内容,但是只是返回一个值并将其馈送到$sum
运算符以“总计”结果
所有这些都说明,简单地处理服务器返回的结果 post 并没有错:
let results = await Transaction.find().lean();
// Then manipulate the items arrays
results = results.map(r =>
({
...r,
total: r.items.reduce((o, i) =>
o + parseFloat((i.price * i.quantity).toFixed(2)), 0),
items: r.items.map(i =>
({ ...i, total: parseFloat((i.price * i.quantity).toFixed(2)) })
)
})
);
请仅在此处注意使用lean()
,该方法将返回纯JavaScript对象而不是Mongoose文档,从而使您可以操纵返回结果的结构。
以下是这两种方法的完整清单:
const { Schema } = mongoose = require('mongoose');
const uri = 'mongodb://localhost:27017/test';
const opts = { useNewUrlParser: true, useUnifiedTopology: true };
mongoose.Promise = global.Promise;
mongoose.set('debug', true);
mongoose.set('useCreateIndex', true);
mongoose.set('useFindAndModify', false);
const transactionItemSchema = new Schema({
productId: String,
quantity: Number,
price: Number
});
const transactionSchema = new Schema({
details: String,
items: [transactionItemSchema]
},{
timestamps: true
});
const Transaction = mongoose.model('Transaction', transactionSchema);
const initialData = [
{
details: 'First Transaction',
items: [
{ price: 5.2, quantity: 2 },
{ price: 4, quantity: 3 }
]
},
{
details: 'Second Transaction',
items: [
{ price: 0.333, quantity: 3 }
]
}
];
const log = data => console.log(JSON.stringify(data, undefined, 2));
(async function() {
try {
const conn = await mongoose.connect(uri, opts);
// Clean data
await Promise.all(
Object.values(conn.models).map(m => m.deleteMany())
);
await Transaction.insertMany(initialData);
// Aggregate example
let result1 = await Transaction.aggregate([
{ "$addFields": {
"total": {
"$sum": {
"$map": {
"input": "$items",
"in": {
"$round": [
{ "$multiply": [ "$$this.price", "$$this.quantity" ] },
2
]
}
}
}
},
"items": {
"$map": {
"input": "$items",
"in": {
"$mergeObjects": [
"$$this",
{ "total": {
"$round": [
{ "$multiply": [ "$$this.price", "$$this.quantity" ] },
2
]
}}
]
}
}
}
}}
]);
log({ result1 });
// Plain JavaScript example
let result2 = await Transaction.find().lean();
result2 = result2.map(r =>
({
...r,
total: r.items.reduce((o, i) =>
o + parseFloat((i.price * i.quantity).toFixed(2)), 0),
items: r.items.map(i =>
({ ...i, total: parseFloat((i.price * i.quantity).toFixed(2)) })
)
})
);
log({ result2 });
} catch (e) {
console.error(e);
} finally {
mongoose.disconnect();
}
})();
输出:
Mongoose: transactions.deleteMany({}, {})
Mongoose: transactions.insertMany([ { _id: 5d8f4dfcaf9f6a2f8ec28039, details: 'First Transaction', items: [ { _id: 5d8f4dfcaf9f6a2f8ec2803b, price: 5.2, quantity: 2 }, { _id: 5d8f4dfcaf9f6a2f8ec2803a, price: 4, quantity: 3 } ], __v: 0, createdAt: 2019-09-28T12:11:40.060Z, updatedAt: 2019-09-28T12:11:40.061Z }, { _id: 5d8f4dfcaf9f6a2f8ec2803c, details: 'Second Transaction', items: [ { _id: 5d8f4dfcaf9f6a2f8ec2803d, price: 0.333, quantity: 3 } ], __v: 0, createdAt: 2019-09-28T12:11:40.062Z, updatedAt: 2019-09-28T12:11:40.062Z } ], {})
Mongoose: transactions.aggregate([ { '$addFields': { total: { '$sum': { '$map': { input: '$items', in: { '$round': [ { '$multiply': [ '$$this.price', '$$this.quantity' ] }, 2 ] } } } }, items: { '$map': { input: '$items', in: { '$mergeObjects': [ '$$this', { total: { '$round': [ { '$multiply': [Array] }, 2 ] } } ] } } } } } ], {})
{
"result1": [
{
"_id": "5d8f4dfcaf9f6a2f8ec28039",
"details": "First Transaction",
"items": [
{
"_id": "5d8f4dfcaf9f6a2f8ec2803b",
"price": 5.2,
"quantity": 2,
"total": 10.4
},
{
"_id": "5d8f4dfcaf9f6a2f8ec2803a",
"price": 4,
"quantity": 3,
"total": 12
}
],
"__v": 0,
"createdAt": "2019-09-28T12:11:40.060Z",
"updatedAt": "2019-09-28T12:11:40.061Z",
"total": 22.4
},
{
"_id": "5d8f4dfcaf9f6a2f8ec2803c",
"details": "Second Transaction",
"items": [
{
"_id": "5d8f4dfcaf9f6a2f8ec2803d",
"price": 0.333,
"quantity": 3,
"total": 1
}
],
"__v": 0,
"createdAt": "2019-09-28T12:11:40.062Z",
"updatedAt": "2019-09-28T12:11:40.062Z",
"total": 1
}
]
}
Mongoose: transactions.find({}, { projection: {} })
{
"result2": [
{
"_id": "5d8f4dfcaf9f6a2f8ec28039",
"details": "First Transaction",
"items": [
{
"_id": "5d8f4dfcaf9f6a2f8ec2803b",
"price": 5.2,
"quantity": 2,
"total": 10.4
},
{
"_id": "5d8f4dfcaf9f6a2f8ec2803a",
"price": 4,
"quantity": 3,
"total": 12
}
],
"__v": 0,
"createdAt": "2019-09-28T12:11:40.060Z",
"updatedAt": "2019-09-28T12:11:40.061Z",
"total": 22.4
},
{
"_id": "5d8f4dfcaf9f6a2f8ec2803c",
"details": "Second Transaction",
"items": [
{
"_id": "5d8f4dfcaf9f6a2f8ec2803d",
"price": 0.333,
"quantity": 3,
"total": 1
}
],
"__v": 0,
"createdAt": "2019-09-28T12:11:40.062Z",
"updatedAt": "2019-09-28T12:11:40.062Z",
"total": 1
}
]
}