More like this with elasticsearch-rails and mongoid returns nothing

637 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
aliibrahim 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
mariya 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.