使用大小查询Mongo嵌入式文档

3

我有一个使用Mongoid和MongoDB v2.4.6的Ruby on Rails应用程序。

我有以下MongoDB结构,一条记录嵌入了许多碎片:

{
  "_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个回答

1
当前方法的问题在于标准的MongoDB查询表单实际上没有以任何方式“过滤”嵌套数组文档。这基本上是您需要在此处“查找重复项”的内容。

为此,MongoDB提供了聚合框架,可能是找到这个问题的最佳方法。没有直接的“mongoid”风格的查询方法,因为那些方法针对处理关联文档的现有“rails”样式。

但是,您可以通过类模型上的.collection访问器访问“moped”表单:

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
}

这至少为您提供了一些处理此处“重复项”的方法。

哇,尼尔做得真好,我自己肯定想不到!谢谢,运行得非常出色 ;) - daveharris

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接