Ruby on rails 在使用squel时,能否有条件地在where块中添加子句?

Ruby on rails 在使用squel时,能否有条件地在where块中添加子句?,ruby-on-rails,ruby,squeel,Ruby On Rails,Ruby,Squeel,首先,我将Rails v3.2.9与Squel 1.0.13一起使用,下面是我要做的: 我想使用三种身份信息中的任意一种来搜索客户-姓名、出生日期(dob)和社会保险号码(sin)。结果集必须包括具有任何标识符或条件的任何记录。我以前在Squel中做过,它看起来像: scope :by_any, ->(sin, name, dob){ where{(client.sin == "#{sin}") | (client.name =~ "%#{name}%") | (client.dob ==

首先,我将Rails v3.2.9与Squel 1.0.13一起使用,下面是我要做的:

我想使用三种身份信息中的任意一种来搜索客户-姓名、出生日期(dob)和社会保险号码(sin)。结果集必须包括具有任何标识符或条件的任何记录。我以前在Squel中做过,它看起来像:

scope :by_any, ->(sin, name, dob){ where{(client.sin == "#{sin}") | (client.name =~ "%#{name}%") | (client.dob == "#{dob}")} }
只要我提供所有的标识符,这就可以正常工作。但是如果我只有一个名字呢?上述范围导致:

SELECT "clients".* FROM "clients" WHERE ((("clients"."sin" IS NULL OR "clients"."name" ILIKE '%John Doe%') OR "clients"."dob" IS NULL))
SELECT "clients".* FROM "clients" WHERE ('t')
这包括sin为null的客户端集和dob为null的客户端集,以及请求的名称为“John Doe”的客户端集

因此,输入我有条件地向
where
块添加子句的尝试。首先,我尝试使用nil检查值?方法:

def self.by_any (sin, name, dob)
  where do
    (clients.sin == "#{sin}" unless sin.nil?) |
    (clients.name =~ "%#{name}" unless name.nil?) |
    (clients.dob == "#{dob}" unless dob.nil?)
  end
其结果是:

SELECT "clients".* FROM "clients" WHERE ((("clients"."sin" IS NULL OR "clients"."name" ILIKE '%John Doe%') OR "clients"."dob" IS NULL))
SELECT "clients".* FROM "clients" WHERE ('t')
提出了许多其他问题,比如“t”是怎么回事,但这是一个切线


除了为每个排列编写where子句外,还有什么方法可以有条件地添加子句吗?

因此,这不是最漂亮的事情,但它能满足您的要求

def self.by_any(sin, name, dob)
  where do
    [
      sin.presence && clients.sin == "#{sin}",
      name.presence && clients.name =~ "%#{name}",
      dob.presence && clients.dob == "#{dob}"
    ].compact.reduce(:|)
    # compact to remove the nils, reduce to combine the cases with |
  end
end
基本上,
[a,b,c].reduce(:f)
返回
(a.f(b)).f(c)
。在本例中,
f
,调用的方法是管道,因此我们得到
(a.|(b))。|(c)
,在不太容易混淆的符号中,它是
(a | b)| c

这是因为,在squel中,谓词操作符(
=
=~
,等等)返回一个
谓词
节点,因此我们可以在将它们与
|
联接之前独立构造它们

在所有三个都为
nil
的情况下,它将返回所有记录。

在最终找到后,我将@bradgonesurfing的替代模式分解为以下解决方案:

def self.by_any (sin, name, dob)
  queries = Array.new
  queries << self.by_sin(sin) unless sin.nil?
  queries << self.by_name(name) unless name.nil?
  queries << self.by_dob(dob) unless dob.nil?

  self.where do
    queries = queries.map { |q| id.in q.select{id} }
    queries.inject { |s, i| s | i }
  end
end
其中,仅当子查询的关联值不是nil时才包含子查询


这有效地允许我将适当的作用域合并为ActiveRecord::Relation。

比我实际想到的更漂亮。从这里偷了一点东西来回答我的问题,但我想我更喜欢你。我将不得不进行压缩和缩减。感谢您对
reduce
-我以后将再次使用它。我在数组创建循环中的if语句之前得到一个“语法错误,意外修饰符\u if,预期为']'”?谢谢你的指点。是的,这显然是错误的,对不起。如果是这样的话,你就不能使用
。我会更新答案的,安迪,太棒了!感谢您的帮助,但更重要的是更新解决方案的惊人速度。我永远也想不到这一点;还在学Ruby。测试和工作。