module SearchFlip::Index::ClassMethods
Public Instance Methods
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
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
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
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
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
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
@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
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
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
@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
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
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
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
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
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
# File lib/search_flip/index.rb, line 507 def import(*args) index(*args) end
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
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
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
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
@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
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
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
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
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
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
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
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
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
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
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
@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
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
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
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
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
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
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
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