我使用 mongo
gem 将 MongoDB 与 Ruby 结合使用。
我有以下场景:
- 对于集合中的每个文档说
coll1
,查看key1
和key2
- 在另一个集合中搜索文档,比如
coll2
以及key1
和key2
的匹配值 - 如果匹配,则使用新键
key3
添加在 #2 中获取的文档,其值设置为 #1< 中引用的文档中key3
的值/li> - 将更新后的散列插入新集合
coll3
MongoDB 的一般准则是在应用程序代码中处理交叉集合操作。
所以我做了以下事情:
client = Mongo::Client.new([ '127.0.0.1:27017' ], :database => some_db,
:server_selection_timeout => 5)
cursor = client[:coll1].find({}, { :projection => {:_id => 0} }) # exclude _id
cursor.each do |doc|
doc_coll2 = client[:coll2].find('$and' => [{:key1 => doc[:key1]}, {:key2 => doc[:key2] }]).limit(1).first # no find_one method
if(doc_coll2 && doc[:key3])
doc_coll2[:key3] = doc[:key3]
doc_coll2.delete(:_id) # remove key :_id
client[:coll3].insert_one(doc_coll2)
end
end
这行得通,但是完成这项工作需要很多时间——集合 coll1
中的每个文档大约需要 250 毫秒,或者大约 15000 条记录需要 3600 秒(1 小时),这看起来很多,这可能与一次阅读一个文档相关联,检查应用程序代码,然后一次将一个文档写回新集合。
有没有办法更快地完成此操作?我的做法是否正确?
示例文档
col1
{ "_id" : ObjectId("588610ead0ae360cb815e55f"), "key1" : "115384042", "key2" : "276209", "key3" : "10101122317876" }
col2
{ "_id" : ObjectId("788610ead0ae360def15e88e"), "key1" : "115384042", "key2" : "276209", "key4" : 10, "key5" : 4, "key6" : 0, "key7" : "false", "key8" : 0, "key9" : "false" }
coll3
{ "_id" : ObjectId("788610ead0ae360def15e88e"), "key1" : "115384042", "key2" : "276209", "key3" : "10101122317876", "key4" : 10, "key5" : 4, "key6" : 0, "key7" : "false", "key8" : 0, "key9" : "false" }
最佳答案
一种解决方案是改用聚合,并在一个查询中执行此操作:
- 使用
$lookup
对 key1 字段执行连接 - 使用
$unwind
展开数组 - 使用
$redact
在coll1.key2 == coll2.key2
处保存文档 - 用
$project
重新格式化文档 - 用
$out
把它写到coll3
所以查询将是:
db.coll1.aggregate([
{ "$lookup": {
"from": "coll2",
"localField": "key1",
"foreignField": "key1",
"as": "coll2_doc"
}},
{ "$unwind": "$coll2_doc" },
{ "$redact": {
"$cond": [
{ "$eq": [ "$key2", "$coll2_doc.key2" ] },
"$$KEEP",
"$$PRUNE"
]
}},
{
$project: {
key1: 1,
key2: 1,
key3: 1,
key4: "$coll2_doc.key4",
key5: "$coll2_doc.key5",
key6: "$coll2_doc.key6",
key7: "$coll2_doc.key7",
key8: "$coll2_doc.key8",
key9: "$coll2_doc.key9",
}
},
{$out: "coll3"}
], {allowDiskUse: true} );
和db.coll3.find()
会返回
{
"_id" : ObjectId("588610ead0ae360cb815e55f"),
"key1" : "115384042",
"key2" : "276209",
"key3" : "10101122317876",
"key4" : 10,
"key5" : 4,
"key6" : 0,
"key7" : "false",
"key8" : 0,
"key9" : "false"
}
编辑:MongoDB 3.4 解决方案
如果你不想在$project
阶段指定所有键,你可以利用$addFields
和$replaceRoot
, MongoDB 3.4 中引入的两个新运算符
查询将变为:
db.coll1.aggregate([
{ "$lookup": {
"from": "coll2",
"localField": "key1",
"foreignField": "key1",
"as": "coll2_doc"
}},
{ "$unwind": "$coll2_doc" },
{ "$redact": {
"$cond": [
{ "$eq": [ "$key2", "$coll2_doc.key2" ] },
"$$KEEP",
"$$PRUNE"
]
}},
{$addFields: {"coll2_doc.key3": "$key3" }},
{$replaceRoot: {newRoot: "$coll2_doc"}},
{$out: "coll3"}
], {allowDiskUse: true})
关于Ruby MongoDB - 提高处理多个集合时的速度,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/41993703/