链接多个 ActiveRecord `or` 查询

Chaining multiple ActiveRecord `or` queries

我有一个列数组,我想遍历这些列,并可选择将 or 查询链接到 ActiveRecord 查询链上。我可以让它工作,但是生成的查询将 or 附加到查询链上,因此使我的初始查询中的列成为可选的。这是我的 class:

class Claim
  class MatchingAttributeFinder
    ATTRIBUTE_GROUPS_TO_MATCH = [
      ["teacher_reference_number"],
      ["email_address"],
      ["national_insurance_number"],
      ["bank_account_number", "bank_sort_code", "building_society_roll_number"],
    ].freeze

    def initialize(source_claim, claims_to_compare = Claim.submitted)
      @source_claim = source_claim
      @claims_to_compare = claims_to_compare
    end

    def matching_claims
      claims = @claims_to_compare.where.not(id: @source_claim.id)

      ATTRIBUTE_GROUPS_TO_MATCH.each do |attributes|
        vals = values_for_attributes(attributes)

        next if vals.blank?

        concatenated_columns = "CONCAT(#{attributes.join(",")})"

        claims = claims.or(
          Claim.where("LOWER(#{concatenated_columns}) = LOWER(?)", vals.join)
        )
      end

      claims
    end

    private

    def values_for_attributes(attributes)
      attributes.map { |attribute|
        @source_claim.read_attribute(attribute)
      }.reject(&:blank?)
    end
  end
end

生成的 SQL 如下所示:

SELECT "claims".* FROM "claims" WHERE (((("claims"."submitted_at" IS NOT NULL AND "claims"."id" != 'a7b25b99-4477-42b1-96ab-8262582c5541' OR (LOWER(CONCAT(teacher_reference_number)) = LOWER('0902344'))) OR (LOWER(CONCAT(email_address)) = LOWER('genghis.khan@mongol-empire.com'))) OR (LOWER(CONCAT(national_insurance_number)) = LOWER('QQ891011C'))) OR (LOWER(CONCAT(bank_account_number,bank_sort_code,building_society_roll_number)) = LOWER('34682151972654123456789/ABCD')))

但我真正想要的更像这样:

SELECT "claims".* FROM "claims" WHERE "claims"."submitted_at" IS NOT NULL AND "claims"."id" != 'd6a53b4d-c569-49e6-a2ea-ac44b69b0451' AND (LOWER(concat(teacher_reference_number)) = LOWER('0902344') OR LOWER(concat(email_address)) = LOWER('genghis.khan@mongol-empire.com') OR LOWER(concat(national_insurance_number)) = LOWER('QQ891011C') OR LOWER(concat(bank_account_number,bank_sort_code,building_society_roll_number)) = LOWER('34682151972654123456789/ABCD'))

有没有什么方法可以设置类似空范围的东西,我可以将我的 OR 查询链接到?

先尝试将所有 "or" 连接在一起,然后再将原始查询链接起来

def matching_claims
  claims = @claims_to_compare.where.not(id: @source_claim.id)

  ors = nil

  ATTRIBUTE_GROUPS_TO_MATCH.each do |attributes|
    vals = values_for_attributes(attributes)

    next if vals.blank?

    concatenated_columns = "CONCAT(#{attributes.join(",")})"

    aux = Claim.where("LOWER(#{concatenated_columns}) = LOWER(?)", vals.join)
    if ors.nil?
      ors = aux
    else
      ors = ors.or(aux)
    end
  end

  claims.merge(ors)
end