diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml new file mode 100644 index 0000000..cf66840 --- /dev/null +++ b/.github/workflows/test.yml @@ -0,0 +1,25 @@ +name: Test + +on: [pull_request] + +# permissions: +# contents: read + +jobs: + lint: + runs-on: ubuntu-latest + # env: + # BUNDLE_ONLY: rubocop + + steps: + - uses: actions/checkout@v4 + + - name: Set up Ruby 3.2.2 + uses: ruby/setup-ruby@v1 + with: + ruby-version: 3.2.2 + bundler-cache: true + + - name: Run Tests + run: bundle exec rubocop --parallel + # run: bundle exec rake \ No newline at end of file diff --git a/.gitignore b/.gitignore index 9106b2a..1f8fbb9 100644 --- a/.gitignore +++ b/.gitignore @@ -6,3 +6,4 @@ /pkg/ /spec/reports/ /tmp/ +Gemfile.lock \ No newline at end of file diff --git a/.rubocop.yml b/.rubocop.yml new file mode 100644 index 0000000..f81f9d0 --- /dev/null +++ b/.rubocop.yml @@ -0,0 +1,75 @@ +# see example at https://gist.github.com/jhass/a5ae80d87f18e53e7b56 + +# <% unless ENV['BYPASS_RUBOCOP_TODO'] %> +# inherit_from: +# <% else %> +# inherit_from: +# - '.rubocop-todo.yml' +# <% end %> + +inherit_from: + - .rubocop_todo.yml + +require: + - rubocop-rake + - rubocop-performance + +AllCops: + NewCops: enable + # TargetRubyVersion: 2.7.8 + # TargetRailsVersion: 6.1.4 + # Exclude: + # - 'Gemfile.lock' + +Naming/VariableNumber: + Enabled: false + +Layout/SpaceInsideHashLiteralBraces: + Enabled: false + +Layout/EmptyLinesAroundModuleBody: + EnforcedStyle: empty_lines_special + Enabled: false + +Layout/TrailingEmptyLines: + Enabled: false + EnforcedStyle: final_blank_line + +Layout/EmptyLinesAroundClassBody: + Enabled: false + +Style/RaiseArgs: + EnforcedStyle: compact + +Naming/MethodParameterName: + Enabled: false + +Naming/VariableName: + Enabled: false + +Layout/FirstHashElementIndentation: + Enabled: false + +Layout/CaseIndentation: + EnforcedStyle: end + +Metrics/ParameterLists: + Enabled: false + +Style/Lambda: + EnforcedStyle: literal + +Layout/IndentationWidth: + Enabled: false + +Layout/EndAlignment: + Enabled: false + +Layout/ElseAlignment: + Enabled: false + +Style/TrivialAccessors: + Enabled: false + +Layout/MultilineMethodCallIndentation: + EnforcedStyle: indented \ No newline at end of file diff --git a/.rubocop_todo.yml b/.rubocop_todo.yml new file mode 100644 index 0000000..1739fcc --- /dev/null +++ b/.rubocop_todo.yml @@ -0,0 +1,55 @@ +# This configuration was generated by +# `rubocop --auto-gen-config` +# on 2024-03-18 18:37:56 UTC using RuboCop version 1.62.1. +# The point is for the user to remove these configuration records +# one by one as the offenses are removed from the code base. +# Note that changes in the inspected code, or installation of new +# versions of RuboCop, may require this file to be generated again. + +# Offense count: 9 +# Configuration parameters: AllowedMethods, AllowedPatterns, CountRepeatedAttributes. +Metrics/AbcSize: + Max: 60 + +# Offense count: 2 +# Configuration parameters: CountComments, CountAsOne, AllowedMethods, AllowedPatterns. +# AllowedMethods: refine +Metrics/BlockLength: + Max: 35 + +# Offense count: 4 +# Configuration parameters: AllowedMethods, AllowedPatterns. +Metrics/CyclomaticComplexity: + Max: 17 + +# Offense count: 14 +# Configuration parameters: CountComments, CountAsOne, AllowedMethods, AllowedPatterns. +Metrics/MethodLength: + Max: 42 + +# Offense count: 1 +# Configuration parameters: CountComments, CountAsOne. +Metrics/ModuleLength: + Max: 153 + +# Offense count: 4 +# Configuration parameters: AllowedMethods, AllowedPatterns. +Metrics/PerceivedComplexity: + Max: 18 + +# Offense count: 6 +# Configuration parameters: AllowedConstants. +Style/Documentation: + Exclude: + - 'spec/**/*' + - 'test/**/*' + - 'lib/elasticsearch_repositories/adapters/multistrategy.rb' + - 'lib/elasticsearch_repositories/model.rb' + - 'lib/elasticsearch_repositories/response/result.rb' + - 'lib/elasticsearch_repositories/search_request.rb' + +# Offense count: 1 +# This cop supports safe autocorrection (--autocorrect). +Style/RescueModifier: + Exclude: + - 'lib/elasticsearch_repositories/strategy/searching.rb' diff --git a/Gemfile b/Gemfile index 0b5af21..b76c9d9 100644 --- a/Gemfile +++ b/Gemfile @@ -1,6 +1,17 @@ -source "https://rubygems.org" +# frozen_string_literal: true -git_source(:github) {|repo_name| "https://github.com/#{repo_name}" } +source 'https://rubygems.org' + +git_source(:github) { |repo_name| "https://github.com/#{repo_name}" } # Specify your gem's dependencies in elasticsearch_model_repositories.gemspec gemspec + +# gem 'bundler', '2.4.22' +gem 'debug', '>= 1.0.0' +gem 'rake', '~> 13.1' + +# rubocop +gem 'rubocop', '~> 1.62' +gem 'rubocop-performance', '~> 1.20' +gem 'rubocop-rake', '~> 0.6' \ No newline at end of file diff --git a/Gemfile.lock b/Gemfile.lock deleted file mode 100644 index 594bc18..0000000 --- a/Gemfile.lock +++ /dev/null @@ -1,74 +0,0 @@ -PATH - remote: . - specs: - elasticsearch_model_repositories (0.11.0) - activesupport (~> 6) - elasticsearch (~> 8) - -GEM - remote: https://rubygems.org/ - specs: - activesupport (6.1.7.3) - concurrent-ruby (~> 1.0, >= 1.0.2) - i18n (>= 1.6, < 2) - minitest (>= 5.1) - tzinfo (~> 2.0) - zeitwerk (~> 2.3) - ansi (1.5.0) - builder (3.2.4) - concurrent-ruby (1.2.2) - debug (1.8.0) - irb (>= 1.5.0) - reline (>= 0.3.1) - elastic-transport (8.2.1) - faraday (< 3) - multi_json - elasticsearch (8.7.0) - elastic-transport (~> 8) - elasticsearch-api (= 8.7.0) - elasticsearch-api (8.7.0) - multi_json - faraday (2.7.4) - faraday-net_http (>= 2.0, < 3.1) - ruby2_keywords (>= 0.0.4) - faraday-net_http (3.0.2) - i18n (1.13.0) - concurrent-ruby (~> 1.0) - io-console (0.6.0) - irb (1.9.1) - rdoc - reline (>= 0.3.8) - minitest (5.15.0) - minitest-reporters (1.6.1) - ansi - builder - minitest (>= 5.0) - ruby-progressbar - multi_json (1.15.0) - psych (5.1.1.1) - stringio - rake (13.1.0) - rdoc (6.6.0) - psych (>= 4.0.0) - reline (0.4.1) - io-console (~> 0.5) - ruby-progressbar (1.13.0) - ruby2_keywords (0.0.5) - stringio (3.1.0) - tzinfo (2.0.6) - concurrent-ruby (~> 1.0) - zeitwerk (2.6.7) - -PLATFORMS - ruby - -DEPENDENCIES - bundler (= 2.4.22) - debug (>= 1.0.0) - elasticsearch_model_repositories! - minitest (~> 5.14) - minitest-reporters (~> 1.6) - rake (~> 13.1) - -BUNDLED WITH - 2.4.22 diff --git a/Rakefile b/Rakefile index bb764f6..b44a4d9 100644 --- a/Rakefile +++ b/Rakefile @@ -1,10 +1,12 @@ -require "bundler/gem_tasks" -require "rake/testtask" +# frozen_string_literal: true + +require 'bundler/gem_tasks' +require 'rake/testtask' Rake::TestTask.new do |t| - t.libs << "test" - t.test_files = FileList["test/**/*_spec.rb"] + t.libs << 'test' + t.test_files = FileList['test/**/*_spec.rb'] t.verbose = true end -task :default => :test \ No newline at end of file +task default: :test \ No newline at end of file diff --git a/bin/console b/bin/console index 258bb9b..8ac2f56 100755 --- a/bin/console +++ b/bin/console @@ -1,7 +1,8 @@ #!/usr/bin/env ruby +# frozen_string_literal: true -require "bundler/setup" -require "elasticsearch_model_repositories" +require 'bundler/setup' +require 'elasticsearch_model_repositories' # You can add fixtures and/or initialization code here to make experimenting # with your gem easier. You can also use a different console, if you like. @@ -10,5 +11,5 @@ require "elasticsearch_model_repositories" # require "pry" # Pry.start -require "irb" +require 'irb' IRB.start(__FILE__) diff --git a/elasticsearch_model_repositories.gemspec b/elasticsearch_model_repositories.gemspec index fd0f749..89085c4 100644 --- a/elasticsearch_model_repositories.gemspec +++ b/elasticsearch_model_repositories.gemspec @@ -1,48 +1,36 @@ +# frozen_string_literal: true -lib = File.expand_path("../lib", __FILE__) -$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib) -require "elasticsearch_repositories/version" +require_relative 'lib/elasticsearch_repositories/version' Gem::Specification.new do |spec| - spec.name = "elasticsearch_model_repositories" + spec.name = 'elasticsearch_model_repositories' spec.version = ElasticsearchRepositories::VERSION - spec.authors = ["Pato"] - spec.email = ["pato_devilla@hotmail.com"] + spec.authors = ['Pato'] + spec.email = ['pato_devilla@hotmail.com'] - spec.summary = %q{Repositories pattern for activerecord models.} - spec.description = %q{Support multiple repositories for each model.} + spec.summary = 'Repositories pattern for activerecord models.' + spec.description = 'Support multiple repositories for each model.' # spec.homepage = "TODO: Put your gem's website or public repo URL here." + # spec.license = 'MIT' + spec.required_ruby_version = '>= 3.1.0' - # Prevent pushing this gem to RubyGems.org. To allow pushes either set the 'allowed_push_host' - # to allow pushing to a single host or delete this section to allow pushing to any host. - # if spec.respond_to?(:metadata) - # spec.metadata["allowed_push_host"] = "TODO: Set to 'http://mygemserver.com'" - - # spec.metadata["homepage_uri"] = spec.homepage - # spec.metadata["source_code_uri"] = "TODO: Put your gem's public repo URL here." - # spec.metadata["changelog_uri"] = "TODO: Put your gem's CHANGELOG.md URL here." - # else - # raise "RubyGems 2.0 or newer is required to protect against " \ - # "public gem pushes." - # end + # spec.metadata["homepage_uri"] = spec.homepage + # spec.metadata["source_code_uri"] = "TODO: Put your gem's public repo URL here." + # spec.metadata["changelog_uri"] = "TODO: Put your gem's CHANGELOG.md URL here." + spec.metadata['rubygems_mfa_required'] = 'true' # Specify which files should be added to the gem when it is released. # The `git ls-files -z` loads the files in the RubyGem that have been added into git. - spec.files = Dir.chdir(File.expand_path('..', __FILE__)) do - `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) } + spec.files = Dir.chdir(__dir__) do + `git ls-files -z`.split("\x0").reject do |f| + (File.expand_path(f) == __FILE__) || + f.start_with?(*%w[bin/ test/ spec/ features/ .git appveyor Gemfile]) + end end - spec.bindir = "exe" + spec.bindir = 'exe' spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) } - spec.require_paths = ["lib"] - - spec.add_dependency "elasticsearch", '~> 8' - spec.add_dependency "activesupport", '~> 6' - - # spec.add_development_dependency 'activemodel', '> 3' - spec.add_development_dependency "bundler", "2.4.22" - spec.add_development_dependency "rake", "~> 13.1" - spec.add_development_dependency "debug", ">= 1.0.0" - spec.add_development_dependency "minitest", "~> 5.14" - spec.add_development_dependency "minitest-reporters", "~> 1.6" + spec.require_paths = ['lib'] + spec.add_dependency 'activesupport', '~> 6' + spec.add_dependency 'elasticsearch', '~> 8' end diff --git a/lib/elasticsearch_model_repositories.rb b/lib/elasticsearch_model_repositories.rb index 12e86a1..39bae68 100644 --- a/lib/elasticsearch_model_repositories.rb +++ b/lib/elasticsearch_model_repositories.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require_relative 'elasticsearch_repositories/version' # elasticsearch @@ -37,7 +39,7 @@ # # Base module that allows gem configuration, and be used to store a cache of an Elasticsearch::Client -# It also provides a *search* method for executing multistrategy searches +# It also provides a *search* method for executing multistrategy searches # module ElasticsearchRepositories @@ -49,7 +51,7 @@ class << self def client @client ||= Elasticsearch::Client.new end - + # Overrides cached elasticsearch client # # @param [Elasticsearch::Client] client @@ -57,13 +59,13 @@ def client def client=(client) @client = client end - + # Search across multiple strategies # # @query_or_payload [String,Hash] # @strategies [Array] array of strategies (ElasticsearchRepositories::BaseStrategy) # @options [Hash] options to pass to the search request ToDo (detail) - def search(query_or_payload, strategies=[], options={}) + def search(query_or_payload, strategies = [], options = {}) wrapper = ElasticsearchRepositories::Multistrategy::MultistrategyWrapper.new(strategies) search = ElasticsearchRepositories::SearchRequest.new(wrapper, query_or_payload, options) ElasticsearchRepositories::Response::Response.new(wrapper, search, options) @@ -78,7 +80,7 @@ def search(query_or_payload, strategies=[], options={}) # # def search_and_scroll # end - + # Yield self to allow configuing in a block def configure yield self diff --git a/lib/elasticsearch_repositories/adapter.rb b/lib/elasticsearch_repositories/adapter.rb index 01fb62f..7fbc132 100644 --- a/lib/elasticsearch_repositories/adapter.rb +++ b/lib/elasticsearch_repositories/adapter.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module ElasticsearchRepositories # Contains a registry for ElasticsearchRepositories::Adapters::* along with a # lambda that is used to match an adapter @@ -40,15 +42,20 @@ class << self # defined?(::DataMapper::Resource) and klass_or_klasses.ancestors.include?(::DataMapper::Resource) # } # ) - def register(name, condition) - self.adapters[name] = condition + # + # @param adapter_module [Module] + # @param condition [Proc] + # + # @return [void] + def register(adapter_module, condition) + adapters[adapter_module] = condition end # @return Hash{adapter_klass => Proc} the collection of registered adapters def adapters @adapters ||= {} end - + end attr_reader :klass_or_klasses @@ -72,15 +79,17 @@ def importing_mixin match_adapter.const_get(:Importing) end - # Returns the adapter module that first evaluates its registered condition as true + # Returns the adapter module that first evaluates its registered condition as true # # @return [Module] ElasticsearchRepositories::Adapters::* def match_adapter - @matched_adapter ||= begin - self.class.adapters.find( lambda {[]} ) { |name, condition| condition.call(klass_or_klasses) }.first - end + @match_adapter ||= self + .class + .adapters + .find(-> { [] }) { |_name, condition| condition.call(klass_or_klasses) } + .first end end - + end diff --git a/lib/elasticsearch_repositories/adapters/active_record.rb b/lib/elasticsearch_repositories/adapters/active_record.rb index cc76c74..b9b0755 100644 --- a/lib/elasticsearch_repositories/adapters/active_record.rb +++ b/lib/elasticsearch_repositories/adapters/active_record.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module ElasticsearchRepositories module Adapters @@ -6,10 +8,10 @@ module Adapters module ActiveRecord # register adapter - Adapter.register self, lambda {|klass_or_klasses| - !!defined?(::ActiveRecord::Base) && - klass_or_klasses.respond_to?(:ancestors) && - klass_or_klasses.ancestors.include?(::ActiveRecord::Base) + Adapter.register self, ->(klass_or_klasses) { + defined?(::ActiveRecord::Base) && + klass_or_klasses.respond_to?(:ancestors) && + klass_or_klasses.ancestors.include?(::ActiveRecord::Base) } # Module for implementing methods and logic related to fetching records from the database @@ -19,39 +21,28 @@ module Records # @return [ActiveRecord::Relation] def records ar_relation = klass_or_klasses.where(klass_or_klasses.primary_key => ids) - ar_relation = ar_relation.includes(self.options[:includes]) if self.options[:includes] + ar_relation = ar_relation.includes(options[:includes]) if options[:includes] # Re-order records based on the order from Elasticsearch hits # by redefining `to_a` or `records`, unless the user has called `order()` ar_relation.instance_exec(response.results) do |results| - break unless self - - ar_records_method_name = if defined?(::ActiveRecord) && ::ActiveRecord::VERSION::MAJOR >= 5 - :records - else - :to_a - end + break unless self # TODO: why? # override method in ActiveRecord_Relation instance - define_singleton_method(ar_records_method_name) do - if defined?(::ActiveRecord) && ::ActiveRecord::VERSION::MAJOR >= 4 - self.load - else - self.__send__(:exec_queries) - end + define_singleton_method(:records) do + self.load # sort unless user called `order()` - if !self.order_values.present? + if order_values.present? + @records + else @records.sort_by do |record| results.index do |result| result.id == record.id.to_s end end - else - @records end end - end ar_relation @@ -91,7 +82,7 @@ module Callbacks # module Importing - BULKIFY_PROC = lambda do |model, strategy| + BULKIFY_PROC = ->(model, strategy) do if strategy.index_without_id { index: { data: strategy.as_indexed_json(model) } } else @@ -103,8 +94,8 @@ module Importing # # @see http://api.rubyonrails.org/classes/ActiveRecord/Batches.html ActiveRecord::Batches.find_in_batches # - def self.find_in_batches(model, query: nil, scope: nil, **find_params, &block) - model = model.__send__(scope) if scope + def self.find_in_batches(model, query: nil, scope: nil, **find_params) + model = model.public_send(scope) if scope model = model.instance_exec(&query) if query model.find_in_batches(**find_params) do |batch| diff --git a/lib/elasticsearch_repositories/adapters/multistrategy.rb b/lib/elasticsearch_repositories/adapters/multistrategy.rb index c04447b..6a48018 100644 --- a/lib/elasticsearch_repositories/adapters/multistrategy.rb +++ b/lib/elasticsearch_repositories/adapters/multistrategy.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module ElasticsearchRepositories module Adapters @@ -8,11 +10,12 @@ module Adapters module Multistrategy # register adapter - Adapter.register self, lambda { |klass_or_klasses| klass_or_klasses.is_a? Array } + Adapter.register self, ->(klass_or_klasses) { klass_or_klasses.is_a? Array } module Records - # Returns a collection of model instances, possibly of different classes and adapters (ActiveRecord, Mongoid, ...) + # Returns a collection of model instances, possibly of different classes and + # adapters (ActiveRecord, Mongoid, ...) # # @note The order of results in the Elasticsearch response must be preserved # @@ -27,8 +30,8 @@ def records ids_by_type = response.results.each_with_object({}) do |result, obj| type = __type_for_result(result) - model = type_model_cache[type] ||= response.strategy_or_wrapper.host_class.detect do |model| - __model_to_type(model) == type + model = type_model_cache[type] ||= response.strategy_or_wrapper.host_class.detect do |curr_model| + __model_to_type(curr_model) == type end next if model.nil? @@ -82,10 +85,10 @@ def __model_to_type(model) # def __records_for_model(model, ids) adapter = __adapter_for_model(model) - case - when ElasticsearchRepositories::Adapters::ActiveRecord.equal?(adapter) - multi_includes = self.options[:multimodel_includes] + if ElasticsearchRepositories::Adapters::ActiveRecord.equal?(adapter) # rubocop:disable Style/GuardClause + + multi_includes = options[:multimodel_includes] model_includes = [] if multi_includes.is_a? Hash model_includes.concat(multi_includes[model.name.underscore.to_sym]) @@ -106,12 +109,12 @@ def __records_for_model(model, ids) # @api private # def __adapter_for_model(model) - Adapter.adapters.select { |name, checker| checker.call(model) }.keys.first + Adapter.adapters.select { |_name, checker| checker.call(model) }.keys.first end end end - + end end \ No newline at end of file diff --git a/lib/elasticsearch_repositories/base_strategy.rb b/lib/elasticsearch_repositories/base_strategy.rb index fa4a9d4..b1629f3 100644 --- a/lib/elasticsearch_repositories/base_strategy.rb +++ b/lib/elasticsearch_repositories/base_strategy.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + # require_relative 'strategy/configuration' # require_relative 'strategy/importing' # require_relative 'strategy/indexing' @@ -6,7 +8,7 @@ # require_relative 'strategy/serializing' module ElasticsearchRepositories - + # # Use this class as a base to create your own strategies by inheriting from it. # Once you've defined this defined your strategy, you can register it in your models @@ -29,15 +31,13 @@ class BaseStrategy include ElasticsearchRepositories::Strategy::Searching include ElasticsearchRepositories::Strategy::Serializing - attr_reader :host_class - attr_reader :client - attr_reader :name + attr_reader :host_class, :client, :name - CONFIGURABLE_METHODS = %i( + CONFIGURABLE_METHODS = %i[ target_index_name search_index_name current_index_name reload_indices_iterator index_without_id as_indexed_json index_record_to_es reindexing_includes_proc custom_doc_id - ).freeze + ].freeze class << self @@ -91,17 +91,17 @@ def configure(hash) # @param [String] name the identifier of the strategy # @param [Proc] &block used to configure the strategy # - def initialize(host_class, client, name=nil, &block) + def initialize(host_class, client, name = nil, &) @host_class = host_class @client = client @name = name - self.instance_eval(&block) if block_given? + instance_eval(&) if block_given? end - + # excecutes the block on the strategy's context. Useful to override/update methods # @return [void] - def update(&block) - self.instance_eval(&block) if block_given? + def update(&) + instance_eval(&) if block_given? end # Same as class method 'configure' but for an instance diff --git a/lib/elasticsearch_repositories/model.rb b/lib/elasticsearch_repositories/model.rb index f8dc575..b808092 100644 --- a/lib/elasticsearch_repositories/model.rb +++ b/lib/elasticsearch_repositories/model.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module ElasticsearchRepositories # @@ -16,7 +18,7 @@ module Importing # @return [void] def create_indices(start_time: nil, end_time: nil, **kwargs) indexing_strategies.each do |strategy| - strategy.reload_indices_iterator(start_time, end_time) do |import_db_query, iterator_options| + strategy.reload_indices_iterator(start_time, end_time) do |_import_db_query, iterator_options| strategy.create_index( iterator_options.slice(:index, :mappings, :settings).merge!(kwargs) ) @@ -25,7 +27,7 @@ def create_indices(start_time: nil, end_time: nil, **kwargs) end # Reloads data into a model's indices - # + # # @param [DateTime] start_time used for reload_indices_iterator # @param [DateTime] end_time used for reload_indices_iterator # @param [Proc] each_batch_proc @@ -60,6 +62,8 @@ def reload_indices( verify_count: false ) + required_options = %i[index settings mappings] + # call reload_indices_iterator for all strategies indexing_strategies.each_with_object({}) do |strategy, return_hash| next if strategy_names && !strategy_names.includes?(strategy.name) @@ -68,13 +72,13 @@ def reload_indices( current_hash = return_hash[key] = [] strategy.reload_indices_iterator(start_time, end_time) do |import_db_query, iterator_options| - # merge passed options options = iterator_options.deep_merge(override_options) - + # ensure important options are present before recreating index - %i[index settings mappings].each do |name| + required_options.each do |name| next unless options[name].nil? + raise ArgumentError.new("missing option #{name} while importing") end @@ -102,7 +106,7 @@ def reload_indices( import_return = if block_given? yield(self, strategy, options) else - import(strategy: strategy, **options, &each_batch_proc) + import(strategy:, **options, &each_batch_proc) end current_hash.push(import_return) @@ -114,7 +118,6 @@ def reload_indices( end verify_index_doc_count(strategy, import_db_query, options[:index], options[:verify_count_query]) end - end end end @@ -127,7 +130,7 @@ def reload_indices( # ruler.call(:foo) do # sleep(1) # end - + # ruler.call(:foo) do # sleep(2) # end @@ -139,9 +142,9 @@ def reload_indices( # @return [Hash{* => Array}] def with_timer(average: false) obj = {} - wrapper = lambda do |key, normalizer = nil, &block| + wrapper = ->(key, normalizer = nil, &block) do key_array = obj[key] ||= [] - + now = Time.now value = block.call took = Time.now - now @@ -150,7 +153,7 @@ def with_timer(average: false) [value, took] end yield wrapper, obj - obj = obj.transform_values! {|v| v.sum(0.0) / v.size } if average + obj = obj.transform_values! { |v| v.sum(0.0) / v.size } if average obj end @@ -166,12 +169,12 @@ def with_refresh_interval(strategy, index, interval) current_interval = strategy .client .indices - .get_settings(index: index) + .get_settings(index:) .dig(index, 'settings', 'index', 'refresh_interval') || '1s' strategy.client.indices.put_settings( body: { index: { refresh_interval: interval } }, - index: index + index: ) yield @@ -179,7 +182,7 @@ def with_refresh_interval(strategy, index, interval) # restore refresh_interval strategy.client.indices.put_settings( body: { index: { refresh_interval: current_interval } }, - index: index + index: ) end @@ -202,7 +205,7 @@ def import( bulkify: nil, **_kwargs # allow passing unknown kwargs ) - + return_hash = { errors: 0, total: 0 } adapter_importing_module = Adapter.new(self).importing_mixin @@ -216,14 +219,14 @@ def import( normalizer = ->(took) { batch_size.fdiv(took) } # time __batch_to_bulk - bulk, _ = timer.call(:bulkify_per_s, normalizer) do + bulk, _took = timer.call(:bulkify_per_s, normalizer) do __batch_to_bulk(batch, strategy, bulkify) end # time bulk request - response, _ = timer.call(:bulk_req_per_s, normalizer) do + response, _took = timer.call(:bulk_req_per_s, normalizer) do strategy.client.bulk({ - index: index, + index:, body: bulk }.merge!(bulk_request_params)) end @@ -239,20 +242,20 @@ def import( # increment errors return_hash[:errors] += error_items.size - + # increment total return_hash[:total] += batch_size - + yield(strategy, response, error_items, timer_data) if block_given? - + sleep(batch_sleep) if batch_sleep end end end - + return_hash.merge!(measurements) end - + # Maps an array of the model's instances by calling the bulkify Proc # # @param [Array] batch @@ -274,23 +277,23 @@ def verify_index_doc_count(strategy, import_db_query, index, verify_count_query) db_count = import_db_query.count es_count = strategy - .client - .count({ - body: verify_count_query, - index: index - })['count'] + .client + .count({ + body: verify_count_query, + index: + })['count'] - if db_count != es_count - puts "MISMATCH! -> (DB=#{db_count}, ES=#{es_count}) for query: #{verify_count_query}" - else + if db_count == es_count puts "(DB=#{db_count}, ES=#{es_count}) for query: #{verify_count_query}" + else + puts "MISMATCH! -> (DB=#{db_count}, ES=#{es_count}) for query: #{verify_count_query}" end db_count == es_count end end - + module ClassMethods include Importing @@ -303,40 +306,36 @@ module ClassMethods # @param [BaseStrategy] strategy_klass # @param [String] name the identifier of the strategy, can be used if updating the strategy later on is desired # @return [void] - def register_strategy(strategy_klass, name='main', &block) + def register_strategy(strategy_klass, name = 'main', &) self.indexing_strategies ||= [] # raise error for diplicate name - if get_strategy(name) - raise StandardError.new("deplicate strategy name '#{name}' on model #{self.name}") - end + raise StandardError.new("deplicate strategy name '#{name}' on model #{self.name}") if get_strategy(name) - strategy = strategy_klass.new(self, ::ElasticsearchRepositories.client, name, &block) + strategy = strategy_klass.new(self, ::ElasticsearchRepositories.client, name, &) indexing_strategies.push(strategy) # add class to registry unless already registered - if self.is_a?(Class) && !::ElasticsearchRepositories::ClassRegistry.all.map(&:to_s).include?(self.name) - ::ElasticsearchRepositories::ClassRegistry.add(self) - end + return unless is_a?(Class) && !::ElasticsearchRepositories::ClassRegistry.all.map(&:to_s).include?(self.name) + + ::ElasticsearchRepositories::ClassRegistry.add(self) end # Update an indexing strategy by name # # @param [String] name the identifier of the strategy # @return [void] - def update_strategy(name, &block) + def update_strategy(name, &) strategy = get_strategy(name) - if !strategy - raise StandardError.new("strategy '#{name}' not found on model #{self.name}") - end + raise StandardError.new("strategy '#{name}' not found on model #{self.name}") unless strategy - strategy.update(&block) + strategy.update(&) end # @param [String] name # @return [NilClass|BaseStrategy] def get_strategy(name) - indexing_strategies&.find{|s| s.name == name } + indexing_strategies&.find { |s| s.name == name } end # Returns the first strategy (for now) @@ -353,7 +352,7 @@ def default_indexing_strategy # # @return [String] def _to_index_model_name - self.name.underscore.dasherize.pluralize + name.underscore.dasherize.pluralize end # this base name is a contract to be followed by each class @@ -375,7 +374,7 @@ def all_klass_indices(*args, **kwargs) end end end - + end module InstanceMethods @@ -386,7 +385,7 @@ module InstanceMethods # @return [void] def index_with_all_strategies(action = 'create', &block) self.class.indexing_strategies.each do |strategy| - index_with_strategy(action, strategy, &block) + index_with_strategy(strategy, action, &block) end end @@ -395,14 +394,14 @@ def index_with_all_strategies(action = 'create', &block) # @param [String] action # @param [String|BaseStrategy] strategy, name or instance # @return [void] - def index_with_strategy(action = 'create', strategy, &block) + def index_with_strategy(strategy, action = 'create', &) strategy = self.class.get_strategy(strategy) unless strategy.is_a? BaseStrategy - strategy.index_record_to_es(action, self, &block) + strategy.index_record_to_es(action, self, &) end private - + # Override this method on your model to handle indexing # # @param [ElasticsearchRepositories::BaseStrategy] strategy @@ -416,7 +415,7 @@ def index_with_strategy(action = 'create', strategy, &block) # @option options [Hash] body # @option options [String] index # @return [void] - def index_document(strategy, action, **options) + def index_document(_strategy, _action, **_options) raise NotImplementedError('need to implement own index_document method') end diff --git a/lib/elasticsearch_repositories/multistrategy.rb b/lib/elasticsearch_repositories/multistrategy.rb index 0345c1c..7326a28 100644 --- a/lib/elasticsearch_repositories/multistrategy.rb +++ b/lib/elasticsearch_repositories/multistrategy.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module ElasticsearchRepositories module Multistrategy @@ -12,22 +14,21 @@ def initialize(strategies) end def search_index_name - strategies.map { |s| s.search_index_name } + strategies.map(&:search_index_name) end # Get the common client for all strategies # @return Elastic::Transport::Client def client - _strategies = strategies.map { |s| s.client }.uniq - if _strategies.size == 1 - _strategies.first - else - raise StandardError.new('Multistrategy defined with strategies with different elasticsearch clients') - end + clients = strategies.map(&:client).uniq + + raise StandardError.new('Strategies must implement same client') unless clients.size == 1 + + clients.first end def host_class - strategies.map { |s| s.host_class } + strategies.map(&:host_class) end end diff --git a/lib/elasticsearch_repositories/registry.rb b/lib/elasticsearch_repositories/registry.rb index 9ba6778..572b024 100644 --- a/lib/elasticsearch_repositories/registry.rb +++ b/lib/elasticsearch_repositories/registry.rb @@ -1,8 +1,10 @@ +# frozen_string_literal: true + module ElasticsearchRepositories # used to keep track which classes have the ElasticsearchRepositories::Model # module included class ClassRegistry - + def initialize @models = [] end @@ -12,7 +14,7 @@ def initialize # @api private # def self.__instance - @instance ||= new + @__instance ||= new end # Adds a model to the registry diff --git a/lib/elasticsearch_repositories/response.rb b/lib/elasticsearch_repositories/response.rb index 919d38e..d8202bf 100644 --- a/lib/elasticsearch_repositories/response.rb +++ b/lib/elasticsearch_repositories/response.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module ElasticsearchRepositories module Response @@ -10,15 +12,15 @@ class Response include Enumerable - #ToDo 'delegate' is rails specific + # TODO: 'delegate' is rails specific delegate :each, :empty?, :size, :slice, :[], :to_ary, to: :results # @param [BaseStrategy|MultistrategyWrapper] strategy_or_wrapper # @param [ElasticsearchRepositories::SearchRequest] search # @param [Hash] options # @option options [Boolean] :use_cache - def initialize(strategy_or_wrapper, search, options={}) - @strategy_or_wrapper = strategy_or_wrapper + def initialize(strategy_or_wrapper, search, options = {}) + @strategy_or_wrapper = strategy_or_wrapper @search = search @use_cache = options[:use_cache] || true @cache = {} @@ -39,7 +41,7 @@ def response # @return [Results] def results with_cache('results') do - response.dig('hits', 'hits').map { |hit| Result.new().merge! hit } + response.dig('hits', 'hits').map { |hit| Result.new.merge! hit } end end @@ -60,12 +62,15 @@ def total # Returns the total number of pages def total_pages(size = @search_size) - unless size - msg = "missing 'size' argument, pass it to .total_pages(size) or ensure query definition contains :size" - raise ArgumentError.new(msg) + if size.nil? + raise ArgumentError.new( + "missing 'size' argument, pass it to .total_pages(size) or ensure query definition contains :size" + ) end - size == 0 ? 0 : (self.total / size.to_f).ceil + return 0 if size.zero? + + (total / size.to_f).ceil end # Returns the max_score @@ -91,14 +96,14 @@ def shards # Returns aggregations def aggregations with_cache('aggregations') do - Aggregations.new().merge! (response['aggregations'] || {}) + Aggregations.new.merge!(response['aggregations'] || {}) end end # Returns suggestions def suggestions with_cache('suggestions') do - Suggestions.new().merge! (response['suggest'] || {}) + Suggestions.new.merge!(response['suggest'] || {}) end end @@ -113,7 +118,7 @@ def with_cache(key) def clear_cache! @cache.clear end - + end end diff --git a/lib/elasticsearch_repositories/response/aggregations.rb b/lib/elasticsearch_repositories/response/aggregations.rb index 0d10ebd..60e731d 100644 --- a/lib/elasticsearch_repositories/response/aggregations.rb +++ b/lib/elasticsearch_repositories/response/aggregations.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module ElasticsearchRepositories module Response diff --git a/lib/elasticsearch_repositories/response/records.rb b/lib/elasticsearch_repositories/response/records.rb index b07cd02..d7e694b 100644 --- a/lib/elasticsearch_repositories/response/records.rb +++ b/lib/elasticsearch_repositories/response/records.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module ElasticsearchRepositories module Response @@ -17,15 +19,14 @@ class Records # @param [Class,Array] klass_or_klasses # @param [ElasticsearchRepositories::Response::Response] response - def initialize(klass_or_klasses, response, options={}) - + def initialize(klass_or_klasses, response, options = {}) @klass_or_klasses = klass_or_klasses @response = response # Include module provided by the adapter in the singleton class # adapter = Adapter.new(klass_or_klasses) - self.singleton_class.include adapter.records_mixin + singleton_class.include adapter.records_mixin self.options = options end @@ -44,25 +45,31 @@ def results # Yields [record, hit] pairs to the block # - def each_with_hit(&block) - records.to_a.zip(results).each(&block) + def each_with_hit(&) + records.to_a.zip(results).each(&) end # Yields [record, hit] pairs and returns the result # - def map_with_hit(&block) - records.to_a.zip(results).map(&block) + def map_with_hit(&) + records.to_a.zip(results).map(&) end - # Delegate methods to `@records` + # Delegate to `@records` if it responds to method (public only) # + # @override def method_missing(method_name, *arguments) - records.respond_to?(method_name) ? records.__send__(method_name, *arguments) : super + if records.respond_to?(method_name) + records.public_send(method_name, *arguments) + else + super + end end - # Respond to methods from `@records` + # Respond to methods from `@records` (public only) # - def respond_to?(method_name, include_private = false) + # @override + def respond_to_missing?(method_name, _include_private = false) records.respond_to?(method_name) || super end diff --git a/lib/elasticsearch_repositories/response/result.rb b/lib/elasticsearch_repositories/response/result.rb index b1da6af..740c475 100644 --- a/lib/elasticsearch_repositories/response/result.rb +++ b/lib/elasticsearch_repositories/response/result.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module ElasticsearchRepositories module Response @@ -9,7 +11,7 @@ class Result < ::Hash # strategies may point to the same index # def id - self.dig('_source', 'id')&.to_s || self['_id'] + dig('_source', 'id')&.to_s || self['_id'] end # # Return document `_type` as `_type` diff --git a/lib/elasticsearch_repositories/response/suggestions.rb b/lib/elasticsearch_repositories/response/suggestions.rb index 8e39504..1af16c1 100644 --- a/lib/elasticsearch_repositories/response/suggestions.rb +++ b/lib/elasticsearch_repositories/response/suggestions.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module ElasticsearchRepositories module Response diff --git a/lib/elasticsearch_repositories/search_request.rb b/lib/elasticsearch_repositories/search_request.rb index e654fe6..4e58046 100644 --- a/lib/elasticsearch_repositories/search_request.rb +++ b/lib/elasticsearch_repositories/search_request.rb @@ -1,7 +1,9 @@ +# frozen_string_literal: true + module ElasticsearchRepositories class SearchRequest attr_reader :strategy_or_wrapper, :definition - + include ActiveSupport::Callbacks define_callbacks :execute @@ -9,29 +11,28 @@ class SearchRequest # @param query_or_payload [String,Hash,Object] The search request definition # (String, Hash, or object responding to `to_hash`) # @param options [Hash] Optional parameters to be passed to the Elasticsearch client - def initialize(strategy_or_wrapper, query_or_payload, options={}) - @strategy_or_wrapper = strategy_or_wrapper + def initialize(strategy_or_wrapper, query_or_payload, options = {}) + @strategy_or_wrapper = strategy_or_wrapper + + index = options[:index] || strategy_or_wrapper.search_index_name - case - # search query: ... - when query_or_payload.respond_to?(:to_hash) - body = query_or_payload.to_hash + # search query: ... + if query_or_payload.respond_to?(:to_hash) + body = query_or_payload.to_hash # search '{ "query" : ... }' - when query_or_payload.is_a?(String) && query_or_payload =~ /^\s*{/ - body = query_or_payload + elsif query_or_payload.is_a?(String) && query_or_payload =~ /^\s*{/ + body = query_or_payload # search '...' - else - q = query_or_payload + else + q = query_or_payload end - __index_name = options[:index] || strategy_or_wrapper.search_index_name - - if body - @definition = options.merge({ index: __index_name, body: body }) + @definition = if body + options.merge({ index:, body: }) else - @definition = options.merge({ index: __index_name, q: q }) + options.merge({ index:, q: }) end end diff --git a/lib/elasticsearch_repositories/strategy/configuration.rb b/lib/elasticsearch_repositories/strategy/configuration.rb index 74cab7d..5e198eb 100644 --- a/lib/elasticsearch_repositories/strategy/configuration.rb +++ b/lib/elasticsearch_repositories/strategy/configuration.rb @@ -1,6 +1,8 @@ +# frozen_string_literal: true + module ElasticsearchRepositories module Strategy - + # # This module contains all methods used by BaseStrategy regarding: # @@ -24,7 +26,7 @@ def search_index_name # # @param record instance to calculate the index name # @return [String] - def target_index_name(record) + def target_index_name(_record) raise NotImplementedError.new('need to implement own target_index_name method') end @@ -44,12 +46,12 @@ def mappings # # @param [Hash] options # @return [void] - def set_mappings(options={}, &block) + def set_mappings(options = {}, &) @cached_mapping ||= Mappings.new(options, self) @cached_mapping.options.update(options) unless options.empty? if block_given? - @cached_mapping.instance_eval(&block) + @cached_mapping.instance_eval(&) else @cached_mapping end @@ -63,19 +65,19 @@ def settings # set/update the settings to the strategy # # @return [void] - def set_settings(settings={}, &block) + def set_settings(settings = {}, &) @cached_settings ||= Settings.new(settings) @cached_settings.settings.update(settings) unless settings.empty? if block_given? - instance_eval(&block) - return self + instance_eval(&) + self else @cached_settings end end - + end end end \ No newline at end of file diff --git a/lib/elasticsearch_repositories/strategy/configuration/mappings.rb b/lib/elasticsearch_repositories/strategy/configuration/mappings.rb index f7524b5..0b84ce0 100644 --- a/lib/elasticsearch_repositories/strategy/configuration/mappings.rb +++ b/lib/elasticsearch_repositories/strategy/configuration/mappings.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module ElasticsearchRepositories module Strategy module Configuration @@ -8,12 +10,12 @@ class Mappings attr_accessor :options, :strategy, :mapping, :runtime_fields, :dynamic_properties_methods # @private - TYPES_WITH_EMBEDDED_PROPERTIES = %w(object nested) + TYPES_WITH_EMBEDDED_PROPERTIES = %w[object nested].freeze # @param [Hash] options index mappings options (dynamic, etc...) # @param [ElasticsearchRepositories::BaseStrategy] strategy - def initialize(options={}, strategy=nil) - instance_variable_set('@dynamic_properties_methods', []) + def initialize(options = {}, strategy = nil) + instance_variable_set(:@dynamic_properties_methods, []) @options = options @strategy = strategy @mapping = {} @@ -27,19 +29,23 @@ def initialize(options={}, strategy=nil) # @param [Hash] definition field definition # # @return [Mappings] self - def indexes(field_name, definition={}, &block) + def indexes(field_name, definition = {}, &) @mapping[field_name] = definition if block_given? @mapping[field_name][:type] ||= 'object' - properties = TYPES_WITH_EMBEDDED_PROPERTIES.include?(@mapping[field_name][:type].to_s) ? :properties : :fields + properties = if TYPES_WITH_EMBEDDED_PROPERTIES.include?(@mapping[field_name][:type].to_s) + :properties + else + :fields + end @mapping[field_name][properties] ||= {} previous = @mapping begin @mapping = @mapping[field_name][properties] - self.instance_eval(&block) + instance_eval(&) ensure @mapping = previous end @@ -68,32 +74,32 @@ def runtime_field(field_name, definition) # @param [Symbol] method_name # @param [Proc] &block # @return [void] - def register_dynamic_properties_method(method_name, &block) - define_singleton_method(method_name, &block) if block_given? + def register_dynamic_properties_method(method_name, &) + define_singleton_method(method_name, &) if block_given? - methods = self.instance_variable_get('@dynamic_properties_methods') + methods = instance_variable_get(:@dynamic_properties_methods) methods.push(method_name).uniq - self.instance_variable_set('@dynamic_properties_methods', methods) + instance_variable_set(:@dynamic_properties_methods, methods) end # Serialize into hash. If any methods where registed using #register_dynamic_properties_method, - # arguments should be passed or use _dynamic_properties_methods_to_skip param - # - # @param [Array] _dynamic_properties_methods_to_skip + # arguments should be passed or use dynamic_properties_methods_to_skip param + # + # @param [Array] dynamic_properties_methods_to_skip # @param [Hash] dynamic_properties_methods_args where key is method name and value are the arguments # @return [Hash] serialized mappings - def to_hash(_dynamic_properties_methods_to_skip: [], **dynamic_properties_methods_args) - + def to_hash(dynamic_properties_methods_to_skip: [], **dynamic_properties_methods_args) # static properties mappings_hash = @options.merge(properties: @mapping, runtime: @runtime_fields) - #prevent pollution of @mapping since it is cached + # prevent pollution of @mapping since it is cached mappings_hash = Marshal.load(Marshal.dump(mappings_hash)) # support dynamic properties dynamic_properties_methods.each do |method_name| - next if _dynamic_properties_methods_to_skip.include?(method_name) - self.public_send(method_name, mappings_hash, *dynamic_properties_methods_args[method_name]) + next if dynamic_properties_methods_to_skip.include?(method_name) + + public_send(method_name, mappings_hash, *dynamic_properties_methods_args[method_name]) end mappings_hash @@ -109,24 +115,24 @@ def to_hash(_dynamic_properties_methods_to_skip: [], **dynamic_properties_method # # @return [Hash] flattened mappings def to_flattened_hash - datatypes = [ - 'binary', 'boolean', 'date', 'date_nanos', 'dense_vector', 'flattened', - 'geo_point', 'geo_shape', 'histogram', 'ip', 'join', 'keyword', 'nested', - 'long', 'integer', 'short' 'byte', 'double', 'float', 'half_float', 'scaled_float', 'unsigned_long', # numeric datatypes - 'object', 'percolator' 'point', 'range', 'rank_feature', 'rank_features', 'search_as_you_type', - 'shape', 'sparce_vector', 'text', 'token_count' 'version' + datatypes = %w[ + binary boolean date date_nanos dense_vector flattened + geo_point geo_shape histogram ip join keyword nested + long integer short byte double float half_float scaled_float unsigned_long + object percolator point range rank_feature rank_features search_as_you_type + shape sparce_vector text token_count version ] - mappings_hash = self.to_hash + mappings_hash = to_hash flattened = SuperHash::Utils.flatten_to_root(mappings_hash[:properties]) - + # remove key value pairs that are not a datatype, like mapping options - flattened = flattened.select{|k,v| datatypes.include?(v) } - + flattened = flattened.select { |_k, v| datatypes.include?(v) } + flattened.transform_keys do |k| # remove '.type' keyword from all keys - new_k = k.to_s.gsub(/\.type/, '') - + new_k = k.to_s.gsub('.type', '') + # remove object keywords new_k.gsub(/\.?properties|\.type|\.?fields/, '') end diff --git a/lib/elasticsearch_repositories/strategy/configuration/settings.rb b/lib/elasticsearch_repositories/strategy/configuration/settings.rb index de908c2..a8dffad 100644 --- a/lib/elasticsearch_repositories/strategy/configuration/settings.rb +++ b/lib/elasticsearch_repositories/strategy/configuration/settings.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module ElasticsearchRepositories module Strategy module Configuration @@ -8,7 +10,7 @@ class Settings attr_accessor :settings # @param [Hash] settings index settings - def initialize(settings={}) + def initialize(settings = {}) @settings = settings end @@ -17,7 +19,7 @@ def to_hash @settings end - alias_method :as_json, :to_hash + alias as_json to_hash end diff --git a/lib/elasticsearch_repositories/strategy/importing.rb b/lib/elasticsearch_repositories/strategy/importing.rb index 03ff449..70dd46c 100644 --- a/lib/elasticsearch_repositories/strategy/importing.rb +++ b/lib/elasticsearch_repositories/strategy/importing.rb @@ -1,6 +1,8 @@ +# frozen_string_literal: true + module ElasticsearchRepositories module Strategy - + # # This module contains all methods used by BaseStrategy regarding: # @@ -18,7 +20,7 @@ module Importing # @yieldparam [] query to import data from DB # @yieldparam [Hash] options passed while reindexing data # @yieldreturn [void] - def reload_indices_iterator(start_time = nil, end_time = nil) + def reload_indices_iterator(_start_time = nil, _end_time = nil) yield( where(''), { @@ -36,9 +38,9 @@ def reload_indices_iterator(start_time = nil, end_time = nil) # # @return [] def reindexing_includes_proc - proc {|query| query.where('') } + proc { |query| query.where('') } end - + end end end \ No newline at end of file diff --git a/lib/elasticsearch_repositories/strategy/indexing.rb b/lib/elasticsearch_repositories/strategy/indexing.rb index 9a858b9..a0643b6 100644 --- a/lib/elasticsearch_repositories/strategy/indexing.rb +++ b/lib/elasticsearch_repositories/strategy/indexing.rb @@ -1,6 +1,8 @@ +# frozen_string_literal: true + module ElasticsearchRepositories module Strategy - + # # This module contains all methods used by BaseStrategy regarding: # - indexing a document (update, create, delete) @@ -23,19 +25,19 @@ def index_record_to_es(action, record) id: record_id, options: { index: target_index_name(record), - id: record_id, + id: record_id } } else hash = { - mappings: mappings, - settings: settings, - index_without_id: index_without_id, + mappings:, + settings:, + index_without_id:, id: record_id, options: { index: target_index_name(record), body: as_indexed_json(record), - id: record_id, + id: record_id } } hash[:options].delete(:id) if index_without_id diff --git a/lib/elasticsearch_repositories/strategy/management.rb b/lib/elasticsearch_repositories/strategy/management.rb index df56b24..427ef31 100644 --- a/lib/elasticsearch_repositories/strategy/management.rb +++ b/lib/elasticsearch_repositories/strategy/management.rb @@ -1,6 +1,8 @@ +# frozen_string_literal: true + module ElasticsearchRepositories module Strategy - + # # This module contains all methods used by BaseStrategy regarding: # - creating the index @@ -26,14 +28,15 @@ def create_index( mappings: self.mappings.to_hash, **options ) - delete_index(index: index) if force + delete_index(index:) if force + + return if index_exists?(index:) - return if index_exists?(index: index) client.indices.create( - index: index, + index:, body: { - settings: settings, - mappings: mappings + settings:, + mappings: }, **options ) @@ -45,7 +48,7 @@ def create_index( # @param [Hash] options Elasticsearch::XPack::API::Indices::IndicesClient#delete options # @return [Hash, nil] nil if not found def delete_index(index: current_index_name, **options) - client.indices.delete(index: index, **options) + client.indices.delete(index:, **options) rescue Elastic::Transport::Transport::Errors::NotFound => e client.transport.logger&.debug("[!!!] Index #{index} does not exist (#{e.class})") end @@ -56,7 +59,7 @@ def delete_index(index: current_index_name, **options) # @param [Hash] options Elasticsearch::XPack::API::Indices::IndicesClient#exists options # @param [Boolean] true if exists def index_exists?(index: current_index_name, **options) - client.indices.exists(index: index, **options) + client.indices.exists(index:, **options) end # Performs the "refresh" operation for the index (useful e.g. in tests) @@ -64,8 +67,7 @@ def index_exists?(index: current_index_name, **options) # @param [Hash] options Elasticsearch::XPack::API::Indices::IndicesClient#refresh options # @return [Hash, nil] nil if not found def refresh_index(index: current_index_name, **options) - client.indices.refresh(index: index, **options) - + client.indices.refresh(index:, **options) rescue Elastic::Transport::Transport::Errors::NotFound => e client.transport.logger&.debug("[!!!] Index #{index} does not exist (#{e.class})") end @@ -76,7 +78,7 @@ def refresh_index(index: current_index_name, **options) # elastisearch's cat api and the search_index_name. Otherwise, # the reload_indices_iterator will be used # - # @param [Boolean] get_from_api + # @param [Boolean] get_from_api # @return [Array] def all_indices(*args, get_from_api: false) array = [] @@ -84,16 +86,16 @@ def all_indices(*args, get_from_api: false) if get_from_api begin client.cat.indices( - index: search_index_name, h: ['index', 'docs.count'] + index: search_index_name, h: %w[index] # 'docs.count' ).each_line do |line| - index, count = line.chomp.split("\s") + index = line.chomp.split("\s") array.push(index) if index.present? end rescue Elastic::Transport::Transport::Errors::NotFound => e client.transport.logger&.debug(e.message) end else - reload_indices_iterator(*args) do |import_db_query, iterator_options| + reload_indices_iterator(*args) do |_import_db_query, iterator_options| index = iterator_options[:index] array.push(index) if index end @@ -101,7 +103,7 @@ def all_indices(*args, get_from_api: false) array end - + end end diff --git a/lib/elasticsearch_repositories/strategy/searching.rb b/lib/elasticsearch_repositories/strategy/searching.rb index db578fc..3706196 100644 --- a/lib/elasticsearch_repositories/strategy/searching.rb +++ b/lib/elasticsearch_repositories/strategy/searching.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module ElasticsearchRepositories module Strategy @@ -8,8 +10,8 @@ module Strategy # module Searching - SCROLL_DURATION = '30s'.freeze - + SCROLL_DURATION = '30s' + # # Builds a search request and a response ready to make a request # @@ -29,13 +31,13 @@ def search(query_or_payload, options = {}) # @param [Hash] options to be passed to scroll API # @return [Array] def scroll(response, limit: nil, clear: true, **options, &block) - stop_proc = -> (response, index) { - if response.response.dig('hits', 'hits').empty? + stop_proc = ->(resp, index) { + if resp.response.dig('hits', 'hits').empty? true elsif limit.present? index > (limit - 1) else - block&.call(response, index) + block&.call(resp, index) end } @@ -48,12 +50,12 @@ def scroll(response, limit: nil, clear: true, **options, &block) current_response = response scroll_id = current_response.response['_scroll_id'] raise StandardError.new('response passed to scroll does not contain _scroll_id') unless scroll_id - + i = 1 # start scrolling loop loop do - raw_response = self.client.scroll( - body: { scroll_id: scroll_id }, + raw_response = client.scroll( + body: { scroll_id: }, **options ) @@ -61,7 +63,7 @@ def scroll(response, limit: nil, clear: true, **options, &block) # a +ElasticsearchRepositories::Response::Response+ so we have the same # methods as the 'search' method current_response = ElasticsearchRepositories::Response::Response.new(self, nil) - current_response.instance_variable_get('@cache')['response'] = raw_response + current_response.instance_variable_get(:@cache)['response'] = raw_response break if stop_proc.call(current_response, i) @@ -71,8 +73,8 @@ def scroll(response, limit: nil, clear: true, **options, &block) end if clear - self.client.clear_scroll( - body: { scroll_id: scroll_id } + client.clear_scroll( + body: { scroll_id: } ) rescue nil end @@ -85,14 +87,14 @@ def scroll(response, limit: nil, clear: true, **options, &block) # @param [Hash] search_options to be passed to +SearchRequest+ and +Response::Response+ # @param [Hash] scroll_options # @return [Array] - def search_and_scroll(query_or_payload, search_options = {}, scroll_options = {}, &block) + def search_and_scroll(query_or_payload, search_options = {}, scroll_options = {}, &) scroll_options[:scroll] = search_options[:scroll] ||= SCROLL_DURATION response = search(query_or_payload, search_options) - responses = scroll(response, **scroll_options, &block) + responses = scroll(response, **scroll_options, &) [response] + responses end - + end end diff --git a/lib/elasticsearch_repositories/strategy/serializing.rb b/lib/elasticsearch_repositories/strategy/serializing.rb index 43eb450..beaca28 100644 --- a/lib/elasticsearch_repositories/strategy/serializing.rb +++ b/lib/elasticsearch_repositories/strategy/serializing.rb @@ -1,6 +1,8 @@ +# frozen_string_literal: true + module ElasticsearchRepositories module Strategy - + # # This module contains all methods used by BaseStrategy regarding: # - serializing a record into an ES document @@ -8,12 +10,14 @@ module Strategy module Serializing # If true, id is autogenerated by ES + # @return [Boolean] def index_without_id false end + # @return [NilClass|String|Integer] def custom_doc_id(record); end - + # Serializes a record into a ES document # # @todo support different adapters? @@ -21,9 +25,9 @@ def custom_doc_id(record); end # @param [your_model_instance] record # @return [Hash] def as_indexed_json(record) - self.as_json #options.merge root: false + record.as_json # options.merge root: false end - + end end diff --git a/lib/elasticsearch_repositories/version.rb b/lib/elasticsearch_repositories/version.rb index d85ef2d..5a54066 100644 --- a/lib/elasticsearch_repositories/version.rb +++ b/lib/elasticsearch_repositories/version.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module ElasticsearchRepositories - VERSION = "0.11.1" + VERSION = '0.11.2' end