module SearchFlip::Index::ClassMethods

Public Instance Methods

analyze(request, params = {}) click to toggle source

Sends an analyze request to Elasticsearch. Raises SearchFlip::ResponseError in case any errors occur.

@example

CommentIndex.analyze(analyzer: "standard", text: "this is a test")

@return [Hash] The raw response

# File lib/search_flip/index.rb, line 489
def analyze(request, params = {})
  response = connection.http_client.headers(accept: "application/json").post("#{index_url}/_analyze", json: request, params: params)

  SearchFlip::JSON.parse(response.to_s)
end
bulk(options = {}) { |indexer| ... } click to toggle source

Initiates and yields the bulk object, such that index, import, create, update and delete requests can be appended to the bulk request. Sends a refresh request afterwards if auto_refresh is enabled.

@see SearchFlip::Config See SearchFlip::Config for auto_refresh

@example

CommentIndex.bulk ignore_errors: [409] do |bulk|
  bulk.create comment.id, CommentIndex.serialize(comment),
    version: comment.version, version_type: "external_gte"

  bulk.delete comment.id, routing: comment.user_id

  # ...
end

@param options [Hash] Specifies options regarding the bulk indexing @option options ignore_errors [Array] Specifies an array of http status

codes that shouldn't raise any exceptions, like eg 409 for conflicts,
ie when optimistic concurrency control is used.

@option options raise [Boolean] Prevents any exceptions from being

raised. Please note that this only applies to the bulk response, not to
the request in general, such that connection errors, etc will still
raise.
# File lib/search_flip/index.rb, line 627
def bulk(options = {})
  default_options = {
    http_client: connection.http_client,
    bulk_limit: connection.bulk_limit,
    bulk_max_mb: connection.bulk_max_mb
  }

  SearchFlip::Bulk.new("#{type_url}/_bulk", default_options.merge(options)) do |indexer|
    yield indexer
  end

  refresh if SearchFlip::Config[:auto_refresh]
end
close_index() click to toggle source

Closes the index within Elasticsearch. Raises SearchFlip::ResponseError in case any errors occur.

@return [Boolean] Returns true or raises SearchFlip::ResponseError

# File lib/search_flip/index.rb, line 343
def close_index
  connection.close_index(index_name_with_prefix)
end
connection() click to toggle source

Returns the SearchFlip::Connection for the index. Override to specify a custom connection.

@return [SearchFlip::Connection] The connection

# File lib/search_flip/index.rb, line 664
def connection
  ConnectionMutex.synchronize do
    @connection ||= SearchFlip::Connection.new(base_url: SearchFlip::Config[:base_url])
  end
end
create(scope, options = {}, additional_index_options = {}) click to toggle source

Indexes the given record set, array of records or individual record using Elasticsearch's create operation via the Bulk API, such that the request will fail if a record with a particular primary key already exists in Elasticsearch.

@see index See index for more details regarding available

params and return values
# File lib/search_flip/index.rb, line 558
def create(scope, options = {}, additional_index_options = {})
  bulk options do |indexer|
    each_record(scope, index_scope: true) do |object|
      indexer.create record_id(object), serialize(object), index_options(object).merge(additional_index_options)
    end
  end

  scope
end
create_index() click to toggle source

Creates the index within Elasticsearch and applies index settings, if specified. Raises SearchFlip::ResponseError in case any errors occur.

@return [Boolean] Returns true or false

# File lib/search_flip/index.rb, line 334
def create_index
  connection.create_index(index_name_with_prefix, index_settings)
end
criteria() click to toggle source

@api private

Creates an SearchFlip::Criteria for the current index, which is used as a base for chaining criteria methods.

@return [SearchFlip::Criteria] The base for chaining criteria methods

# File lib/search_flip/index.rb, line 246
def criteria
  SearchFlip::Criteria.new(target: self)
end
delete(scope, options = {}, additional_index_options = {}) click to toggle source

Deletes the given record set, array of records or individual record from Elasticsearch using the Bulk API.

@see index See index for more details regarding available

params and return values
# File lib/search_flip/index.rb, line 592
def delete(scope, options = {}, additional_index_options = {})
  bulk options do |indexer|
    each_record(scope) do |object|
      indexer.delete record_id(object), index_options(object).merge(additional_index_options)
    end
  end

  scope
end
delete_index() click to toggle source

Deletes the index from Elasticsearch. Raises SearchFlip::ResponseError in case any errors occur.

# File lib/search_flip/index.rb, line 387
def delete_index
  connection.delete_index(index_name_with_prefix)
end
each_record(scope, index_scope: false) { |record| ... } click to toggle source

@api private

