More like this with elasticsearch-rails and mongoid returns nothing

631 Views Asked by At

I use MongoDB to store information about shoes. Every shoe has a title (string), photo(string, that's the url to the photo), link(string, the url to the original site), sizes(aray of strings), colors(array of strings) and price(string) and gender(string).

In my project/app/models/shoe.rb file I have:

class Shoe
  include Mongoid::Document
  include Elasticsearch::Model
  include Elasticsearch::Model::Callbacks

  field :title
  field :photo
  field :link
  field :price
  field :gender
  field :sizes, type: Array, default: []
  field :colors, type: Array, default: []

  def as_indexed_json
    as_json({only: [:title, :gender, :colors, :price, :sizes]})
  end

  settings index: { number_of_shards: 1 } do
    mappings dynamic: 'false' do
      indexes :title, analyzer: 'bulgarian'
      indexes :colors, analyzer: 'bulgarian'
      indexes :gender, analyzer: 'bulgarian'
    end
  end

  def self.more_like_this(shoe)
    __elasticsearch__.search(
      {
        query: {
          bool: {
              must: {match_all: { }},
              should: [
                # {
                #   more_like_this: {
                #     fields: ['title'],
                #     like: [
                #       {
                #         _index: 'shoes',
                #         _type: 'shoe',
                #         _id: shoe.id
                #       }
                #     ],
                #     boost: 2.0
                #   }
                # },
                {
                  more_like_this: {
                    fields: ['gender'],
                    like: [
                      {
                        _index: 'shoes',
                        _type: 'shoe',
                        _id: shoe.id
                      }
                    ],
                  }
                }
              ],
              minimum_should_match: 1
          }
        }
      }
    )
  end
end

# Delete the previous shoes index in Elasticsearch
Shoe.__elasticsearch__.client.indices.delete index: Shoe.index_name rescue nil

# Create the new index with the new mapping
Shoe.__elasticsearch__.client.indices.create \
  index: Shoe.index_name,
  body: { settings: Shoe.settings.to_hash, mappings: Shoe.mappings.to_hash }

# Index all shoe records from the DB to Elasticsearch
Shoe.import

In project/config/initializers/elasticsearch.rb I have:

config = {
  host: "http://localhost:9200/",
  transport_options: {
    request: { timeout: 5 }
  },
}

if File.exists?("config/elasticsearch.yml")
  config.merge!(YAML.load_file("config/elasticsearch.yml").symbolize_keys)
end

Elasticsearch::Model.client = Elasticsearch::Client.new(config)

unless Shoe.__elasticsearch__.index_exists?
  Shoe.__elasticsearch__.create_index! force: true
  Shoe.import
end

In project/config/mongoid.yml I have:

    development:
      # Configure available database clients. (required)
      clients:
        # Defines the default client. (required)
        default:
          # Defines the name of the default database that Mongoid can connect to.
          # (required).
          database: cornersport
          # Provides the hosts the default client can connect to. Must be an array
          # of host:port pairs. (required)
          hosts:
            - localhost:27017
    test:
      clients:
        default:
          database: calceus_test
          hosts:
            - localhost:27017
          options:
            read:
              mode: :primary
            max_pool_size: 1

Now, in rails console, with elasticsearch running, I called Shoe.more_like_this(some_shoe_object).results.to_a and it returned []. First my query included the commented part but when I didn't get any results I commented it out and I still get no results although there are many more records in the database with the same gender as the one of some_shoe_object. Shouldn't it return something? Am I using elasticsearch wrong? I am a beginner. Please help!

2

There are 2 best solutions below

7
On

Looks like your index is being created via the the initializer file elasticsearch.rb and there you have not specified the settings.

Try to change the initializer file to

config = {
  host: "http://localhost:9200/",
  transport_options: {
    request: { timeout: 5 }
  },
}

if File.exists?("config/elasticsearch.yml")
  config.merge!(YAML.load_file("config/elasticsearch.yml").symbolize_keys)
end

Elasticsearch::Model.client = Elasticsearch::Client.new(config)

unless Shoe.__elasticsearch__.index_exists?
  Shoe.__elasticsearch__.client.indices.create index: Shoe.index_name,
   body: { settings: Shoe.settings.to_hash, mappings: Shoe.mappings.to_hash }
  Shoe.import
end

Also from your shoe.rb file, in the end, remove the index creation/delete part which is

# Delete the previous shoes index in Elasticsearch
Shoe.__elasticsearch__.client.indices.delete index: Shoe.index_name rescue nil

# Create the new index with the new mapping
Shoe.__elasticsearch__.client.indices.create \
  index: Shoe.index_name,
  body: { settings: Shoe.settings.to_hash, mappings: Shoe.mappings.to_hash }

# Index all shoe records from the DB to Elasticsearch
Shoe.import

Restart your server, ES and try again!


UPDATE

Try changing your more_like_it clause like this:

more_like_this: {
  fields: ['gender'],
    like: [
      {
        _index: 'cornersport',
        _type: 'shoes',
        _id: shoe.id
       },
       shoe.gender
     ]
}
0
On

I edited it like this:

def self.more_like_this(shoe)
  __elasticsearch__.search(
    {
      query: {
        bool: {
            must: {match_all: { }},
            should: [
              {
                more_like_this: {
                  fields: ['gender'],
                  docs: [
                    {
                      _index: 'shoes',
                      _type: 'shoe',
                      _id: shoe.id
                    }
                  ],
                  ids: [shoe.id]
                }
              }
            ],
            minimum_should_match: 1
        }
      }
    }
  )
end

And it works well now. Just replaced like with docs and added ids like it is explained here: https://www.elastic.co/guide/en/elasticsearch/reference/1.4/query-dsl-mlt-query.html Until now I was trying to make it work as explained here: https://www.elastic.co/guide/en/elasticsearch/reference/current/query-dsl-mlt-query.html

I am working with elasticsearch-2.2.0, so I don't get it how the functionality for 2.2.0 is not working for me but the 1.4 functionality. Maybe elasticsearch-rails works with 1.4.