查询 Mongoid/rails 3 中的嵌入对象(“低于”、Min 运算符和排序)
Posted
技术标签:
【中文标题】查询 Mongoid/rails 3 中的嵌入对象(“低于”、Min 运算符和排序)【英文标题】:Querying embedded objects in Mongoid/rails 3 ("Lower than", Min operators and sorting) 【发布时间】:2011-07-12 15:52:05 【问题描述】:我正在使用带有 mongoid 的 rails 3。 我有一组带有嵌入式价格集合的股票:
class Stock
include Mongoid::Document
field :name, :type => String
field :code, :type => Integer
embeds_many :prices
class Price
include Mongoid::Document
field :date, :type => DateTime
field :value, :type => Float
embedded_in :stock, :inverse_of => :prices
我想获取自给定日期以来的最低价格低于给定价格 p 的股票,然后能够对每只股票的价格进行排序。
但看起来 Mongodb 不允许这样做。 因为这行不通:
@stocks = Stock.Where(:prices.value.lt => p)
另外,mongoDB 似乎无法对嵌入的对象进行排序。
那么,是否有替代方案来完成这项任务?
也许我应该把所有东西都放在一个集合中,这样我就可以轻松地运行以下查询:
@stocks = Stock.Where(:prices.lt => p)
但我真的想在查询后获得按股票名称分组的结果(例如,具有一系列有序价格的不同股票)。我听说过使用 group 功能的 map/reduce,但我不确定如何正确使用 Mongoid。
http://www.mongodb.org/display/DOCS/Aggregation
SQL 中的等价物是这样的:
SELECT name, code, min(price) from Stock WHERE price<p GROUP BY name, code
感谢您的帮助。
【问题讨论】:
【参考方案1】:MongoDB / Mongoid 确实允许您这样做。您的示例将起作用,只是语法不正确。
@stocks = Stock.Where(:prices.value.lt => p) #does not work
@stocks = Stock.where('prices.value' => '$lt' => p) #this should work
而且,它仍然是可链接的,因此您也可以按名称订购:
@stocks = Stock.where('prices.value' => '$lt' => p).asc(:name)
希望这会有所帮助。
【讨论】:
请注意,仍然可以进行更复杂的查询,例如:all_of(:'books.name' => 'name', :'books.author' => /joe/i)
使用字符串作为参数的名称对我来说很好......感谢您提供的信息,这真的很有帮助【参考方案2】:
我也遇到过类似的问题...这是我的建议:
scope :price_min, lambda |price_min| price_min.nil? ? : where("price.value" => '$lte' => price_min.to_f )
将此范围放置在 父 模型中。这将使您能够进行如下查询:
Stock.price_min(1000).count
请注意,我的作用域仅在您实际插入一些数据时才有效。如果您使用 Mongoid 构建复杂的查询,这将非常方便。
祝你好运!
非常好, 鲁伊
【讨论】:
【参考方案3】:MongoDB确实允许查询嵌入式文档,http://www.mongodb.org/display/DOCS/Advanced+Queries#AdvancedQueries-ValueinanEmbeddedObject
您缺少的是 Price 模型的范围,如下所示:
scope :greater_than, lambda |value| :where => :value.gt => value
这将允许你传入任何你想要的值,并返回一个 Mongoid 价格集合,其值大于你传入的值。这将是一个未排序的集合,因此你必须在 Ruby 中对其进行排序。
prices.sort |a,b| a.value <=> b.value.each |price| puts price.value
Mongoid 确实有一个 map_reduce 方法,您可以将两个包含 javascript 函数的字符串变量传递给该方法来执行 map/reduce,这可能是您需要的最佳方式,但是代码以上暂时可以使用。
【讨论】:
感谢您的回答。但我不确定如何使用这个范围功能。我将范围函数放在我的 Price 模型中,并查询 @Stocks = Stock.where(:prices.greater_than => 200) ,它给了我一个错误:“未定义的方法 `greater_than' for :prices:Symbol” 范围应该在单个 Stock 上调用,而不是在集合上。所以,你的代码确实行不通。因为您希望像这样进行过滤,所以最好使用 MapReduce 调用。我会看看我是否可以在某个地方发布一个示例。 是的,这就是我刚刚发现的。尽管如此,我还是找到了一个愚蠢而缓慢的解决方案,例如可以获取价格在 100 到 200 之间的股票列表,但我对此并不满意:i = 100 while i<200 results = Stock.where("prices.value" => i) if(@stocks == nil) @stocks = results else @stocks += results end i += 1 end
@mathieurip 的更大范围只是Stock
上的一个类方法,所以这样称呼它:Stock.greather_than
。以上是关于查询 Mongoid/rails 3 中的嵌入对象(“低于”、Min 运算符和排序)的主要内容,如果未能解决你的问题,请参考以下文章
Heroku 的 Rails、Mongoid 和 Unicorn 配置
Mongoid Rails 4 按 asc 或 desc order created_at 排序