1

Using Elastic Search and Rails for a Compound Query with Text Strings and User I...

 3 years ago
source link: https://fuzzyblog.io/blog/rails/2018/04/19/using-elastic-search-and-rails-for-a-compound-query-with-text-strings-and-user-id.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

Using Elastic Search and Rails for a Compound Query with Text Strings and User ID

Apr 19, 2018

I'm writing this blog post, my first in quite a while, because I recently had to implement ElasticSearch for Rails for a new application I'm building and I find life in the Elastic world a bit different than I had expected. If you're an Elastic Search veteran then you should definitely move along because:

  • This is fairly basic
  • I'm writing this mostly to cement this in my own brain

Curiously I found very few examples about how to do this type of compound query in Rails and that's also part of my motivation for writing it. The closest example I found was in a four year old Stack Overflow post.

The Problem: Everyone Should See Only Their Own Data

I have a series of ActiveRecord models that I want to be able to search using Elastic Search. This can easily be done with this code fragment:

Job.search_user(params[:q])

Given that I was initially the only user on this code base, I didn't even notice the issue until I thought about deployment. At which point there was the obligatory light bulb / I'm an idiot moment. The problem here is that this code searches everyone's jobs, not just the jobs that you created. Now since every bit of data encompasses a user_id attribute, this should boil down to two problems:

  1. Getting user_id into the index
  2. Constructing a JSON query for ElasticSearch to execute

Sidebar: But What About SearchKick

I'm sure a number of people are shouting out "Use SearchKick (dummy)" but whenever I tried to use SearchKick, I got odd errors and I eventually just pulled it from Gemfile. Given that I'm a long time believer in Ankane's ChartKick, I'm sure it is me but I still couldn't make use of it.

Step 1: Getting user_id into the Index

I've opted to setup each of my searchable models as follows:

include Elasticsearch::Model
include Elasticsearch::Model::Callbacks

settings do
  mappings dynamic: false do
    indexes :company^5, type: :text, analyzer: :english
    indexes :title^5, type: :text, analyzer: :english
    indexes :why_rejected, type: :text, analyzer: :english
    indexes :location, type: :text, analyzer: :english
    indexes :name, type: :text, analyzer: :english
    indexes :domain, type: :text, analyzer: :english
    indexes :created_at, type: :date
    indexes :user_id, type: :text
  end
end

The :title^5 bumps the ranking of search results in the title or company attributes by a factor of 5.

Step 2: Constructing the JSON query

I ended up with a class method on each of my searchable objects like this:

def self.search_user(query, user)
  self.search({
    query: {
      bool: {
        must: [
        {
          multi_match: {
            query: query,
            fields: [:company, :title, :description, :name]
          }
        },
        {
          match: {
            user_id: user.id.to_s
          }
        }]
      }
    }
  })
end

I can call this from my search controller like this:

@jobs = Job.search_user(params[:q], current_user)

and get results bound only to the logged in user – exactly what I was looking for.

Step 3: Re-indexing Everything

After making changes to your settings / mappings, you need to re-index everything. I handle this with a simple rake task like this:

namespace :search do
  # bundle exec rake search:index_all --trace
  task :index_all => :environment do
    klasses = [Job, Note, CoverLetter, Task]
    klasses.each do |klass|
      klass.send(:import, :force => true)
    end
  end
end

This re-indexes every model in full. You don't always need this but it is very convenient to have for development when you're playing with schema changes and the like.

Recommended Reading

I made heavy use of Iridakos's excellent tutorial and I recommend you do too.


Posted In: #rails #ruby #elastic_search


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK