module Searchkick
based on gist.github.com/mnutt/566725
thread-local (technically fiber-local) indexer used to aggregate bulk callbacks across models
based on gist.github.com/mnutt/566725
Constants
- VERSION
Attributes
aws_credentials[R]
client[W]
client_options[RW]
client_type[RW]
env[W]
index_prefix[RW]
index_suffix[RW]
model_options[RW]
models[RW]
queue_name[RW]
redis[RW]
search_method_name[RW]
search_timeout[W]
timeout[RW]
Public Class Methods
aws_credentials=(creds)
click to toggle source
# File lib/searchkick.rb, line 256 def self.aws_credentials=(creds) require "faraday_middleware/aws_sigv4" @aws_credentials = creds @client = nil # reset client end
callbacks(value = nil, message: nil) { || ... }
click to toggle source
message is private
# File lib/searchkick.rb, line 229 def self.callbacks(value = nil, message: nil) if block_given? previous_value = callbacks_value begin self.callbacks_value = value result = yield if callbacks_value == :bulk && indexer.queued_items.any? event = {} if message message.call(event) else event[:name] = "Bulk" event[:count] = indexer.queued_items.size end ActiveSupport::Notifications.instrument("request.searchkick", event) do indexer.perform end end result ensure self.callbacks_value = previous_value end else self.callbacks_value = value end end
callbacks?(default: true)
click to toggle source
# File lib/searchkick.rb, line 220 def self.callbacks?(default: true) if callbacks_value.nil? default else callbacks_value != false end end
callbacks_value()
click to toggle source
private
# File lib/searchkick.rb, line 328 def self.callbacks_value Thread.current[:searchkick_callbacks_enabled] end
callbacks_value=(value)
click to toggle source
private
# File lib/searchkick.rb, line 333 def self.callbacks_value=(value) Thread.current[:searchkick_callbacks_enabled] = value end
client()
click to toggle source
# File lib/searchkick.rb, line 71 def self.client @client ||= begin client_type = if self.client_type self.client_type elsif defined?(OpenSearch::Client) && defined?(Elasticsearch::Client) raise Error, "Multiple clients found - set Searchkick.client_type = :elasticsearch or :opensearch" elsif defined?(OpenSearch::Client) :opensearch elsif defined?(Elasticsearch::Client) :elasticsearch else raise Error, "No client found - install the `elasticsearch` or `opensearch-ruby` gem" end # check after client to ensure faraday is installed # TODO remove in Searchkick 6 if defined?(Typhoeus) && Gem::Version.new(Faraday::VERSION) < Gem::Version.new("0.14.0") require "typhoeus/adapters/faraday" end if client_type == :opensearch OpenSearch::Client.new({ url: ENV["OPENSEARCH_URL"], transport_options: {request: {timeout: timeout}, headers: {content_type: "application/json"}}, retry_on_failure: 2 }.deep_merge(client_options)) do |f| f.use Searchkick::Middleware f.request :aws_sigv4, signer_middleware_aws_params if aws_credentials end else raise Error, "The `elasticsearch` gem must be 7+" if Elasticsearch::VERSION.to_i < 7 Elasticsearch::Client.new({ url: ENV["ELASTICSEARCH_URL"], transport_options: {request: {timeout: timeout}, headers: {content_type: "application/json"}}, retry_on_failure: 2 }.deep_merge(client_options)) do |f| f.use Searchkick::Middleware f.request :aws_sigv4, signer_middleware_aws_params if aws_credentials end end end end
disable_callbacks()
click to toggle source
# File lib/searchkick.rb, line 216 def self.disable_callbacks self.callbacks_value = false end
enable_callbacks()
click to toggle source
callbacks
# File lib/searchkick.rb, line 212 def self.enable_callbacks self.callbacks_value = nil end
env()
click to toggle source
# File lib/searchkick.rb, line 116 def self.env @env ||= ENV["RAILS_ENV"] || ENV["RACK_ENV"] || "development" end
indexer()
click to toggle source
private
# File lib/searchkick.rb, line 323 def self.indexer Thread.current[:searchkick_indexer] ||= Indexer.new end
knn_support?()
click to toggle source
private
# File lib/searchkick.rb, line 147 def self.knn_support? if opensearch? !server_below?("2.4.0", true) else !server_below?("8.6.0") end end
load_model(class_name, allow_child: false)
click to toggle source
public (for reindexing conversions)
# File lib/searchkick.rb, line 307 def self.load_model(class_name, allow_child: false) model = class_name.safe_constantize raise Error, "Could not find class: #{class_name}" unless model if allow_child unless model.respond_to?(:searchkick_klass) raise Error, "#{class_name} is not a searchkick model" end else unless Searchkick.models.include?(model) raise Error, "#{class_name} is not a searchkick model" end end model end
load_records(relation, ids)
click to toggle source
private
# File lib/searchkick.rb, line 290 def self.load_records(relation, ids) relation = if relation.respond_to?(:primary_key) primary_key = relation.primary_key raise Error, "Need primary key to load records" if !primary_key relation.where(primary_key => ids) elsif relation.respond_to?(:queryable) relation.queryable.for_ids(ids) end raise Error, "Not sure how to load records" if !relation relation end
multi_search(queries)
click to toggle source
# File lib/searchkick.rb, line 190 def self.multi_search(queries) return if queries.empty? queries = queries.map { |q| q.send(:query) } event = { name: "Multi Search", body: queries.flat_map { |q| [q.params.except(:body).to_json, q.body.to_json] }.map { |v| "#{v}\n" }.join } ActiveSupport::Notifications.instrument("multi_search.searchkick", event) do MultiSearch.new(queries).perform end end
not_allowed_error?(e)
click to toggle source
private
# File lib/searchkick.rb, line 381 def self.not_allowed_error?(e) (defined?(Elastic::Transport) && e.is_a?(Elastic::Transport::Transport::Errors::MethodNotAllowed)) || (defined?(Elasticsearch::Transport) && e.is_a?(Elasticsearch::Transport::Transport::Errors::MethodNotAllowed)) || (defined?(OpenSearch) && e.is_a?(OpenSearch::Transport::Transport::Errors::MethodNotAllowed)) end
not_found_error?(e)
click to toggle source
private
# File lib/searchkick.rb, line 367 def self.not_found_error?(e) (defined?(Elastic::Transport) && e.is_a?(Elastic::Transport::Transport::Errors::NotFound)) || (defined?(Elasticsearch::Transport) && e.is_a?(Elasticsearch::Transport::Transport::Errors::NotFound)) || (defined?(OpenSearch) && e.is_a?(OpenSearch::Transport::Transport::Errors::NotFound)) end
opensearch?()
click to toggle source
# File lib/searchkick.rb, line 133 def self.opensearch? unless defined?(@opensearch) @opensearch = server_info["version"]["distribution"] == "opensearch" end @opensearch end
reindex_status(index_name)
click to toggle source
# File lib/searchkick.rb, line 263 def self.reindex_status(index_name) raise Error, "Redis not configured" unless redis batches_left = Index.new(index_name).batches_left { completed: batches_left == 0, batches_left: batches_left } end
relation?(klass)
click to toggle source
private methods are forwarded to base class this check to see if scope exists on that class it’s a bit tricky, but this seems to work
# File lib/searchkick.rb, line 346 def self.relation?(klass) if klass.respond_to?(:current_scope) !klass.current_scope.nil? else klass.is_a?(Mongoid::Criteria) || !Mongoid::Threaded.current_scope(klass).nil? end end
scope(model)
click to toggle source
private
# File lib/searchkick.rb, line 355 def self.scope(model) # safety check to make sure used properly in code raise Error, "Cannot scope relation" if relation?(model) if model.searchkick_options[:unscope] model.unscoped else model end end
script(source, **options)
click to toggle source
experimental
# File lib/searchkick.rb, line 206 def self.script(source, **options) Script.new(source, **options) end
search(term = "*", model: nil, **options, &block)
click to toggle source
# File lib/searchkick.rb, line 155 def self.search(term = "*", model: nil, **options, &block) options = options.dup klass = model # convert index_name into models if possible # this should allow for easier upgrade if options[:index_name] && !options[:models] && Array(options[:index_name]).all? { |v| v.respond_to?(:searchkick_index) } options[:models] = options.delete(:index_name) end # make Searchkick.search(models: [Product]) and Product.search equivalent unless klass models = Array(options[:models]) if models.size == 1 klass = models.first options.delete(:models) end end if klass if (options[:models] && Array(options[:models]) != [klass]) || Array(options[:index_name]).any? { |v| v.respond_to?(:searchkick_index) && v != klass } raise ArgumentError, "Use Searchkick.search to search multiple models" end end # TODO remove in Searchkick 6 if options[:execute] == false Searchkick.warn("The execute option is no longer needed") options.delete(:execute) end options = options.merge(block: block) if block Relation.new(klass, term, **options) end
search_timeout()
click to toggle source
# File lib/searchkick.rb, line 120 def self.search_timeout (defined?(@search_timeout) && @search_timeout) || timeout end
server_below?(version, true_version = false)
click to toggle source
TODO always check true version in Searchkick
6
# File lib/searchkick.rb, line 141 def self.server_below?(version, true_version = false) server_version = !true_version && opensearch? ? "7.10.2" : self.server_version Gem::Version.new(server_version.split("-")[0]) < Gem::Version.new(version.split("-")[0]) end
server_info()
click to toggle source
private
# File lib/searchkick.rb, line 125 def self.server_info @server_info ||= client.info end
server_version()
click to toggle source
# File lib/searchkick.rb, line 129 def self.server_version @server_version ||= server_info["version"]["number"] end
signer_middleware_aws_params()
click to toggle source
private
# File lib/searchkick.rb, line 338 def self.signer_middleware_aws_params {service: "es", region: "us-east-1"}.merge(aws_credentials) end
transport_error?(e)
click to toggle source
private
# File lib/searchkick.rb, line 374 def self.transport_error?(e) (defined?(Elastic::Transport) && e.is_a?(Elastic::Transport::Transport::Error)) || (defined?(Elasticsearch::Transport) && e.is_a?(Elasticsearch::Transport::Transport::Error)) || (defined?(OpenSearch) && e.is_a?(OpenSearch::Transport::Transport::Error)) end
warn(message)
click to toggle source
Calls superclass method
# File lib/searchkick.rb, line 285 def self.warn(message) super("[searchkick] WARNING: #{message}") end
with_redis() { |r| ... }
click to toggle source
# File lib/searchkick.rb, line 273 def self.with_redis if redis if redis.respond_to?(:with) redis.with do |r| yield r end else yield redis end end end