Hoe bereikquery’s koppelen aan OR in plaats van AND?

Ik gebruik Rails3, ActiveRecord

Ik vraag me alleen af ​​hoe ik de scopes kan koppelen aan OR-statements in plaats van AND.

bijv.

Person.where(:name => "John").where(:lastname => "Smith")

Dat keert normaal gesproken terug:

name = 'John' AND lastname = 'Smith'

maar ik wil graag:

`name = 'John' OR lastname = 'Smith'

Antwoord 1, autoriteit 100%

Je zou doen

Person.where('name=? OR lastname=?', 'John', 'Smith')

Op dit moment is er geen andere OF-ondersteuning door de nieuwe AR3-syntaxis (dat wil zeggen zonder een juweeltje van derden te gebruiken).


Antwoord 2, autoriteit 67%

Volgens dit pull-verzoekondersteunt Rails 5 nu de volgende syntaxis voor het koppelen van query’s :

Post.where(id: 1).or(Post.where(id: 2))

Er is ook een backport van de functionaliteit in Rails 4.2 via dit juweeltje.


Antwoord 3, autoriteit 47%

Gewoon de array-syntaxis posten voor dezelfdekolom OF zoekopdrachten om te kijken hoe het eruit ziet.

Person.where(name: ["John", "Steve"])

Antwoord 4, autoriteit 44%

Gebruik ARel

t = Person.arel_table
results = Person.where(
  t[:name].eq("John").
  or(t[:lastname].eq("Smith"))
)

Antwoord 5, autoriteit 34%

Update voor Rails4

vereist geen edelstenen van derden

a = Person.where(name: "John") # or any scope 
b = Person.where(lastname: "Smith") # or any scope 
Person.where([a, b].map{|s| s.arel.constraints.reduce(:and) }.reduce(:or))\
  .tap {|sc| sc.bind_values = [a, b].map(&:bind_values) }

Oud antwoord

vereist geen edelstenen van derden

Person.where(
    Person.where(:name => "John").where(:lastname => "Smith")
      .where_values.reduce(:or)
)

Antwoord 6, autoriteit 21%

In het geval dat iemand op zoek is naar een bijgewerkt antwoord hierop, het lijkt erop dat er een bestaand pull-verzoek is om dit in Rails te krijgen: https://github.com/rails/rails/pull/9052.

Dankzij @j-mcnally’s Monkey patch voor ActiveRecord (https://gist.github.com/j-mcnally/ 250eaaceef234dd8971b) kunt u het volgende doen:

Person.where(name: 'John').or.where(last_name: 'Smith').all

Nog waardevoller is de mogelijkheid om scopes te koppelen met OR:

scope :first_or_last_name, ->(name) { where(name: name.split(' ').first).or.where(last_name: name.split(' ').last) }
scope :parent_last_name, ->(name) { includes(:parents).where(last_name: name) }

Dan kun je alle personen vinden met voor- of achternaam of wiens ouder met achternaam

Person.first_or_last_name('John Smith').or.parent_last_name('Smith')

Niet het beste voorbeeld om dit te gebruiken, maar probeer het gewoon bij de vraag te passen.


7, Autoriteit 2%

Zie ook deze gerelateerde vragen: hier, hieren hier

Voor rails 4, gebaseerd op dit artikelen dit originele antwoord

Person
  .unscoped # See the caution note below. Maybe you want default scope here, in which case just remove this line.
  .where( # Begin a where clause
    where(:name => "John").where(:lastname => "Smith")  # join the scopes to be OR'd
    .where_values  # get an array of arel where clause conditions based on the chain thus far
    .inject(:or)  # inject the OR operator into the arels 
    # ^^ Inject may not work in Rails3. But this should work instead:
    .joins(" OR ")
    # ^^ Remember to only use .inject or .joins, not both
  )  # Resurface the arels inside the overarching query

Let opde waarschuwing van het artikel aan het einde:

Rails 4.1+

Rails 4.1 behandelt default_scope net als een gewone scope. De standaard
scope (als je die hebt) is opgenomen in het where_values ​​resultaat en
inject(:or) zal een statement toevoegen tussen het standaardbereik en uw
waar is. Dat is slecht.

Om dat op te lossen, hoeft u alleen het bereik van de zoekopdracht uit te schakelen.


Antwoord 8, autoriteit 2%

Dit is een erg handige manier en het werkt prima in Rails 5:

Transaction
  .where(transaction_type: ["Create", "Correspond"])
  .or(
    Transaction.where(
      transaction_type: "Status",
      field: "Status",
      newvalue: ["resolved", "deleted"]
    )
  )
  .or(
    Transaction.where(transaction_type: "Set", field: "Queue")
  )

Antwoord 9

de squeel-edelsteen biedt een ongelooflijk gemakkelijke manier om dit te bereiken (voorheen gebruikte ik zoiets als de methode van @coloradoblue):

names = ["Kroger", "Walmart", "Target", "Aldi"]
matching_stores = Grocery.where{name.like_any(names)}

Antwoord 10

Dus het antwoord op de oorspronkelijke vraag, kun je scopes samenvoegen met ‘of’ in plaats van ‘en’ lijkt te zijn ‘nee, dat kan niet’. Maar u kunt een geheel andere scope of query met de hand coderen die het werk doet, of een ander framework gebruiken dan ActiveRecord, b.v. Meta Where of Squeel. Niet nuttig in mijn geval

Ik gebruik een bereik dat is gegenereerd door pg_search, dat iets meer doet dan selecteren, het bevat een bestelling door ASC, wat een puinhoop maakt van een schone unie. Ik wil het ‘of’ met een handgemaakte scope die dingen doet die ik niet kan doen in pg_search. Dus ik heb het zo moeten doen.

Product.find_by_sql("(#{Product.code_starts_with('Tom').to_sql}) union (#{Product.name_starts_with('Tom').to_sql})")

D.w.z. verander de scopes in sql, plaats haakjes om elke scope, voeg ze samen en find_by_sql met behulp van de gegenereerde sql. Het is een beetje onzin, maar het werkt wel.

Nee, vertel me niet dat ik “tegen: [:name,:code]” kan gebruiken in pg_search, ik zou het zo willen doen, maar het veld ‘name’ is een hstore, wat pg_search kan nog niet aan. Dus het bereik op naam moet met de hand worden gemaakt en vervolgens worden samengevoegd met het bereik pg_search.


Antwoord 11

Ik werk nu in Rails 6 en het lijkt erop dat dit nu mogelijk is. Met behulp van de vragen van de OP:

# in the Person model:
scope :john, -> { where(name: "John") }
scope :smith, -> { where(lastname: "Smith") }
scope :john_or_smith, -> { john.or(self.smith) }

Antwoord 12

names = ["tim", "tom", "bob", "alex"]
sql_string = names.map { |t| "name = '#{t}'" }.join(" OR ")
@people = People.where(sql_string)

Other episodes