链接此查询会引发错误

Chaining this query throws error

我正在尝试查询:

  1. Finds/Gets 对象 (Coupon.code)
  2. 检查优惠券是否过期 (expires_at)
  3. 检查优惠券是否已用完。 (coupons_remaining)

我使用较新版本的 ruby 获得了一些语法,但它不适用于我的 2.2.1 版本我的语法是

  def self.get(code)
   where(code: normalize_code(code)).
   where("coupon_count > ? OR coupon_count IS NULL", 0).
   where("expires_at > ? OR expires_at IS NULL", Time.now).
   take(1)
 end

这会引发 wrong number of arguments (2 for 1) 错误,这是因为我的 rails 似乎无法识别 2 个参数 ("coupon_count > ? OR coupon_count IS NULL", 0) 所以我尝试更改它但是当我把它们改成这样的时候(我心里觉得很不对劲)

def self.get(code)
 where(code: normalize_code(code)).
 where(coupon_count: self.coupon_count > 0 || self.coupon_count.nil? ).
 where(expires_at: self.expires_at > Time.now || self.expires_at.nil? ).
 take(1)
end

我得到undefined method `coupon_count' for Coupon:Class

我缺乏想法有人可以帮助我在我的模型中获得此 get 方法的语法吗?顺便说一句,如果重要的话,我正在使用 mongoid 5.1.0

您定义了一个 class 方法,因此 self 在这种情况下引用 Coupon class 而不是 Coupon 实例。

尝试以下操作:

 scope :not_expired, -> { where("expires_at > ? OR expires_at IS NULL", Time.now) }
 scope :previously_used, -> { where("coupon_count > 0 OR coupon_count IS NULL") }

 def self.get(code)
   previously_used.not_expired.find_by!(code: normalize_code(code))
 end

我感受到你的痛苦。在 MongoDB 中组合 OR 和 AND 有点混乱,因为您根本没有真正使用查询语言,您只是在构建哈希。如果您可能将多个条件应用于同一字段,则类似的复杂情况也适用。这也是为什么您不能像使用 ActiveRecord 那样包含 SQL-like 片段的原因。

例如表示:

"coupon_count > ? OR coupon_count IS NULL", 0

你需要像这样构建一个哈希:

:$or => [
    { :coupon_count.gt => 0   },
    { :coupon_count    => nil }
]

但如果您尝试向其添加另一个 OR,您将覆盖现有的 :$or 键并导致混淆。相反,您需要注意会有多个 OR,并通过说 :$and:

手动避免重复
:$and => [
    {
        :$or => [
            { :coupon_count.gt => 0   },
            { :coupon_count    => nil }
        ]
    }, {
        :$or => [
            { :expires_at.gt => Time.now },
            { :expires_at    => nil      }
        ]
    }
]

然后添加 code 条件就很简单了:

:code => normalize_code(code),
:$and => [ ... ]

这使得整个事情变得相当可怕的怪物:

def self.get(code)
  where(
    :code => normalize_code(code),
    :$and => [
      {
        :$or => [
          { :coupon_count.gt => 0   },
          { :coupon_count    => nil }
        ]
      }, {
        :$or => [
          { :expires_at.gt => Time.now },
          { :expires_at    => nil      }
        ]
      }
    ]
  ).first
end

您也可以使用 find_by(that_big_mess) 而不是 where(that_big_mess).first。此外,如果您希望查询匹配多个文档,那么您可能想要添加一个 order 调用以确保您获得所需的那个。您可能会使用 andor 查询方法而不是单个哈希,但我怀疑它会使事情易于阅读、理解或维护。

我尽量避免使用 MongoDB 的 OR,因为查询会很快失去理智,你会留下一些你不想考虑太多的令人毛骨悚然的恐惧。您通常最好使用生成的字段预先计算部分查询(您必须维护和完整性检查以确保它们是正确的);例如,您可以添加另一个字段,如果 coupon_count 为正或 nil 则为真,然后在 coupon_count 更改时在 before_validation 挂钩中更新该字段。