查询具有大小的Mongo嵌入式文档

时间:2014-07-21 03:45:19

标签: ruby mongodb mongodb-query mongoid aggregation-framework

我使用Mongoid和MongoDB v2.4.6在rails应用程序上有一个ruby。

我有以下MongoDB结构,embeds_many片段的记录:

{
  "_id" : "76561198045636214",
  "fragments" : [
    {
        "id" : 76561198045636215,
        "source_id" : "source1"
    },
    {
        "id" : 76561198045636216,
        "source_id" : "source2"
    },
    {
        "id" : 76561198045636217,
        "source_id" : "source2"
    }
  ]
}

我正在尝试查找数据库中包含具有重复source_ids 的片段的所有记录。

我非常确定我需要使用$ elemMatch来查询嵌入式文档。

我试过了

Record.elem_match(fragments: {source_id: 'source2'})

有效,但不限制重复。

然后我尝试了

Record.elem_match(fragments: {source_id: 'source2', :source_id.with_size => 2})

不返回任何结果(但是是有效的查询)。 Mongoid产生的查询是:

selector: {"fragments"=>{"$elemMatch"=>{:source_id=>"source2", "source_id"=>{"$size"=>2}}}}

一旦有效,我需要将其更新为$ size是> 1.

这可能吗?感觉就像我非常接近。这是一次性的清理操作,因此查询性能不是太大的问题(但是我们确实需要更新数百万条记录!)

非常感谢任何帮助!

我已经能够达到预期的效果,但在测试中它太慢了(在我们的生产系统中运行需要数周时间)。问题是每条记录的双重查询(我们在生产中有大约3000万条记录)。

Record.where('fragments.source_id' => 'source2').each do |record|
  query = record.fragments.where(source_id: 'source2')
  if query.count > 1
    # contains duplicates, delete all but latest
    query.desc(:updated_at).skip(1).delete_all
  end
  # needed to trigger after_save filters
  record.save!
end

1 个答案:

答案 0 :(得分:1)

目前的方法存在的问题是标准的MongoDB查询表单实际上并没有过滤"嵌套数组文档以任何方式。这基本上是你需要的,以找到重复的"在你的文件中。

为此,MongoDB提供聚合框架可能是找到它的最佳方法。没有直接的" mongoid"查询的样式方法,因为它们面向现有的" rails"处理关系文件的方式。

您可以访问"轻便摩托车"通过类模型上的.collection访问器形成:

Record.collection.aggregate([

    # Find arrays two elements or more as possibles
    { "$match" => {
        "$and" => [
            { "fragments" => { "$not" => { "$size" => 0 } } },
            { "fragments" => { "$not" => { "$size" => 1 } } }
        ]
    }},

    # Unwind the arrays to "de-normalize" as documents
    { "$unwind" => "$fragments" },

    # Group back and get counts of the "key" values
    { "$group" => {
        "_id" => { "_id" => "$_id", "source_id" => "$fragments.source_id" },
        "fragments" => { "$push" => "$fragments.id" },
        "count" => { "$sum" => 1 }
    }},

    # Match the keys found more than once
    { "$match" => { "count" => { "$gte" => 2 } } }
])

这会让你得到如下结果:

{
    "_id" : { "_id": "76561198045636214", "source_id": "source2" },
    "fragments": ["76561198045636216","76561198045636217"],
    "count": 2
}

至少可以帮助你解决如何处理"重复问题"这里