Used to iterate a record set. Here, a record set may be a) an ActiveRecord::Relation or anything responding to find_each, b) an Array of records or anything responding to each or c) a single record.

@param scope The record set that gets iterated @param index_scope [Boolean] Set to true if you want the the index

scope to be applied to the scope
# File lib/search_flip/index.rb, line 155
def each_record(scope, index_scope: false)
  return enum_for(:each_record, scope, index_scope: index_scope) unless block_given?

  if scope.respond_to?(:find_each)
    (index_scope ? self.index_scope(scope) : scope).find_each do |record|
      yield record
    end
  else
    (scope.respond_to?(:each) && !scope.is_a?(SearchFlip::Result) ? scope : Array(scope)).each do |record|
      yield record
    end
  end
end
fetch_records(ids) click to toggle source

Returns a record set, usually an ActiveRecord::Relation, for the specified ids, ie primary keys. Override this method for custom primary keys and/or ORMs.

@param ids [Array] The array of ids to fetch the records for

@return The record set or an array of records

# File lib/search_flip/index.rb, line 199
def fetch_records(ids)
  model.where(id: ids)
end
freeze_index() click to toggle source

Freezes the index within Elasticsearch. Raises SearchFlip::ResponseError in case any errors occur.

@return [Boolean] Returns true or raises SearchFlip::ResponseError

# File lib/search_flip/index.rb, line 361
def freeze_index
  connection.freeze_index(index_name_with_prefix)
end
get(id, params = {}) click to toggle source

Retrieves the document specified by id from Elasticsearch. Raises SearchFlip::ResponseError specific exceptions in case any errors occur.

@example

UserIndex.get(1)
UserIndex.get(1, routing: "key")

@param id [String, Fixnum] The id to get @param params [Hash] Optional params for the request

@return [Hash] The specified document

# File lib/search_flip/index.rb, line 457
def get(id, params = {})
  response = connection.http_client.headers(accept: "application/json").get("#{type_url}/#{id}", params: params)

  SearchFlip::JSON.parse(response.to_s)
end
get_index_settings() click to toggle source

Fetches the index settings from Elasticsearch. Sends a GET request to index_url/_settings. Raises SearchFlip::ResponseError in case any errors occur.

@return [Hash] The index settings

# File lib/search_flip/index.rb, line 324
def get_index_settings
  connection.get_index_settings(index_name_with_prefix)
end
get_mapping() click to toggle source

Retrieves the current type mapping from Elasticsearch. Raises SearchFlip::ResponseError in case any errors occur.

@return [Hash] The current type mapping

# File lib/search_flip/index.rb, line 426
def get_mapping
  if include_type_name?
    connection.get_mapping(index_name_with_prefix, type_name: type_name)
  else
    connection.get_mapping(index_name_with_prefix)
  end
end
import(*args) click to toggle source
# File lib/search_flip/index.rb, line 507
               def import(*args)
  index(*args)
end
include_type_name?() click to toggle source

Returns whether or not to include a type name. As types are deprecated in Elasticsearch 7, this method controls whether or not to include the type name. By default, the method returns true for Elasticsearch versions before 7 or if the specified type name for the index is not equal to _doc.

# File lib/search_flip/index.rb, line 440
def include_type_name?
  type_name != "_doc" || connection.version.to_i < 7
end
index(scope, options = {}, additional_index_options = {}) click to toggle source

Indexes the given record set, array of records or individual record. A record set usually is an ActiveRecord::Relation, but can be any other ORM as well. Uses the Elasticsearch bulk API no matter what is provided. Refreshes the index if auto_refresh is enabled. Raises SearchFlip::ResponseError in case any errors occur.

@see fetch_records See fetch_records for other/custom ORMs @see record_id See record_id for other/custom ORMs @see SearchFlip::Config See SearchFlip::Config for auto_refresh

@example

CommentIndex.import Comment.all
CommentIndex.import [comment1, comment2]
CommentIndex.import Comment.first
CommentIndex.import Comment.all, ignore_errors: [409]
CommentIndex.import Comment.all, raise: false

@param scope A record set, array of records or individual record to index @param options [Hash] Specifies options regarding the bulk indexing @option options ignore_errors [Array] Specifies an array of http status

codes that shouldn't raise any exceptions, like eg 409 for conflicts,
ie when optimistic concurrency control is used.

@option options raise [Boolean] Prevents any exceptions from being

raised. Please note that this only applies to the bulk response, not to
the request in general, such that connection errors, etc will still
raise.

@param additional_index_options [Hash] Provides custom index options for eg

