我有以下结构的文件:
{
...,
trials:[ {...,
ref:[{a:1,b:2},{a:2,b:2},...]
},
{...,
ref:[{a:1,b:2}]
},
...,
]
}
其中ref
是一个保证长度至少为1的数组。
如果我想计数每个ref
数组中每个元素的个别出现情况,我将使用以下聚合。(这个很好)
db.cl.aggregate([
{$unwind:"$trials"},
{$unwind:"$trials.ref"},
{$group:{_id:"$trials.ref", count:{$sum:1}}}
])
现在,我想做同样的事情,但只对每个ref
数组中的最后一个元素执行。我需要一种方法只选择聚合管道中每个数组的最后一个元素。
首先,我认为我可以添加一个中间步骤,通过这样的操作来获得我想要分组的所有元素:
db.cl.aggregate([
{$unwind:"$trials"},
{$group:{_id:null,arr:{$push:"$trials.ref.-1"}}},...
])
我还尝试在$match
中使用一个位置运算符。
db.cl.aggregate([
{$unwind:"$trials"},
{$match:{"trials.ref.$":-1}},...
])
或者试图投射最后一个元素。
db.cl.aggregate([
{$unwind:"$trials"},
{$project:{ref:"$trials.ref.1"}}
])
这两件都不让我有任何进展。$pop
运算符在聚合管道中无效。$last
运算符在这里并不是很有用。
对于如何只使用ref
数组的最后一个元素有什么想法吗?我宁愿坚持使用聚合框架,而不使用Map。
发布于 2014-06-18 09:11:07
聚合框架实际上无法处理这个问题。除了缺少任何“片”类型操作符之外,这里真正的问题是缺乏任何标记来判断内部数组的结束位置,而且也没有任何其他形式的文档重新成形的方法。
至少就目前而言,mapReduce方法非常简单,甚至不需要还原剂:
db.cl.mapReduce(
function() {
this.trials.forEach(function(trial) {
trial.ref = trial.ref.slice(-1);
});
var id = this._id;
delete this._id;
emit( id, this );
},
function(){},
{ "out": { "inline": 1 } }
)
未来可能会有一些希望。某种形式的$slice
已经被追捧了一段时间。但我确实注意到了$map 运算符码中的这个有趣的片段。我只想在这里列出:
output.reserve(input.size());
for (size_t i=0; i < input.size(); i++) {
vars->setValue(_varId, input[i]);
Value toInsert = _each->evaluateInternal(vars);
if (toInsert.missing())
toInsert = Value(BSONNULL); // can't insert missing values into array
output.push_back(toInsert);
}
注意for
循环和索引值。例如,我将投票将其公开为$map运算符中的变量,因为您知道数组的当前位置和长度,因此可以有效地进行“切片”。
但就目前而言,没有一种方法可以使用$map判断数组中的位置,而且如果您同时使用$unwind,则需要释放内部数组的端点。因此,目前缺乏聚合框架的解决方案。
https://stackoverflow.com/questions/24266224
复制相似问题