Ruby on rails Mongoid:按嵌套子值排序

Ruby on rails Mongoid:按嵌套子值排序,ruby-on-rails,ruby,mongodb,mongoid,Ruby On Rails,Ruby,Mongodb,Mongoid,所有的盒子都有一些默认的东西:袜子和戒指。每个人都可以把这个或其他东西放进一个盒子里。所以现在我需要按袜子数量订购所有箱子: class Box embeds_many :things after_init :add_default_things def add_default_things self.things.build(title: "Socks", value: 2) self.things.build(title: "Ring", value: 1)

所有的盒子都有一些默认的东西:袜子和戒指。每个人都可以把这个或其他东西放进一个盒子里。所以现在我需要按袜子数量订购所有箱子:

class Box
  embeds_many :things
  after_init :add_default_things

  def add_default_things
    self.things.build(title: "Socks", value: 2)
    self.things.build(title: "Ring", value: 1)
  end
end

class Thing
  field :title
  field :value, type: Integer
end
这很难(或不可能)用单个查询实现。我玩了一会儿。首先,让我发布我的整个测试脚本:

Box.order_by(:thing_with_title_socks.value.desc) # ???
所以你可以用几种方法来做。首先,您可以找到所有符合条件的对象,然后对这些对象进行处理。例如,获取框ID

require 'mongoid'

Mongoid.configure do |config|
  config.master = Mongo::Connection.new.db("test")
end

class Box
  include Mongoid::Document
  embeds_many :things
  field :name
end

class Thing
  include Mongoid::Document
  field :title
  field :value, type: Integer
end

b = Box.new
b.name = "non sock box"
b.things.push Thing.create!(title: "Ring", value:1)
b.save!

b1 = Box.new
b1.name = "sock box"
b1.things.push Thing.create!(title:"Socks", value:50)
b1.save!

b2 = Box.new
b2.name = "huge sock box"
b2.things.push Thing.create!(title:"Socks", value:1000)
b2.things.push Thing.create!(title:"Ring", value:1100)
b2.save!

b3 = Box.new
b3.name = "huge ring box"
b3.things.push Thing.create!(title:"Socks", value:100)
b3.things.push Thing.create!(title:"Ring", value:2600)
b3.save!

b4 = Box.new
b4.name = "ring first sock box"
b4.things.push Thing.create!(title: "Ring", value: 1200)
b4.things.push Thing.create!(title: "Socks", value: 5)
b4.save!
这将问题推到了应用层,这在一些mongo查询中是典型的。但那不是你想要的。所以我提出这个令人讨厌的问题。只有当您始终在things embedded文档中首先使用Sock things时,这才有效

Thing.desc(:value).where(:title => "Socks").to_a
这真的很烦人,因为你会注意到在我的示例数据中,我有一个“巨大的戒指盒”,它有袜子,但也有最大的值属性,所以实际上它首先被返回。如果您对这些内容进行了重新排序,那么这个查询实际上会起作用。我不知道如何按事物排序。值等于“foo”。我敢打赌你不能

Box.all(conditions: { "things.title" => "Socks" }, 
  sort:[["things.0.value", :desc]]).to_a
看,这是错误的。大袜子盒应该放在第一位。但这并不是因为这些东西。0.0.value是在(b4)样本数据中拾取最大的2600环值

您可以使用一行程序,但它实际上(至少)执行两个查询:

>> Box.all(conditions: { "things.title" => "Socks" }, 
sort:[["things.0.value", :desc]]).collect(&:name)
=> ["ring first sock box", "huge sock box", "huge ring box", "sock box"]
但再一次,这实际上是在ruby中进行提升,而不是让mongo进行提升。有很多这样的连接问题。您必须执行两个查询

另一种选择可能是对数据进行非标准化,只将数据存储为嵌入的属性数组,或者只存储属性。如果箱子可以包含任何东西,您甚至不需要提前定义属性。MongoDB有点酷

Thing.desc(:value).where(:title => "Socks").collect(&:id).each {|thing| 
  puts Box.where("things._id" => thing).first.name }

我不擅长ruby,所以我将尝试用java术语解释它。问题在于如何设计数据。例如,您有n个Box类,每个类中都有一个项目列表:

class Chest
  include Mongoid::Document
  field :name
end

c = Chest.create!(:name => "sock chest", :socks => 50, :ring => 1)
c1 = Chest.create!(:name => "non sock chest", :ring => 10)
c2 = Chest.create!(:name => "huge sock chest", :ring => 5, :socks => 100)
c3 = Chest.create!(:name => "huge ring chest", :ring => 100, :socks => 25)

Chest.where(:socks.exists => true).desc(:socks).collect(&:name)
=> ["huge sock chest", "sock chest", "huge ring chest"]
但是,如果您要对“已知”对象(如戒指或袜子)进行排序/直接访问(在查询中使用),那么您的数据应该如下所示:

{
  things : [ {"name" : "socks", "value" : 2} , {"name" : "ring", "value" : 5} ]
}
如果您有附加到此项目的额外信息,它们也可以是:

{
  things : { "socks": 2, "ring" : 5 }
}
这样,您就可以直接访问框中的项目

希望这会有所帮助


编辑:我认为这很明显,但只是想澄清一点:除非知道确切位置,否则无法高效地查询“列出的”子数据。(您可以始终使用map/reduce)

您需要使用map/reduce:我会尝试在几分钟后返回并回答更详细的问题。这就是我现在实际使用的:MapReduce,它为我返回[id,value]。呸,对不起,我忘了回来回答这个问题。你明白了吗?我写过mapreduce,但它看起来像是一个昂贵的解决方案,与我的解决方案非常相似:)。谢谢您的回复+1.谢谢您的回复!但我不能接受你的两个答案。它们都是关于我已经使用过的解决方案。是的,我经常遇到这个问题。感谢+1,很抱歉这是一个限制。我已找到解决方案,但我需要等待24小时:)
{
  things : [ {"name" : "socks", "value" : 2} , {"name" : "ring", "value" : 5} ]
}
{
  things : { "socks": 2, "ring" : 5 }
}
{
  things : { "S01" : { "name" : "super socks", "value" : 1 }, "S02" : { "name" : "different socks", "value" : 2} }
}