【问题标题】:Multiple limit condition in mongodbMongoDB中的多重限制条件
【发布时间】:2014-08-25 09:09:51
【问题描述】:

我有一个集合,其中一个字段是“类型”。我想根据所有类型相同的条件获取每种类型的一些值。就像我想要 A 类型的 2 个文档,B 类型的 2 个文档。 如何在单个查询中执行此操作?我正在使用 Ruby Active Record。

【问题讨论】:

    标签: ruby mongodb activerecord aggregation-framework rails-activerecord


    【解决方案1】:

    一般来说,您所描述的是 MongoDB 社区中一个相对常见的问题,我们可以将其描述为“顶级n 结果问题”。这是当给定一些可能以某种方式排序的输入时,如何在不依赖数据中的任意索引值的情况下获得顶部的n 结果。

    MongoDB 有 $first 运算符,aggregation framework 可以使用它处理问题的“前 1”部分,因为这实际上采用了在分组边界上找到的“第一个”项目,例如您的“类型”。但是,获得超过“一个”的结果当然会涉及更多。关于修改其他运算符以处理 n 结果或“限制”或“切片”的一些 JIRA 问题。值得注意的是SERVER-6074。但是这个问题可以通过几种方式来解决。

    用于 MongoDB 存储的 Rails Active Record 模式的流行实现是 MongoidMongo Mapper,它们都允许通过 .collection 访问器访问“本机”mongodb 集合函数。这就是您基本上需要能够使用原生方法(例如 .aggregate()),它支持比一般 Active Record 聚合更多的功能。

    这是一种使用 mongoid 的聚合方法,尽管一旦您可以访问本机集合对象,通用代码就不会改变:

    require "mongoid"
    require "pp";
    
    Mongoid.configure.connect_to("test");
    
    class Item
      include Mongoid::Document
      store_in collection: "item"
    
      field :type, type: String
      field :pos, type: String
    end
    
    Item.collection.drop
    
    Item.collection.insert( :type => "A", :pos => "First" )
    Item.collection.insert( :type => "A", :pos => "Second"  )
    Item.collection.insert( :type => "A", :pos => "Third" )
    Item.collection.insert( :type => "A", :pos => "Forth" )
    Item.collection.insert( :type => "B", :pos => "First" )
    Item.collection.insert( :type => "B", :pos => "Second" )
    Item.collection.insert( :type => "B", :pos => "Third" )
    Item.collection.insert( :type => "B", :pos => "Forth" )
    
    res = Item.collection.aggregate([
      { "$group" => {
          "_id" => "$type",
          "docs" => {
            "$push" => {
              "pos" => "$pos", "type" => "$type"
            }
          },
          "one" => {
            "$first" => {
              "pos" => "$pos", "type" => "$type"
            }
          }
      }},
      { "$unwind" =>  "$docs" },
      { "$project" => {
        "docs" => {
          "pos" => "$docs.pos",
          "type" => "$docs.type",
          "seen" => {
            "$eq" => [ "$one", "$docs" ]
          },
        },
        "one" => 1
      }},
      { "$match" => {
        "docs.seen" => false
      }},
      { "$group" => {
        "_id" => "$_id",
        "one" => { "$first" => "$one" },
        "two" => {
          "$first" => {
            "pos" => "$docs.pos",
            "type" => "$docs.type"
          }
        },
        "splitter" => {
          "$first" => {
            "$literal" => ["one","two"]
          }
        }
      }},
      { "$unwind" => "$splitter" },
      { "$project" => {
        "_id" => 0,
        "type" => {
          "$cond" => [
            { "$eq" => [ "$splitter", "one" ] },
            "$one.type",
            "$two.type"
          ]
        },
        "pos" => {
          "$cond" => [
            { "$eq" => [ "$splitter", "one" ] },
            "$one.pos",
            "$two.pos"
          ]
        }
      }}
    ])
    
    pp res
    

    文档中的命名实际上并没有被代码使用,“First”、“Second”等显示的数据中的标题实际上只是为了说明您确实从中获得了“top 2”文档结果是列表。

    所以这里的方法本质上是创建一个按您的键“分组”的文档“堆栈”,例如“类型”。这里的第一件事是使用 $first 运算符从该堆栈中取出“第一个”文档。

    随后的步骤匹配堆栈中“已见”的元素并过滤它们,然后使用$first 运算符再次将“下一个”文档从堆栈中取出。那里的最后一步实际上只是将文档返回到输入中找到的原始形式,这通常是此类查询所期望的。

    所以结果当然是每种类型的前 2 个文档:

    { "type"=>"A", "pos"=>"First" }
    { "type"=>"A", "pos"=>"Second" }
    { "type"=>"B", "pos"=>"First" }
    { "type"=>"B", "pos"=>"Second" }
    

    在最近的回答中,对此以及其他解决方案进行了更长的讨论和版本:

    Mongodb aggregation $group, restrict length of array

    尽管有标题,但本质上是相同的,并且该案例希望匹配最多 10 个或更多的顶级条目。那里还有一些管道生成代码,用于处理更大的匹配,以及可能根据您的数据考虑的一些替代方法。

    【讨论】:

      【解决方案2】:

      您将无法仅使用类型列和它必须是一个查询的约束直接执行此操作。然而,(一如既往)有一种方法可以做到这一点。

      要查找不同类型的文档,您需要有某种类型的附加值,平均而言,根据您希望数据返回的方式分配类型。

      db.users.insert({type: 'A', index: 1})
      db.users.insert({type: 'B', index: 2})
      db.users.insert({type: 'A', index: 3})
      db.users.insert({type: 'B', index: 4})
      db.users.insert({type: 'A', index: 5})
      db.users.insert({type: 'B', index: 6})
      

      那么在查询带有db.users.find(index: {$gt: 2, $lt: 7}) 的项目时,您将获得正确的项目分布。

      虽然我不确定这就是你要找的东西

      【讨论】:

        猜你喜欢
        • 2011-06-18
        • 1970-01-01
        • 1970-01-01
        • 2019-09-21
        • 1970-01-01
        • 2020-08-02
        • 2019-04-20
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多