为了从MongoDB
中选择100个最新文档,其中每个文档由同一个集合中具有相似字段的多个文档组成(在本例中为timestamp
),I' m使用Node.js
中的以下一系列查询:
return q.ninvoke(collection, 'aggregate',
[
{
$match : { active: true }
},
{
$limit : 100
},
{
$group : {
_id : "$timestamp",
mintime : {
$min : "$seconds"
},
timestamp : {
$first : "$timestamp"
},
data : {
$first : "$data"
}
}
}
]);
当集合中的文档少于$limit
时,这样可以正常工作。如果有更多,则选择最旧的文档(先插入),而不是选择timestamp
最高的文档(通常但不总是最后插入的文档)。
这是意料之外的,因为文档会使用以下保证索引插入到集合中:
collection.ensureIndex({
timestamp : -1,
seconds : -1,
active : -1
}, {
sparse : false
});
我的印象是,-1
上的timestamp
第一个索引意味着它们按降序编入索引,从而形成一个集合,其中第一个$limit
文档始终是最高timestamp
。
为什么这不能按预期工作?
我错了吗?
答案 0 :(得分:1)
实际上你真正的问题是没有选择索引。您可以通过调用聚合的explain
形式,通过db.runCommand
选项(MongoDB 2.6中提供或实际来自MongoDB 2.4.9,但未记录)来检查。
使用MongoDB,当匹配第一个时,指定要在索引中使用的字段非常很重要。所以索引定义为:
collection.ensureIndex({ "active": 1 })
在这种情况下,即使-1
被选中也是如此。您的索引不会,因为您没有引用任何其他字段。
这个可以强制进行更大的选择,当优化器认识到这将是最佳情况时,但在当前2.6版本中这实际上似乎是broken (直到修复)。
附录:因此可能涉及“排序”组件,但更多的是关于如何再次指定复合索引。为了确保分组边界的“时间戳”值,请确保在初始选择器之后包含该值,如:
collection.ensureIndex({ "active": -1, "timestamp": -1 })
按照您要求的顺序。
答案 1 :(得分:0)
补充@NeilLunn给出的非常重要的答案:
我不知道技术细节,但即使是正确的声明也可以从索引中始终选择错误的文档,如果你的磁盘空间“低”。 Mongo甚至可能没有抱怨这个,它会只选择错误的文件。
即使MongoDB会创建四个千兆字节的稀疏文件,如果可用空间下降低于千兆字节,Mongo仍然会窒息。
如果发生这种情况,请释放至少两千兆字节并对数据进行碎片整理:
根据经验,我会说:始终保持至少2̶G̶B̶2+ 4 = 6GB免费。