9

我的模型中定义了以下范围:

scope :upcoming, -> { where(:start_time.gt => Time.now).asc(:start_time) }
scope :in_progress, -> {
   now = Time.now
   where(:start_time.lte => now).where(:end_time.gte => now).asc(:start_time)
}

我想创建另一个范围,它结合了这两个范围的结果,称为 current。我试过这样的事情:

scope :current, -> { self.in_progress | self.upcoming }

但这最终只会将它们都视为数组并将它们连接起来。这样做的问题是,当我尝试使用 Model.current 调用我的范围时,我收到以下错误消息:

NoMethodError: undefined method `as_conditions' for #<Array:0xaceb008>

这是因为它将 Mongoid Criteria 对象转换为数组,但我不希望这样。我希望该对象保留为 Mongoid Criteria 对象。

我真正想要的是 in_progress 集和即将到来的集的联合。

有任何想法吗?谢谢。

4

2 回答 2

7

您可以尝试使用 Mongoid 的查询方法和取消对条件选择器的引用来组合您的条件,但我不一定推荐这样做——请参见下面的示例。我支持制作第三个范围的建议。请记住,这些范围对应于您希望高效的数据库查询,因此可能值得您花时间检查和理解生成的结果和底层 MongoDB 查询。

模型

class Episode
  include Mongoid::Document
  field :name, type: String
  field :start_time, type: Time
  field :end_time, type: Time

  scope :upcoming, -> { where(:start_time.gt => Time.now).asc(:start_time) }
  scope :in_progress, -> {
     now = Time.now
     where(:start_time.lte => now).where(:end_time.gte => now).asc(:start_time)
  }
  scope :current, -> { any_of([upcoming.selector, in_progress.selector]) }
  scope :current_simpler, -> { where(:end_time.gte => Time.now) }
end

测试

require 'test_helper'

class EpisodeTest < ActiveSupport::TestCase
  def setup
    Episode.delete_all
  end

  test "scope composition" do
    #p Episode.in_progress
    #p Episode.upcoming
    #p Episode.current
    #p Episode.current_simpler

    in_progress_name = 'In Progress'
    upcoming_name = 'Upcoming'
    Episode.create(:name => in_progress_name, :start_time => Time.now, :end_time => 1.hour.from_now)
    Episode.create(:name => upcoming_name, :start_time => 1.hour.from_now, :end_time => 2.hours.from_now)

    assert_equal([in_progress_name], Episode.in_progress.to_a.map(&:name))
    assert_equal([upcoming_name], Episode.upcoming.to_a.map(&:name))
    assert_equal([in_progress_name, upcoming_name], Episode.current.to_a.map(&:name))
    assert_equal([in_progress_name, upcoming_name], Episode.current_simpler.to_a.map(&:name))
  end
end
于 2012-04-23T21:07:13.300 回答
2

您必须将 Array 映射回 Mongoid::Criteria。您的任何数组都可以使用 any_in 转换为条件:

scope :has_data, -> { any_in(:_id => all.select{ |record| record.data.size > 0 }.map{ |r| r.id }) }

所以,这样的事情应该可以解决问题:(未经测试)

scope :current, -> { any_in(:_id => (self.in_progress + self.upcoming).map{ |r| r.id }) }

我希望存在更好的解决方案,但这至少解决了这个等式。

于 2012-05-05T16:20:16.287 回答