如何找出实际插入了多少Mongo文档?

时间:2014-11-06 09:27:10

标签: ruby mongodb

我有一个看起来像这样的函数:

def insert_multiple_cakes(cake_list)
  ensure_indexes

  insert_list = cake_list.map { |cake| mongofy_values(cake.to_hash) }

  inserted = db[CAKE_COLLECTION].insert(insert_list, w: 0)

  return inserted.length
end

该功能的目标是将cake_list中的所有蛋糕插入Mongo数据库。应忽略数据库中已存在的任何蛋糕。该函数应返回插入的蛋糕数,因此如果cake_list包含5个蛋糕,并且数据库中已存在2个蛋糕,则该函数应返回3.

我的问题是经过一个小时的实验后,我得出以下结论:

  • 如果写入问题(:w选项)为0,则插入调用将静默忽略所有重复插入,并且返回值包含 all 输入文档,甚至那些没有插入的。我设置:continue_on_error:collect_on_error的内容并不重要,返回值始终包含所有文档,收集的错误列表始终为空。

  • 如果写入关注点为1,则如果输入文档中存在任何重复项,则插入调用将失败并显示Mongo::OperationFailure。我设置:continue_on_error:collect_on_error的内容并不重要,当重复时,插入始终会失败。

所以在我看来,实现这一目标的唯一方法是迭代输入列表,搜索每个文档并过滤掉已经存在的文档。我的应用程序将同时处理(至少)数千个插页,所以我喜欢这个计划,就像我想跳下桥一样。

我是否误解了某些内容,或者Ruby客户端是否可能被窃听?


为了演示,这个功能完全符合我的要求和工作:

def insert_multiple_cakes(cake_list)
  ensure_indexes

  collection = db[CAKE_COLLECTION]

  # Filters away any cakes that already exists in the database.
  filtered_list = cake_list.reject { |cake|
    collection.count(query: {"name" => cake.name}) == 1
  }

  insert_list = filtered_list.map { |cake| mongofy_values(cake.to_hash) }

  inserted = collection.insert(insert_list)

  return inserted.length
end

问题在于它执行大量搜索,只需要进行一次插入。


Documentation for Mongo::Collection#insert

2 个答案:

答案 0 :(得分:5)

您可以执行以下操作(source):

coll = MongoClient.new().db('test').collection('cakes')
  bulk = coll.initialize_unordered_bulk_op
  bulk.insert({'_id' => "strawberry"})
  bulk.insert({'_id' => "strawberry"}) # duplicate key
  bulk.insert({'_id' => "chocolate"})
  bulk.insert({'_id' => "chocolate"}) # duplicate key
begin
  bulk.execute({:w => 1}) # this is the default but don't change it to 0 or you won't get the errors
rescue => ex
  p ex
  p ex.result
end

ex.result包含ninserted以及每个失败的原因。

{"ok"=>1,
 "n"=>2,
 "code"=>65,
 "errmsg"=>"batch item errors occurred",
 "nInserted"=>2,
 "writeErrors"=>
  [{"index"=>1,
    "code"=>11000,
    "errmsg"=>
     "insertDocument :: caused by :: 11000 E11000 duplicate key error index: test.cakes.$_id_  dup key: { : \"strawberry\" }"},
   {"index"=>3,
    "code"=>11000,
    "errmsg"=>
     "insertDocument :: caused by :: 11000 E11000 duplicate key error index: test.cakes.$_id_  dup key: { : \"chocolate\" }"}]}

答案 1 :(得分:1)

批量运营是可行的方法。我接受了拉曼的回答,但我想我应该分享我的最终代码:

def insert_documents(collection_name, documents)
  collection = db[collection_name]

  bulk = collection.initialize_unordered_bulk_op
  inserts = 0

  documents.each { |doc|
    bulk.insert doc

    inserts += 1
  }

  begin
    bulk.execute
  rescue Mongo::BulkWriteError => e
    inserts = e.result["nInserted"]
  end

  return inserts
end

def insert_cakes(cakes)
  ensure_cake_indexes

  doc_list = cakes.map { |cake|
    mongofy_values(cake.to_hash)
  }

  return insert_documents(CAKE_COLLECTION, doc_list)
end