Working on a project some time ago, and my boss wanted to have an advanced search, where the users could write the search, adding fields, values, like you can do with google, but tailored for our application.
In google you can do things like searching for all posts from sobrecodigo.com that have Ruby in the title searching for “allintitle: ruby site:sobrecodigo.com”
First try I implemented a complex field search algorithm, breaking the search string, accounting for apostrophes, having a field/content separator, it worked but it is a lot of work and very error prone, the second time I had to do something similar, but a little more powerful for another project, a coworker introduced me to “search_cop“, a really cool ruby gem that made my life a lot easier.
So I thought this kind of request could be more common and maybe not everyone know about this gem that helped me a lot 😀
To use it, let’s create a simple Rails project and some models like this:
rails new advancedsearch_sample
cd advancedsearch_sample
rails g model reporter full_name:string email:string
rails g model category name:string
rails g model status name:string
rails g model story reporter:belongs_to headline:string content:text status:belongs_to
rails g model story_category story:belongs_to category:belongs_to
Now we can add the ‘search_cop’ gem to our Gemfile, run bundler and db:migrate, then jump straight to editing the story model to add some search capabilities like this:
class Story < ApplicationRecord include SearchCop belongs_to :reporter belongs_to :status has_many :story_categories has_many :categories, through: :story_categories search_scope :search do attributes :headline, :content attributes reporter: 'reporters.full_name' attributes category: 'categories.name' scope do joins(:categories, :reporter, :status) .where(statuses: {name: 'Published'}) .order('id desc') end end end
I’ll explain everything, but with this, you can now search for stories, like this:
Story.search("headline:ruby content:'pro tip' reporter:rodrigo category:'full article'")
or better yet, like in my case, let your users build the query, using a really cool text field with syntax highlight and code completion 😛
But back to the subject, the requirement was to let users write advances queries, and this gem allowed me to do so in a really easy way.
For example, the query above will search for:
- Published stories
- Having ‘ruby’ in the headline
- Having ‘pro tip’ in the content
- Reported by someone with rodrigo in the name
- in a category that has ‘full article’ in the name
In the search string, you can also pass AND, OR, negate a condition with a simple ‘-‘ sign, for sates and numbers you can use < and > comparisons.
You can also make the ‘reporter’ field search both in the reporter full_name and email, just by passing an array with both fields in the attributes configuration, and specify joins and wheres in the scope section.
Of course you can have multiple search scopes for one model, and you can also chain the search scope with any active record method you need.
And of course it is not perfect, all the string comparisons are done with “like ‘%…%'”, so the performance will not be great.
If you are using MySQL or PostgreSQL full text capabilities you can configure search_cop to use it instead of LIKE for that fields.
If you have the specific field value, you can use ‘=’ instead of ‘:’, or quoting search_cop documentation, you can use any of these operators: AND/and, OR/or, :, =, !=, <, <=, >, >=, NOT/not/-, ()
And whenever you do not specify AND or OR “AND” is assumed.
This gem has helped me a lot by now, I just wanted to share this, because it can help others.
Did you already know this gem? are you using other gems that do a similar job?