在巨大的 mongodb 集合中快速搜索非常稀有的字段

quickly search for very rare field in a huge mongodb collection

我有一个包含约 10 亿个文档的庞大集合,这些文档中很少(少于 200 个)包含一些字段“rare_field”。

我怎样才能尽快找到包含该字段的所有文档?

如果我只是这样做:

collection.find({ "rare_field" : { $exists : true }})

超时了。 这可能需要几天时间才能完成,所以我不确定即使通过查询标志来防止超时也会有所帮助,但也许我错了。

我也可以编写一个脚本来遍历所有文档,但这会很慢,因为它需要通过网络将所有 10 亿个文档传递到我的服务器,我想要一些不需要任何传递的解决方案线上数据,很快。

注意:这是一个 sharded 合集。

我会 post 我目前的解决方案作为答案,但我不确定它是否 100% 正确,而且它没有我想要的那么快。

一个可能的解决方案是使用 findOne 而不是 find:

var doc = collection.findOne({ "rare_field" : { $exists : true }});

然后循环得到下一个:

var doc = collection.findOne({ _id : { $gt : doc._id}, "rare_field" : { $exists : true }});

但是,我不能 100% 确定这些调用一定会给我按 _id 排序的结果,而且我不确定单个 findOne 也不会超时。

我担心显式添加 sort({_id : 1}) 会强制查询获取所有结果,然后传递第一个结果。

如果 rare-field 上没有索引,在最坏的情况下,mongodb 将需要遍历集合中的所有文档。在这种情况下游标可能会超时,因此您需要为游标添加一个标志以防止它这样做。

在 mongo shell 中,这意味着这样的查询:

var cursor = db.collection.find({ "rare_field" : { $exists : true }}).noCursorTimeout();

如果您担心网络问题或其他问题会在游标填充 batchSize 匹配文档之前中断查询,那么您确实可以按照您在答案中建议的那样逐一获取文档,但是您需要按 { _id: 1 } 排序,并使用 noCursorTimeout()limit(1),即:

var doc = db.collection.find({ "rare_field" : { $exists : true }})
            .sort({ _id: 1 })
            .limit(1)
            .noCursorTimeout()
            .next();

然后按照您的建议,通过在向查询对象添加条件 { _id: { $gt: doc._id } } 的同时重复查询来检索下一个文档。