Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Rails elasticsearch - named scope search

I'm just moving over from the Tire gem to the official elasticsearch Ruby wrapper and am working on implementing better search functionality.

I have a model InventoryItem and a model Store. Store has_many :inventory_items. I have a model scope on Store called local

scope :local, lambda{|user| near([user.latitude, user.longitude], user.distance_preference, :order => :distance)}

I want the search to only return results from this scope so I tried: InventoryItem.local(user).search.... but it searches the entire index, not the scope. After doing some research, it looks like filter's are a good way to achieve this, but I'm unsure how to implement. I'm open to other ways of achieving this as well. My ultimate goal is be able to search a subset of the InventoryItem model based on store location.

like image 727
settheline Avatar asked Sep 23 '14 19:09

settheline


Video Answer


1 Answers

Another thing you can do is to send the list of valid ids right to elastic, so it will filter records out by itself and then perform a search on ones that left. We were not doing tests whether it is faster yet, but I think it should, because elastic is a search engine after all.

I'll try to compose an example using you classes + variables and our experience with that:

def search
  # retrieve ids you want to perform search within
  @store_ids = Store.local(current_user).select(:id).pluck(:id)
  # you could also check whether there are any ids available
  # if there is none - no need to request elastic to search
  @response = InventoryItem.search_by_store_ids('whatever', @store_ids)
end

And a model:

class InventoryItem
  # ...

  # search elastic only for passed store ids
  def self.search_by_store_ids(query, store_ids, options = {})       
    # use method below
    # also you can use it separately when you don't need any id filtering
    self.search_all(query, options.deep_merge({
      query: {
        filtered: {
          filter: {
            terms: {
              store_id: store_ids
            }
          }
        }
      }
    }))
  end

  # search elastic for all inventory items
  def self.search_all(query, options = {})
    self.__elasticsearch__.search(
      {
        query: {
          filtered: {
            query: {
              # use your fields you want to search, our's was 'text'
              match: { text: query },
            },
            filter: {},
            strategy: 'leap_frog_filter_first' # do the filter first
          }
        }
      }.deep_merge(options)
      # merge options from self.search_by_store_ids if calling from there
      # so the ids filter will be applied
    )
  end
  # ...
end

That way you also have to index store_id.

You can read more about filters here.

like image 182
Michael Radionov Avatar answered Oct 16 '22 16:10

Michael Radionov