4

まず、Railsv3.2.9とSqueel1.0.13を使用しています。これが、私がやろうとしていることです。

名前、生年月日(dob)、社会保険番号(sin)の3つの識別情報のいずれかを使用してクライアントを検索したいと思います。結果セットには、条件の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であるクライアントのセット、および「JohnDoe」のような名前の要求されたクライアントのセットが含まれます。

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ます。したがって、で結合する前に、述語演算子を個別に構築できます|

3つすべてがの場合、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_sinで、、、self.by_nameおよびself.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>)

サブクエリは、関連付けられた値がnilでない場合にのみ含まれます。

これにより、適切なスコープをActiveRecord::Relationとして効果的に結合できます。

于 2013-01-03T19:25:39.653 に答える