ruby-on-rails - microsoft - visual studio installer
Combina dos ámbitos con nombre con OR(en lugar de Y) (5)
Quiero encontrar todas las Annotations
cuyos cuerpos sean:
- Igual a "?"
- o
- Me gusta "[?]"
¿Cuál es la mejor manera de hacer esto?
Me gustaría usar SearchLogic si es posible, pero aunque SearchLogic te permite hacer cada uno de los siguientes:
-
Annotation.body_equals(''?'')
-
Annotation.body_like(''[?]'')
y siempre puedes encadenarlas juntas: Annotation.body_equals(''?'').body_like(''[?]'')
No estoy seguro de cómo combinarlos con OR
Tenga en cuenta que puede combinar ámbitos con nombre OR
si su argumento es el mismo. Por ejemplo, podría hacer:
Annotation.body_equals_or_body_like(''?'')
Pero esto no ayudaría.
Tenga en cuenta que no estoy vinculado a SearchLogic, pero sería genial para una solución que no requiera romper su abstracción.
¿Los resultados ''me gusta'' tampoco incluirían los resultados ''iguales''?
También puede usar un alcance nombrado al final de otro para crear un ámbito con nombre realmente largo. De los documentos de Searchlogic (de esta manera parece un poco largo para mí):
User.username_or_first_name_like("ben")
=> "username LIKE ''%ben%'' OR first_name like''%ben%''"
User.id_or_age_lt_or_username_or_first_name_begins_with(10)
=> "id < 10 OR age < 10 OR username LIKE ''ben%'' OR first_name like''ben%''"
O puede usar una unión para combinar las matrices de resultados de búsqueda mientras elimina los duplicados:
@equal_results = Annotation.body_equals(''?'')
@like_results = Annotation.body_like(''[?]'')
@results = @equal_results | @like_results
Me acerqué a esta pregunta en busca de la respuesta a "o" two named_scopes y todas las respuestas me parecieron demasiado complejas. Investigué un poco y encontré una solución usando un scope_name llamado "or" que hace el truco.
Siguiendo el ejemplo dado:
Annotation.body_equals(''?'')
Annotation.body_like(''[?]'')
ambos devuelven un objeto named_scope que constructo selecciona devolver registros de anotación
ahora definimos otro alcance nombrado esperando dos ámbitos con nombre como parámetros como:
named_scope :or, lambda { |l, r| {
:conditions =>
"annotations.id IN (#{l.send(:construct_finder_sql,{:select => :id})}) or " +
"annotations.id IN (#{r.send(:construct_finder_sql,{:select => :id})})"
}}
Puede usar:
Annotation.or(Annotation.body_equals(''?''), Annotation.body_like(''[?]''))
Esto creará una consulta como:
select * from annotations
where (annotations.id IN (select id from annotations where body=''?'') or
(annotations.id IN (select id from annotations where body like ''%?%'')
Que es lo que estabas buscando
Como o también es un named_scope, es posible encadenar con otros named_scopes incluyendo otro o:
Annotation.or(Annotation.or(Annotation.body_equals(''?''),
Annotation.body_like(''[?]'')),
Annotation.some_other)
No pude encontrar ninguna solución simple, pero este problema me intrigó, así que lancé mi propia solución:
class ActiveRecord::Base
def self.or_scopes(*scopes)
# Cleanup input
scopes.map! do |scope|
scope = scope.respond_to?(:to_a) ? scope.to_a : [*scope]
scope.unshift(scope.shift.to_sym)
end
# Check for existence of scopes
scopes.each{|scope| raise ArgumentError, "invalid scope: #{scope.first}" unless self.scopes.has_key?(scope.first) }
conditions = scopes.map do |scope|
scope = self.scopes[scope.first].call(self, *scope[1..-1])
self.merge_conditions(scope.proxy_options[:conditions])
end
or_conditions = conditions.compact.join(" OR ")
merged_scopes = scopes.inject(self){|merged, scope| merged.scopes[scope.first].call(self, *scope[1..-1]) }
# We ignore other scope types but so does named_scopes
find_options = merged_scopes.scope(:find).merge(:conditions => or_conditions)
self.scoped(find_options)
end
end
Considere la siguiente configuración:
class Person < ActiveRecord::Base
named_scope :men, :conditions => { :sex => ''M'' }
named_scope :women, :conditions => { :sex => ''F'' }
named_scope :children, :conditions => "age < 18"
named_scope :named, lambda{|name|
{ :conditions => { :name => name } }
}
end
Usted lo llama con los nombres de una serie de ámbitos como tal:
Person.or_scopes(:women, :children)
Esto devuelve un alcance como este:
Person.or_scopes(:women, :children).proxy_options
# => {:conditions=>"(`people`.`sex` = ''F'') OR (age < 18)"}
También puede llamarlo con una matriz de matrices cuando el alcance requiera parámetros:
Person.or_scopes(:women, [:named, ''Sue'']).proxy_options
# => {:conditions=>"(`people`.`sex` = ''F'') OR (`people`.`name` = ''Sue'')"}
En tu caso, Horace, podrías usar lo siguiente:
Annotation.or_scopes([:body_equals, ''?''], [:body_like, ''[?'']).all
Para Rails 2.x, puede usar el siguiente alcance nombrado para simular OR:
__or_fn = lambda do |*scopes|
where = []
joins = []
includes = []
# for some reason, flatten is actually executing the scope
scopes = scopes[0] if scopes.size == 1
scopes.each do |s|
s = s.proxy_options
begin
where << merge_conditions(s[:conditions])
rescue NoMethodError
where << scopes[0].first.class.merge_conditions(s[:conditions])
end
joins << s[:joins] unless s[:joins].nil?
includes << s[:include] unless s[:include].nil?
end
scoped = self
scoped = scoped.includes(includes.uniq.flatten) unless includes.blank?
scoped = scoped.joins(joins.uniq.flatten) unless joins.blank?
scoped.where(where.join(" OR "))
end
named_scope :or, __or_fn
Usemos esta función usando su ejemplo anterior.
q1 = Annotation.body_equals(''?'')
q2 = Annotation.body_like(''[?]'')
Annotation.or(q1,q2)
El código anterior ejecuta solo una consulta. q1
y q2
no contienen los resultados de la consulta, sino que su clase es ActiveRecord::NamedScope::Scope
.
The or
named_scope combina estas consultas y une las condiciones con un OR.
También puede anidar OR, como en este ejemplo artificial:
rabbits = Animal.rabbits
#<Animal id: 1 ...>
puppies = Animal.puppies
#<Animal id: 2 ...>
snakes = Animal.snakes
#<Animal id: 3 ...>
lizards = Animal.lizards
#<Animal id: 4 ...>
Animal.or(rabbits, puppies)
[#<Animal id: 1 ...>, #<Animal id: 2 ...>]
Animal.or(rabbits, puppies, snakes)
[#<Animal id: 1 ...>, #<Animal id: 2 ...>, #<Animal id: 3 ...>]
Porque or
devuelve un ActiveRecord::NamedScope::Scope
sí mismo, podemos ActiveRecord::NamedScope::Scope
realmente locos:
# now let''s get crazy
or1 = Animal.or(rabbits, puppies)
or2 = Animal.or(snakes, lizards)
Animal.or(or1, or2)
[#<Animal id: 1 ...>, #<Animal id: 2 ...>, #<Animal id: 3 ...>, #<Animal id: 4...>]
Creo que la mayoría de estos ejemplos funcionarían bien usando scope
s en Rails 3, aunque no lo he intentado.
Un poco de autopromoción descarada: esta funcionalidad está disponible en la gema fake_arel .
Probablemente es
Annotation.body_equals_or_body_like([''?'', ''[?]''])