Skip to content

Commit

Permalink
Remove Chewy::Type, simplify DSL
Browse files Browse the repository at this point in the history
  • Loading branch information
Ivan Rabotyaga committed Apr 15, 2021
1 parent 5ba13f3 commit 5e3bc7f
Show file tree
Hide file tree
Showing 72 changed files with 1,230 additions and 1,841 deletions.
16 changes: 8 additions & 8 deletions .rubocop_todo.yml
Original file line number Diff line number Diff line change
Expand Up @@ -30,16 +30,16 @@ Lint/EmptyBlock:
- 'spec/chewy/search/scrolling_spec.rb'
- 'spec/chewy/strategy/atomic_spec.rb'
- 'spec/chewy/strategy_spec.rb'
- 'spec/chewy/type/import/bulk_request_spec.rb'
- 'spec/chewy/type/witchcraft_spec.rb'
- 'spec/chewy/index/import/bulk_request_spec.rb'
- 'spec/chewy/index/witchcraft_spec.rb'
- 'spec/chewy_spec.rb'

# Offense count: 3
Lint/MissingSuper:
Exclude:
- 'lib/chewy/strategy/atomic.rb'
- 'lib/chewy/type/adapter/object.rb'
- 'lib/chewy/type/adapter/orm.rb'
- 'lib/chewy/index/adapter/object.rb'
- 'lib/chewy/index/adapter/orm.rb'

# Offense count: 35
# Configuration parameters: IgnoredMethods, CountRepeatedAttributes.
Expand Down Expand Up @@ -85,8 +85,8 @@ Style/DocumentDynamicEvalDefinition:
- 'lib/chewy/index/actions.rb'
- 'lib/chewy/repository.rb'
- 'lib/chewy/search/pagination/kaminari.rb'
- 'lib/chewy/type/crutch.rb'
- 'lib/chewy/type/witchcraft.rb'
- 'lib/chewy/index/crutch.rb'
- 'lib/chewy/index/witchcraft.rb'

# Offense count: 58
Style/Documentation:
Expand Down Expand Up @@ -120,8 +120,8 @@ Style/IfUnlessModifier:
- 'lib/chewy/railtie.rb'
- 'lib/chewy/rspec/update_index.rb'
- 'lib/chewy/search/query_proxy.rb'
- 'lib/chewy/type/import.rb'
- 'lib/chewy/type/witchcraft.rb'
- 'lib/chewy/index/import.rb'
- 'lib/chewy/index/witchcraft.rb'
- 'spec/support/active_record.rb'

# Offense count: 53
Expand Down
73 changes: 14 additions & 59 deletions lib/chewy.rb
Original file line number Diff line number Diff line change
Expand Up @@ -44,88 +44,43 @@ def try_require(path)
require 'chewy/log_subscriber'
require 'chewy/strategy'
require 'chewy/index'
require 'chewy/type'
require 'chewy/fields/base'
require 'chewy/fields/root'
require 'chewy/journal'
require 'chewy/railtie' if defined?(::Rails::Railtie)

ActiveSupport.on_load(:active_record) do
extend Chewy::Type::Observe::ActiveRecordMethods
extend Chewy::Index::Observe::ActiveRecordMethods
end

module Chewy
@adapters = [
Chewy::Type::Adapter::ActiveRecord,
Chewy::Type::Adapter::Object
Chewy::Index::Adapter::ActiveRecord,
Chewy::Index::Adapter::Object
]

class << self
attr_accessor :adapters

# Derives a single type for the passed string identifier if possible.
# Derives an index for the passed string identifier if possible.
#
# @example
# Chewy.derive_types(UsersIndex::User) # => UsersIndex::User
# Chewy.derive_types('namespace/users') # => Namespace::UsersIndex::User
# Chewy.derive_types('places') # => raises Chewy::UnderivableType
# Chewy.derive_types('places#city') # => PlacesIndex::City
# Chewy.derive_name(UsersIndex) # => UsersIndex
# Chewy.derive_name('namespace/users') # => Namespace::UsersIndex
# Chewy.derive_name('missing') # => raises Chewy::UndefinedIndex
#
# @param name [String, Chewy::Type] string type identifier
# @raise [Chewy::UnderivableType] in cases when it is impossble to find index or type or more than one type found
# @return [Chewy::Type] an array of derived types
def derive_type(name)
return name if name.is_a?(Class) && name < Chewy::Type

types = derive_types(name)
unless types.one?
raise Chewy::UnderivableType,
"Index `#{types.first.index}` has more than one type, please specify type via `#{types.first.index.derivable_name}#type_name`"
end

types.first
end

