4

首先,我使用带有 Squeel 1.0.13 的 Rails v3.2.9,这就是我想要做的:

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

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))

这包括 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 ('t')

提出了许多其他问题,比如那个“t”是怎么回事,但这是一个切线。

没有为每个排列编写 where 子句,有没有办法可以有条件地添加子句?

4

2 回答 2

3

所以,这不是最漂亮的东西,但它可以满足你的需求。

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

它之所以有效,是因为在 Squeel 中,谓词运算符(===~等)返回一个Predicate节点,因此我们可以在将它们与 连接之前独立地构造它们|

如果三个都是nil,则返回所有记录。

于 2013-01-03T17:42:41.503 回答
0

在最终找到这个相关的帖子之后,我蚕食了@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

其中self.by_sinself.by_nameself.by_dob是带有过滤器的简单范围。这会产生以下内容:

SELECT * 
FROM clients 
WHERE clients.id IN (<subquery for sin>) 
   OR clients.id IN (<subquery for name>) 
   OR clients.id IN (<subquery for dob>)

其中子查询仅在其关联值不为零时才包含。

这有效地允许我将适当的范围联合在一起作为 ActiveRecord::Relation。

于 2013-01-03T19:25:39.653 回答