我有一个名为inbound_pos
的集合,它的文档有一个称为lines
的键。示例如下:
{
"_id": ObjectId("537e16cf8be10a5e7e000008"),
"cdt": ISODate("2014-05-22T15:25:03.379Z"),
"comments": [
[
]
],
"cust_shipto_id": "103",
"doc_dt": "20140522",
"isa_control_id": "000030456",
"kind": "edi",
"lines": [
{
"linenumber": "1",
"net_qty": NumberInt(10),
"uom": "EA",
"unitcost": 10.04,
"v_nmbr": "005-2964",
"tm_desc": "NA"
},
{
"linenumber": "2",
"net_qty": NumberInt(10),
"uom": "EA",
"unitcost": 13.59,
"v_nmbr": "005-2966",
"tm_desc": "NA"
},
{
"linenumber": "3",
"net_qty": NumberInt(6),
"uom": "BX",
"unitcost": 18.36,
"v_nmbr": "2201254",
"tm_desc": "LANTISEPTIC"
}
]
}
在组成lines
阵列的每个对象中,v_nmbr
、net_qty
和unitcost
键分别指示正在购买的特定物品、该物品的购买数量和该物品的单价。
我想做的是获得集合中所有文档中每个v_nmbr
花费的总金额。地图缩减查询如下:
db.inbound_pos.mapReduce(function() {
for (var i=0; i<this.lines.length; i++) {
emit(this.lines[i].v_nmbr, this.lines[i])
}
}, function(key, values) {
var totalExpenses = 0;
for (var i=0; i<values.length; i++) {
totalExpenses += values[i].unitcost * values[i].net_qty
}
return totalExpenses
}, {
out: "total_expenses_per_item"
})
此查询为已多次购买的所有项目正确生成总额,但对于只购买过一次的项目则失败。
以下是一些输出示例:
成功(物品购买20次)
{
"_id": "005-BUHW2076HRF",
"value": 2366.4
}
(物品已购买2次):
{
"_id": "P54072",
"value": 29.13
}
失败(物品只购买过一次):
{
"_id": "OTC11780",
"value": {
"linenumber": "1",
"v_nmbr": "OTC11780",
"net_qty": NumberInt(5),
"unitcost": 13.68,
"uom": "BT",
"tm_desc": "VITAMIN E 1000 IU SOFTGEL 100/BTL"
}
}
如果您有时间,我们将非常感谢您的帮助和解释。
试试这个:
db.inbound_pos.mapReduce(function() {
for (var i=0; i<this.lines.length; i++) {
emit(this.lines[i].v_nmbr, this.lines[i])
}
}, function(key, values) {
var totalExpenses = 0;
for (var i=0; i<values.length; i++) {
totalExpenses += values[i].unitcost * values[i].net_qty
}
return totalExpenses
}, {
finalize: function(key,reducedValue) {
if (typeof reducedValue.linenumber != 'undefined') {
return reducedValue.unitcost;
} else {
return reducedValue;
}
},
out: "total_expenses_per_item"
});
你的失败的原因是,如果只有一个值,map reduce不会进入reduce阶段,在这种情况下,你可以使用finalize函数只返回你想要的值。
根据文档:MongoDB不会为只有一个值的键调用reduce函数。它只是返回映射值作为结果。
但MongoDB无论如何都会为单个映射值调用finalize函数,所以您可以在那里修改结果。
最好的解决方案是遵循以下规则:reduce函数的返回对象的类型必须与map函数发出的值的类型相同。
在你的情况下,我会这样做地图减少:
db.inbound_pos.mapReduce(function() {
for (var i=0; i<this.lines.length; i++) {
emit(this.lines[i].v_nmbr, this.lines[i].unitcost * this.lines[i].net_qty)
}
}, function(key, values) {
var totalExpenses = 0;
for (var i=0; i<values.length; i++) {
totalExpenses += values[i]
}
return totalExpenses
}, {
out: "total_expenses_per_item"
})