routing, versioning, etc
# File lib/search_flip/index.rb, line 540
def index(scope, options = {}, additional_index_options = {})
  bulk options do |indexer|
    each_record(scope, index_scope: true) do |object|
      indexer.index record_id(object), serialize(object), index_options(object).merge(additional_index_options)
    end
  end

  scope
end
index_exists?() click to toggle source

Returns whether or not the associated Elasticsearch index already exists.

@return [Boolean] Whether or not the index exists

# File lib/search_flip/index.rb, line 314
def index_exists?
  connection.index_exists?(index_name_with_prefix)
end
index_name() click to toggle source

Returns the base name of the index within Elasticsearch, ie the index name without prefix.

@return [String] The base name of the index, ie without prefix

# File lib/search_flip/index.rb, line 275
def index_name
  raise SearchFlip::MethodNotImplemented, "You must implement #{name}::index_name"
end
index_name_with_prefix() click to toggle source

@api private

Returns the full name of the index within Elasticsearch, ie with prefix specified via SearchFlip::Config.

@return [String] The full index name

# File lib/search_flip/index.rb, line 286
def index_name_with_prefix
  "#{SearchFlip::Config[:index_prefix]}#{index_name}"
end
index_options(record) click to toggle source

Override this method to automatically pass index options for a record at index-time, like routing or versioning.

@example

def self.index_options(comment)
  {
    routing: comment.user_id,
    version: comment.version,
    version_type: "external_gte"
  }
end

@param record The record that gets indexed @return [Hash] The index options

# File lib/search_flip/index.rb, line 68
def index_options(record)
  {}
end
index_scope(scope) click to toggle source

Override this method to specify an index scope, which will automatically be applied to scopes, eg. ActiveRecord::Relation objects, passed to import or index. This can be used to preload associations that are used when serializing records or to restrict the records you want to index.

@example Preloading an association

class CommentIndex
  # ...

  def self.index_scope(scope)
    scope.preload(:user)
  end
end

CommentIndex.import(Comment.all) # => CommentIndex.import(Comment.preload(:user))

@example Restricting records

class CommentIndex
  # ...

  def self.index_scope(scope)
    scope.where(public: true)
  end
end

CommentIndex.import(Comment.all) # => CommentIndex.import(Comment.where(public: true))

@param scope The supplied scope to extend

@return The extended scope

# File lib/search_flip/index.rb, line 235
def index_scope(scope)
  scope
end
index_settings() click to toggle source

Override to specify index settings like number of shards, analyzers, refresh interval, etc.

@example

def self.index_settings
  {
    settings: {
      number_of_shards: 10,
      number_of_replicas: 2
    }
  }
end

@return [Hash] The index settings

# File lib/search_flip/index.rb, line 305
def index_settings
  {}
end
index_url() click to toggle source

Returns the Elasticsearch index URL, ie base URL and index name with prefix.

@return [String] The Elasticsearch index URL

# File lib/search_flip/index.rb, line 655
def index_url
  connection.index_url(index_name_with_prefix)
end
mapping() click to toggle source

Specifies a type mapping. Override to specify a custom mapping. Please note that you don't have to include the type name, even for Elasticsearch versions before 7, as SearchFlip automatically adds the type name if neccessary.

@example

def self.mapping
  {
    properties: {
      email: { type: "string", analyzer: "custom_analyzer" }
    }
  }
end
# File lib/search_flip/index.rb, line 405
def mapping
  {}
end
mget(request, params = {}) click to toggle source

Retrieves the documents specified by ids from elasticsearch.

@example

UserIndex.mget(ids: [1, 2, 3])
UserIndex.mget(docs: [{ _id: 1, routing: "key1" }, { _id: 2, routing: "key2" }])
UserIndex.mget({ ids: [1, 2, 3] }, stored_fields: "username,full_name")

@param request [Hash] The raw request @param params [Hash] Optional params for the request

@return [Hash] The raw response

# File lib/search_flip/index.rb, line 475
def mget(request, params = {})
  response = connection.http_client.headers(accept: "application/json").post("#{type_url}/_mget", json: request, params: params)

  SearchFlip::JSON.parse(response.to_s)
end
open_index() click to toggle source

Opens the index within Elasticsearch. Raises SearchFlip::ResponseError in case any errors occur.

@return [Boolean] Returns true or raises SearchFlip::ResponseError

# File lib/search_flip/index.rb, line 352
def open_index
  connection.open_index(index_name_with_prefix)
end
record_id(record) click to toggle source

Returns the record's id, ie the unique identifier or primary key of a record. Override this method for custom primary keys, but return a String or Fixnum.

@example Default implementation

def self.record_id(record)
  record.id
end

@example Custom primary key

def self.record_id(user)
  user.username
end

@param record The record to get the primary key for

