module Elasticsearch::Persistence::Model

When included, extends a plain Ruby class with persistence-related features via the ActiveRecord pattern

@example Include the repository in a custom class

require 'elasticsearch/persistence/model'

class MyObject
  include Elasticsearch::Persistence::Repository
end

Public Class Methods

attribute(name, type = nil, options = {}, &block) click to toggle source

Re-define the Virtus’ ‘attribute` method, to configure Elasticsearch mapping as well

Calls superclass method
# File lib/elasticsearch/persistence/model.rb, line 64
def attribute(name, type = nil, options = {}, &block)
  mapping = options.delete(:mapping) || {}

  if type == :keyword || type.nil?
    type = String
    mapping = { type: "keyword" }.merge(mapping)
  end

  super

  gateway.mapping do
    indexes name, { type: Utils::lookup_type(type) }.merge(mapping)
  end

  gateway.mapping(&block) if block_given?
end
default_sort_key(field = nil) click to toggle source

Set the default sort key to be used in sort operations

# File lib/elasticsearch/persistence/model.rb, line 91
def default_sort_key(field = nil)
  @default_sort_key = field unless field.nil?
  @default_sort_key
end
gateway(&block) click to toggle source

Return the {Repository::Class} instance

# File lib/elasticsearch/persistence/model.rb, line 83
def gateway(&block)
  @gateway ||= Elasticsearch::Persistence::Repository::Class.new host: self
  block.arity < 1 ? @gateway.instance_eval(&block) : block.call(@gateway) if block_given?
  @gateway
end
included(base) click to toggle source
# File lib/elasticsearch/persistence/model.rb, line 29
def self.included(base)
  base.class_eval do
    include ActiveModel::Naming
    include ActiveModel::Conversion
    include ActiveModel::Serialization
    include ActiveModel::Serializers::JSON
    include ActiveModel::Validations
    include ActiveModel::Validations::Callbacks

    include Virtus.model
    extend ActiveModel::Callbacks

    define_model_callbacks :create, :save, :update, :destroy
    define_model_callbacks :find, :touch, only: :after

    include Elasticsearch::Persistence::Model::Callbacks

    include Elasticsearch::Persistence::Model::Base::InstanceMethods

    extend Elasticsearch::Persistence::Model::Store::ClassMethods
    include Elasticsearch::Persistence::Model::Store::InstanceMethods

    extend Elasticsearch::Persistence::Model::GatewayDelegation

    extend Elasticsearch::Persistence::Model::Find::ClassMethods
    extend Elasticsearch::Persistence::Querying
    extend Elasticsearch::Persistence::Inheritence
    extend Elasticsearch::Persistence::Delegation::DelegateCache

    include Elasticsearch::Persistence::Scoping

    class << self

      # Re-define the Virtus' `attribute` method, to configure Elasticsearch mapping as well
      #
      def attribute(name, type = nil, options = {}, &block)
        mapping = options.delete(:mapping) || {}

        if type == :keyword || type.nil?
          type = String
          mapping = { type: "keyword" }.merge(mapping)
        end

        super

        gateway.mapping do
          indexes name, { type: Utils::lookup_type(type) }.merge(mapping)
        end

        gateway.mapping(&block) if block_given?
      end

      # Return the {Repository::Class} instance
      #
      def gateway(&block)
        @gateway ||= Elasticsearch::Persistence::Repository::Class.new host: self
        block.arity < 1 ? @gateway.instance_eval(&block) : block.call(@gateway) if block_given?
        @gateway
      end

      # Set the default sort key to be used in sort operations
      #
      def default_sort_key(field = nil)
        @default_sort_key = field unless field.nil?
        @default_sort_key
      end

      private

      # Return a Relation instance to chain queries
      #
      def relation
        Relation.create(self, {})
      end
    end

    # Configure the repository based on the model (set up index_name, etc)
    #
    gateway do
      klass base
      index_name base.model_name.collection.gsub(/\//, "-")
      document_type base.model_name.element

      def serialize(document)
        document.to_hash.except(:id, "id")
      end

      def deserialize(document)
        object = klass.new document["_source"] || document["fields"]

        # Set the meta attributes when fetching the document from Elasticsearch
        #
        object.instance_variable_set :@_id, document["_id"]
        object.instance_variable_set :@_index, document["_index"]
        object.instance_variable_set :@_type, document["_type"]
        object.instance_variable_set :@_version, document["_version"]

        # Store the "hit" information (highlighting, score, ...)
        #
        object.instance_variable_set :@hit,
                                     HashWrapper.new(document.except("_index", "_type", "_id", "_version", "_source"))

        object.instance_variable_set(:@persisted, true)
        object
      end
    end

    # Set up common attributes
    #
    attribute :created_at, DateTime, default: lambda { |o, a| Time.now.utc }
    attribute :updated_at, DateTime, default: lambda { |o, a| Time.now.utc }

    default_sort_key :created_at

    attr_reader :hit
  end
end

Private Class Methods

relation() click to toggle source

Return a Relation instance to chain queries

# File lib/elasticsearch/persistence/model.rb, line 100
def relation
  Relation.create(self, {})
end

Public Instance Methods

deserialize(document) click to toggle source
# File lib/elasticsearch/persistence/model.rb, line 116
def deserialize(document)
  object = klass.new document["_source"] || document["fields"]

  # Set the meta attributes when fetching the document from Elasticsearch
  #
  object.instance_variable_set :@_id, document["_id"]
  object.instance_variable_set :@_index, document["_index"]
  object.instance_variable_set :@_type, document["_type"]
  object.instance_variable_set :@_version, document["_version"]

  # Store the "hit" information (highlighting, score, ...)
  #
  object.instance_variable_set :@hit,
                               HashWrapper.new(document.except("_index", "_type", "_id", "_version", "_source"))

  object.instance_variable_set(:@persisted, true)
  object
end
serialize(document) click to toggle source
# File lib/elasticsearch/persistence/model.rb, line 112
def serialize(document)
  document.to_hash.except(:id, "id")
end