# Derives all the types for the passed string identifier if possible.
#
# @example
# Chewy.derive_types('namespace/users') # => [Namespace::UsersIndex::User]
# Chewy.derive_types('places') # => [PlacesIndex::City, PlacesIndex::Country]
# Chewy.derive_types('places#city') # => [PlacesIndex::City]
#
# @param from [String] string type identifier
# @raise [Chewy::UnderivableType] in cases when it is impossible to find index or type
# @return [Array<Chewy::Type>] an array of derived types
def derive_types(from)
return from.types if from.is_a?(Class) && (from < Chewy::Index || from < Chewy::Type)
# @param index_name [String, Chewy::Index] index identifier or class
# @raise [Chewy::UndefinedIndex] in cases when it is impossible to find index
# @return [Chewy::Index]
def derive_name(index_name)
return index_name if index_name.is_a?(Class) && index_name < Chewy::Index

index_name, type_name = from.split('#', 2)
class_name = "#{index_name.camelize.gsub(/Index\z/, '')}Index"
index = class_name.safe_constantize
raise Chewy::UnderivableType, "Can not find index named `#{class_name}`" unless index && index < Chewy::Index

if type_name.present?
type = index.type_hash[type_name] or raise Chewy::UnderivableType,
"Index `#{class_name}` doesn`t have type named `#{type_name}`"
[type]
else
index.types
end
end

# Creates Chewy::Type ancestor defining index and adapter methods.
#
def create_type(index, target, options = {}, &block)
type = Class.new(Chewy::Type)

adapter = adapters.find { |klass| klass.accepts?(target) }.new(target, **options)

index.const_set(adapter.name, type)
type.send(:define_singleton_method, :index) { index }
type.send(:define_singleton_method, :adapter) { adapter }
return index if index && index < Chewy::Index

type.class_eval(&block) if block
type
raise Chewy::UndefinedIndex, "Can not find index named `#{class_name}`"
end

# Main elasticsearch-ruby client instance
Expand Down
93 changes: 60 additions & 33 deletions lib/chewy/index.rb
Original file line number Diff line number Diff line change
@@ -1,23 +1,48 @@
require 'chewy/search'
require 'chewy/index/actions'
require 'chewy/index/adapter/active_record'
require 'chewy/index/adapter/object'
require 'chewy/index/aliases'
require 'chewy/index/crutch'
require 'chewy/index/import'
require 'chewy/index/mapping'
require 'chewy/index/observe'
require 'chewy/index/settings'
require 'chewy/index/specification'
require 'chewy/index/syncer'
require 'chewy/index/witchcraft'
require 'chewy/index/wrapper'

module Chewy
class Index
IMPORT_OPTIONS_KEYS = %i[
batch_size bulk_size consistency direct_import journal
pipeline raw_import refresh replication
].freeze

include Search
include Actions
include Aliases
include Import
include Mapping
include Observe
include Crutch
include Witchcraft
include Wrapper

singleton_class.delegate :client, to: 'Chewy'

class_attribute :type_hash
self.type_hash = {}
class_attribute :adapter
self.adapter = Chewy::Index::Adapter::Object.new(:default)

class_attribute :index_scope_defined

class_attribute :_settings
self._settings = Chewy::Index::Settings.new

class_attribute :_default_import_options
self._default_import_options = {}

class << self
# @overload index_name(suggest)
# If suggested name is passed, it is set up as the new base name for
Expand Down Expand Up @@ -113,70 +138,67 @@ def prefix
Chewy.configuration[:prefix]
end

# Defines type for the index. Arguments depends on adapter used. For
# Defines scope and options for the index. Arguments depends on adapter used. For
# ActiveRecord you can pass model or scope and options
#
# class CarsIndex < Chewy::Index
# define_type Car do
# ...
# end # defines VehiclesIndex::Car type
# index_scope Car
# ...
# end
#
# Type name might be passed in complicated cases:
#
# class VehiclesIndex < Chewy::Index
# define_type Vehicle.cars.includes(:manufacturer), name: 'cars' do
# ...
# end # defines VehiclesIndex::Cars type
# class CarsIndex < Chewy::Index
# index_scope Vehicle.cars.includes(:manufacturer), name: 'cars'
# ...
# end
#
# define_type Vehicle.motocycles.includes(:manufacturer), name: 'motocycles' do
# ...
# end # defines VehiclesIndex::Motocycles type
# class MotorcyclesIndex < Chewy::Index
# index_scope Vehicle.motorcycles.includes(:manufacturer), name: 'motorcycles'
# ...
# end
#
# For plain objects:
# For plain objects you can completely omit this directive, unless you need to specify some options:
#
# class PlanesIndex < Chewy::Index
# define_type :plane do
# ...
# end # defines PlanesIndex::Plane type
# ...
# end
#
# The main difference between using plain objects or ActiveRecord models for indexing
# is import. If you will call `CarsIndex::Car.import` - it will import all the cars
# automatically, while `PlanesIndex::Plane.import(my_planes)` requires import data to be
# is import. If you will call `CarsIndex.import` - it will import all the cars
# automatically, while `PlanesIndex.import(my_planes)` requires import data to be
# passed.
#
def define_type(target, options = {}, &block)
raise 'Multiple types are deprecated' if type_hash.present?
def index_scope(target, options = {})
raise 'Index scope is already defined' if index_scope_defined?

