Created
September 25, 2013 20:52
-
-
Save steveklabnik/6705873 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
def self.search(query) | |
query | |
.split(" ") | |
.collect do |query| | |
query.split(":") | |
end.inject(self) do |klass, (name, q)| | |
association = klass.reflect_on_association(name.pluralize.to_sym) | |
association_table = association.klass.arel_table | |
if [:has_and_belongs_to_many, :belongs_to].include?(association.macro) | |
joins(name.pluralize.to_sym).where(association_table["name"].eq(q)) | |
else | |
all | |
end | |
end | |
end |
query
.scan(/(\w+):(\w+)(?:\s+|\Z)/)
.map {|name, q| [name.pluralize.to_sym, q] }
.map {|name, q| [klass.reflect_on_association(name.pluralize.to_sym), name, q] }
.select {|association, name, q| [:has_and_belongs_to_many, :belongs_to].include?(association.macro) }
.inject(self) do |ar_query, (association, name, q)|
ar_query.joins(name).where(association.klass.arel_table["name"].eq(q)
end
Sample query is "tag:ruby state:closed"
And that's supposed to generate?:
klass.joins(:tags).where("tags.name" => "ruby").joins(:states).where("states.name" => "closed")
Shouldn't you really be injecting a scope?
def self.search(query)
query
.split(" ")
.collect do |query|
query.split(":")
end.inject(scoped) do |scope, (name, q)|
association = reflect_on_association(name.pluralize.to_sym)
association_table = association.klass.arel_table
if [:has_and_belongs_to_many, :belongs_to].include?(association.macro)
scope.joins(name.pluralize.to_sym).where(association_table["name"].eq(q))
else
scope
end
end
end
Making it a bit smaller:
- use split limit to ensure
a:b:c
=>a, b:c
- put it into a hash to avoid building where / joins multiple times
- use where(Hash) with assoc.field syntax instead of arel nodes.
assoc_name_to_q = query.split(" ")
.map{ |t| t.split ':', 2 }
.each_with_object({}) do |(name, q), hash|
hash[name.pluralize.to_sym] = q
end
assoc_name_to_q.delete_if do |name, q|
![:has_and_belongs_to_many, :belongs_to].include?(klass.reflect_on_association(name).macro)
end
joins(assoc_name_to_q.keys)
.where(Hash[assoc_name_to_q.map{|assoc_name, q| ["#{assoc_name}.name",q] }])
@soulcutter, in Rails 4, all just returns a scope. The naming is better that way, though.
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
How's it supposed to behave? Could you show me an example?