Elasticsearch-rails: Search method conflicting with Ransack gem

Created on 1 May 2014  路  17Comments  路  Source: elastic/elasticsearch-rails

I've been using the Ransack Gem for a while, and recently started playing around with ElasticSearch. It seems that the search method added to AR objects by ElasticSearch overrides the one added by Ransack, which obviously breaks search forms built using Ransack.

Example:

# Gemfile
gem "ransack"
gem "elasticsearch-model" 
gem "elasticsearch-rails"
# /models/article.rb
class Article < ActiveRecord::Base
     include Elasticsearch::Model
end

Using the above will return an Elastic Search object

Article.search
=> #<Elasticsearch::Model::Response::Response:0x007fc449f37680....

When removing "include Elasticsearch::Model" as follows:

# Gemfile
gem "ransack"
gem "elasticsearch-model" 
gem "elasticsearch-rails"
# /models/article.rb
class Article < ActiveRecord::Base
end

Using the above will return a Ransack object

Article.search
=> Ransack::Search<class: OpportunityLocation, base: Grouping <combinator: and>>

What would be the best way to resolve this?

Most helpful comment

def self.search_for(*args,&block)
      __elasticsearch__.search(*args, &block)
end

worked for me ;)

All 17 comments

Hi @constantm, Ransack _tries_ to create a #search method, but if one exists already (as it will when you include Elasticsearch::Model), you can simply change #search to #ransack.

Read more here: https://github.com/activerecord-hackery/ransack#ransack-search-method

Thanks @AaronRustad. Not sure how I missed that!

@constantm @AaronRustad It's actually more fine grained with elasticsearch-model -- it will delegate the search method only when it isn't already defined, so it's a question of order: the first gem you require probably wins.

@karmi
It seems that this code do not work.

def self.included(base)
  base.class_eval do
    ...
    pp base.respond_to?(:search)  # => true
    class << self
      pp respond_to?(:search)  # => false
      delegate :search,  to: :__elasticsearch__ unless respond_to?(:search)
      delegate :mapping, to: :__elasticsearch__ unless respond_to?(:mapping)
      ...
    end
    ...
  end
  ...
end

@lonre what "does not work" does mean? that the search method is not properly detected in the class <<聽self block? In what kind of circumstances does it "not work", etc?

@karmi
Yes, the search method is not properly detected in the class << self block.

"Not work" means: delegate :search, to: :__elasticsearch__ unless respond_to?(:search) override predefined search method.

@lonre Right you were! Pushed a fix with using self.public_instance_methods.include?(method) instead of respond_to?.

Hello. I'm using active_admin. And active_admin use ransack by default. I want to use elastic search not for admin interface, but i can't, because:
NoMethodError: undefined method delete_if' for "word":String from /home/rodionov/.rvm/gems/ruby-2.1.1/gems/ransack-1.2.3/lib/ransack/adapters/active_record/base.rb:15:inransack'

How i can use elastic search methods by default instead ransack?

@krim So, the gem shouldn't override an _existing_ method, are you sure you're including Ransack _before_ the gem?

And if you want to use the search results in the admin interface, you have to use the @results.records method -- that should be transparent. Please provide more info about what you're doing otherwise...

I'm having this issue too. I don't directly include Ransack, but it's a dependency of ActiveAdmin, elasticsearch-model is listed in the Gemfile above ActiveAdmin

@aubergene I repeat: the method must be _already defined_ so the elasticsearch-model doesn't re-define it. Include ActiveAdmin _first_!

Sorry for the confusion. I mean the other way round, I wanted to keep search pointing to elasticsearch-model, but ransack was grabbing it instead, regardless of which order I put the gems in, and it was only present as a dependency of ActiveAdmin. I made my own class method and pointed that to __elasticsearch__ and that works fine.

I have the same issue that @aubergene have. Is there are any other solution, than defining elasticsearch-model search method like:

    def self.search_with_elasticsearch(query_or_payload, options={})
      search = Elasticsearch::Model::Searching::SearchRequest.new(self, query_or_payload, options)
      Elasticsearch::Model::Response::Response.new(self, search)
    end

@dmitry Raise the issue with Ransack, that it simply overwrites the search method on your model. The elasticsearch-model doesn't overwrite any existing method.

You shouldn't use the SearchRequest class -- you should simply wrap the MyModel.__elasticsearch__.search proxy method. It is designed specifically with problems like these in mind. Have you checked the corresponding README section? https://github.com/elasticsearch/elasticsearch-rails/tree/master/elasticsearch-model#the-__elasticsearch__-proxy

Nice, so the solution is to define method something like that:

   def self.search_with_elasticsearch(*args)
      __elasticsearch__.search(*args)
    end

@dmitry Yes, indeed it is.

def self.search_for(*args,&block)
      __elasticsearch__.search(*args, &block)
end

worked for me ;)

Was this page helpful?
0 / 5 - 0 ratings