我有一个没有错误运行的代码,但由于MongoDB中的查询数量很多,它实在很慢。
我有一个很大的python列表(大约5000个项目),我想在MongoDB中的文档中同时存在两个项目时进行测试。
mylist = ['apple','banana','melon','orange']
for i in mylist:
for j in mylist[mylist.index(i):]:
test = db.collection.find_one({'mylabel':{'$all':[i,j]}})
if test:
then do my stuff
其中mylabel
也是包含mylist
项目的列表。
在150-200件物品的清单中,完成后只需几分钟。但在像我这样的清单中,即使有更多的物品,我也需要几个小时。有没有办法在时间的基础上改进它?
根据要求编辑:
我还没有创建索引。这是我的数据库中的结构示例(但它太简单了)
{
"_id": 1,
"postid": 1,
"mylabel":['apple','banana','melon']
}
{
"_id": 2,
"postid": 2,
"mylabel":['banana','melon']
}
_id
是MongoDB的标准ID,postid
是我为文档提供的自定义ID,mylabel
是标签列表。
答案 0 :(得分:1)
所以在得到所有必需的信息之后,我的回答是:
myLabel
上的索引。这是你如何做到的:db.collection.ensureIndex({'myLabel': 1});
我希望这会加快查询速度。这是一种标准技术。就这段代码的复杂性而言,你不能做得比O(n ^ 2)好。
答案 1 :(得分:0)
对于数组中的5000个项目可能不是最好的事情(你可以试试)但是如果你有一个MongoDB 2.6或更高版本的聚合框架中的一些运算符,你至少可以加快速度。
为了简单起见只是用JavaScript注释,无论如何都没有与python代码有太大的不同
var mylist = ['apple','banana','melon','orange'];
db.collection.aggregate([
// Match possible documents excluding single element and empty
// arrays that cannot possibly match
{ "$match": {
"$and": [
{ "mylabel": { "$in": mylist } },
{ "mylabel": { "$not": { "$size": 1 } } }
]
}},
// Project the size of the set-intersection
{ "$project": {
"postid": 1,
"mylabel": 1,
"size": { "$size": { "$setIntersection": [ "$mylabel", "mylist" ] } }
}},
// Match only the documents with "size" greater than or equal to 2
{ "$match": { "size": { "$gte": 2 } }
])
非常自我解释,匹配包含至少一个匹配列表项的文档,并且必须具有自己的数组大小为2或更大才能产生可能的匹配。然后$setIntersection
运算符进入比较数组并找到相同的项。生成的数组使用$size
进行测试,在此上下文中是一个聚合运算符,用于确定结果数组的大小。
最终$match
过滤掉任何未报告"尺寸"匹配的2个或更多项目。所以这些是包含匹配列表中至少两个项目的文档。这应该真的过滤掉结果,实际上就是你要求的。
查看匹配列表中是否包含5000个元素,或者尝试拆分匹配列表。它不应该打破BSON的限制,虽然我不认为这种比较在性能方面会很好,但它仍然不应该花费数小时。
您当前流程中最大的时间问题是需要触发所有两个组合的所有查询。这里的要点是否定网络流量并让引擎完成工作。
同样,您的find_one
方法存在一个缺陷,即任何配对组合无疑都会有多个匹配。这意味着您当前的流程可能会错过实际符合条件的文档。
另外,请为您的数组字段添加索引。虽然此处的$match
语句不会自行过滤所有内容,但很可能至少可以过滤掉集合中的某些文档,索引将有助于避免扫描集合中的每个文档。 / p>
最后,结果可能仍然是大量文档,因此建议使用"光标"作为输出方法,而不是默认值,这是一个大的列表。有关语法和示例,请参阅aggregate上的pymongo文档。
如果没有在MongoDB 2.6或更高版本上,则mapReduce可能是最安全的选择:
var mylist = ['apple','banana','melon','orange'];
db.collection.mapReduce(
function () {
result = mylist.filter(function(n) {
return this.mylist.indexOf(n) != -1
});
if ( result.length >= 2 )
emit( this._id, this );
},
function(){},
{
"query": {
"$and": [
{ "mylabel": { "$in": mylist } },
{ "mylabel": { "$not": { "$size": 1 } } }
]
},
"out": "outputcollection",
"scope": { "mylist": mylist }
}
)
同样的事情,找到与" set intersection"匹配的文档。大小大于2."范围"中的参数和"查询"将只是将序列化为请求的python列表。