@return [String, Fixnum] The record's primary key

# File lib/search_flip/index.rb, line 187
def record_id(record)
  record.id
end
refresh() click to toggle source

Sends a index refresh request to Elasticsearch. Raises SearchFlip::ResponseError in case any errors occur.

# File lib/search_flip/index.rb, line 498
def refresh
  connection.refresh(index_name_with_prefix)
end
scope(name, &block) click to toggle source

Adds a named scope to the index.

@example

scope(:active) { where(active: true) }

UserIndex.active

@example

scope(:active) { |value| where(active: value) }

UserIndex.active(true)
UserIndex.active(false)

@param name [Symbol] The name of the scope @param block The scope definition. Add filters, etc.

# File lib/search_flip/index.rb, line 141
def scope(name, &block)
  define_singleton_method(name, &block)
end
serialize(record) click to toggle source

@abstract

Override this method to generate a hash representation of a record, used to generate the JSON representation of it.

@example

def self.serialize(comment)
  {
    id: comment.id,
    user_id: comment.user_id,
    message: comment.message,
    created_at: comment.created_at,
    updated_at: comment.updated_at
  }
end

@param record The record that gets serialized

@return [Hash] The hash-representation of the record

# File lib/search_flip/index.rb, line 121
def serialize(record)
  raise SearchFlip::MethodNotImplemented, "You must implement #{name}::serialize(record)"
end
type_name() click to toggle source

Override to specify the type name used within Elasticsearch. Recap, this gem uses an individual index for each index class, because Elasticsearch requires to have the same mapping for the same field name, even if the field is living in different types of the same index.

@return [String] The name used for the type within the index

# File lib/search_flip/index.rb, line 266
def type_name
  "_doc"
end
type_url() click to toggle source

Returns the full Elasticsearch type URL, ie base URL, index name with prefix and type name.

@return [String] The Elasticsearch type URL

# File lib/search_flip/index.rb, line 646
def type_url
  connection.type_url(index_name_with_prefix, type_name)
end
unfreeze_index() click to toggle source

Unfreezes the index within Elasticsearch. Raises SearchFlip::ResponseError in case any errors occur.

@return [Boolean] Returns true or raises SearchFlip::ResponseError

# File lib/search_flip/index.rb, line 370
def unfreeze_index
  connection.unfreeze_index(index_name_with_prefix)
end
update(scope, options = {}, additional_index_options = {}) click to toggle source

Indexes the given record set, array of records or individual record using Elasticsearch's update operation via the Bulk API, such that the request will fail if a record you want to update does not already exist in Elasticsearch.

@see index See index for more details regarding available

params and return values
# File lib/search_flip/index.rb, line 576
def update(scope, options = {}, additional_index_options = {})
  bulk options do |indexer|
    each_record(scope, index_scope: true) do |object|
      indexer.update record_id(object), { doc: serialize(object) }, index_options(object).merge(additional_index_options)
    end
  end

  scope
end
update_index_settings() click to toggle source

Updates the index settings within Elasticsearch according to the index settings specified. Raises SearchFlip::ResponseError in case any errors occur.

@return [Boolean] Returns true or false

# File lib/search_flip/index.rb, line 380
def update_index_settings
  connection.update_index_settings(index_name_with_prefix, index_settings)
end
update_mapping() click to toggle source

Updates the type mapping within Elasticsearch according to the mapping currently specified. Raises SearchFlip::ResponseError in case any errors occur.

# File lib/search_flip/index.rb, line 413
def update_mapping
  if include_type_name?
    connection.update_mapping(index_name_with_prefix, { type_name => mapping }, type_name: type_name)
  else
    connection.update_mapping(index_name_with_prefix, mapping)
  end
end
with_settings(index_name: nil, connection: nil) click to toggle source

Creates an anonymous class inheriting from the current index with a custom index name and/or connection. This is e.g. useful when working with aliases or proxies.

@example Basic usage

UserIndex.with_settings(index_name: 'new_user_index')
# => #<Class:0x...>

@example Working with aliases

new_index = UserIndex.with_settings(index_name: 'new_user_index')
new_index.create_index
new_index.import User.all
new_index.connection.update_aliases("...")

@example Working with proxies

query = UserIndex.with_settings(connection: ProxyConnection).where("...")

@param index_name [String] A custom index_name @param connection [SearchFlip::Connection] A custom connection

@return [Class] An anonymous class

# File lib/search_flip/index.rb, line 94
def with_settings(index_name: nil, connection: nil)
  Class.new(self).tap do |klass|
    klass.define_singleton_method(:index_name) { index_name } if index_name
    klass.define_singleton_method(:connection) { connection } if connection
  end
end