我有一个运行没有错误的代码,但由于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
是标签列表。
因此,在获得所有必需的信息后,这是我的答案:
- 您需要在字段上有一个索引
myLabel
.这是你这样做的方式:db.collection.ensureIndex({'myLabel': 1});
我希望这会加快查询速度。这是一种标准技术。 - 除此之外,您还可以尝试将整个集合放入内存中。或者,如果您的内存不够,您可以升级它以获得更快的性能。
- 如果您的收藏非常大,则可以对其进行分片。这将在查询中包含一些并行性。
就这段代码的复杂性而言,你不能比 O(n^2( 做得更好。
对于数组中的 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
语句不会自行过滤所有内容,但可能至少可以过滤掉集合中的某些文档,索引将有助于避免扫描集合中的每个文档。
最后,结果可能仍然是大量文档,因此建议考虑使用"光标"作为输出方法,而不是默认的,这是一个大列表。有关语法和示例,请参阅有关聚合的 pymongo 文档。
如果不是在MongoDB 2.6或更高版本上,则可能是最安全的选择:
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 }
}
)
同样,查找与大于 2 的"设置交集"大小匹配的文档。"scope"和"query"中的参数将只是将序列化为请求的python列表。