type_class = Chewy.create_type(self, target, options, &block)
self.type_hash = type_hash.merge(type_class.type_name => type_class)
self.adapter = Chewy.adapters.find { |klass| klass.accepts?(target) }.new(target, **options)
self.index_scope_defined = true
end

# Returns defined type:
#
# UsersIndex.types # => [UsersIndex::User]
#
def types
type_hash.values
end
# def types
# type_hash.values
# end

# Returns defined types names:
#
# UsersIndex.type_names # => ['admin', 'manager', 'user']
#
def type_names
type_hash.keys
end
# def type_names
# type_hash.keys
# end

# Returns named type:
#
# UserIndex.type('admin') # => UsersIndex::Admin
#
def type(type_name)
type_hash.fetch(type_name) { raise UndefinedType, "Unknown type in #{name}: #{type_name}" }
end
# def type(type_name)
# type_hash.fetch(type_name) { raise UndefinedType, "Unknown type in #{name}: #{type_name}" }
# end

# Used as a part of index definition DSL. Defines settings:
#
Expand Down Expand Up @@ -212,7 +234,7 @@ def settings_hash
end

def mappings_hash
mappings = types.map(&:mappings_hash).inject(:merge)
mappings = root.mappings_hash
mappings.present? ? {mappings: mappings} : {}
end

Expand All @@ -230,6 +252,11 @@ def specification_hash
def specification
@specification ||= Specification.new(self)
end

def default_import_options(params)
params.assert_valid_keys(IMPORT_OPTIONS_KEYS)
self._default_import_options = _default_import_options.merge(params)
end
end
end
end
49 changes: 17 additions & 32 deletions lib/chewy/index/actions.rb
Original file line number Diff line number Diff line change
Expand Up @@ -129,37 +129,6 @@ def purge!(suffix = nil)
create! suffix
end

# Perform import operation for every defined type
#
# UsersIndex.import # imports default data for every index type
# UsersIndex.import user: User.active # imports specified objects for user type and default data for other types
# UsersIndex.import refresh: false # to disable index refreshing after import
# UsersIndex.import suffix: Time.now.to_i # imports data to index with specified suffix if such is exists
# UsersIndex.import batch_size: 300 # import batch size
#
# See [import.rb](lib/chewy/type/import.rb) for more details.
#
%i[import import!].each do |method|
class_eval <<-METHOD, __FILE__, __LINE__ + 1
def #{method}(*args)
return true if args.first.blank? && !args.first.nil?
options = args.extract_options!
if args.one? && type_names.one?
objects = {type_names.first.to_sym => args.first}
elsif args.one?
fail ArgumentError, "Please pass objects for `#{method}` as a hash with type names"
else
objects = options.reject { |k, v| !type_names.map(&:to_sym).include?(k) }
end
types.map do |type|
args = [objects[type.type_name.to_sym], options.dup].reject(&:blank?)
type.#{method} *args
end.all?
end
METHOD
end

# Deletes, creates and imports data to the index. Returns the
# import result. If index name suffix is passed as the first
# argument - performs zero-downtime index resetting.
Expand Down Expand Up @@ -213,6 +182,7 @@ def reset!(suffix = nil, apply_journal: true, journal: false, **import_options)
specification.lock!
result
end
alias_method :reset, :reset!

# A {Chewy::Journal} instance for the particular index
#
Expand Down Expand Up @@ -243,13 +213,28 @@ def reindex(source: index_name, dest: index_name)
# @example
# Chewy.client.update_mapping('cities', {properties: {new_field: {type: :text}}})
#
def update_mapping(name = index_name, body = mappings_hash)
def update_mapping(name = index_name, body = root.mappings_hash)
client.indices.put_mapping(
index: name,
body: body
)['acknowledged']
end

# Performs missing and outdated objects synchronization for the current index.
#
# @example
# UsersIndex.sync
#
# @see Chewy::Index::Syncer
# @param parallel [true, Integer, Hash] options for parallel execution or the number of processes
# @return [Hash{Symbol, Object}, nil] a number of missing and outdated documents re-indexed and their ids,
# nil in case of errors
def sync(parallel: nil)
syncer = Syncer.new(self, parallel: parallel)
count = syncer.perform
{count: count, missing: syncer.missing_ids, outdated: syncer.outdated_ids} if count
end

private

def optimize_index_settings(index_name)
Expand Down
Loading

0 comments on commit 5e3bc7f

Please sign in to comment.