Skip to content

Instantly share code, notes, and snippets.

@steveklabnik
Created October 21, 2013 16:53
Show Gist options
  • Save steveklabnik/7087076 to your computer and use it in GitHub Desktop.
Save steveklabnik/7087076 to your computer and use it in GitHub Desktop.
Halp!
class Foo
has_many :tags
has_many :states
# q is hash: { "tags" => "ruby", "states" => "open" }
def self.search(q)
# wat do?
end
end
# search should return a relation that scopes everything to the terms
# the values 'ruby' and 'open' are in the 'name' attribute of the Tag and State model.
@coreyward
Copy link

I don't think Foo should be responsible for knowing that it's searching for the name attribute for Tag or State. I do like the idea of Class.search being a consistent interface across searchable and searched models, though. Something like:

def self.search(value, associated_values = {})
  # search self by the value
  # call `AssociatedClass.search` for each of the key-value pairs in associated_values
  # return the association chain
end

Crucially, each object doesn't need to define what other objects can be queried or which of their parameters get queried. Either that information is passed in via params (e.g. { tags: { name: 'ruby' }, states: { name: 'open' } } }) or those objects figure it out themselves.

@saturnflyer
Copy link

shot in the dark. didn't run it

def search(q)
  tags.where(name: q['tags'].split).merge(states.where(name: q['states'].split))
end

@liveh2o
Copy link

liveh2o commented Oct 21, 2013

We typically avoid joins and just do an additional query (joins are expensive in our data set). Building on the scope example:

scope :by_tag, ->(*tags) {
  ids = Tag.where(:name => tags.flatten).uniq.pluck(:foo_id)
  where(:id => ids)
}
scope :by_state, ->(*states) {
  ids = State.where(:name => states.flatten).uniq.pluck(:foo_id)
  where(:id => ids)
}

def self.search(query)
  records = all
  records = records.by_tags(query["tags"].split(' ')) if query["tags"].present?
  records = records.by_states(query["states"].split(' ')) if query["states"].present?
  records
end

If joins aren't a concern, then this could be easily tweaked, but I prefer the pluck pattern.

@steveklabnik
Copy link
Author

@coreyword yes, the real search method takes a string, and i do some processing to get it into the hash form. figured i'd just ignore those details here though.

@steveklabnik
Copy link
Author

Thanks so much everyone!

@litch
Copy link

litch commented Oct 21, 2013

I'd love to know what you settle on. In a system of mine I do something like @subdigital's solution...

@pier-oliviert
Copy link

I prefer what you did @ https://gist.github.com/steveklabnik/6705873

I've tried like 20 different things with search module and stuff in the past year and never was satisfied by it. I think searching would be much easier to build in an abstract way if it would be possible to list the scopes for an ActiveRecord::Base. Is there a reason why it's not available?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment