Search code examples
ruby-on-railselasticsearchdenormalization

How to setup elasticsearch-rails denormalization mappings


I am trying to setup mappings for elasticsearch denormalization like the one in this link

I have the following models:

Brand

class Brand < ApplicationRecord
  include Elasticsearch::Model

  # title

  has_and_belongs_to_many :products

  index_name Rails.application.engine_name.split('_').first

  mapping do
    indexes :title, type: 'keyword'
  end
end

Category

class Category < ApplicationRecord
  include Elasticsearch::Model

  # title

  has_many :categorizations
  has_many :products, through: :categorizations

  index_name Rails.application.engine_name.split('_').first

  mapping do
    indexes :title, type: 'keyword'
  end
end

Product

class Product < ApplicationRecord
  include Elasticsearch::Model

  # title
  # description

  has_many :categorizations
  has_many :categories, through: :categorizations
  has_many :variations
  has_and_belongs_to_many :brands

  index_name Rails.application.engine_name.split('_').first

  mapping do
    indexes :id
    indexes :title
    indexes :description

    indexes :brands, type: 'keyword'
    indexes :categories, type: 'keyword'

    indexes :variations  do
      indexes :price, index: :not_analyzed
      indexes :color, index: :not_analyzed
      indexes :size, index: :not_analyzed
    end
  end


  after_commit lambda { __elasticsearch__.index_document },  on: :create
  after_commit lambda { __elasticsearch__.update_document },  on: :update
  after_commit lambda { __elasticsearch__.delete_document },  on: :destroy
end

Variation

class Variation < ApplicationRecord
  include Elasticsearch::Model

  # price
  # color
  # size

  belongs_to :product

  index_name Rails.application.engine_name.split('_').first

  mapping do
    indexes :price, index: :not_analyzed
    indexes :color, index: :not_analyzed
    indexes :size, index: :not_analyzed
  end
end

Searchable Module

module Searchable
  INDEX_NAME = Rails.application.engine_name.split('_').first

  def create_index!(options={})
    client = Product.__elasticsearch__.client
    client.indices.delete index: INDEX_NAME rescue nil if options[:force]

    settings = Product.settings.to_hash.merge Variation.settings.to_hash.merge Brand.settings.to_hash.merge Category.settings.to_hash
    mappings = Product.settings.to_hash.merge Variation.mappings.to_hash.merge Brand.mappings.to_hash.merge Category.mappings.to_hash

    client.indices.create index: INDEX_NAME,
        body: {
            settings: settings.to_hash,
            mappings: mappings.to_hash
        }
  end

  def setup
    Searchable.create_index! force: true
    Product.__elasticsearch__.refresh_index!
  end

  extend self
end

When I run it it 200 out of the 30,000 products and without the categories. Where am I going wrong?


Solution

  • I got it

    User

    class User < ApplicationRecord
      include Elasticsearch::Model
      has_and_belongs_to_many :blogposts, touch: true
    
      index_name 'blog'
    
      mapping do
        indexes :name
        indexes :email
      end
    
      after_commit lambda { __elasticsearch__.index_document  },  on: :create
      after_touch  lambda { __elasticsearch__.index_document  },  on: :touch
      after_commit lambda { __elasticsearch__.update_document },  on: :update
      after_commit lambda { __elasticsearch__.delete_document },  on: :destroy
    end
    

    Blogpost

    class Blogpost < ApplicationRecord
      include Elasticsearch::Model
      has_and_belongs_to_many :user, touch: true
    
      index_name 'blog'
    
      mapping do
        indexes :title
        indexes :body
    
        indexes :user do
          indexes :id, type: 'long'
          indexes :name, type: 'string' do
            indexes :raw, type: 'keyword', index: 'not_analyzed'
          end
        end
      end
    
      def as_indexed_json(options={})
        hash = self.as_json()
        hash['user.id'] = self.user.first.id
        hash['user.name'] = self.user.first.name
        hash
      end
    
    
      after_commit lambda { __elasticsearch__.index_document  },  on: :create
      after_touch  lambda { __elasticsearch__.index_document  },  on: :touch
      after_commit lambda { __elasticsearch__.update_document },  on: :update
      after_commit lambda { __elasticsearch__.delete_document },  on: :destroy
    end
    

    Searchable Module module Searchable INDEX_NAME = 'blog'

      def create_index!(options={})
        client = User.__elasticsearch__.client
        client.indices.delete index: INDEX_NAME rescue nil if options[:force]
    
        settings = User.settings.to_hash.merge Blogpost.settings.to_hash
        mappings = User.mappings.to_hash.merge Blogpost.mappings.to_hash
    
        client.indices.create index: INDEX_NAME,
                              body: {
                                  settings: settings.to_hash,
                                  mappings: mappings.to_hash }
      end
    
      def setup
        Searchable.create_index! force: true
    
        10.times do
          n = Faker::Name.name
          u = User.create name: n,
                          email: Faker::Internet.free_email(n.split(' ').last)
    
          rand(1..10).times do
            s = Faker::Lorem.sentence
            u.blogposts.create title: s.split(' ').first, body: s
          end
        end
    
        User.__elasticsearch__.refresh_index!
      end
    
      extend self
    end