【发布时间】:2018-11-26 10:16:08
【问题描述】:
在一个集合中,我有包含一个数组的对象,我想在该数组中查找某些对象而不查看整个数组。我收藏的对象如下所示:
{
"transactions": [
{"id": randint(0, 100000), "hello": randint(0, 1000)} for _ in range(100000)
]
}
我想获取集合中所有 id 为 17 的交易。所以我创建了这个索引:
db.toto.createIndex({'transactions.id': 1})
但要仅查看我想要的交易,我必须进行 $unwind 并且此 unwind 仍然很慢:
db.toto.aggregate(
[
{"$match": {"transactions.id": 17}},
{"$unwind": "$transactions"},
{"$match": {"transactions.id": 17}},
]
)
给我
[{'_id': ObjectId('5bf854f685699a394ce5ba82'),
'transactions': {'hello': 920, 'id': 17}},
{'_id': ObjectId('5bf854f685699a394ce5ba82'),
'transactions': {'hello': 446, 'id': 17}},
{'_id': ObjectId('5bf854f685699a394ce5ba84'),
'transactions': {'hello': 822, 'id': 17}},
{'_id': ObjectId('5bf854f685699a394ce5ba84'),
'transactions': {'hello': 830, 'id': 17}},
[...]
{'_id': ObjectId('5bf854f885699a394ce5ba89'),
'transactions': {'hello': 301, 'id': 17}},
{'_id': ObjectId('5bf854f985699a394ce5ba8b'),
'transactions': {'hello': 666, 'id': 17}}]
添加第一个 $match 会使查询稍微快一些,因为它确实使用索引来仅查找包含我要查找的事务的对象。但它不会使用索引来使 $unwind 更快。 MongoDB 仍然会遍历包含 100000 个事务的整个数组来找到我想要的事务。
查询需要 5 秒才能找到大约 100 个对象。而像 db.toto.count({"transactions.id": 17}) 这样使用索引的查询只需要不到 0.1 秒。
这是我用来研究这个问题的python file。您可以通过以下方式重现该问题:
pip3 install fire pymongo
chmod +x toto_mongo.py
./toto_mongo.py insert
./toto_mongo.py create_index
time ./toto_mongo.py slow_query
【问题讨论】:
-
索引只能在第一阶段使用,并且只能用于某些阶段。试试docs.mongodb.com/manual/reference/operator/aggregation/filter 是否比 unwind + match 快
-
我也确实使用了过滤器,它稍微快一点,但完成查询仍然需要一秒钟以上的时间,所以它显然也不使用索引。查询在python文件中。
-
不,在可预见的将来它不会也不会这样做:docs.mongodb.com/manual/core/aggregation-pipeline/… 说“只有 $match 和 $sort 可以从索引中受益,并且只有在第一阶段使用时”。它是 db 端,无论您使用哪种客户端驱动程序/语言。
-
$unwind 将仅迭代每个文档的数组中的所有元素,并将 thrm 扩展到不同的文档。索引在这里帮不了你,你唯一能做的就是(如上所述)在展开阶段之前尽可能多地过滤结果,或者考虑以不同的格式(而不是数组)存储你的数据..
标签: mongodb