diff --git a/.rubocop_todo.yml b/.rubocop_todo.yml index 2b1cccd91..de4efe5b1 100644 --- a/.rubocop_todo.yml +++ b/.rubocop_todo.yml @@ -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. @@ -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: @@ -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 diff --git a/CHANGELOG.md b/CHANGELOG.md index 20354f0eb..03a9bb8b2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,21 @@ ### Changes + * [#783](https://github.com/toptal/chewy/pull/783): **(Breaking)** Remove `Chewy::Type`, simplify DSL ([@rabotyaga][]) + * Remove the `Chewy::Type` class + * e.g. remove `CitiesIndex::City` / `CitiesIndex.city` + * `CitiesIndex::City.import! ...` becomes `CitiesIndex.import! ...` + * Simplify index DSL: + * `define_type` block -> `index_scope` clause + * it can be omitted completely, if you don't need to specify the scope or options, e.g. `name` + * Remove type names from string representations: + * in `update_index` ActiveRecord helper and RSpec matcher, e.g. + * `update_index('cities#city')` -> `update_index('cities')` + * `update_index(UsersIndex::User)` -> `update_index(UsersIndex)` + * in rake tasks (e.g. `rake chewy:update[cities#city]` -> `rake chewy:update[cities]`) + * in rake tasks output (e.g. `Imported CitiesIndex::City in 1s, stats: index 3` -> `Imported CitiesIndex in 1s, stats: index 3`) + * Use index name instead of type name in loader additional scope + * e.g. `CitiesIndex.filter(...).load(city: {scope: City.where(...)})` -> `CitiesIndex.filter(...).load(cities: {scope: City.where(...)})` * [#692](https://github.com/toptal/chewy/issues/692): Add `.update_mapping` to Index class ([@Vitalina-Vakulchyk][]): * Wrapped Elasticsearch gem `.put_mapping` with `.update_mapping` in Index class * Add `rake chewy:update_mapping` task diff --git a/README.md b/README.md index f89d059b5..de8337969 100644 --- a/README.md +++ b/README.md @@ -109,11 +109,10 @@ class UsersIndex < Chewy::Index } } - define_type User do - field :first_name - field :last_name - field :email, analyzer: 'email' - end + index_scope User + field :first_name + field :last_name + field :email, analyzer: 'email' end ``` @@ -130,7 +129,7 @@ Add `update_index` to app/models/user.rb: ```ruby class User < ApplicationRecord - update_index('users#user') { self } + update_index('users') { self } end ``` @@ -145,7 +144,7 @@ User.create( email: 'test1@example.com', # other fields ) -# UsersIndex::User Import (355.3ms) {:index=>1} +# UsersIndex Import (355.3ms) {:index=>1} # => # ``` @@ -268,41 +267,38 @@ Chewy.settings = { end ``` -2. Add one or more types mapping +2. Define index scope (you can omit this part if you don't need to specify a scope (i.e. use PORO objects for import) or options) ```ruby class UsersIndex < Chewy::Index - define_type User.active # or just model instead_of scope: define_type User + index_scope User.active # or just model instead_of scope: index_scope User end ``` - Newly-defined index type class is accessible via `UsersIndex.user` or `UsersIndex::User` - -3. Add some type mappings +3. Add some mappings ```ruby class UsersIndex < Chewy::Index - define_type User.active.includes(:country, :badges, :projects) do - field :first_name, :last_name # multiple fields without additional options - field :email, analyzer: 'email' # Elasticsearch-related options - field :country, value: ->(user) { user.country.name } # custom value proc - field :badges, value: ->(user) { user.badges.map(&:name) } # passing array values to index - field :projects do # the same block syntax for multi_field, if `:type` is specified - field :title - field :description # default data type is `text` - # additional top-level objects passed to value proc: - field :categories, value: ->(project, user) { project.categories.map(&:name) if user.active? } - end - field :rating, type: 'integer' # custom data type - field :created, type: 'date', include_in_all: false, - value: ->{ created_at } # value proc for source object context + index_scope User.active.includes(:country, :badges, :projects) + field :first_name, :last_name # multiple fields without additional options + field :email, analyzer: 'email' # Elasticsearch-related options + field :country, value: ->(user) { user.country.name } # custom value proc + field :badges, value: ->(user) { user.badges.map(&:name) } # passing array values to index + field :projects do # the same block syntax for multi_field, if `:type` is specified + field :title + field :description # default data type is `text` + # additional top-level objects passed to value proc: + field :categories, value: ->(project, user) { project.categories.map(&:name) if user.active? } end + field :rating, type: 'integer' # custom data type + field :created, type: 'date', include_in_all: false, + value: ->{ created_at } # value proc for source object context end ``` [See here for mapping definitions](https://www.elastic.co/guide/en/elasticsearch/reference/current/mapping.html). -4. Add some index- and type-related settings. Analyzer repositories might be used as well. See `Chewy::Index.settings` docs for details: +4. Add some index-related settings. Analyzer repositories might be used as well. See `Chewy::Index.settings` docs for details: ```ruby class UsersIndex < Chewy::Index @@ -315,23 +311,22 @@ Chewy.settings = { } } - define_type User.active.includes(:country, :badges, :projects) do - root date_detection: false do - template 'about_translations.*', type: 'text', analyzer: 'standard' - - field :first_name, :last_name - field :email, analyzer: 'email' - field :country, value: ->(user) { user.country.name } - field :badges, value: ->(user) { user.badges.map(&:name) } - field :projects do - field :title - field :description - end - field :about_translations, type: 'object' # pass object type explicitly if necessary - field :rating, type: 'integer' - field :created, type: 'date', include_in_all: false, - value: ->{ created_at } + index_scope User.active.includes(:country, :badges, :projects) + root date_detection: false do + template 'about_translations.*', type: 'text', analyzer: 'standard' + + field :first_name, :last_name + field :email, analyzer: 'email' + field :country, value: ->(user) { user.country.name } + field :badges, value: ->(user) { user.badges.map(&:name) } + field :projects do + field :title + field :description end + field :about_translations, type: 'object' # pass object type explicitly if necessary + field :rating, type: 'integer' + field :created, type: 'date', include_in_all: false, + value: ->{ created_at } end end ``` @@ -345,39 +340,32 @@ Chewy.settings = { ```ruby class User < ActiveRecord::Base - update_index('users#user') { self } # specifying index, type and back-reference + update_index('users') { self } # specifying index and back-reference # for updating after user save or destroy end class Country < ActiveRecord::Base has_many :users - update_index('users#user') { users } # return single object or collection + update_index('users') { users } # return single object or collection end class Project < ActiveRecord::Base - update_index('users#user') { user if user.active? } # you can return even `nil` from the back-reference - end - - class Badge < ActiveRecord::Base - has_and_belongs_to_many :users - - update_index('users') { users } # if index has only one type - # there is no need to specify updated type + update_index('users') { user if user.active? } # you can return even `nil` from the back-reference end class Book < ActiveRecord::Base - update_index(->(book) {"books#book_#{book.language}"}) { self } # dynamic index and type with proc. - # For book with language == "en" - # this code will generate `books#book_en` + update_index(->(book) {"books_#{book.language}"}) { self } # dynamic index name with proc. + # For book with language == "en" + # this code will generate `books_en` end ``` Also, you can use the second argument for method name passing: ```ruby - update_index('users#user', :self) - update_index('users#user', :users) + update_index('users', :self) + update_index('users', :users) ``` In the case of a belongs_to association you may need to update both associated objects, previous and current: @@ -386,29 +374,28 @@ Chewy.settings = { class City < ActiveRecord::Base belongs_to :country - update_index('cities#city') { self } - update_index 'countries#country' do + update_index('cities') { self } + update_index 'countries' do previous_changes['country_id'] || country end end ``` -### Type default import options +### Default import options -Every type has `default_import_options` configuration to specify, suddenly, default import options: +Every index has `default_import_options` configuration to specify, suddenly, default import options: ```ruby class ProductsIndex < Chewy::Index - define_type Post.includes(:tags) do - default_import_options batch_size: 100, bulk_size: 10.megabytes, refresh: false + index_scope Post.includes(:tags) + default_import_options batch_size: 100, bulk_size: 10.megabytes, refresh: false - field :name - field :tags, value: -> { tags.map(&:name) } - end + field :name + field :tags, value: -> { tags.map(&:name) } end ``` -See [import.rb](lib/chewy/type/import.rb) for available options. +See [import.rb](lib/chewy/index/import.rb) for available options. ### Multi (nested) and object field types @@ -459,10 +446,9 @@ Assume you are defining your index like this (product has_many categories throug ```ruby class ProductsIndex < Chewy::Index - define_type Product.includes(:categories) do - field :name - field :category_names, value: ->(product) { product.categories.map(&:name) } # or shorter just -> { categories.map(&:name) } - end + index_scope Product.includes(:categories) + field :name + field :category_names, value: ->(product) { product.categories.map(&:name) } # or shorter just -> { categories.map(&:name) } end ``` @@ -482,20 +468,19 @@ If you meet complicated cases when associations are not applicable you can repla ```ruby class ProductsIndex < Chewy::Index - define_type Product do - crutch :categories do |collection| # collection here is a current batch of products - # data is fetched with a lightweight query without objects initialization - data = ProductCategory.joins(:category).where(product_id: collection.map(&:id)).pluck(:product_id, 'categories.name') - # then we have to convert fetched data to appropriate format - # this will return our data in structure like: - # {123 => ['sweets', 'juices'], 456 => ['meat']} - data.each.with_object({}) { |(id, name), result| (result[id] ||= []).push(name) } - end - - field :name - # simply use crutch-fetched data as a value: - field :category_names, value: ->(product, crutches) { crutches.categories[product.id] } + index_scope Product + crutch :categories do |collection| # collection here is a current batch of products + # data is fetched with a lightweight query without objects initialization + data = ProductCategory.joins(:category).where(product_id: collection.map(&:id)).pluck(:product_id, 'categories.name') + # then we have to convert fetched data to appropriate format + # this will return our data in structure like: + # {123 => ['sweets', 'juices'], 456 => ['meat']} + data.each.with_object({}) { |(id, name), result| (result[id] ||= []).push(name) } end + + field :name + # simply use crutch-fetched data as a value: + field :category_names, value: ->(product, crutches) { crutches.categories[product.id] } end ``` @@ -517,22 +502,21 @@ So Chewy Crutches™ technology is able to increase your indexing performance in ### Witchcraft™ technology -One more experimental technology to increase import performance. As far as you know, chewy defines value proc for every imported field in mapping, so at the import time each of this procs is executed on imported object to extract result document to import. It would be great for performance to use one huge whole-document-returning proc instead. So basically the idea or Witchcraft™ technology is to compile a single document-returning proc from the type definition. +One more experimental technology to increase import performance. As far as you know, chewy defines value proc for every imported field in mapping, so at the import time each of this procs is executed on imported object to extract result document to import. It would be great for performance to use one huge whole-document-returning proc instead. So basically the idea or Witchcraft™ technology is to compile a single document-returning proc from the index definition. ```ruby -define_type Product do - witchcraft! - - field :title - field :tags, value: -> { tags.map(&:name) } - field :categories do - field :name, value: -> (product, category) { category.name } - field :type, value: -> (product, category, crutch) { crutch.types[category.name] } - end +index_scope Product +witchcraft! + +field :title +field :tags, value: -> { tags.map(&:name) } +field :categories do + field :name, value: -> (product, category) { category.name } + field :type, value: -> (product, category, crutch) { crutch.types[category.name] } end ``` -The type definition above will be compiled to something close to: +The index definition above will be compiled to something close to: ```ruby -> (object, crutches) do @@ -562,7 +546,7 @@ Obviously not every type of definition might be compiled. There are some restric end ``` -However, it is quite possible that your type definition will be supported by Witchcraft™ technology out of the box in the most of the cases. +However, it is quite possible that your index definition will be supported by Witchcraft™ technology out of the box in the most of the cases. ### Raw Import @@ -589,13 +573,12 @@ class LightweightProduct end end -define_type Product do - default_import_options raw_import: ->(hash) { - LightweightProduct.new(hash) - } +index_scope Product +default_import_options raw_import: ->(hash) { + LightweightProduct.new(hash) +} - field :created_at, 'datetime' -end +field :created_at, 'datetime' ``` Also, you can pass `:raw_import` option to the `import` method explicitly. @@ -611,14 +594,13 @@ To do so you need to set `skip_index_creation_on_import` parameter to `false` in You can use `ignore_blank: true` to skip fields that return `true` for the `.blank?` method: ```ruby -define_type Country do +index_scope Country +field :id +field :cities, ignore_blank: true do field :id - field :cities, ignore_blank: true do - field :id - field :name - field :surname, ignore_blank: true - field :description - end + field :name + field :surname, ignore_blank: true + field :description end ``` @@ -638,7 +620,6 @@ Common journal record looks like this: "action": "index", "object_id": [1, 2, 3], "index_name": "...", - "type_name": "...", "created_at": "" } ``` @@ -664,9 +645,8 @@ Or as a default import option for an index: ```ruby class CityIndex - define_type City do - default_import_options journal: true - end + index_scope City + default_import_options journal: true end ``` @@ -674,19 +654,6 @@ You may be wondering why do you need it? The answer is simple: not to lose the d Imagine that you reset your index in a zero-downtime manner (to separate index), and at the meantime somebody keeps updating the data frequently (to old index). So all these actions will be written to the journal index and you'll be able to apply them after index reset using the `Chewy::Journal` interface. -### Types access - -You can access index-defined type with the following API: - -```ruby -UsersIndex::User # => UsersIndex::User -UsersIndex.type_hash['user'] # => UsersIndex::User -UsersIndex.type('user') # => UsersIndex::User -UsersIndex.type('foo') # => raises error UndefinedType("Unknown type in UsersIndex: foo") -UsersIndex.types # => [UsersIndex::User] -UsersIndex.type_names # => ['user'] -``` - ### Index manipulation ```ruby @@ -699,24 +666,21 @@ UsersIndex.create! # use bang or non-bang methods UsersIndex.purge UsersIndex.purge! # deletes then creates index -UsersIndex::User.import # import with 0 arguments process all the data specified in type definition - # literally, User.active.includes(:country, :badges, :projects).find_in_batches -UsersIndex::User.import User.where('rating > 100') # or import specified users scope -UsersIndex::User.import User.where('rating > 100').to_a # or import specified users array -UsersIndex::User.import [1, 2, 42] # pass even ids for import, it will be handled in the most effective way -UsersIndex::User.import User.where('rating > 100'), update_fields: [:email] # if update fields are specified - it will update their values only with the `update` bulk action +UsersIndex.import # import with 0 arguments process all the data specified in index_scope definition +UsersIndex.import User.where('rating > 100') # or import specified users scope +UsersIndex.import User.where('rating > 100').to_a # or import specified users array +UsersIndex.import [1, 2, 42] # pass even ids for import, it will be handled in the most effective way +UsersIndex.import User.where('rating > 100'), update_fields: [:email] # if update fields are specified - it will update their values only with the `update` bulk action -UsersIndex.import # same as UsersIndex::User.import -UsersIndex.import user: User.where('rating > 100') # import only specified users UsersIndex.reset! # purges index and imports default data for all types ``` -If the passed user is `#destroyed?`, or satisfies a `delete_if` type option, or the specified id does not exist in the database, import will perform delete from index action for this object. +If the passed user is `#destroyed?`, or satisfies a `delete_if` index_scope option, or the specified id does not exist in the database, import will perform delete from index action for this object. ```ruby -define_type User, delete_if: :deleted_at -define_type User, delete_if: -> { deleted_at } -define_type User, delete_if: ->(user) { user.deleted_at } +index_scope User, delete_if: :deleted_at +index_scope User, delete_if: -> { deleted_at } +index_scope User, delete_if: ->(user) { user.deleted_at } ``` See [actions.rb](lib/chewy/index/actions.rb) for more details. @@ -727,13 +691,12 @@ Assume you've got the following code: ```ruby class City < ActiveRecord::Base - update_index 'cities#city', :self + update_index 'cities', :self end class CitiesIndex < Chewy::Index - define_type City do - field :name - end + index_scope City + field :name end ``` @@ -875,7 +838,7 @@ Chewy has notifying the following events: #### `import_objects.chewy` payload - * `payload[:type]`: currently imported type + * `payload[:index]`: currently imported index name * `payload[:import]`: imports stats, total imported and deleted objects count: ```ruby @@ -979,14 +942,13 @@ Quick introduction. #### Composing requests -The request DSL have the same chainable nature as AR. The main class is `Chewy::Search::Request`. It is possible to perform requests on behalf of indices or types: +The request DSL have the same chainable nature as AR. The main class is `Chewy::Search::Request`. ```ruby -CitiesIndex.query(match: {name: 'London'}) # or -CitiesIndex::City.query(match: {name: 'London'}) +CitiesIndex.query(match: {name: 'London'}) ``` -Main methods of the request DSL are: `query`, `filter` and `post_filter`, it is possible to pass pure query hashes or use `elasticsearch-dsl`. Also, there is an additional +Main methods of the request DSL are: `query`, `filter` and `post_filter`, it is possible to pass pure query hashes or use `elasticsearch-dsl`. ```ruby CitiesIndex @@ -1034,7 +996,7 @@ See [Chewy::Search::Scrolling](lib/chewy/search/scrolling.rb) for details. It is possible to load ORM/ODM source objects with the `objects` method. To provide additional loading options use `load` method: ```ruby -CitiesIndex.load(scope: -> { active }).to_a # to_a returns `Chewy::Type` wrappers. +CitiesIndex.load(scope: -> { active }).to_a # to_a returns `Chewy::Index` wrappers. CitiesIndex.load(scope: -> { active }).objects # An array of AR source objects. ``` @@ -1084,35 +1046,33 @@ rake chewy:upgrade[-users,cities] # upgrades every index in the application exce It doesn't create indexes, it simply imports everything to the existing ones and fails if the index was not created before. -Unlike `reset` or `upgrade` tasks, it is possible to pass type references to update the particular type. In index name is passed without the type specified, it will update all the types defined for this index. - ```bash rake chewy:update # updates all the existing indices rake chewy:update[users] # updates UsersIndex only -rake chewy:update[users,cities#city] # updates UsersIndex and CitiesIndex (if City type is defined on CitiesIndex) -rake chewy:update[-users,cities#city] # updates every index in the application except UsersIndex and CitiesIndex::City +rake chewy:update[users,cities] # updates UsersIndex and CitiesIndex +rake chewy:update[-users,cities] # updates every index in the application except UsersIndex and CitiesIndex ``` #### `chewy:sync` -Provides a way to synchronize outdated indexes with the source quickly and without doing a full reset. By default field `updated_at` is used to find outdated records, but this could be customized by `outdated_sync_field` as described at [Chewy::Type::Syncer](lib/chewy/type/syncer.rb). +Provides a way to synchronize outdated indexes with the source quickly and without doing a full reset. By default field `updated_at` is used to find outdated records, but this could be customized by `outdated_sync_field` as described at [Chewy::Index::Syncer](lib/chewy/index/syncer.rb). -Arguments are similar to the ones taken by `chewy:update` task. It is possible to specify a particular type or a whole index. +Arguments are similar to the ones taken by `chewy:update` task. -See [Chewy::Type::Syncer](lib/chewy/type/syncer.rb) for more details. +See [Chewy::Index::Syncer](lib/chewy/index/syncer.rb) for more details. ```bash rake chewy:sync # synchronizes all the existing indices rake chewy:sync[users] # synchronizes UsersIndex only -rake chewy:sync[users,cities#city] # synchronizes UsersIndex and CitiesIndex (if City type is defined on CitiesIndex) -rake chewy:sync[-users,cities#city] # synchronizes every index in the application except except UsersIndex and CitiesIndex::City +rake chewy:sync[users,cities] # synchronizes UsersIndex and CitiesIndex +rake chewy:sync[-users,cities] # synchronizes every index in the application except except UsersIndex and CitiesIndex ``` #### `chewy:deploy` This rake task is especially useful during the production deploy. It is a combination of `chewy:upgrade` and `chewy:sync` and the latter is called only for the indexes that were not reset during the first stage. -It is not possible to specify any particular types/indexes for this task as it doesn't make much sense. +It is not possible to specify any particular indexes for this task as it doesn't make much sense. Right now the approach is that if some data had been updated, but index definition was not changed (no changes satisfying the synchronization algorithm were done), it would be much faster to perform manual partial index update inside data migrations or even manually after the deploy. @@ -1129,14 +1089,14 @@ If the number of processes is not specified explicitly - `parallel` gem tries to ```bash rake chewy:parallel:reset rake chewy:parallel:upgrade[4] -rake chewy:parallel:update[4,cities#city] +rake chewy:parallel:update[4,cities] rake chewy:parallel:sync[4,-users] rake chewy:parallel:deploy[4] # performs parallel upgrade and parallel sync afterwards ``` #### `chewy:journal` -This namespace contains two tasks for the journal manipulations: `chewy:journal:apply` and `chewy:journal:clean`. Both are taking time as the first argument (optional for clean) and a list of indexes/types exactly as the tasks above. Time can be in any format parsable by ActiveSupport. +This namespace contains two tasks for the journal manipulations: `chewy:journal:apply` and `chewy:journal:clean`. Both are taking time as the first argument (optional for clean) and a list of indexes exactly as the tasks above. Time can be in any format parsable by ActiveSupport. ```bash rake chewy:journal:apply["$(date -v-1H -u +%FT%TZ)"] # apply journaled changes for the past hour @@ -1173,7 +1133,7 @@ Chewy.use_after_commit_callbacks = !Rails.env.test? 5. Push to the branch (`git push origin my-new-feature`) 6. Create new Pull Request -Use the following Rake tasks to control the Elasticsearch cluster while developing. +Use the following Rake tasks to control the Elasticsearch cluster while developing, if you prefer native Elasticsearch installation over the dockerized one: ```bash rake elasticsearch:start # start Elasticsearch cluster on 9250 port for tests diff --git a/lib/chewy.rb b/lib/chewy.rb index 97fcc3bd9..8aeb123ab 100644 --- a/lib/chewy.rb +++ b/lib/chewy.rb @@ -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] 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 diff --git a/lib/chewy/errors.rb b/lib/chewy/errors.rb index 8c29fa88f..870caf049 100644 --- a/lib/chewy/errors.rb +++ b/lib/chewy/errors.rb @@ -5,12 +5,6 @@ class Error < StandardError class UndefinedIndex < Error end - class UndefinedType < Error - end - - class UnderivableType < Error - end - class UndefinedUpdateStrategy < Error def initialize(_type) super <<-MESSAGE @@ -36,10 +30,4 @@ def initialize(type, import_errors) super message end end - - class RemovedFeature < Error - end - - class PluginMissing < Error - end end diff --git a/lib/chewy/index.rb b/lib/chewy/index.rb index 3a645db19..cdb3fa055 100644 --- a/lib/chewy/index.rb +++ b/lib/chewy/index.rb @@ -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 @@ -113,69 +138,30 @@ 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 - # - # define_type Vehicle.motocycles.includes(:manufacturer), name: 'motocycles' do - # ... - # end # defines VehiclesIndex::Motocycles type - # 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? - - type_class = Chewy.create_type(self, target, options, &block) - self.type_hash = type_hash.merge(type_class.type_name => type_class) - end + def index_scope(target, options = {}) + raise 'Index scope is already defined' if index_scope_defined? - # Returns defined type: - # - # UsersIndex.types # => [UsersIndex::User] - # - def types - type_hash.values - end - - # Returns defined types names: - # - # UsersIndex.type_names # => ['admin', 'manager', 'user'] - # - 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}" } + self.adapter = Chewy.adapters.find { |klass| klass.accepts?(target) }.new(target, **options) + self.index_scope_defined = true end # Used as a part of index definition DSL. Defines settings: @@ -212,7 +198,7 @@ def settings_hash end def mappings_hash - mappings = types.map(&:mappings_hash).inject(:merge) + mappings = root.mappings_hash mappings.present? ? {mappings: mappings} : {} end @@ -230,6 +216,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 diff --git a/lib/chewy/index/actions.rb b/lib/chewy/index/actions.rb index a7bcc517c..c1f434d77 100644 --- a/lib/chewy/index/actions.rb +++ b/lib/chewy/index/actions.rb @@ -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. @@ -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 # @@ -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) diff --git a/lib/chewy/type/adapter/active_record.rb b/lib/chewy/index/adapter/active_record.rb similarity index 98% rename from lib/chewy/type/adapter/active_record.rb rename to lib/chewy/index/adapter/active_record.rb index 371eeb05b..7526e2f4a 100644 --- a/lib/chewy/type/adapter/active_record.rb +++ b/lib/chewy/index/adapter/active_record.rb @@ -1,7 +1,7 @@ -require 'chewy/type/adapter/orm' +require 'chewy/index/adapter/orm' module Chewy - class Type + class Index module Adapter class ActiveRecord < Orm def self.accepts?(target) diff --git a/lib/chewy/type/adapter/base.rb b/lib/chewy/index/adapter/base.rb similarity index 95% rename from lib/chewy/type/adapter/base.rb rename to lib/chewy/index/adapter/base.rb index 5076a3f44..eefb6e241 100644 --- a/lib/chewy/type/adapter/base.rb +++ b/lib/chewy/index/adapter/base.rb @@ -1,5 +1,5 @@ module Chewy - class Type + class Index module Adapter # Basic adapter class. Contains interface, need to implement to add any classes support class Base @@ -13,8 +13,7 @@ def self.accepts?(_target) true end - # Camelcased name, used as type class constant name. - # For returned value 'Product' will be generated class name `ProductsIndex::Product` + # Camelcased name. # def name raise NotImplementedError diff --git a/lib/chewy/type/adapter/object.rb b/lib/chewy/index/adapter/object.rb similarity index 87% rename from lib/chewy/type/adapter/object.rb rename to lib/chewy/index/adapter/object.rb index 3ea167656..156a10232 100644 --- a/lib/chewy/type/adapter/object.rb +++ b/lib/chewy/index/adapter/object.rb @@ -1,7 +1,7 @@ -require 'chewy/type/adapter/base' +require 'chewy/index/adapter/base' module Chewy - class Type + class Index module Adapter # This adapter provides an ability to import documents from any # source. You can actually use any class or even a symbol as @@ -14,15 +14,15 @@ module Adapter # @see #import # @see #load class Object < Base - # The signature of the type definition. + # The signature of the index scope definition. # # @example - # define_type :geoname - # define_type Geoname - # define_type -> { Geoname.all_the_places }, name: 'geoname' + # index_scope :geoname + # index_scope Geoname + # index_scope -> { Geoname.all_the_places }, name: 'geoname' # # @param target [Class, Symbol, String, Proc] a source of data and everything - # @option options [String, Symbol] :name redefines the inferred type name if necessary + # @option options [String, Symbol] :name redefines the inferred name if necessary # @option options [String, Symbol] :import_all_method redefines import method name # @option options [String, Symbol] :load_all_method redefines batch load method name # @option options [String, Symbol] :load_one_method redefines per-object load method name @@ -31,14 +31,13 @@ def initialize(target, **options) @options = options end - # Name is used for the type class creation. Inferred from the target - # by default if possible. + # Inferred from the target by default if possible. # # @example - # # defines MyIndex::Geoname - # define_type :geoname - # # still defines MyIndex::Geoname - # define_type -> { Geoname.all_the_places }, name: 'geoname' + # # defines name = Geoname + # index_scope :geoname + # # still defines name = Geoname + # index_scope -> { Geoname.all_the_places }, name: 'geoname' # # @return [String] def name @@ -54,14 +53,14 @@ def identify(collection) Array.wrap(collection) end - # This method is used internally by `Chewy::Type.import`. + # This method is used internally by `Chewy::Index.import`. # # The idea is that any object can be imported to ES if # it responds to `#to_json` method. # # If method `destroyed?` is defined for object (or, in case of hash object, # it has `:_destroyed` or `'_destroyed'` key) and returns `true` or object - # satisfy `delete_if` type option then object will be deleted from index. + # satisfy `delete_if` option then object will be deleted from index. # But in order to be destroyable, objects need to respond to `id` method # or have an `id` key so ElasticSearch could know which one to delete. # @@ -78,10 +77,10 @@ def identify(collection) # end # end # - # # All the folloving variants will work: - # define_type Geoname - # define_type Geoname, import_all_method: 'import_all' - # define_type -> { FancyGeoAPI.all_points_collection }, name: 'geoname' + # # All the following variants will work: + # index_scope Geoname + # index_scope Geoname, import_all_method: 'import_all' + # index_scope -> { FancyGeoAPI.all_points_collection }, name: 'geoname' # # @param args [Array<#to_json>] # @option options [Integer] :batch_size import processing batch size @@ -113,7 +112,7 @@ def import(*args, &block) # end # end # - # @see Chewy::Type::Adapter::Base#import_fields + # @see Chewy::Index::Adapter::Base#import_fields def import_fields(*args, &block) return enum_for(:import_fields, *args) unless block_given? @@ -139,7 +138,7 @@ def import_fields(*args, &block) # For the Object adapter returns the objects themselves in batches. # - # @see Chewy::Type::Adapter::Base#import_references + # @see Chewy::Index::Adapter::Base#import_references def import_references(*args, &block) return enum_for(:import_references, *args) unless block_given? @@ -154,7 +153,7 @@ def import_references(*args, &block) # # If none of the `load_all_method` or `load_one_method` is implemented # for the target - the method will return nil. This means that the - # loader will return an array `Chewy::Type` objects that actually was passed. + # loader will return an array `Chewy::Index` objects that actually was passed. # # To use loading for objects it is obviously required to provide # some meaningful ids for ES documents. @@ -172,7 +171,7 @@ def import_references(*args, &block) # end # end # - # MyIndex::Geoname.load(additional_data: true).objects + # MyIndex.load(additional_data: true).objects # # @param ids [Array] an array of ids from ES hits # @param options [Hash] any options passed here with the request DSL `load` method. diff --git a/lib/chewy/type/adapter/orm.rb b/lib/chewy/index/adapter/orm.rb similarity index 90% rename from lib/chewy/type/adapter/orm.rb rename to lib/chewy/index/adapter/orm.rb index d516b4b15..7c04d9e9e 100644 --- a/lib/chewy/type/adapter/orm.rb +++ b/lib/chewy/index/adapter/orm.rb @@ -1,7 +1,7 @@ -require 'chewy/type/adapter/base' +require 'chewy/index/adapter/base' module Chewy - class Type + class Index module Adapter class Orm < Base attr_reader :default_scope @@ -54,24 +54,23 @@ def identify(collection) # # users = User.all # users.each { |user| user.destroy if user.inactive? } - # UsersIndex::User.import users # inactive users will be deleted from index + # UsersIndex.import users # inactive users will be deleted from index # # or - # UsersIndex::User.import users.map(&:id) # deleted user ids will be deleted from index + # UsersIndex.import users.map(&:id) # deleted user ids will be deleted from index # # Also there is custom type option `delete_if`. It it returns `true` # object will be deleted from index. Note that if this option is defined and # return `false` Chewy will still check `destroyed?` method. This is useful # for paranoid objects deleting implementation. # - # define_type User, delete_if: ->{ deleted_at } do - # ... - # end + # index_scope User, delete_if: ->{ deleted_at } + # ... # # users = User.all # users.each { |user| user.deleted_at = Time.now } - # UsersIndex::User.import users # paranoid deleted users will be deleted from index + # UsersIndex.import users # paranoid deleted users will be deleted from index # # or - # UsersIndex::User.import users.map(&:id) # user ids will be deleted from index + # UsersIndex.import users.map(&:id) # user ids will be deleted from index # def import(*args, &block) collection, options = import_args(*args) @@ -99,7 +98,7 @@ def import_fields(*args, &block) def load(ids, **options) scope = all_scope_where_ids_in(ids) - additional_scope = options[options[:_type].type_name.to_sym].try(:[], :scope) || options[:scope] + additional_scope = options[options[:_index].to_sym].try(:[], :scope) || options[:scope] loaded_objects = load_scope_objects(scope, additional_scope) .index_by do |object| diff --git a/lib/chewy/type/crutch.rb b/lib/chewy/index/crutch.rb similarity index 73% rename from lib/chewy/type/crutch.rb rename to lib/chewy/index/crutch.rb index 833a43372..a36fc4ef8 100644 --- a/lib/chewy/type/crutch.rb +++ b/lib/chewy/index/crutch.rb @@ -1,5 +1,5 @@ module Chewy - class Type + class Index module Crutch extend ActiveSupport::Concern @@ -9,13 +9,13 @@ module Crutch end class Crutches - def initialize(type, collection) - @type = type + def initialize(index, collection) + @index = index @collection = collection - @type._crutches.each_key do |name| + @index._crutches.each_key do |name| singleton_class.class_eval <<-METHOD, __FILE__, __LINE__ + 1 def #{name} - @#{name} ||= @type._crutches[:#{name}].call @collection + @#{name} ||= @index._crutches[:#{name}].call @collection end METHOD end diff --git a/lib/chewy/type/import.rb b/lib/chewy/index/import.rb similarity index 84% rename from lib/chewy/type/import.rb rename to lib/chewy/index/import.rb index 3dcc8be3c..a5aca8912 100644 --- a/lib/chewy/type/import.rb +++ b/lib/chewy/index/import.rb @@ -1,39 +1,39 @@ -require 'chewy/type/import/journal_builder' -require 'chewy/type/import/bulk_builder' -require 'chewy/type/import/bulk_request' -require 'chewy/type/import/routine' +require 'chewy/index/import/journal_builder' +require 'chewy/index/import/bulk_builder' +require 'chewy/index/import/bulk_request' +require 'chewy/index/import/routine' module Chewy - class Type + class Index module Import extend ActiveSupport::Concern - IMPORT_WORKER = lambda do |type, options, total, ids, index| - ::Process.setproctitle("chewy [#{type}]: import data (#{index + 1}/#{total})") - routine = Routine.new(type, **options) - type.adapter.import(*ids, routine.options) do |action_objects| + IMPORT_WORKER = lambda do |index, options, total, ids, iteration| + ::Process.setproctitle("chewy [#{index}]: import data (#{iteration + 1}/#{total})") + routine = Routine.new(index, **options) + index.adapter.import(*ids, routine.options) do |action_objects| routine.process(**action_objects) end {errors: routine.errors, import: routine.stats, leftovers: routine.leftovers} end - LEFTOVERS_WORKER = lambda do |type, options, total, body, index| - ::Process.setproctitle("chewy [#{type}]: import leftovers (#{index + 1}/#{total})") - routine = Routine.new(type, **options) + LEFTOVERS_WORKER = lambda do |index, options, total, body, iteration| + ::Process.setproctitle("chewy [#{index}]: import leftovers (#{iteration + 1}/#{total})") + routine = Routine.new(index, **options) routine.perform_bulk(body) routine.errors end module ClassMethods # @!method import(*collection, **options) - # Basically, one of the main methods for type. Performs any objects import - # to the index for a specified type. Does all the objects handling routines. + # Basically, one of the main methods for an index. Performs any objects import + # to the index. Does all the objects handling routines. # Performs document import by utilizing bulk API. Bulk size and objects batch # size are controlled by the corresponding options. # # It accepts ORM/ODM objects, PORO, hashes, ids which are used by adapter to - # fetch objects from the source depenting on the used adapter. It destroys - # passed objects from the index if they are not in the default type scope + # fetch objects from the source depending on the used adapter. It destroys + # passed objects from the index if they are not in the default scope # or marked for destruction. # # It handles parent-child relationships: if the object parent_id has been @@ -51,15 +51,15 @@ module ClassMethods # # Utilizes `ActiveSupport::Notifications`, so it is possible to get imported # objects later by listening to the `import_objects.chewy` queue. It is also - # possible to get the list of occured errors from the payload if something + # possible to get the list of occurred errors from the payload if something # went wrong. # # Import can also be run in parallel using the Parallel gem functionality. # # @example - # UsersIndex::User.import(parallel: true) # imports everything in parallel with automatic workers number - # UsersIndex::User.import(parallel: 3) # using 3 workers - # UsersIndex::User.import(parallel: {in_threads: 10}) # in 10 threads + # UsersIndex.import(parallel: true) # imports everything in parallel with automatic workers number + # UsersIndex.import(parallel: 3) # using 3 workers + # UsersIndex.import(parallel: {in_threads: 10}) # in 10 threads # # @see https://github.com/elastic/elasticsearch-ruby/blob/master/elasticsearch-api/lib/elasticsearch/api/actions/bulk.rb # @param collection [Array] and array or anything to import @@ -95,7 +95,7 @@ def import!(*args) # `bulk_size` and `suffix`. # # @see https://github.com/elastic/elasticsearch-ruby/blob/master/elasticsearch-api/lib/elasticsearch/api/actions/bulk.rb - # @see Chewy::Type::Import::Bulk + # @see Chewy::Index::Import::BulkRequest # @param options [Hash{Symbol => Object}] besides specific import options, it accepts all the options suitable for the bulk API call like `refresh` or `timeout` # @option options [String] suffix bulk API chunk size in bytes; if passed, the request is performed several times for each chunk, empty by default # @option options [Integer] bulk_size bulk API chunk size in bytes; if passed, the request is performed several times for each chunk, empty by default @@ -116,7 +116,7 @@ def bulk(**options) # @param fields [Array] and array of fields to restrict the generated document # @return [Hash] a JSON-ready hash def compose(object, crutches = nil, fields: []) - crutches ||= Chewy::Type::Crutch::Crutches.new self, [object] + crutches ||= Chewy::Index::Crutch::Crutches.new self, [object] if witchcraft? && root.children.present? cauldron(fields: fields).brew(object, crutches) @@ -149,7 +149,7 @@ def empty_objects_or_scope?(objects_or_scope) end def import_linear(objects, routine) - ActiveSupport::Notifications.instrument 'import_objects.chewy', type: self do |payload| + ActiveSupport::Notifications.instrument 'import_objects.chewy', index: self do |payload| adapter.import(*objects, routine.options) do |action_objects| routine.process(**action_objects) end @@ -161,11 +161,9 @@ def import_linear(objects, routine) end def import_parallel(objects, routine) - unless '::Parallel'.safe_constantize - raise "The `parallel` gem is required for parallel import, please add `gem 'parallel'` to your Gemfile" - end + raise "The `parallel` gem is required for parallel import, please add `gem 'parallel'` to your Gemfile" unless '::Parallel'.safe_constantize - ActiveSupport::Notifications.instrument 'import_objects.chewy', type: self do |payload| + ActiveSupport::Notifications.instrument 'import_objects.chewy', index: self do |payload| batches = adapter.import_references(*objects, routine.options.slice(:batch_size)).to_a ::ActiveRecord::Base.connection.close if defined?(::ActiveRecord::Base) diff --git a/lib/chewy/type/import/bulk_builder.rb b/lib/chewy/index/import/bulk_builder.rb similarity index 81% rename from lib/chewy/type/import/bulk_builder.rb rename to lib/chewy/index/import/bulk_builder.rb index 4f87c3e09..c139761c0 100644 --- a/lib/chewy/type/import/bulk_builder.rb +++ b/lib/chewy/index/import/bulk_builder.rb @@ -1,5 +1,5 @@ module Chewy - class Type + class Index module Import # This class purpose is to build ES client-acceptable bulk # request body from the passed objects for index and deletion. @@ -9,13 +9,13 @@ module Import # If fields are passed - it creates partial update entries except for # the cases when the type has parent and parent_id has been changed. class BulkBuilder - # @param type [Chewy::Type] desired type - # @param index [Array] objects to index + # @param index [Chewy::Index] desired index + # @param to_index [Array] objects to index # @param delete [Array] objects or ids to delete # @param fields [Array] and array of fields for documents update - def initialize(type, index: [], delete: [], fields: []) - @type = type + def initialize(index, to_index: [], delete: [], fields: []) @index = index + @to_index = to_index @delete = delete @fields = fields.map!(&:to_sym) end @@ -24,7 +24,7 @@ def initialize(type, index: [], delete: [], fields: []) # @see https://github.com/elastic/elasticsearch-ruby/blob/master/elasticsearch-api/lib/elasticsearch/api/actions/bulk.rb # @return [Array] bulk body def bulk_body - @bulk_body ||= @index.flat_map(&method(:index_entry)).concat( + @bulk_body ||= @to_index.flat_map(&method(:index_entry)).concat( @delete.flat_map(&method(:delete_entry)) ) end @@ -40,7 +40,7 @@ def index_objects_by_id private def crutches - @crutches ||= Chewy::Type::Crutch::Crutches.new @type, @index + @crutches ||= Chewy::Index::Crutch::Crutches.new @index, @to_index end def parents @@ -53,7 +53,7 @@ def parents ids.concat(@delete.map do |object| object.respond_to?(:id) ? object.id : object end) - @type.filter(ids: {values: ids}).order('_doc').pluck(:_id, :_parent).to_h + @index.filter(ids: {values: ids}).order('_doc').pluck(:_id, :_parent).to_h end end @@ -67,15 +67,15 @@ def index_entry(object) end if parent && entry[:parent].to_s != parent - entry[:data] = @type.compose(object, crutches) + entry[:data] = @index.compose(object, crutches) [{delete: entry.except(:data).merge(parent: parent)}, {index: entry}] elsif @fields.present? return [] unless entry[:_id] - entry[:data] = {doc: @type.compose(object, crutches, fields: @fields)} + entry[:data] = {doc: @index.compose(object, crutches, fields: @fields)} [{update: entry}] else - entry[:data] = @type.compose(object, crutches) + entry[:data] = @index.compose(object, crutches) [{index: entry}] end end @@ -109,14 +109,14 @@ def entry_id(object) end def index_object_ids - @index_object_ids ||= @index.each_with_object({}) do |object, result| + @index_object_ids ||= @to_index.each_with_object({}) do |object, result| id = entry_id(object) result[object] = id if id.present? end end def type_root - @type_root ||= @type.root + @type_root ||= @index.root end end end diff --git a/lib/chewy/type/import/bulk_request.rb b/lib/chewy/index/import/bulk_request.rb similarity index 88% rename from lib/chewy/type/import/bulk_request.rb rename to lib/chewy/index/import/bulk_request.rb index fb94e4aa5..9cae80ddd 100644 --- a/lib/chewy/type/import/bulk_request.rb +++ b/lib/chewy/index/import/bulk_request.rb @@ -1,5 +1,5 @@ module Chewy - class Type + class Index module Import # Adds additional features to elasticsearch-api bulk method: # * supports Chewy index suffix if necessary; @@ -10,12 +10,12 @@ module Import # # @see https://github.com/elastic/elasticsearch-ruby/blob/master/elasticsearch-api/lib/elasticsearch/api/actions/bulk.rb class BulkRequest - # @param type [Chewy::Type] a type for the request + # @param index [Chewy::Index] an index for the request # @param suffix [String] an index name optional suffix # @param bulk_size [Integer] bulk size in bytes # @param bulk_options [Hash] options passed to the elasticsearch-api bulk method - def initialize(type, suffix: nil, bulk_size: nil, **bulk_options) - @type = type + def initialize(index, suffix: nil, bulk_size: nil, **bulk_options) + @index = index @suffix = suffix @bulk_size = bulk_size - 1.kilobyte if bulk_size # 1 kilobyte for request header and newlines @bulk_options = bulk_options @@ -33,7 +33,7 @@ def perform(body) return [] if body.blank? request_bodies(body).each_with_object([]) do |request_body, results| - response = @type.client.bulk request_base.merge(body: request_body) if request_body.present? + response = @index.client.bulk request_base.merge(body: request_body) if request_body.present? next unless response.try(:[], 'errors') @@ -47,7 +47,7 @@ def perform(body) def request_base @request_base ||= { - index: @type.index_name(suffix: @suffix) + index: @index.index_name(suffix: @suffix) }.merge!(@bulk_options) end diff --git a/lib/chewy/type/import/journal_builder.rb b/lib/chewy/index/import/journal_builder.rb similarity index 67% rename from lib/chewy/type/import/journal_builder.rb rename to lib/chewy/index/import/journal_builder.rb index cd88b21b8..9d7fccce0 100644 --- a/lib/chewy/type/import/journal_builder.rb +++ b/lib/chewy/index/import/journal_builder.rb @@ -1,18 +1,18 @@ module Chewy - class Type + class Index module Import class JournalBuilder - def initialize(type, index: [], delete: []) - @type = type + def initialize(index, to_index: [], delete: []) @index = index + @to_index = to_index @delete = delete end def bulk_body - Chewy::Type::Import::BulkBuilder.new( - Chewy::Stash::Journal::Journal, - index: [ - entries(:index, @index), + Chewy::Index::Import::BulkBuilder.new( + Chewy::Stash::Journal, + to_index: [ + entries(:index, @to_index), entries(:delete, @delete) ].compact ).bulk_body.each do |item| @@ -28,8 +28,7 @@ def entries(action, objects) return unless objects.present? { - index_name: @type.index.derivable_name, - type_name: @type.type_name, + index_name: @index.derivable_name, action: action, references: identify(objects).map { |item| Base64.encode64(::Elasticsearch::API.serializer.dump(item)) }, created_at: Time.now.utc @@ -37,7 +36,7 @@ def entries(action, objects) end def identify(objects) - @type.adapter.identify(objects) + @index.adapter.identify(objects) end end end diff --git a/lib/chewy/type/import/routine.rb b/lib/chewy/index/import/routine.rb similarity index 84% rename from lib/chewy/type/import/routine.rb rename to lib/chewy/index/import/routine.rb index bcc71c134..556510572 100644 --- a/lib/chewy/type/import/routine.rb +++ b/lib/chewy/index/import/routine.rb @@ -1,5 +1,5 @@ module Chewy - class Type + class Index module Import # This class performs the import routine for the options and objects given. # @@ -20,7 +20,7 @@ module Import # when the document doesn't exist only if `update_failover` option is true. In order to # restore, it indexes such an objects completely on the next iteration. # - # @see Chewy::Type::Import::ClassMethods#import + # @see Chewy::Index::Import::ClassMethods#import class Routine BULK_OPTIONS = %i[ suffix bulk_size @@ -33,18 +33,18 @@ class Routine refresh: true, update_fields: [], update_failover: true, - batch_size: Chewy::Type::Adapter::Base::BATCH_SIZE + batch_size: Chewy::Index::Adapter::Base::BATCH_SIZE }.freeze attr_reader :options, :parallel_options, :errors, :stats, :leftovers # Basically, processes passed options, extracting bulk request specific options. - # @param type [Chewy::Type] chewy type - # @param options [Hash] import options, see {Chewy::Type::Import::ClassMethods#import} - def initialize(type, **options) - @type = type + # @param index [Chewy::Index] chewy index + # @param options [Hash] import options, see {Chewy::Index::Import::ClassMethods#import} + def initialize(index, **options) + @index = index @options = options - @options.reverse_merge!(@type._default_import_options) + @options.reverse_merge!(@index._default_import_options) @options.reverse_merge!(journal: Chewy.configuration[:journal]) @options.reverse_merge!(DEFAULT_OPTIONS) @bulk_options = @options.slice(*BULK_OPTIONS) @@ -61,13 +61,13 @@ def initialize(type, **options) @leftovers = [] end - # Creates the journal index and the type corresponding index if necessary. + # Creates the journal index and the corresponding index if necessary. # @return [Object] whatever def create_indexes! Chewy::Stash::Journal.create if @options[:journal] return if Chewy.configuration[:skip_index_creation_on_import] - @type.index.create!(**@bulk_options.slice(:suffix)) unless @type.index.exists? + @index.create!(**@bulk_options.slice(:suffix)) unless @index.exists? end # The main process method. Converts passed objects to the bulk request body, @@ -78,11 +78,11 @@ def create_indexes! # @param delete [Array] any acceptable objects for deleting # @return [true, false] the result of the request, true if no errors def process(index: [], delete: []) - bulk_builder = BulkBuilder.new(@type, index: index, delete: delete, fields: @options[:update_fields]) + bulk_builder = BulkBuilder.new(@index, to_index: index, delete: delete, fields: @options[:update_fields]) bulk_body = bulk_builder.bulk_body if @options[:journal] - journal_builder = JournalBuilder.new(@type, index: index, delete: delete) + journal_builder = JournalBuilder.new(@index, to_index: index, delete: delete) bulk_body.concat(journal_builder.bulk_body) end @@ -127,11 +127,11 @@ def extract_leftovers(errors, index_objects_by_id) errors_to_cleanup.each { |error| errors.delete(error) } failed_objects = index_objects_by_id.values_at(*failed_ids_for_reimport) - BulkBuilder.new(@type, index: failed_objects).bulk_body + BulkBuilder.new(@index, to_index: failed_objects).bulk_body end def bulk - @bulk ||= BulkRequest.new(@type, **@bulk_options) + @bulk ||= BulkRequest.new(@index, **@bulk_options) end end end diff --git a/lib/chewy/type/mapping.rb b/lib/chewy/index/mapping.rb similarity index 82% rename from lib/chewy/type/mapping.rb rename to lib/chewy/index/mapping.rb index 5efd5ffec..523047745 100644 --- a/lib/chewy/type/mapping.rb +++ b/lib/chewy/index/mapping.rb @@ -1,5 +1,5 @@ module Chewy - class Type + class Index module Mapping extend ActiveSupport::Concern @@ -13,42 +13,39 @@ module Mapping end module ClassMethods - # Defines root object for mapping and is optional for type + # Defines root object for mapping and is optional for index # definition. Use it only if you need to pass options for root # object mapping, such as `date_detection` or `dynamic_date_formats` # # @example # class UsersIndex < Chewy::Index - # define_type User do - # # root object defined implicitly and optionless for current type - # field :full_name, type: 'keyword' - # end + # index_scope User + # # root object defined implicitly and optionless for current type + # field :full_name, type: 'keyword' # end # # class CarsIndex < Chewy::Index - # define_type Car do - # # explicit root definition with additional options - # root dynamic_date_formats: ['yyyy-MM-dd'] do - # field :model_name, type: 'keyword' - # end + # index_scope Car + # # explicit root definition with additional options + # root dynamic_date_formats: ['yyyy-MM-dd'] do + # field :model_name, type: 'keyword' # end # end # def root(**options) - self.root_object ||= Chewy::Fields::Root.new(type_name, **Chewy.default_root_options.merge(options)) + self.root_object ||= Chewy::Fields::Root.new(:root, **Chewy.default_root_options.merge(options)) root_object.update_options!(**options) yield if block_given? root_object end - # Defines mapping field for current type + # Defines mapping field for index # # @example # class UsersIndex < Chewy::Index - # define_type User do - # # passing all the options to field definition: - # field :full_name, analyzer: 'special' - # end + # index_scope User + # # passing all the options to field definition: + # field :full_name, analyzer: 'special' # end # # The `type` is optional and defaults to `string` if not defined: @@ -138,14 +135,13 @@ def field(*args, **options, &block) # # Suppose that a user has posts and each post has ratings # # avg_post_rating is the mean of all ratings # class UsersIndex < Chewy::Index - # define_type User do - # field :posts do - # field :rating - # end - # - # agg :avg_rating do - # { avg: { field: 'posts.rating' } } - # end + # index_scope User + # field :posts do + # field :rating + # end + # + # agg :avg_rating do + # { avg: { field: 'posts.rating' } } # end # end def agg(name, &block) @@ -157,11 +153,10 @@ def agg(name, &block) # # @example # class CarsIndex < Chewy::Index - # define_type Car do - # template 'model.*', type: 'text', analyzer: 'special' - # field 'model', type: 'object' # here we can put { de: 'Der Mercedes', en: 'Mercedes' } - # # and template will be applyed to this field - # end + # index_scope Car + # template 'model.*', type: 'text', analyzer: 'special' + # field 'model', type: 'object' # here we can put { de: 'Der Mercedes', en: 'Mercedes' } + # # and template will be applied to this field # end # # Name for each template is generated with the following @@ -172,7 +167,7 @@ def agg(name, &block) # template 'title.*', mapping_hash # dot in template causes "path_match" using # template /tit.+/, mapping_hash # using "match_pattern": "regexp" # template /title\..+/, mapping_hash # "\." - escaped dot causes "path_match" using - # template /tit.+/, type: 'text', mapping_hash # "match_mapping_type" as the optionsl second argument + # template /tit.+/, type: 'text', mapping_hash # "match_mapping_type" as an optional second argument # template template42: {match: 'hello*', mapping: {type: 'object'}} # or even pass a template as is # def template(*args) diff --git a/lib/chewy/type/observe.rb b/lib/chewy/index/observe.rb similarity index 81% rename from lib/chewy/type/observe.rb rename to lib/chewy/index/observe.rb index 347fe11c3..bdc4c8d1d 100644 --- a/lib/chewy/type/observe.rb +++ b/lib/chewy/index/observe.rb @@ -1,25 +1,25 @@ module Chewy - class Type + class Index module Observe extend ActiveSupport::Concern module Helpers - def update_proc(type_name, *args, &block) + def update_proc(index_name, *args, &block) options = args.extract_options! method = args.first proc do - reference = if type_name.is_a?(Proc) - if type_name.arity.zero? - instance_exec(&type_name) + reference = if index_name.is_a?(Proc) + if index_name.arity.zero? + instance_exec(&index_name) else - type_name.call(self) + index_name.call(self) end else - type_name + index_name end - type = Chewy.derive_type(reference) + index = Chewy.derive_name(reference) next if Chewy.strategy.current.name == :bypass @@ -31,7 +31,7 @@ def update_proc(type_name, *args, &block) instance_eval(&block) end - type.update_index(backreference, options) + index.update_index(backreference, options) end end diff --git a/lib/chewy/type/syncer.rb b/lib/chewy/index/syncer.rb similarity index 74% rename from lib/chewy/type/syncer.rb rename to lib/chewy/index/syncer.rb index 7ffc2ec39..4016989da 100644 --- a/lib/chewy/type/syncer.rb +++ b/lib/chewy/index/syncer.rb @@ -1,5 +1,5 @@ module Chewy - class Type + class Index # This class is able to find missing and outdated documents in the ES # comparing ids from the data source and the ES index. Also, if `outdated_sync_field` # exists in the index definition, it performs comparison of this field @@ -9,10 +9,10 @@ class Type # should be reindexed. # # To fetch necessary data from the source it uses adapter method - # {Chewy::Type::Adapter::Base#import_fields}, in case when the Object + # {Chewy::Index::Adapter::Base#import_fields}, in case when the Object # adapter is used it makes sense to read corresponding documentation. # - # If `parallel` option is passed to the initializer - it will fetch surce and + # If `parallel` option is passed to the initializer - it will fetch source and # index data in parallel and then perform outdated objects calculation in # parallel processes. Also, further import (if required) will be performed # in parallel as well. @@ -24,17 +24,17 @@ class Type # ATTENTION: synchronization may be slow in case when synchronized tables # are missing compound index on primary key and `outdated_sync_field`. # - # @see Chewy::Type::Actions::ClassMethods#sync + # @see Chewy::Index::Actions::ClassMethods#sync class Syncer DEFAULT_SYNC_BATCH_SIZE = 20_000 ISO_DATETIME = /\A(\d{4})-(\d\d)-(\d\d) (\d\d):(\d\d):(\d\d)(\.\d+)?\z/.freeze - OUTDATED_IDS_WORKER = lambda do |outdated_sync_field_type, source_data_hash, type, total, index_data| - ::Process.setproctitle("chewy [#{type}]: sync outdated calculation (#{::Parallel.worker_number + 1}/#{total})") if type + OUTDATED_IDS_WORKER = lambda do |outdated_sync_field_type, source_data_hash, index, total, index_data| + ::Process.setproctitle("chewy [#{index}]: sync outdated calculation (#{::Parallel.worker_number + 1}/#{total})") if index index_data.each_with_object([]) do |(id, index_sync_value), result| next unless source_data_hash[id] outdated = if outdated_sync_field_type == 'date' - !Chewy::Type::Syncer.dates_equal(typecast_date(source_data_hash[id]), Time.iso8601(index_sync_value)) + !Chewy::Index::Syncer.dates_equal(typecast_date(source_data_hash[id]), Time.iso8601(index_sync_value)) else source_data_hash[id] != index_sync_value end @@ -42,8 +42,8 @@ class Syncer result.push(id) if outdated end end - SOURCE_OR_INDEX_DATA_WORKER = lambda do |syncer, type, kind| - ::Process.setproctitle("chewy [#{type}]: sync fetching data (#{kind})") + SOURCE_OR_INDEX_DATA_WORKER = lambda do |syncer, index, kind| + ::Process.setproctitle("chewy [#{index}]: sync fetching data (#{kind})") result = case kind when :source syncer.send(:fetch_source_data) @@ -71,18 +71,10 @@ def self.dates_equal(one, two) [one.to_i, one.usec / 1000] == [two.to_i, two.usec / 1000] end - # In ActiveSupport ~> 4.0 json dumpled times without any - # milliseconds, so ES stored time with the seconds precision. - if ActiveSupport::VERSION::STRING < '4.1.0' - def self.dates_equal(one, two) - one.to_i == two.to_i - end - end - - # @param type [Chewy::Type] chewy type + # @param index [Chewy::Index] chewy index # @param parallel [true, Integer, Hash] options for parallel execution or the number of processes - def initialize(type, parallel: nil) - @type = type + def initialize(index, parallel: nil) + @index = index @parallel = if !parallel || parallel.is_a?(Hash) parallel elsif parallel.is_a?(Integer) @@ -99,7 +91,7 @@ def perform ids = missing_ids | outdated_ids return 0 if ids.blank? - @type.import(ids, parallel: @parallel) && ids.count + @index.import(ids, parallel: @parallel) && ids.count end # Finds ids of all the objects that are not indexed yet or deleted @@ -117,15 +109,14 @@ def missing_ids end end - # If type supports outdated sync, it compares for the values of the - # type `outdated_sync_field` for each object and document in the source - # and index and returns the ids of entities which which are having - # different values there. + # If index supports outdated sync, it compares the values of the + # `outdated_sync_field` for each object and document in the source + # and index and returns the ids of entities which differ. # - # @see Chewy::Type::Mapping::ClassMethods#supports_outdated_sync? + # @see Chewy::Index::Mapping::ClassMethods#supports_outdated_sync? # @return [Array] an array of outdated ids def outdated_ids - return [] if source_data.blank? || index_data.blank? || !@type.supports_outdated_sync? + return [] if source_data.blank? || index_data.blank? || !@index.supports_outdated_sync? @outdated_ids ||= if @parallel parallel_outdated_ids @@ -147,7 +138,7 @@ def index_data def source_and_index_data @source_and_index_data ||= if @parallel ::ActiveRecord::Base.connection.close if defined?(::ActiveRecord::Base) - result = ::Parallel.map(%i[source index], @parallel, &SOURCE_OR_INDEX_DATA_WORKER.curry[self, @type]) + result = ::Parallel.map(%i[source index], @parallel, &SOURCE_OR_INDEX_DATA_WORKER.curry[self, @index]) ::ActiveRecord::Base.connection.reconnect! if defined?(::ActiveRecord::Base) if result.first.keys.first == :source [result.first.values.first, result.second.values.first] @@ -160,32 +151,32 @@ def source_and_index_data end def fetch_source_data - if @type.supports_outdated_sync? + if @index.supports_outdated_sync? import_fields_args = { - fields: [@type.outdated_sync_field], + fields: [@index.outdated_sync_field], batch_size: DEFAULT_SYNC_BATCH_SIZE, typecast: false } - @type.adapter.import_fields(import_fields_args).to_a.flatten(1).each do |data| + @index.adapter.import_fields(import_fields_args).to_a.flatten(1).each do |data| data[0] = data[0].to_s end else - @type.adapter.import_fields(batch_size: DEFAULT_SYNC_BATCH_SIZE, typecast: false).to_a.flatten(1).map(&:to_s) + @index.adapter.import_fields(batch_size: DEFAULT_SYNC_BATCH_SIZE, typecast: false).to_a.flatten(1).map(&:to_s) end end def fetch_index_data - if @type.supports_outdated_sync? - @type.pluck(:_id, @type.outdated_sync_field).each do |data| + if @index.supports_outdated_sync? + @index.pluck(:_id, @index.outdated_sync_field).each do |data| data[0] = data[0].to_s end else - @type.pluck(:_id).map(&:to_s) + @index.pluck(:_id).map(&:to_s) end end def data_ids(data) - return data unless @type.supports_outdated_sync? + return data unless @index.supports_outdated_sync? data.map(&:first) end @@ -199,7 +190,7 @@ def parallel_outdated_ids batches = index_data.each_slice(size) ::ActiveRecord::Base.connection.close if defined?(::ActiveRecord::Base) - curried_outdated_ids_worker = OUTDATED_IDS_WORKER.curry[outdated_sync_field_type, source_data.to_h, @type, batches.size] + curried_outdated_ids_worker = OUTDATED_IDS_WORKER.curry[outdated_sync_field_type, source_data.to_h, @index, batches.size] result = ::Parallel.map( batches, @parallel, @@ -215,13 +206,13 @@ def processor_count def outdated_sync_field_type return @outdated_sync_field_type if instance_variable_defined?(:@outdated_sync_field_type) - return unless @type.outdated_sync_field + return unless @index.outdated_sync_field - mappings = @type.client.indices.get_mapping(index: @type.index_name).values.first.fetch('mappings', {}) + mappings = @index.client.indices.get_mapping(index: @index.index_name).values.first.fetch('mappings', {}) @outdated_sync_field_type = mappings .fetch('properties', {}) - .fetch(@type.outdated_sync_field.to_s, {})['type'] + .fetch(@index.outdated_sync_field.to_s, {})['type'] rescue Elasticsearch::Transport::Transport::Errors::NotFound nil end diff --git a/lib/chewy/type/witchcraft.rb b/lib/chewy/index/witchcraft.rb similarity index 91% rename from lib/chewy/type/witchcraft.rb rename to lib/chewy/index/witchcraft.rb index a246cae1f..a6b8dc0a7 100644 --- a/lib/chewy/type/witchcraft.rb +++ b/lib/chewy/index/witchcraft.rb @@ -7,7 +7,7 @@ end module Chewy - class Type + class Index module Witchcraft extend ActiveSupport::Concern @@ -23,15 +23,9 @@ def witchcraft! def check_requirements! messages = [] - unless Proc.method_defined?(:source) - messages << "MethodSource gem is required for the Witchcraft, please add `gem 'method_source'` to your Gemfile" - end - unless '::Parser'.safe_constantize - messages << "Parser gem is required for the Witchcraft, please add `gem 'parser'` to your Gemfile" - end - unless '::Unparser'.safe_constantize - messages << "Unparser gem is required for the Witchcraft, please add `gem 'unparser'` to your Gemfile" - end + messages << "MethodSource gem is required for the Witchcraft, please add `gem 'method_source'` to your Gemfile" unless Proc.method_defined?(:source) + messages << "Parser gem is required for the Witchcraft, please add `gem 'parser'` to your Gemfile" unless '::Parser'.safe_constantize + messages << "Unparser gem is required for the Witchcraft, please add `gem 'unparser'` to your Gemfile" unless '::Unparser'.safe_constantize messages = messages.join("\n") raise messages if messages.present? @@ -49,10 +43,10 @@ def cauldron(**options) class Cauldron attr_reader :locals - # @param type [Chewy::Type] type for composition + # @param index [Chewy::Index] index for composition # @param fields [Array] restricts the fields for composition - def initialize(type, fields: []) - @type = type + def initialize(index, fields: []) + @index = index @locals = [] @fields = fields end @@ -66,7 +60,7 @@ def brew(object, crutches = nil) def alicorn @alicorn ||= singleton_class.class_eval <<-RUBY, __FILE__, __LINE__ + 1 -> (locals, object0, crutches) do - #{composed_values(@type.root, 0)} + #{composed_values(@index.root, 0)} end RUBY end diff --git a/lib/chewy/type/wrapper.rb b/lib/chewy/index/wrapper.rb similarity index 97% rename from lib/chewy/type/wrapper.rb rename to lib/chewy/index/wrapper.rb index 68af3aace..e69a316ea 100644 --- a/lib/chewy/type/wrapper.rb +++ b/lib/chewy/index/wrapper.rb @@ -1,5 +1,5 @@ module Chewy - class Type + class Index module Wrapper extend ActiveSupport::Concern @@ -28,7 +28,7 @@ def initialize(attributes = {}) def ==(other) return true if super - if other.is_a?(Chewy::Type) + if other.is_a?(Chewy::Index) self.class == other.class && (respond_to?(:id) ? id == other.id : attributes == other.attributes) elsif other.respond_to?(:id) self.class.adapter.target.is_a?(Class) && diff --git a/lib/chewy/journal.rb b/lib/chewy/journal.rb index 90e6ab80f..41d3b3233 100644 --- a/lib/chewy/journal.rb +++ b/lib/chewy/journal.rb @@ -2,19 +2,18 @@ module Chewy # A class to perform journal-related actions for the specified indexes/types. # # @example - # journal = Chewy::Journal.new('places#city', UsersIndex) + # journal = Chewy::Journal.new('places', UsersIndex) # journal.apply(20.minutes.ago) # journal.clean # class Journal - # @param only [Array] indexes/types or even string references to - # perform actions on + # @param only [Array] indexes or string references to perform actions on def initialize(*only) @only = only end # Applies all changes that were done since the specified time to the - # specified indexes/types. + # specified indexes. # # @param since_time [Time, DateTime] timestamp from which changes will be applied # @param retries [Integer] maximum number of attempts to make journal empty, 10 by default @@ -28,8 +27,8 @@ def apply(since_time, retries: 10, **import_options) count += entries.size groups = reference_groups(entries) ActiveSupport::Notifications.instrument 'apply_journal.chewy', stage: stage, groups: groups - groups.each do |type, references| - type.import(references, import_options.merge(journal: false)) + groups.each do |index, references| + index.import(references, import_options.merge(journal: false)) end stage += 1 since_time = entries.map(&:created_at).max @@ -48,9 +47,9 @@ def clean(until_time = nil) private def reference_groups(entries) - entries.group_by(&:type).transform_values do |grouped_entries| - grouped_entries.map(&:references).inject(:|) - end + entries.group_by(&:index_name) + .transform_keys { |index_name| Chewy.derive_name(index_name) } + .transform_values { |grouped_entries| grouped_entries.map(&:references).inject(:|) } end end end diff --git a/lib/chewy/minitest/helpers.rb b/lib/chewy/minitest/helpers.rb index c09316d20..1c7a90bfd 100644 --- a/lib/chewy/minitest/helpers.rb +++ b/lib/chewy/minitest/helpers.rb @@ -6,7 +6,7 @@ module Helpers extend ActiveSupport::Concern # Assert that an index *changes* during a block. - # @param index [Chewy::Type] the index / type to watch, eg EntitiesIndex::Entity. + # @param index [Chewy::Index] the index to watch, eg EntitiesIndex. # @param strategy [Symbol] the Chewy strategy to use around the block. See Chewy docs. # @param bypass_actual_index [true, false] # True to preempt the http call to Elastic, false otherwise. @@ -15,10 +15,10 @@ module Helpers # @return [SearchIndexReceiver] for optional further assertions on the nature of the index changes. # def assert_indexes(index, strategy: :atomic, bypass_actual_index: true, &block) - type = Chewy.derive_type index + index_class = Chewy.derive_name index receiver = SearchIndexReceiver.new - bulk_method = type.method :bulk + bulk_method = index_class.method :bulk # Manually mocking #bulk because we need to properly capture `self` bulk_mock = lambda do |*bulk_args| receiver.catch bulk_args, self @@ -28,11 +28,11 @@ def assert_indexes(index, strategy: :atomic, bypass_actual_index: true, &block) {} end - type.define_singleton_method :bulk, bulk_mock + index_class.define_singleton_method :bulk, bulk_mock Chewy.strategy(strategy, &block) - type.define_singleton_method :bulk, bulk_method + index_class.define_singleton_method :bulk, bulk_method assert_includes receiver.updated_indexes, index, "Expected #{index} to be updated but it wasn't" diff --git a/lib/chewy/minitest/search_index_receiver.rb b/lib/chewy/minitest/search_index_receiver.rb index e4d3a935b..d8f0bd4f8 100644 --- a/lib/chewy/minitest/search_index_receiver.rb +++ b/lib/chewy/minitest/search_index_receiver.rb @@ -1,28 +1,28 @@ # Test helper class to provide minitest hooks for Chewy::Index testing. # # @note Intended to be used in conjunction with a test helper which mocks over the #bulk -# method on a {Chewy::Type} class. (See SearchTestHelper) +# method on a {Chewy::Index} class. (See {Chewy::Minitest::Helpers}) # -# The class will capture the data from the *param on the Chewy::Type.bulk method and +# The class will capture the data from the *param on the Chewy::Index.bulk method and # aggregate the data for test analysis. class SearchIndexReceiver def initialize @mutations = {} end - # @param bulk_params [Hash] the bulk_params that should be sent to the Chewy::Type.bulk method. - # @param type [Chewy::Type] the type executing this query. - def catch(bulk_params, type) + # @param bulk_params [Hash] the bulk_params that should be sent to the Chewy::Index.bulk method. + # @param index [Chewy::Index] the index executing this query. + def catch(bulk_params, index) Array.wrap(bulk_params).map { |y| y[:body] }.flatten.each do |update| if update[:delete] - mutation_for(type).deletes << update[:delete][:_id] + mutation_for(index).deletes << update[:delete][:_id] elsif update[:index] - mutation_for(type).indexes << update[:index] + mutation_for(index).indexes << update[:index] end end end - # @param index [Chewy::Index] return only index requests to the specified {Chewy::Type} index. + # @param index [Chewy::Index] return only index requests to the specified {Chewy::Index} index. # @return [Hash] the index changes captured by the mock. def indexes_for(index = nil) if index @@ -33,7 +33,7 @@ def indexes_for(index = nil) end alias_method :indexes, :indexes_for - # @param index [Chewy::Index] return only delete requests to the specified {Chewy::Type} index. + # @param index [Chewy::Index] return only delete requests to the specified {Chewy::Index} index. # @return [Hash] the index deletes captured by the mock. def deletes_for(index = nil) if index @@ -46,31 +46,31 @@ def deletes_for(index = nil) # Check to see if a given object has been indexed. # @param obj [#id] obj the object to look for. - # @param type [Chewy::Type] what type the object should be indexed as. + # @param index [Chewy::Index] what index the object should be indexed in. # @return [true, false] if the object was indexed. - def indexed?(obj, type) - indexes_for(type).map { |i| i[:_id] }.include? obj.id + def indexed?(obj, index) + indexes_for(index).map { |i| i[:_id] }.include? obj.id end # Check to see if a given object has been deleted. # @param obj [#id] obj the object to look for. - # @param type [Chewy::Type] what type the object should have been deleted from. + # @param index [Chewy::Index] what index the object should have been deleted from. # @return [true, false] if the object was deleted. - def deleted?(obj, type) - deletes_for(type).include? obj.id + def deleted?(obj, index) + deletes_for(index).include? obj.id end - # @return [Array] a list of types indexes changed. + # @return [Array] a list of indexes changed. def updated_indexes @mutations.keys end private - # Get the mutation object for a given type. - # @param type [Chewy::Type] the index type to fetch. + # Get the mutation object for a given index. + # @param index [Chewy::Index] the index to fetch. # @return [#indexes, #deletes] an object with a list of indexes and a list of deletes. - def mutation_for(type) - @mutations[type] ||= OpenStruct.new(indexes: [], deletes: []) + def mutation_for(index) + @mutations[index] ||= OpenStruct.new(indexes: [], deletes: []) end end diff --git a/lib/chewy/rake_helper.rb b/lib/chewy/rake_helper.rb index b7e295cee..27d1f90ef 100644 --- a/lib/chewy/rake_helper.rb +++ b/lib/chewy/rake_helper.rb @@ -3,7 +3,7 @@ module RakeHelper IMPORT_CALLBACK = lambda do |output, _name, start, finish, _id, payload| duration = (finish - start).ceil stats = payload.fetch(:import, {}).map { |key, count| "#{key} #{count}" }.join(', ') - output.puts " Imported #{payload[:type]} in #{human_duration(duration)}, stats: #{stats}" + output.puts " Imported #{payload[:index]} in #{human_duration(duration)}, stats: #{stats}" payload[:errors]&.each do |action, errors| output.puts " #{action.to_s.humanize} errors:" errors.each do |error, documents| @@ -85,23 +85,21 @@ def upgrade(only: nil, except: nil, parallel: nil, output: $stdout) # # @example # Chewy::RakeHelper.update # updates everything - # Chewy::RakeHelper.update(only: 'places') # updates only PlacesIndex::City and PlacesIndex::Country - # Chewy::RakeHelper.update(only: 'places#city') # updates PlacesIndex::City only - # Chewy::RakeHelper.update(except: PlacesIndex::Country) # updates everything, but PlacesIndex::Country - # Chewy::RakeHelper.update(only: 'places', except: 'places#country') # updates PlacesIndex::City only + # Chewy::RakeHelper.update(only: 'places') # updates only PlacesIndex + # Chewy::RakeHelper.update(except: PlacesIndex) # updates everything, but PlacesIndex # - # @param only [Array, Chewy::Index, Chewy::Type, String] indexes or types to update; if nothing is passed - uses all the types defined in the app - # @param except [Array, Chewy::Index, Chewy::Type, String] indexes or types to exclude from processing + # @param only [Array, Chewy::Index, String] indexes to update; if nothing is passed - uses all the indexes defined in the app + # @param except [Array, Chewy::Index, String] indexes to exclude from processing # @param parallel [true, Integer, Hash] any acceptable parallel options for import # @param output [IO] output io for logging - # @return [Array] types that were actually updated + # @return [Array] indexes that were actually updated def update(only: nil, except: nil, parallel: nil, output: $stdout) subscribed_task_stats(output) do - types_from(only: only, except: except).group_by(&:index).each_with_object([]) do |(index, types), update_types| + indexes_from(only: only, except: except).each_with_object([]) do |index, updated_indexes| if index.exists? output.puts "Updating #{index}" - types.each { |type| type.import(parallel: parallel) } - update_types.concat(types) + index.import(parallel: parallel) + updated_indexes.push(index) else output.puts "Skipping #{index}, it does not exists (use rake chewy:reset[#{index.derivable_name}] to create and update it)" end @@ -113,31 +111,29 @@ def update(only: nil, except: nil, parallel: nil, output: $stdout) # # @example # Chewy::RakeHelper.sync # synchronizes everything - # Chewy::RakeHelper.sync(only: 'places') # synchronizes only PlacesIndex::City and PlacesIndex::Country - # Chewy::RakeHelper.sync(only: 'places#city') # synchronizes PlacesIndex::City only - # Chewy::RakeHelper.sync(except: PlacesIndex::Country) # synchronizes everything, but PlacesIndex::Country - # Chewy::RakeHelper.sync(only: 'places', except: 'places#country') # synchronizes PlacesIndex::City only + # Chewy::RakeHelper.sync(only: 'places') # synchronizes only PlacesIndex + # Chewy::RakeHelper.sync(except: PlacesIndex) # synchronizes everything, but PlacesIndex # - # @param only [Array, Chewy::Index, Chewy::Type, String] indexes or types to synchronize; if nothing is passed - uses all the types defined in the app - # @param except [Array, Chewy::Index, Chewy::Type, String] indexes or types to exclude from processing + # @param only [Array, Chewy::Index, String] indexes to synchronize; if nothing is passed - uses all the indexes defined in the app + # @param except [Array, Chewy::Index, String] indexes to exclude from processing # @param parallel [true, Integer, Hash] any acceptable parallel options for sync # @param output [IO] output io for logging - # @return [Array] types that were actually updated + # @return [Array] indexes that were actually updated def sync(only: nil, except: nil, parallel: nil, output: $stdout) subscribed_task_stats(output) do - types_from(only: only, except: except).each_with_object([]) do |type, synced_types| - output.puts "Synchronizing #{type}" - output.puts " #{type} doesn't support outdated synchronization" unless type.supports_outdated_sync? + indexes_from(only: only, except: except).each_with_object([]) do |index, synced_indexes| + output.puts "Synchronizing #{index}" + output.puts " #{index} doesn't support outdated synchronization" unless index.supports_outdated_sync? time = Time.now - sync_result = type.sync(parallel: parallel) + sync_result = index.sync(parallel: parallel) if !sync_result - output.puts " Something went wrong with the #{type} synchronization" + output.puts " Something went wrong with the #{index} synchronization" elsif (sync_result[:count]).positive? output.puts " Missing documents: #{sync_result[:missing]}" if sync_result[:missing].present? output.puts " Outdated documents: #{sync_result[:outdated]}" if sync_result[:outdated].present? - synced_types.push(type) + synced_indexes.push(index) else - output.puts " Skipping #{type}, up to date" + output.puts " Skipping #{index}, up to date" end output.puts " Took #{human_duration(Time.now - time)}" end @@ -145,50 +141,46 @@ def sync(only: nil, except: nil, parallel: nil, output: $stdout) end # Applies changes that were done after the specified time for the - # specified indexes/types or all of them. + # specified indexes or all of them. # # @example # Chewy::RakeHelper.journal_apply(time: 1.minute.ago) # applies entries created for the last minute - # Chewy::RakeHelper.journal_apply(time: 1.minute.ago, only: 'places') # applies only PlacesIndex::City and PlacesIndex::Country entries reated for the last minute - # Chewy::RakeHelper.journal_apply(time: 1.minute.ago, only: 'places#city') # applies PlacesIndex::City entries reated for the last minute only - # Chewy::RakeHelper.journal_apply(time: 1.minute.ago, except: PlacesIndex::Country) # applies everything, but PlacesIndex::Country entries reated for the last minute - # Chewy::RakeHelper.journal_apply(time: 1.minute.ago, only: 'places', except: 'places#country') # applies PlacesIndex::City entries reated for the last minute only + # Chewy::RakeHelper.journal_apply(time: 1.minute.ago, only: 'places') # applies only PlacesIndex entries created for the last minute + # Chewy::RakeHelper.journal_apply(time: 1.minute.ago, except: PlacesIndex) # applies everything, but PlacesIndex, entries created for the last minute # # @param time [Time, DateTime] use only journal entries created after this time - # @param only [Array, Chewy::Index, Chewy::Type, String] indexes or types to synchronize; if nothing is passed - uses all the types defined in the app - # @param except [Array, Chewy::Index, Chewy::Type, String] indexes or types to exclude from processing + # @param only [Array, Chewy::Index, String] indexes to synchronize; if nothing is passed - uses all the indexes defined in the app + # @param except [Array, Chewy::Index, String] indexes to exclude from processing # @param output [IO] output io for logging - # @return [Array] types that were actually updated + # @return [Array] indexes that were actually updated def journal_apply(time: nil, only: nil, except: nil, output: $stdout) raise ArgumentError, 'Please specify the time to start with' unless time subscribed_task_stats(output) do output.puts "Applying journal entries created after #{time}" - count = Chewy::Journal.new(types_from(only: only, except: except)).apply(time) + count = Chewy::Journal.new(indexes_from(only: only, except: except)).apply(time) output.puts 'No journal entries were created after the specified time' if count.zero? end end # Removes journal records created before the specified timestamp for - # the specified indexes/types or all of them. + # the specified indexes or all of them. # # @example # Chewy::RakeHelper.journal_clean # cleans everything # Chewy::RakeHelper.journal_clean(time: 1.minute.ago) # leaves only entries created for the last minute - # Chewy::RakeHelper.journal_clean(only: 'places') # cleans only PlacesIndex::City and PlacesIndex::Country entries - # Chewy::RakeHelper.journal_clean(only: 'places#city') # cleans PlacesIndex::City entries only - # Chewy::RakeHelper.journal_clean(except: PlacesIndex::Country) # cleans everything, but PlacesIndex::Country entries - # Chewy::RakeHelper.journal_clean(only: 'places', except: 'places#country') # cleans PlacesIndex::City entries only + # Chewy::RakeHelper.journal_clean(only: 'places') # cleans only PlacesIndex entries + # Chewy::RakeHelper.journal_clean(except: PlacesIndex) # cleans everything, but PlacesIndex entries # # @param time [Time, DateTime] clean all the journal entries created before this time - # @param only [Array, Chewy::Index, Chewy::Type, String] indexes or types to synchronize; if nothing is passed - uses all the types defined in the app - # @param except [Array, Chewy::Index, Chewy::Type, String] indexes or types to exclude from processing + # @param only [Array, Chewy::Index, String] indexes to synchronize; if nothing is passed - uses all the indexes defined in the app + # @param except [Array, Chewy::Index, String] indexes to exclude from processing # @param output [IO] output io for logging - # @return [Array] types that were actually updated + # @return [Array] indexes that were actually updated def journal_clean(time: nil, only: nil, except: nil, output: $stdout) subscribed_task_stats(output) do output.puts "Cleaning journal entries created before #{time}" if time - response = Chewy::Journal.new(types_from(only: only, except: except)).clean(time) + response = Chewy::Journal.new(indexes_from(only: only, except: except)).clean(time) count = response['deleted'] || response['_indices']['_all']['deleted'] output.puts "Cleaned up #{count} journal entries" end @@ -227,7 +219,7 @@ def reindex(source:, dest:, output: $stdout) def update_mapping(name:, output: $stdout) subscribed_task_stats(output) do output.puts "Index name is #{name}" - Chewy::Index.update_mapping(name) + normalize_index(name).update_mapping output.puts "#{name} index successfully updated" end end @@ -239,7 +231,7 @@ def normalize_indexes(*identifiers) def normalize_index(identifier) return identifier if identifier.is_a?(Class) && identifier < Chewy::Index - "#{identifier.to_s.gsub(/identifier\z/i, '').camelize}Index".constantize + "#{identifier.to_s.camelize}Index".constantize end def subscribed_task_stats(output = $stdout, &block) @@ -268,30 +260,6 @@ def indexes_from(only: nil, except: nil) indexes.sort_by(&:derivable_name) end - def types_from(only: nil, except: nil) - types = if only.present? - normalize_types(Array.wrap(only)) - else - all_indexes.flat_map(&:types) - end - - types = if except.present? - types - normalize_types(Array.wrap(except)) - else - types - end - - types.sort_by(&:derivable_name) - end - - def normalize_types(*identifiers) - identifiers.flatten(1).flat_map { |identifier| normalize_type(identifier) } - end - - def normalize_type(identifier) - Chewy.derive_types(identifier) - end - def human_duration(seconds) [[60, :s], [60, :m], [24, :h]].map do |amount, unit| if seconds.positive? diff --git a/lib/chewy/rspec/update_index.rb b/lib/chewy/rspec/update_index.rb index ced885629..d1bdb8e47 100644 --- a/lib/chewy/rspec/update_index.rb +++ b/lib/chewy/rspec/update_index.rb @@ -2,25 +2,25 @@ # Rspec matcher `update_index` # To use it - add `require 'chewy/rspec'` to the `spec_helper.rb` -# Simple usage - just pass type as argument. +# Simple usage - just pass index as argument. # -# specify { expect { user.save! }.to update_index(UsersIndex::User) } -# specify { expect { user.save! }.to update_index('users#user') } -# specify { expect { user.save! }.not_to update_index('users#user') } +# specify { expect { user.save! }.to update_index(UsersIndex) } +# specify { expect { user.save! }.to update_index('users') } +# specify { expect { user.save! }.not_to update_index('users') } # # This example will pass as well because user1 was reindexed # and nothing was said about user2: # # specify { expect { [user1, user2].map(&:save!) } -# .to update_index(UsersIndex.user).and_reindex(user1) } +# .to update_index(UsersIndex).and_reindex(user1) } # # If you need to specify reindexed records strictly - use `only` chain. # Combined matcher chain methods: # # specify { expect { user1.destroy!; user2.save! } } -# .to update_index(UsersIndex::User).and_reindex(user2).and_delete(user1) } +# .to update_index(UsersIndex).and_reindex(user2).and_delete(user1) } # -RSpec::Matchers.define :update_index do |type_name, options = {}| # rubocop:disable Metrics/BlockLength +RSpec::Matchers.define :update_index do |index_name, options = {}| # rubocop:disable Metrics/BlockLength if !respond_to?(:failure_message) && respond_to?(:failure_message_for_should) alias_method :failure_message, :failure_message_for_should alias_method :failure_message_when_negated, :failure_message_for_should_not @@ -28,30 +28,30 @@ # Specify indexed records by passing record itself or id. # - # specify { expect { user.save! }.to update_index(UsersIndex::User).and_reindex(user) - # specify { expect { user.save! }.to update_index(UsersIndex::User).and_reindex(42) + # specify { expect { user.save! }.to update_index(UsersIndex).and_reindex(user) + # specify { expect { user.save! }.to update_index(UsersIndex).and_reindex(42) # specify { expect { [user1, user2].map(&:save!) } - # .to update_index(UsersIndex::User).and_reindex(user1, user2) } + # .to update_index(UsersIndex).and_reindex(user1, user2) } # specify { expect { [user1, user2].map(&:save!) } - # .to update_index(UsersIndex::User).and_reindex(user1).and_reindex(user2) } + # .to update_index(UsersIndex).and_reindex(user1).and_reindex(user2) } # # Specify indexing count for every particular record. Useful in case # urgent index updates. # # specify { expect { 2.times { user.save! } } - # .to update_index(UsersIndex::User).and_reindex(user, times: 2) } + # .to update_index(UsersIndex).and_reindex(user, times: 2) } # # Specify reindexed attributes. Note that arrays are # compared position-independently. # # specify { expect { user.update_attributes!(name: 'Duke') } - # .to update_index(UsersIndex.user).and_reindex(user, with: {name: 'Duke'}) } + # .to update_index(UsersIndex).and_reindex(user, with: {name: 'Duke'}) } # # You can combine all the options and chain `and_reindex` method to # specify options for every indexed record: # # specify { expect { 2.times { [user1, user2].map { |u| u.update_attributes!(name: "Duke#{u.id}") } } } - # .to update_index(UsersIndex.user) + # .to update_index(UsersIndex) # .and_reindex(user1, with: {name: 'Duke42'}) } # .and_reindex(user2, times: 1, with: {name: 'Duke43'}) } # @@ -62,8 +62,8 @@ # Specify deleted records with record itself or id passed. # - # specify { expect { user.destroy! }.to update_index(UsersIndex::User).and_delete(user) } - # specify { expect { user.destroy! }.to update_index(UsersIndex::User).and_delete(user.id) } + # specify { expect { user.destroy! }.to update_index(UsersIndex).and_delete(user) } + # specify { expect { user.destroy! }.to update_index(UsersIndex).and_delete(user.id) } # chain(:and_delete) do |*args| @delete ||= {} @@ -73,14 +73,14 @@ # Used for specifying than no other records would be indexed or deleted: # # specify { expect { [user1, user2].map(&:save!) } - # .to update_index(UsersIndex.user).and_reindex(user1, user2).only } + # .to update_index(UsersIndex).and_reindex(user1, user2).only } # specify { expect { [user1, user2].map(&:destroy!) } - # .to update_index(UsersIndex.user).and_delete(user1, user2).only } + # .to update_index(UsersIndex).and_delete(user1, user2).only } # # This example will fail: # # specify { expect { [user1, user2].map(&:save!) } - # .to update_index(UsersIndex.user).and_reindex(user1).only } + # .to update_index(UsersIndex).and_reindex(user1).only } # chain(:only) do |*_args| raise 'Use `only` in conjunction with `and_reindex` or `and_delete`' if @reindex.blank? && @delete.blank? @@ -98,13 +98,13 @@ def supports_block_expectations? @missed_reindex = [] @missed_delete = [] - type = Chewy.derive_type(type_name) + index = Chewy.derive_name(index_name) if defined?(Mocha) && RSpec.configuration.mock_framework.to_s == 'RSpec::Core::MockingAdapters::Mocha' - Chewy::Type::Import::BulkRequest.stubs(:new).with(type, any_parameters).returns(mock_bulk_request) + Chewy::Index::Import::BulkRequest.stubs(:new).with(index, any_parameters).returns(mock_bulk_request) else - mocked_already = ::RSpec::Mocks.space.proxy_for(Chewy::Type::Import::BulkRequest).method_double_if_exists_for_message(:new) - allow(Chewy::Type::Import::BulkRequest).to receive(:new).and_call_original unless mocked_already - allow(Chewy::Type::Import::BulkRequest).to receive(:new).with(type, any_args).and_return(mock_bulk_request) + mocked_already = ::RSpec::Mocks.space.proxy_for(Chewy::Index::Import::BulkRequest).method_double_if_exists_for_message(:new) + allow(Chewy::Index::Import::BulkRequest).to receive(:new).and_call_original unless mocked_already + allow(Chewy::Index::Import::BulkRequest).to receive(:new).with(index, any_args).and_return(mock_bulk_request) end Chewy.strategy(options[:strategy] || :atomic) { block.call } @@ -146,9 +146,9 @@ def supports_block_expectations? output = '' if mock_bulk_request.updates.none? - output << "Expected index `#{type_name}` to be updated, but it was not\n" + output << "Expected index `#{index_name}` to be updated, but it was not\n" elsif @missed_reindex.present? || @missed_delete.present? - message = "Expected index `#{type_name}` " + message = "Expected index `#{index_name}` " message << [ ("to update documents #{@reindex.keys}" if @reindex.present?), ("to delete documents #{@delete.keys}" if @delete.present?) @@ -197,9 +197,9 @@ def supports_block_expectations? failure_message_when_negated do if mock_bulk_request.updates.present? - "Expected index `#{type_name}` not to be updated, but it was with #{mock_bulk_request.updates.map(&:values).flatten.group_by { |documents| documents[:_id] }.map do |id, documents| - "\n document id `#{id}` (#{documents.count} times)" - end.join}\n" + "Expected index `#{index_name}` not to be updated, but it was with #{mock_bulk_request.updates.map(&:values).flatten.group_by { |documents| documents[:_id] }.map do |id, documents| + "\n document id `#{id}` (#{documents.count} times)" + end.join}\n" end end diff --git a/lib/chewy/search.rb b/lib/chewy/search.rb index 452a02da2..170d48547 100644 --- a/lib/chewy/search.rb +++ b/lib/chewy/search.rb @@ -9,8 +9,7 @@ module Chewy # This module being included to any provides an interface to the - # request DSL. By default it is included to {Chewy::Index} and - # {Chewy::Type}. + # request DSL. By default it is included to {Chewy::Index}. # # The class used as a request DSL provider is # inherited from {Chewy::Search::Request} @@ -19,9 +18,7 @@ module Chewy # # @example # PlacesIndex.query(match: {name: 'Moscow'}) - # PlacesIndex::City.query(match: {name: 'Moscow'}) # @see Chewy::Index - # @see Chewy::Type # @see Chewy::Search::Request # @see Chewy::Search::ClassMethods # @see Chewy::Search::Pagination::Kaminari @@ -51,15 +48,14 @@ def all # @see https://www.elastic.co/guide/en/elasticsearch/reference/current/search-uri-request.html # @return [Hash] the request result def search_string(query, options = {}) - options = options.merge(all.render.slice(:index, :type).merge(q: query)) + options = options.merge(all.render.slice(:index).merge(q: query)) Chewy.client.search(options) end - # Delegates methods from the request class to the index or type class + # Delegates methods from the request class to the index class # # @example # PlacesIndex.query(match: {name: 'Moscow'}) - # PlacesIndex::City.query(match: {name: 'Moscow'}) def method_missing(name, *args, &block) if search_class::DELEGATED_METHODS.include?(name) all.send(name, *args, &block) @@ -81,11 +77,6 @@ def search_class def build_search_class(base) search_class = Class.new(base) - if self < Chewy::Type - index_scopes = index.scopes - scopes - delegate_scoped index, search_class, index_scopes - end - delegate_scoped self, search_class, scopes const_set('Query', search_class) end diff --git a/lib/chewy/search/loader.rb b/lib/chewy/search/loader.rb index 5f8c3c066..f984e2093 100644 --- a/lib/chewy/search/loader.rb +++ b/lib/chewy/search/loader.rb @@ -3,33 +3,28 @@ module Search # This class is used for two different purposes: load ORM/ODM # source objects. # - # @see Chewy::Type::Import + # @see Chewy::Index::Import # @see Chewy::Search::Request#load # @see Chewy::Search::Response#objects # @see Chewy::Search::Scrolling#scroll_objects class Loader - # @param indexes [Array] list of indexes to lookup types + # @param indexes [Array] list of indexes to lookup # @param options [Hash] adapter-specific load options - # @see Chewy::Type::Adapter::Base#load + # @see Chewy::Index::Adapter::Base#load def initialize(indexes: [], **options) @indexes = indexes @options = options end - # Returns a {Chewy::Type} object for index name and type name passed. Caches - # the result for each pair to make lookup faster. - # - # @param index [String] index name - # @param type [String] type name - # @return [Chewy::Type] - # @raise [Chewy::UnderivableType] when index or hash were not found - def derive_type(index, type) - (@derive_type ||= {})[[index, type]] ||= begin - index_class = derive_index(index) - raise Chewy::UnderivableType, "Can not find index named `#{index}`" unless index_class + def derive_index(index_name) + index = (@derive_index ||= {})[index_name] ||= indexes_hash[index_name] || + indexes_hash[indexes_hash.keys.sort_by(&:length) + .reverse.detect do |name| + index_name.match(/#{name}(_.+|\z)/) + end] + raise Chewy::UndefinedIndex, "Can not find index named `#{index}`" unless index - index_class.type_hash.values.first - end + index end # For each passed hit this method loads an ORM/ORD source object @@ -38,17 +33,17 @@ def derive_type(index, type) # will be returned at the corresponding position in array. # # Records/documents are loaded in an efficient manner, performing - # a single query for each type present. + # a single query for each index present. # # @param hits [Array] ES hits array # @return [Array] the array of corresponding ORM/ODM objects def load(hits) - hit_groups = hits.group_by { |hit| [hit['_index'], hit['_type']] } - loaded_objects = hit_groups.each_with_object({}) do |((index_name, type_name), hit_group), result| - type = derive_type(index_name, type_name) + hit_groups = hits.group_by { |hit| hit['_index'] } + loaded_objects = hit_groups.each_with_object({}) do |(index_name, hit_group), result| + index = derive_index(index_name) ids = hit_group.map { |hit| hit['_id'] } - loaded = type.adapter.load(ids, **@options.merge(_type: type)) - loaded ||= hit_group.map { |hit| type.build(hit) } + loaded = index.adapter.load(ids, **@options.merge(_index: index.base_name)) + loaded ||= hit_group.map { |hit| index.build(hit) } result.merge!(hit_group.zip(loaded).to_h) end @@ -58,14 +53,6 @@ def load(hits) private - def derive_index(index_name) - (@derive_index ||= {})[index_name] ||= indexes_hash[index_name] || - indexes_hash[indexes_hash.keys.sort_by(&:length) - .reverse.detect do |name| - index_name.match(/#{name}(_.+|\z)/) - end] - end - def indexes_hash @indexes_hash ||= @indexes.index_by(&:index_name) end diff --git a/lib/chewy/search/request.rb b/lib/chewy/search/request.rb index fbd5a6643..150fa9331 100644 --- a/lib/chewy/search/request.rb +++ b/lib/chewy/search/request.rb @@ -8,11 +8,11 @@ module Search # @see Chewy::Search # @example # scope = Chewy::Search::Request.new(PlacesIndex) - # # => ["places"], :type=>["city", "country"]}> + # # => ["places"], :body=>{}}> # scope.limit(20) - # # => ["places"], :type=>["city", "country"], :body=>{:size=>20}}> + # # => ["places"], :body=>{:size=>20}}> # scope.order(:name).offset(10) - # # => ["places"], :type=>["city", "country"], :body=>{:sort=>["name"], :from=>10}}> + # # => ["places"], :body=>{:sort=>["name"], :from=>10}}> class Request include Enumerable include Scoping @@ -52,33 +52,21 @@ class Request alias_method :total_count, :total alias_method :total_entries, :total - # The class is initialized with the list of chewy indexes and/or - # types, which are later used to compose requests. + # The class is initialized with the list of chewy indexes, + # which are later used to compose requests. # Any symbol/string passed is treated as an index identifier. # # @example # Chewy::Search::Request.new(:places) - # # => ["places"]}> + # # => ["places"], :body=>{}}> # Chewy::Search::Request.new(PlacesIndex) - # # => ["places"]}> - # Chewy::Search::Request.new(PlacesIndex::City) - # # => ["places"]}> - # Chewy::Search::Request.new(UsersIndex, PlacesIndex::City) - # # => ["users", "places"]}> - # @param indexes_or_types [Array] indices and types in any combinations - def initialize(*indices_or_types) - indices = indices_or_types.reject do |klass| - klass.is_a?(Class) && klass < Chewy::Type - end - - types = indices_or_types.select do |klass| - klass.is_a?(Class) && klass < Chewy::Type - end - - indices += types.map(&:index) - + # # => ["places"], :body=>{}}> + # Chewy::Search::Request.new(UsersIndex, PlacesIndex) + # # => ["users", "places"], :body=>{}}> + # @param indexes [Array] indexes + def initialize(*indexes) parameters.modify!(:indices) do - replace!(indices: indices) + replace!(indices: indexes) end end @@ -140,17 +128,17 @@ def inspect "<#{self.class} #{render}>" end - # @!group Chainable request modificators + # @!group Chainable request modifications # @!method query(query_hash=nil, &block) - # Adds `quer` parameter to the search request body. + # Adds `query` parameter to the search request body. # # @see https://www.elastic.co/guide/en/elasticsearch/reference/current/search-request-query.html # @see Chewy::Search::Parameters::Query # @return [Chewy::Search::Request, Chewy::Search::QueryProxy] # # @overload query(query_hash) - # If pure hash is passed it goes straight to the `quer` parameter storage. + # If pure hash is passed it goes straight to the `query` parameter storage. # Acts exactly the same way as {Chewy::Search::QueryProxy#must}. # # @see https://www.elastic.co/guide/en/elasticsearch/reference/current/query-dsl.html @@ -169,7 +157,7 @@ def inspect # @example # PlacesIndex.query { match name: 'Moscow' } # # => {:query=>{:match=>{:name=>"Moscow"}}}}> - # @yield the block is processed by `elasticsearch-ds` gem + # @yield the block is processed by `elasticsearch-dsl` gem # @return [Chewy::Search::Request] # # @overload query @@ -185,7 +173,7 @@ def inspect # @return [Chewy::Search::QueryProxy] # # @!method filter(query_hash=nil, &block) - # Adds `filte` context of the `quer` parameter at the + # Adds `filter` context of the `query` parameter at the # search request body. # # @see https://www.elastic.co/guide/en/elasticsearch/reference/current/query-filter-context.html @@ -193,7 +181,7 @@ def inspect # @return [Chewy::Search::Request, Chewy::Search::QueryProxy] # # @overload filter(query_hash) - # If pure hash is passed it goes straight to the `filte` context of the `quer` parameter storage. + # If pure hash is passed it goes straight to the `filter` context of the `query` parameter storage. # Acts exactly the same way as {Chewy::Search::QueryProxy#must}. # # @see https://www.elastic.co/guide/en/elasticsearch/reference/current/query-dsl.html @@ -214,7 +202,7 @@ def inspect # PlacesIndex.filter { match name: 'Moscow' } # # => {:query=>{:bool=>{ # # :filter=>{:match=>{:name=>"Moscow"}}}}}}> - # @yield the block is processed by `elasticsearch-ds` gem + # @yield the block is processed by `elasticsearch-dsl` gem # @return [Chewy::Search::Request] # # @overload filter @@ -232,7 +220,7 @@ def inspect # @!method post_filter(query_hash=nil, &block) # Adds `post_filter` parameter to the search request body. # - # @see https://www.elastic.co/guide/en/elasticsearch/reference/current/search-request-post-filter.html + # @see https://www.elastic.co/guide/en/elasticsearch/reference/current/filter-search-results.html#post-filter # @see Chewy::Search::Parameters::PostFilter # @return [Chewy::Search::Request, Chewy::Search::QueryProxy] # @@ -256,7 +244,7 @@ def inspect # @example # PlacesIndex.post_filter { match name: 'Moscow' } # # => {:post_filter=>{:match=>{:name=>"Moscow"}}}}> - # @yield the block is processed by `elasticsearch-ds` gem + # @yield the block is processed by `elasticsearch-dsl` gem # @return [Chewy::Search::Request] # # @overload post_filter @@ -287,7 +275,7 @@ def inspect # PlacesIndex.order(:name, population: {order: :asc}).order(:coordinates) # # => {:sort=>["name", {"population"=>{:order=>:asc}}, "coordinates"]}}> # @see Chewy::Search::Parameters::Order - # @see https://www.elastic.co/guide/en/elasticsearch/reference/current/search-request-sort.html + # @see https://www.elastic.co/guide/en/elasticsearch/reference/current/sort-search-results.html # @param values [Array] sort fields and options # @return [Chewy::Search::Request] # @@ -298,7 +286,7 @@ def inspect # PlacesIndex.docvalue_fields(:name).docvalue_fields(:population, :coordinates) # # => {:docvalue_fields=>["name", "population", "coordinates"]}}> # @see Chewy::Search::Parameters::DocvalueFields - # @see https://www.elastic.co/guide/en/elasticsearch/reference/current/search-request-docvalue-fields.html + # @see https://www.elastic.co/guide/en/elasticsearch/reference/current/search-fields.html#docvalue-fields # @param values [Array] field names # @return [Chewy::Search::Request] %i[order docvalue_fields].each do |name| @@ -328,7 +316,7 @@ def indices(value, *values) # PlacesIndex.order(:name, population: {order: :asc}).reorder(:coordinates) # # => {:sort=>["coordinates"]}}> # @see Chewy::Search::Parameters::Order - # @see https://www.elastic.co/guide/en/elasticsearch/reference/current/search-request-sort.html + # @see https://www.elastic.co/guide/en/elasticsearch/reference/current/sort-search-results.html # @param values [Array] sort fields and options # @return [Chewy::Search::Request] def reorder(value, *values) @@ -342,9 +330,9 @@ def reorder(value, *values) # PlacesIndex.track_scores # # => {:track_scores=>true}}> # PlacesIndex.track_scores.track_scores(false) - # # => ["places"], :type=>["city", "country"]}> + # # => ["places"]}> # @see Chewy::Search::Parameters::TrackScores - # @see https://www.elastic.co/guide/en/elasticsearch/reference/5.4/search-request-sort.html#_track_scores + # @see https://www.elastic.co/guide/en/elasticsearch/reference/current/sort-search-results.html#_track_scores # @param value [true, false] # @return [Chewy::Search::Request] # @@ -355,9 +343,9 @@ def reorder(value, *values) # PlacesIndex.explain # # => {:explain=>true}}> # PlacesIndex.explain.explain(false) - # # => ["places"], :type=>["city", "country"]}> + # # => ["places"]}> # @see Chewy::Search::Parameters::Explain - # @see https://www.elastic.co/guide/en/elasticsearch/reference/5.4/search-request-explain.html + # @see https://www.elastic.co/guide/en/elasticsearch/reference/current/search-explain.html # @param value [true, false] # @return [Chewy::Search::Request] # @@ -368,9 +356,9 @@ def reorder(value, *values) # PlacesIndex.version # # => {:version=>true}}> # PlacesIndex.version.version(false) - # # => ["places"], :type=>["city", "country"]}> + # # => ["places"]}> # @see Chewy::Search::Parameters::Version - # @see https://www.elastic.co/guide/en/elasticsearch/reference/5.4/search-request-version.html + # @see https://www.elastic.co/guide/en/elasticsearch/reference/current/search-search.html # @param value [true, false] # @return [Chewy::Search::Request] # @@ -381,9 +369,9 @@ def reorder(value, *values) # PlacesIndex.profile # # => {:profile=>true}}> # PlacesIndex.profile.profile(false) - # # => ["places"], :type=>["city", "country"]}> + # # => ["places"]}> # @see Chewy::Search::Parameters::Profile - # @see https://www.elastic.co/guide/en/elasticsearch/reference/5.4/search-profile.html + # @see https://www.elastic.co/guide/en/elasticsearch/reference/current/search-profile.html # @param value [true, false] # @return [Chewy::Search::Request] # @@ -417,7 +405,7 @@ def reorder(value, *values) # PlacesIndex.request_cache(false) # # => {:request_cache=>false}}> # @see Chewy::Search::Parameters::RequestCache - # @see https://www.elastic.co/guide/en/elasticsearch/reference/5.4/shard-request-cache.html#_enabling_and_disabling_caching_per_request + # @see https://www.elastic.co/guide/en/elasticsearch/reference/current/shard-request-cache.html#_enabling_and_disabling_caching_per_request # @param value [true, false, nil] # @return [Chewy::Search::Request] # @@ -428,7 +416,7 @@ def reorder(value, *values) # PlacesIndex.search_type(:dfs_query_then_fetch) # # => {:search_type=>"dfs_query_then_fetch"}}> # @see Chewy::Search::Parameters::SearchType - # @see https://www.elastic.co/guide/en/elasticsearch/reference/5.4/search-request-search-type.html + # @see https://www.elastic.co/guide/en/elasticsearch/reference/current/search-search.html#search-type # @param value [String, Symbol] # @return [Chewy::Search::Request] # @@ -439,7 +427,7 @@ def reorder(value, *values) # PlacesIndex.preference(:_primary_first) # # => {:preference=>"_primary_first"}}> # @see Chewy::Search::Parameters::Preference - # @see https://www.elastic.co/guide/en/elasticsearch/reference/5.4/search-request-preference.html + # @see https://www.elastic.co/guide/en/elasticsearch/reference/current/search-search.html#search-preference # @param value [String, Symbol] # @return [Chewy::Search::Request] # @@ -450,7 +438,7 @@ def reorder(value, *values) # PlacesIndex.timeout('1m') # {:timeout=>"1m"}}> # @see Chewy::Search::Parameters::Timeout - # @see https://www.elastic.co/guide/en/elasticsearch/reference/5.4/common-options.html#time-units + # @see https://www.elastic.co/guide/en/elasticsearch/reference/current/common-options.html#time-units # @param value [String, Symbol] # @return [Chewy::Search::Request] # @@ -461,7 +449,7 @@ def reorder(value, *values) # PlacesIndex.limit(10) # {:size=>10}}> # @see Chewy::Search::Parameters::Limit - # @see https://www.elastic.co/guide/en/elasticsearch/reference/5.4/search-request-from-size.html + # @see https://www.elastic.co/guide/en/elasticsearch/reference/current/paginate-search-results.html # @param value [String, Integer] # @return [Chewy::Search::Request] # @@ -472,7 +460,7 @@ def reorder(value, *values) # PlacesIndex.offset(10) # {:from=>10}}> # @see Chewy::Search::Parameters::Offset - # @see https://www.elastic.co/guide/en/elasticsearch/reference/5.4/search-request-from-size.html + # @see https://www.elastic.co/guide/en/elasticsearch/reference/current/paginate-search-results.html # @param value [String, Integer] # @return [Chewy::Search::Request] # @@ -483,7 +471,7 @@ def reorder(value, *values) # PlacesIndex.terminate_after(10) # {:terminate_after=>10}}> # @see Chewy::Search::Parameters::Offset - # @see https://www.elastic.co/guide/en/elasticsearch/reference/5.4/search-request-body.html + # @see https://www.elastic.co/guide/en/elasticsearch/reference/current/search-your-data.html#quickly-check-for-matching-docs # @param value [String, Integer] # @return [Chewy::Search::Request] # @@ -494,7 +482,7 @@ def reorder(value, *values) # PlacesIndex.min_score(2) # {:min_score=>2.0}}> # @see Chewy::Search::Parameters::Offset - # @see https://www.elastic.co/guide/en/elasticsearch/reference/5.4/search-request-min-score.html + # @see https://www.elastic.co/guide/en/elasticsearch/reference/current/search-search.html#search-api-min-score # @param value [String, Integer, Float] # @return [Chewy::Search::Request] %i[request_cache search_type preference timeout limit offset terminate_after min_score].each do |name| @@ -506,7 +494,7 @@ def reorder(value, *values) # @!method source(*values) # Updates `_source` request part. Accepts either an array # of field names/templates or a hash with `includes` and `excludes` - # keys. Source also can be disabled entierly or enabled again. + # keys. Source also can be disabled entirely or enabled again. # # @example # PlacesIndex.source(:name).source(includes: [:popularity], excludes: :description) @@ -514,13 +502,13 @@ def reorder(value, *values) # PlacesIndex.source(false) # # => {:_source=>false}}> # @see Chewy::Search::Parameters::Source - # @see https://www.elastic.co/guide/en/elasticsearch/reference/5.4/search-request-source-filtering.html + # @see https://www.elastic.co/guide/en/elasticsearch/reference/current/search-fields.html#source-filtering # @param values [true, false, {Symbol => Array, String, Symbol}, Array, String, Symbol] # @return [Chewy::Search::Request] # # @!method stored_fields(*values) # Updates `stored_fields` request part. Accepts an array of field - # names. Can be entierly disabled and enabled back. + # names. Can be entirely disabled and enabled back. # # @example # PlacesIndex.stored_fields(:name).stored_fields(:description) @@ -528,7 +516,7 @@ def reorder(value, *values) # PlacesIndex.stored_fields(false) # # => {:stored_fields=>"_none_"}}> # @see Chewy::Search::Parameters::StoredFields - # @see https://www.elastic.co/guide/en/elasticsearch/reference/5.4/search-request-stored-fields.html + # @see https://www.elastic.co/guide/en/elasticsearch/reference/current/search-fields.html#stored-fields # @param values [true, false, String, Symbol, Array] # @return [Chewy::Search::Request] %i[source stored_fields].each do |name| @@ -544,7 +532,7 @@ def reorder(value, *values) # PlacesIndex.search_after(42, 'Moscow').search_after('London') # # => {:search_after=>["London"]}}> # @see Chewy::Search::Parameters::SearchAfter - # @see https://www.elastic.co/guide/en/elasticsearch/reference/5.4/search-request-search-after.html + # @see https://www.elastic.co/guide/en/elasticsearch/reference/current/paginate-search-results.html#search-after # @param value [Array, Object] # @return [Chewy::Search::Request] def search_after(value, *values) @@ -552,9 +540,9 @@ def search_after(value, *values) end # Stores ORM/ODM objects loading options. Options - # might be define per-type or be global, depends on the adapter + # might be define per-index or be global, depends on the adapter # loading implementation. Also, there are 2 loading options to select - # or exclude types from loading: `only` and `except` respectively. + # or exclude indexes from loading: `only` and `except` respectively. # Options are updated on further method calls. # # @example @@ -579,7 +567,7 @@ def load(options = nil) # # "field1"=>{:script=>{:lang=>"painless", :inline=>"some script here"}}, # # "field2"=>{:script=>{:lang=>"painless", :inline=>"some script here"}}}}}> # @see Chewy::Search::Parameters::ScriptFields - # @see https://www.elastic.co/guide/en/elasticsearch/reference/5.4/search-request-script-fields.html + # @see https://www.elastic.co/guide/en/elasticsearch/reference/current/search-fields.html#script-fields # @param value [Hash] # @return [Chewy::Search::Request] # @@ -591,7 +579,7 @@ def load(options = nil) # PlacesIndex.indices_boost(index1: 1.2, index2: 1.3).indices_boost(index1: 1.5) # # => {:indices_boost=>[{"index2"=>1.3}, {"index1"=>1.5}]}}> # @see Chewy::Search::Parameters::IndicesBoost - # @see https://www.elastic.co/guide/en/elasticsearch/reference/5.4/search-request-index-boost.html + # @see https://www.elastic.co/guide/en/elasticsearch/reference/current/search-multiple-indices.html#index-boost # @param value [{String, Symbol => String, Integer, Float}] # @return [Chewy::Search::Request] # @@ -603,7 +591,7 @@ def load(options = nil) # PlacesIndex.rescore(window_size: 100, query: {}).rescore(window_size: 200, query: {}) # # => {:rescore=>[{:window_size=>100, :query=>{}}, {:window_size=>200, :query=>{}}]}}> # @see Chewy::Search::Parameters::Rescore - # @see https://www.elastic.co/guide/en/elasticsearch/reference/5.4/search-request-rescore.html + # @see https://www.elastic.co/guide/en/elasticsearch/reference/current/filter-search-results.html#rescore # @param value [Hash, Array] # @return [Chewy::Search::Request] # @@ -619,7 +607,7 @@ def load(options = nil) # # "fields"=>{:description=>{:type=>"plain"}}, # # "pre_tags"=>[""], "post_tags"=>[""]}}}> # @see Chewy::Search::Parameters::Highlight - # @see https://www.elastic.co/guide/en/elasticsearch/reference/5.4/search-request-highlighting.html + # @see https://www.elastic.co/guide/en/elasticsearch/reference/current/highlighting.html # @param value [Hash] # @return [Chewy::Search::Request] %i[script_fields indices_boost rescore highlight].each do |name| @@ -642,7 +630,7 @@ def load(options = nil) # # "names"=>{:text=>"tring out Elasticsearch"}, # # "descriptions"=>{:text=>"some other text"}}}}> # @see Chewy::Search::Parameters::Suggest - # @see https://www.elastic.co/guide/en/elasticsearch/reference/5.4/search-suggesters.html + # @see https://www.elastic.co/guide/en/elasticsearch/reference/current/search-suggesters.html # @param value [Hash] # @return [Chewy::Search::Request] # @@ -846,14 +834,14 @@ def exists? # @overload first # If nothing is passed - it returns a single object. # - # @return [Chewy::Type] result document + # @return [Chewy::Index] result document # # @overload first(limit) # If limit is provided - it returns the limit amount or less # of wrapper objects. # # @param limit [Integer] amount of requested results - # @return [Array] result document collection + # @return [Array] result document collection def first(limit = UNDEFINED) request_limit = limit == UNDEFINED ? 1 : limit @@ -872,7 +860,7 @@ def first(limit = UNDEFINED) # If single id is passed - it returns a single object. # # @param id [Integer, String] id of the desired document - # @return [Chewy::Type] result document + # @return [Chewy::Index] result document # # @overload find(*ids) # If several field are passed - it returns an array of wrappers. @@ -880,7 +868,7 @@ def first(limit = UNDEFINED) # batch size - uses scroll API to retrieve everything. # # @param ids [Array] ids of the desired documents - # @return [Array] result documents + # @return [Array] result documents def find(*ids) return super if block_given? diff --git a/lib/chewy/search/response.rb b/lib/chewy/search/response.rb index 9169d0367..0a5becb24 100644 --- a/lib/chewy/search/response.rb +++ b/lib/chewy/search/response.rb @@ -64,12 +64,12 @@ def aggs end alias_method :aggregations, :aggs - # {Chewy::Type} wrappers collection instantiated on top of hits. + # {Chewy::Index} wrappers collection instantiated on top of hits. # - # @return [Array] + # @return [Array] def wrappers @wrappers ||= hits.map do |hit| - @loader.derive_type(hit['_index'], hit['_type']).build(hit) + @loader.derive_index(hit['_index']).build(hit) end end @@ -102,7 +102,7 @@ def objects # end # @see #wrappers # @see #objects - # @return [{Chewy::Type => Object}] a hash with wrappers as keys and ORM/ODM objects as values + # @return [{Chewy::Index => Object}] a hash with wrappers as keys and ORM/ODM objects as values def object_hash @object_hash ||= wrappers.zip(objects).to_h end diff --git a/lib/chewy/search/scoping.rb b/lib/chewy/search/scoping.rb index f37e59c64..0596555a2 100644 --- a/lib/chewy/search/scoping.rb +++ b/lib/chewy/search/scoping.rb @@ -9,17 +9,16 @@ module Search # query(match: {name: name}) # end # - # define_type :user do - # def self.by_age(age) - # filter(term: {age: age}) - # end + # + # def self.by_age(age) + # filter(term: {age: age}) # end # end # # UsersIndex.limit(10).by_name('Martin') # # => {:size=>10, :query=>{:match=>{:name=>"Martin"}}}}> - # UsersIndex::User.limit(10).by_name('Martin').by_age(42) - # # => {:size=>10, :query=>{:bool=>{ + # UsersIndex.limit(10).by_name('Martin').by_age(42) + # # => {:size=>10, :query=>{:bool=>{ # # :must=>{:match=>{:name=>"Martin"}}, # # :filter=>{:term=>{:age=>42}}}}}}> module Scoping @@ -28,7 +27,7 @@ module Scoping module ClassMethods # The scopes stack. # - # @return [Array] array of scopes + # @return [Array] array of scopes def scopes Thread.current[:chewy_scopes] ||= [] end diff --git a/lib/chewy/search/scrolling.rb b/lib/chewy/search/scrolling.rb index 25e35e88f..685b1552a 100644 --- a/lib/chewy/search/scrolling.rb +++ b/lib/chewy/search/scrolling.rb @@ -72,7 +72,7 @@ def scroll_hits(**options, &block) # @!method scroll_wrappers(batch_size: 1000, scroll: '1m') # Iterates through the documents of the scope in batches. Yields - # each hit wrapped with {Chewy::Type}. + # each hit wrapped with {Chewy::Index}. # # @param batch_size [Integer] batch size obviously, replaces `size` query parameter # @param scroll [String] cursor expiration time @@ -80,7 +80,7 @@ def scroll_hits(**options, &block) # @overload scroll_wrappers(batch_size: 1000, scroll: '1m') # @example # PlaceIndex.scroll_wrappers { |object| p object.id } - # @yieldparam object [Chewy::Type] block is executed for each hit object + # @yieldparam object [Chewy::Index] block is executed for each hit object # # @overload scroll_wrappers(batch_size: 1000, scroll: '1m') # @example @@ -90,7 +90,7 @@ def scroll_wrappers(**options) return enum_for(:scroll_wrappers, **options) unless block_given? scroll_hits(**options).each do |hit| - yield loader.derive_type(hit['_index'], hit['_type']).build(hit) + yield loader.derive_index(hit['_index']).build(hit) end end diff --git a/lib/chewy/stash.rb b/lib/chewy/stash.rb index b9fc900d2..2181ee492 100644 --- a/lib/chewy/stash.rb +++ b/lib/chewy/stash.rb @@ -9,11 +9,9 @@ module Stash class Specification < Chewy::Index index_name 'chewy_specifications' - define_type :specification do - default_import_options journal: false + default_import_options journal: false - field :specification, type: 'binary' - end + field :specification, type: 'binary' end class Journal < Chewy::Index @@ -43,33 +41,26 @@ def self.clean(until_time = nil, only: []) # @param indices [Chewy::Index, Array] def self.for(*something) something = something.flatten.compact - types = something.flat_map { |s| Chewy.derive_types(s) } - return none if something.present? && types.blank? + indexes = something.flat_map { |s| Chewy.derive_name(s) } + return none if something.present? && indexes.blank? scope = all - types.map(&:index).uniq.each do |index| + indexes.each do |index| scope = scope.or(filter(term: {index_name: index.derivable_name})) end scope end - define_type :journal do - default_import_options journal: false - - field :index_name, type: 'keyword' - field :type_name, type: 'keyword' - field :action, type: 'keyword' - field :references, type: 'binary' - field :created_at, type: 'date' + default_import_options journal: false - def type - @type ||= Chewy.derive_type("#{index_name}##{type_name}") - end + field :index_name, type: 'keyword' + field :action, type: 'keyword' + field :references, type: 'binary' + field :created_at, type: 'date' - def references - @references ||= Array.wrap(@attributes['references']).map do |item| - JSON.load(Base64.decode64(item)) # rubocop:disable Security/JSONLoad - end + def references + @references ||= Array.wrap(@attributes['references']).map do |item| + JSON.load(Base64.decode64(item)) # rubocop:disable Security/JSONLoad end end end diff --git a/lib/chewy/type.rb b/lib/chewy/type.rb deleted file mode 100644 index 159b44ecf..000000000 --- a/lib/chewy/type.rb +++ /dev/null @@ -1,119 +0,0 @@ -require 'chewy/search' -require 'chewy/type/adapter/object' -require 'chewy/type/adapter/active_record' -require 'chewy/type/mapping' -require 'chewy/type/wrapper' -require 'chewy/type/observe' -require 'chewy/type/actions' -require 'chewy/type/syncer' -require 'chewy/type/crutch' -require 'chewy/type/import' -require 'chewy/type/witchcraft' - -module Chewy - class Type - IMPORT_OPTIONS_KEYS = %i[ - batch_size bulk_size consistency direct_import journal - pipeline raw_import refresh replication - ].freeze - - include Search - include Mapping - include Wrapper - include Observe - include Actions - include Crutch - include Witchcraft - include Import - - singleton_class.delegate :index_name, :client, to: :index - - class_attribute :_default_import_options - self._default_import_options = {} - - class << self - # Chewy index current type belongs to. Defined inside `Chewy.create_type` - # - def index - raise NotImplementedError, - 'Looks like this type was defined outside the index scope and `.index` method is undefined for it' - end - - # Current type adapter. Defined inside `Chewy.create_type`, derived from - # `Chewy::Index.define_type` arguments. - # - def adapter - raise NotImplementedError - end - - # Returns type name string - # - def type_name - adapter.type_name - end - - # Appends type name to {Chewy::Index.derivable_name} - # - # @example - # class Namespace::UsersIndex < Chewy::Index - # define_type User - # end - # UsersIndex::User.derivable_name # => 'namespace/users#user' - # - # @see Chewy::Index.derivable_name - # @return [String, nil] derivable name or nil when it is impossible to calculate - def derivable_name - @derivable_name ||= [index.derivable_name, type_name].join('#') if index&.derivable_name - end - - # This method is an API shared with {Chewy::Index}, added for convenience. - # - # @return [Chewy::Type] array containing itself - def types - [self] - end - - # Returns list of public class methods defined in current type - # - def scopes - public_methods - Chewy::Type.public_methods - end - - def default_import_options(params) - params.assert_valid_keys(IMPORT_OPTIONS_KEYS) - self._default_import_options = _default_import_options.merge(params) - end - - def method_missing(method, *args, &block) - if index.scopes.include?(method) - define_singleton_method method do |*method_args, &method_block| - all.scoping { index.public_send(method, *method_args, &method_block) } - end - send(method, *args, &block) - else - super - end - end - - def respond_to_missing?(method, _) - index.scopes.include?(method) || super - end - - def const_missing(name) - to_resolve = "#{self}::#{name}" - to_resolve[index.to_s] = '' - - @__resolved_constants ||= {} - - if to_resolve.empty? || @__resolved_constants[to_resolve] - super - else - @__resolved_constants[to_resolve] = true - to_resolve.constantize - end - rescue NotImplementedError - super - end - end - end -end diff --git a/lib/chewy/type/actions.rb b/lib/chewy/type/actions.rb deleted file mode 100644 index 452b4216a..000000000 --- a/lib/chewy/type/actions.rb +++ /dev/null @@ -1,44 +0,0 @@ -module Chewy - class Type - module Actions - extend ActiveSupport::Concern - - module ClassMethods - # Deletes all documents of a type and reimports them - # - # @example - # UsersIndex::User.reset - # - # @see Chewy::Type::Import::ClassMethods#import - # @see Chewy::Type::Import::ClassMethods#import - # @return [true, false] the result of import - def reset - delete_all - import - end - - # Performs missing and outdated objects synchronization for the current type. - # - # @example - # UsersIndex::User.sync - # - # @see Chewy::Type::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 reindexed 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 - - # A {Chewy::Journal} instance for the particular type - # - # @return [Chewy::Journal] journal instance - def journal - @journal ||= Chewy::Journal.new(self) - end - end - end - end -end diff --git a/spec/chewy/fields/base_spec.rb b/spec/chewy/fields/base_spec.rb index 135559676..1d427e990 100644 --- a/spec/chewy/fields/base_spec.rb +++ b/spec/chewy/fields/base_spec.rb @@ -141,14 +141,12 @@ context 'default field type' do before do stub_index(:events) do - define_type :event do + field :id + field :category do field :id - field :category do + field :licenses do field :id - field :licenses do - field :id - field :created_at, type: 'time' - end + field :created_at, type: 'time' end end end @@ -162,18 +160,20 @@ end specify do - expect(EventsIndex::Event.mappings_hash).to eq( - properties: { - id: {type: 'integer'}, - category: { - type: 'object', - properties: { - id: {type: 'integer'}, - licenses: { - type: 'object', - properties: { - id: {type: 'integer'}, - created_at: {type: 'time'} + expect(EventsIndex.mappings_hash).to eq( + mappings: { + properties: { + id: {type: 'integer'}, + category: { + type: 'object', + properties: { + id: {type: 'integer'}, + licenses: { + type: 'object', + properties: { + id: {type: 'integer'}, + created_at: {type: 'time'} + } } } } @@ -186,14 +186,12 @@ context 'objects, hashes and arrays' do before do stub_index(:events) do - define_type :event do + field :id + field :category do field :id - field :category do + field :licenses do field :id - field :licenses do - field :id - field :name - end + field :name end end end @@ -201,13 +199,13 @@ specify do expect( - EventsIndex::Event.root.compose({id: 1, category: {id: 2, licenses: {id: 3, name: 'Name'}}}) + EventsIndex.root.compose({id: 1, category: {id: 2, licenses: {id: 3, name: 'Name'}}}) ).to eq('id' => 1, 'category' => {'id' => 2, 'licenses' => {'id' => 3, 'name' => 'Name'}}) end specify do expect( - EventsIndex::Event.root.compose({id: 1, category: [ + EventsIndex.root.compose({id: 1, category: [ {id: 2, 'licenses' => {id: 3, name: 'Name1'}}, {id: 4, licenses: nil} ]}) @@ -219,7 +217,7 @@ specify do expect( - EventsIndex::Event.root.compose({ + EventsIndex.root.compose({ 'id' => 1, category: { id: 2, licenses: [ @@ -242,7 +240,7 @@ specify do expect( - EventsIndex::Event.root.compose({id: 1, category: [ + EventsIndex.root.compose({id: 1, category: [ {id: 2, licenses: [ {id: 3, 'name' => 'Name1'}, {id: 4, name: 'Name2'} ]}, @@ -261,13 +259,13 @@ end specify do expect( - EventsIndex::Event.root.compose(double(id: 1, category: double(id: 2, licenses: double(id: 3, name: 'Name')))) + EventsIndex.root.compose(double(id: 1, category: double(id: 2, licenses: double(id: 3, name: 'Name')))) ).to eq('id' => 1, 'category' => {'id' => 2, 'licenses' => {'id' => 3, 'name' => 'Name'}}) end specify do expect( - EventsIndex::Event.root.compose(double(id: 1, category: [ + EventsIndex.root.compose(double(id: 1, category: [ double(id: 2, licenses: double(id: 3, name: 'Name1')), double(id: 4, licenses: nil) ])) @@ -279,7 +277,7 @@ specify do expect( - EventsIndex::Event.root.compose(double(id: 1, category: double(id: 2, licenses: [ + EventsIndex.root.compose(double(id: 1, category: double(id: 2, licenses: [ double(id: 3, name: 'Name1'), double(id: 4, name: 'Name2') ]))) ).to eq('id' => 1, 'category' => {'id' => 2, 'licenses' => [ @@ -289,7 +287,7 @@ specify do expect( - EventsIndex::Event.root.compose(double(id: 1, category: [ + EventsIndex.root.compose(double(id: 1, category: [ double(id: 2, licenses: [ double(id: 3, name: 'Name1'), double(id: 4, name: 'Name2') ]), @@ -309,14 +307,12 @@ context 'custom methods' do before do stub_index(:events) do - define_type :event do + field :id, type: 'integer' + field :category, value: -> { categories } do field :id, type: 'integer' - field :category, value: -> { categories } do + field :licenses, value: -> { license } do field :id, type: 'integer' - field :licenses, value: -> { license } do - field :id, type: 'integer' - field :name - end + field :name end end end @@ -324,7 +320,7 @@ specify do expect( - EventsIndex::Event.root.compose( + EventsIndex.root.compose( double( id: 1, categories: double( id: 2, license: double( @@ -340,34 +336,34 @@ context 'objects and multi_fields' do before do stub_index(:events) do - define_type :event do - field :id, type: 'integer' - field :name, type: 'integer' do - field :raw, analyzer: 'my_own' - end - field :category, type: 'object' + field :id, type: 'integer' + field :name, type: 'integer' do + field :raw, analyzer: 'my_own' end + field :category, type: 'object' end end specify do - expect(EventsIndex::Event.mappings_hash).to eq( - properties: { - id: {type: 'integer'}, - name: { - type: 'integer', - fields: { - raw: {analyzer: 'my_own', type: Chewy.default_field_type} - } - }, - category: {type: 'object'} + expect(EventsIndex.mappings_hash).to eq( + mappings: { + properties: { + id: {type: 'integer'}, + name: { + type: 'integer', + fields: { + raw: {analyzer: 'my_own', type: Chewy.default_field_type} + } + }, + category: {type: 'object'} + } } ) end specify do expect( - EventsIndex::Event.root.compose( + EventsIndex.root.compose( double( id: 1, name: 'Jonny', category: double( id: 2, as_json: {'name' => 'Borogoves'} @@ -383,7 +379,7 @@ specify do expect( - EventsIndex::Event.root.compose( + EventsIndex.root.compose( double(id: 1, name: 'Jonny', category: [ double(id: 2, as_json: {'name' => 'Borogoves1'}), double(id: 3, as_json: {'name' => 'Borogoves2'}) @@ -415,14 +411,13 @@ context 'text fields with and without ignore_blank option' do before do stub_index(:countries) do - define_type Country do + index_scope Country + field :id + field :cities do field :id - field :cities do - field :id - field :name - field :historical_name, ignore_blank: false - field :description, ignore_blank: true - end + field :name + field :historical_name, ignore_blank: false + field :description, ignore_blank: true end end end @@ -437,7 +432,7 @@ end specify do - expect(CountriesIndex::Country.root.compose(country_with_cities)).to eq( + expect(CountriesIndex.root.compose(country_with_cities)).to eq( 'id' => 1, 'cities' => [ {'id' => 1, 'name' => '', 'historical_name' => ''}, {'id' => 2, 'name' => '', 'historical_name' => ''} @@ -450,14 +445,13 @@ context 'with ignore_blank: true option' do before do stub_index(:countries) do - define_type Country do + index_scope Country + field :id + field :cities, ignore_blank: true do field :id - field :cities, ignore_blank: true do - field :id - field :name - field :historical_name, ignore_blank: true - field :description - end + field :name + field :historical_name, ignore_blank: true + field :description end end end @@ -466,14 +460,14 @@ context('without cities') do let(:cities) { [] } specify do - expect(CountriesIndex::Country.root.compose(country)) + expect(CountriesIndex.root.compose(country)) .to eq('id' => 1) end end context('with cities') do let(:cities) { [City.create!(id: 1, name: '', historical_name: '')] } specify do - expect(CountriesIndex::Country.root.compose(country)).to eq( + expect(CountriesIndex.root.compose(country)).to eq( 'id' => 1, 'cities' => [ {'id' => 1, 'name' => '', 'description' => nil} ] @@ -485,14 +479,13 @@ context 'with ignore_blank: false option' do before do stub_index(:countries) do - define_type Country do + index_scope Country + field :id + field :cities, ignore_blank: false do field :id - field :cities, ignore_blank: false do - field :id - field :name - field :historical_name - field :description - end + field :name + field :historical_name + field :description end end end @@ -500,7 +493,7 @@ let(:country_with_cities) { Country.create!(id: 1) } specify do - expect(CountriesIndex::Country.root.compose(country_with_cities)) + expect(CountriesIndex.root.compose(country_with_cities)) .to eq('id' => 1, 'cities' => []) end end @@ -508,14 +501,13 @@ context 'without ignore_blank: true option' do before do stub_index(:countries) do - define_type Country do + index_scope Country + field :id + field :cities do field :id - field :cities do - field :id - field :name - field :historical_name - field :description - end + field :name + field :historical_name + field :description end end end @@ -523,7 +515,7 @@ let(:country_with_cities) { Country.create!(id: 1) } specify do - expect(CountriesIndex::Country.root.compose(country_with_cities)) + expect(CountriesIndex.root.compose(country_with_cities)) .to eq('id' => 1, 'cities' => []) end end @@ -533,15 +525,14 @@ context 'with ignore_blank: true option' do before do stub_index(:countries) do - define_type Country do + index_scope Country + field :id + field :cities do field :id - field :cities do - field :id - field :name - field :location, type: :geo_point, ignore_blank: true do - field :lat - field :lon - end + field :name + field :location, type: :geo_point, ignore_blank: true do + field :lat + field :lon end end end @@ -549,7 +540,7 @@ specify do expect( - CountriesIndex::Country.root.compose({ + CountriesIndex.root.compose({ 'id' => 1, 'cities' => [ {'id' => 1, 'name' => 'City1', 'location' => {}}, @@ -568,15 +559,14 @@ context 'without ignore_blank option' do before do stub_index(:countries) do - define_type Country do + index_scope Country + field :id + field :cities do field :id - field :cities do - field :id - field :name - field :location, type: :geo_point do - field :lat - field :lon - end + field :name + field :location, type: :geo_point do + field :lat + field :lon end end end @@ -584,7 +574,7 @@ specify do expect( - CountriesIndex::Country.root.compose({ + CountriesIndex.root.compose({ 'id' => 1, 'cities' => [ {'id' => 1, 'name' => 'City1', 'location' => {}}, @@ -603,15 +593,14 @@ context 'with ignore_blank: false flag' do before do stub_index(:countries) do - define_type Country do + index_scope Country + field :id + field :cities do field :id - field :cities do - field :id - field :name - field :location, type: :geo_point, ignore_blank: false do - field :lat - field :lon - end + field :name + field :location, type: :geo_point, ignore_blank: false do + field :lat + field :lon end end end @@ -619,7 +608,7 @@ specify do expect( - CountriesIndex::Country.root.compose({ + CountriesIndex.root.compose({ 'id' => 1, 'cities' => [ {'id' => 1, 'location' => {}, 'name' => 'City1'}, @@ -646,12 +635,11 @@ Country.has_many :cities, -> { order :id } stub_index(:countries) do - define_type Country do + index_scope Country + field :id + field :cities do field :id - field :cities do - field :id - field :name - end + field :name end end end @@ -663,7 +651,7 @@ end specify do - expect(CountriesIndex::Country.root.compose(country_with_cities)).to eq('id' => 1, 'cities' => [ + expect(CountriesIndex.root.compose(country_with_cities)).to eq('id' => 1, 'cities' => [ {'id' => 1, 'name' => 'City1'}, {'id' => 2, 'name' => 'City2'} ]) end @@ -671,19 +659,18 @@ context 'nested object' do before do stub_index(:cities) do - define_type City do + index_scope City + field :id + field :country do field :id - field :country do - field :id - field :name - end + field :name end end end specify do expect( - CitiesIndex::City.root.compose(City.create!(id: 1, country: Country.create!(id: 1, name: 'Country'))) + CitiesIndex.root.compose(City.create!(id: 1, country: Country.create!(id: 1, name: 'Country'))) ).to eq('id' => 1, 'country' => {'id' => 1, 'name' => 'Country'}) end end diff --git a/spec/chewy/fields/root_spec.rb b/spec/chewy/fields/root_spec.rb index dc6bfb3c7..18bf7cb0c 100644 --- a/spec/chewy/fields/root_spec.rb +++ b/spec/chewy/fields/root_spec.rb @@ -46,18 +46,18 @@ before do stub_model(:city) stub_index(:places) do - define_type City + index_scope City end end let(:city) { City.new(name: 'London', rating: 100) } specify do - expect(PlacesIndex::City.root.compose(city)) + expect(PlacesIndex.root.compose(city)) .to match(hash_including('name' => 'London', 'rating' => 100)) end specify do - expect(PlacesIndex::City.root.compose(city, fields: %i[name borogoves])) + expect(PlacesIndex.root.compose(city, fields: %i[name borogoves])) .to eq('name' => 'London') end end @@ -65,20 +65,18 @@ context 'has children' do before do stub_index(:places) do - define_type :city do - field :name, :rating - end + field :name, :rating end end let(:city) { double(name: 'London', rating: 100) } specify do - expect(PlacesIndex::City.root.compose(city)) + expect(PlacesIndex.root.compose(city)) .to eq('name' => 'London', 'rating' => 100) end specify do - expect(PlacesIndex::City.root.compose(city, fields: %i[name borogoves])) + expect(PlacesIndex.root.compose(city, fields: %i[name borogoves])) .to eq('name' => 'London') end end @@ -86,21 +84,19 @@ context 'root value provided' do before do stub_index(:places) do - define_type :city do - root value: ->(o) { {name: "#{o.name}Modified", rating: o.rating.next} } - end + root value: ->(o) { {name: "#{o.name}Modified", rating: o.rating.next} } end end let(:city) { double(name: 'London', rating: 100) } specify do - expect(PlacesIndex::City.root.compose(city)) + expect(PlacesIndex.root.compose(city)) .to eq('name' => 'LondonModified', 'rating' => 101) end specify do - expect(PlacesIndex::City.root.compose(city, fields: %i[name borogoves])) + expect(PlacesIndex.root.compose(city, fields: %i[name borogoves])) .to eq('name' => 'LondonModified') end end @@ -108,11 +104,9 @@ context 'complex evaluations' do before do stub_index(:places) do - define_type :city do - root value: ->(o) { {name: "#{o.name}Modified", rating: o.rating.next} } do - field :name, value: ->(o) { "#{o[:name]}Modified" } - field :rating - end + root value: ->(o) { {name: "#{o.name}Modified", rating: o.rating.next} } do + field :name, value: ->(o) { "#{o[:name]}Modified" } + field :rating end end end @@ -120,12 +114,12 @@ let(:city) { double(name: 'London', rating: 100) } specify do - expect(PlacesIndex::City.root.compose(city)) + expect(PlacesIndex.root.compose(city)) .to eq('name' => 'LondonModifiedModified', 'rating' => 101) end specify do - expect(PlacesIndex::City.root.compose(city, fields: %i[name borogoves])) + expect(PlacesIndex.root.compose(city, fields: %i[name borogoves])) .to eq('name' => 'LondonModifiedModified') end end @@ -134,14 +128,12 @@ describe '#child_hash' do before do stub_index(:places) do - define_type :city do - field :name, :rating - end + field :name, :rating end end specify do - expect(PlacesIndex::City.root.child_hash).to match( + expect(PlacesIndex.root.child_hash).to match( name: an_instance_of(Chewy::Fields::Base).and(have_attributes(name: :name)), rating: an_instance_of(Chewy::Fields::Base).and(have_attributes(name: :rating)) ) diff --git a/spec/chewy/fields/time_fields_spec.rb b/spec/chewy/fields/time_fields_spec.rb index 395033f34..31aef9777 100644 --- a/spec/chewy/fields/time_fields_spec.rb +++ b/spec/chewy/fields/time_fields_spec.rb @@ -5,14 +5,12 @@ before do stub_index(:posts) do - define_type :post do - field :published_at, type: 'date' - end + field :published_at, type: 'date' end end before do - PostsIndex::Post.import( + PostsIndex.import( double(published_at: ActiveSupport::TimeZone[-28_800].parse('2014/12/18 19:00')), double(published_at: ActiveSupport::TimeZone[-21_600].parse('2014/12/18 20:00')), double(published_at: ActiveSupport::TimeZone[-21_600].parse('2014/12/17 20:00')) diff --git a/spec/chewy/index/actions_spec.rb b/spec/chewy/index/actions_spec.rb index 9a943940d..0ca812d92 100644 --- a/spec/chewy/index/actions_spec.rb +++ b/spec/chewy/index/actions_spec.rb @@ -349,7 +349,7 @@ before do stub_model(:city) stub_index(:cities) do - define_type City + index_scope City end end let!(:dummy_cities) { Array.new(3) { |i| City.create(id: i + 1, name: "name#{i}") } } @@ -371,9 +371,8 @@ context do before do stub_index(:cities) do - define_type City do - field :name, type: 'object' - end + index_scope City + field :name, type: 'object' end end @@ -385,7 +384,7 @@ before do stub_model(:city) stub_index(:cities) do - define_type City + index_scope City end end let!(:dummy_cities) { Array.new(3) { |i| City.create(id: i + 1, name: "name#{i}") } } @@ -407,9 +406,8 @@ context do before do stub_index(:cities) do - define_type City do - field :name, type: 'object' - end + index_scope City + field :name, type: 'object' end end @@ -421,7 +419,7 @@ before do stub_model(:city) stub_index(:cities) do - define_type City + index_scope City end end @@ -512,7 +510,7 @@ before do stub_index(:cities) do settings index: {refresh_interval: '2s'} - define_type City + index_scope City end end @@ -588,12 +586,11 @@ xcontext 'applying journal' do before do stub_index(:cities) do - define_type City do - field :name, value: (lambda do - sleep(rating) - name - end) - end + index_scope City + field :name, value: (lambda do + sleep(rating) + name + end) end end @@ -663,12 +660,12 @@ context 'other options' do specify do - expect(CitiesIndex::City).to receive(:import).with(parallel: true, journal: false).once.and_return(true) + expect(CitiesIndex).to receive(:import).with(parallel: true, journal: false).once.and_return(true) expect(CitiesIndex.reset!(parallel: true)).to eq(true) end specify do - expect(CitiesIndex::City) + expect(CitiesIndex) .to receive(:import) .with(suffix: 'suffix', parallel: true, journal: false, refresh: true) .once.and_return(true) @@ -677,6 +674,64 @@ end end + describe '.reset' do + before do + stub_model(:city) + stub_index(:cities) do + index_scope City + end + end + + context do + before { City.create!(id: 1, name: 'Moscow') } + + specify { expect(CitiesIndex.reset).to eq(true) } + specify { expect(CitiesIndex.reset('2013')).to eq(true) } + + context do + before { CitiesIndex.reset } + + specify { expect(CitiesIndex.all).to have(1).item } + specify { expect(CitiesIndex.aliases).to eq([]) } + specify { expect(CitiesIndex.indexes).to eq(['cities']) } + end + end + end + + describe '.sync' do + before do + stub_model(:city) + stub_index(:cities) do + index_scope City + field :name + field :updated_at, type: 'date' + end + end + + let!(:cities) { Array.new(3) { |i| City.create!(name: "Name#{i + 1}") } } + + before do + CitiesIndex.import + cities.first.destroy + cities.last.update(name: 'Name5') + end + + let!(:additional_city) { City.create!(name: 'Name4') } + + specify do + expect(CitiesIndex.sync).to match( + count: 3, + missing: contain_exactly(cities.first.id.to_s, additional_city.id.to_s), + outdated: [cities.last.id.to_s] + ) + end + specify do + expect { CitiesIndex.sync }.to update_index(CitiesIndex) + .and_reindex(additional_city, cities.last) + .and_delete(cities.first).only + end + end + describe '.journal' do specify { expect(DummiesIndex.journal).to be_a(Chewy::Journal) } end @@ -685,7 +740,7 @@ before do stub_model(:city) stub_index(:cities) do - define_type City + index_scope City end end @@ -703,7 +758,7 @@ .to receive(:clear_cache) .and_call_original expect { CitiesIndex.clear_cache({index: index_name_with_prefix}) } - .not_to raise_error Elasticsearch::Transport::Transport::Errors::NotFound + .not_to raise_error end end @@ -727,7 +782,7 @@ .to receive(:clear_cache) .and_call_original expect { CitiesIndex.clear_cache } - .not_to raise_error Elasticsearch::Transport::Transport::Errors::NotFound + .not_to raise_error end end end @@ -736,7 +791,7 @@ before do stub_model(:city) stub_index(:cities) do - define_type City + index_scope City end CitiesIndex.create(source_index) DummiesIndex.create(dest_index) @@ -807,7 +862,7 @@ before do stub_model(:city) stub_index(:cities) do - define_type City + index_scope City end CitiesIndex.create end diff --git a/spec/chewy/type/adapter/active_record_spec.rb b/spec/chewy/index/adapter/active_record_spec.rb similarity index 92% rename from spec/chewy/type/adapter/active_record_spec.rb rename to spec/chewy/index/adapter/active_record_spec.rb index f53953516..699e91e98 100644 --- a/spec/chewy/type/adapter/active_record_spec.rb +++ b/spec/chewy/index/adapter/active_record_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe Chewy::Type::Adapter::ActiveRecord, :active_record do +describe Chewy::Index::Adapter::ActiveRecord, :active_record do before do stub_model(:city) stub_model(:country) @@ -509,30 +509,28 @@ def delete_already? let(:city_ids) { cities.map(&:id) } let(:deleted_ids) { deleted.map(&:id) } - let(:type) { double(type_name: 'user') } - subject { described_class.new(City) } - specify { expect(subject.load(city_ids, _type: type)).to eq(cities) } - specify { expect(subject.load(city_ids.reverse, _type: type)).to eq(cities.reverse) } - specify { expect(subject.load(deleted_ids, _type: type)).to eq([nil, nil]) } - specify { expect(subject.load(city_ids + deleted_ids, _type: type)).to eq([*cities, nil, nil]) } + specify { expect(subject.load(city_ids, _index: 'users')).to eq(cities) } + specify { expect(subject.load(city_ids.reverse, _index: 'users')).to eq(cities.reverse) } + specify { expect(subject.load(deleted_ids, _index: 'users')).to eq([nil, nil]) } + specify { expect(subject.load(city_ids + deleted_ids, _index: 'users')).to eq([*cities, nil, nil]) } specify do - expect(subject.load(city_ids, _type: type, scope: -> { where(rating: 0) })) + expect(subject.load(city_ids, _index: 'users', scope: -> { where(rating: 0) })) .to eq(cities.first(2) + [nil]) end specify do expect( - subject.load(city_ids, _type: type, scope: -> { where(rating: 0) }, user: {scope: -> { where(rating: 1) }}) + subject.load(city_ids, _index: 'users', scope: -> { where(rating: 0) }, users: {scope: -> { where(rating: 1) }}) ).to eq([nil, nil] + cities.last(1)) end specify do - expect(subject.load(city_ids, _type: type, scope: City.where(rating: 1))) + expect(subject.load(city_ids, _index: 'users', scope: City.where(rating: 1))) .to eq([nil, nil] + cities.last(1)) end specify do expect( - subject.load(city_ids, _type: type, scope: City.where(rating: 1), user: {scope: -> { where(rating: 0) }}) + subject.load(city_ids, _index: 'users', scope: City.where(rating: 1), users: {scope: -> { where(rating: 0) }}) ).to eq(cities.first(2) + [nil]) end end @@ -544,33 +542,31 @@ def delete_already? let(:city_ids) { cities.map(&:rating) } let(:deleted_ids) { deleted.map(&:rating) } - let(:type) { double(type_name: 'user') } - subject { described_class.new(City) } - specify { expect(subject.load(city_ids, _type: type)).to eq(cities) } - specify { expect(subject.load(city_ids.reverse, _type: type)).to eq(cities.reverse) } - specify { expect(subject.load(deleted_ids, _type: type)).to eq([nil, nil]) } - specify { expect(subject.load(city_ids + deleted_ids, _type: type)).to eq([*cities, nil, nil]) } + specify { expect(subject.load(city_ids, _index: 'users')).to eq(cities) } + specify { expect(subject.load(city_ids.reverse, _index: 'users')).to eq(cities.reverse) } + specify { expect(subject.load(deleted_ids, _index: 'users')).to eq([nil, nil]) } + specify { expect(subject.load(city_ids + deleted_ids, _index: 'users')).to eq([*cities, nil, nil]) } specify do - expect(subject.load(city_ids, _type: type, scope: -> { where(country_id: 0) })) + expect(subject.load(city_ids, _index: 'users', scope: -> { where(country_id: 0) })) .to eq(cities.first(2) + [nil]) end specify do expect( subject.load( - city_ids, _type: type, scope: -> { where(country_id: 0) }, user: {scope: -> { where(country_id: 1) }} + city_ids, _index: 'users', scope: -> { where(country_id: 0) }, users: {scope: -> { where(country_id: 1) }} ) ).to eq([nil, nil] + cities.last(1)) end specify do - expect(subject.load(city_ids, _type: type, scope: City.where(country_id: 1))) + expect(subject.load(city_ids, _index: 'users', scope: City.where(country_id: 1))) .to eq([nil, nil] + cities.last(1)) end specify do expect( subject.load( - city_ids, _type: type, scope: City.where(country_id: 1), user: {scope: -> { where(country_id: 0) }} + city_ids, _index: 'users', scope: City.where(country_id: 1), users: {scope: -> { where(country_id: 0) }} ) ).to eq(cities.first(2) + [nil]) end diff --git a/spec/chewy/type/adapter/object_spec.rb b/spec/chewy/index/adapter/object_spec.rb similarity index 99% rename from spec/chewy/type/adapter/object_spec.rb rename to spec/chewy/index/adapter/object_spec.rb index 6c59140ba..640d96d64 100644 --- a/spec/chewy/type/adapter/object_spec.rb +++ b/spec/chewy/index/adapter/object_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe Chewy::Type::Adapter::Object do +describe Chewy::Index::Adapter::Object do before { stub_class(:product) } subject { described_class.new(:product) } diff --git a/spec/chewy/type/import/bulk_builder_spec.rb b/spec/chewy/index/import/bulk_builder_spec.rb similarity index 76% rename from spec/chewy/type/import/bulk_builder_spec.rb rename to spec/chewy/index/import/bulk_builder_spec.rb index 507156845..c793af93f 100644 --- a/spec/chewy/type/import/bulk_builder_spec.rb +++ b/spec/chewy/index/import/bulk_builder_spec.rb @@ -1,11 +1,11 @@ require 'spec_helper' -describe Chewy::Type::Import::BulkBuilder do +describe Chewy::Index::Import::BulkBuilder do before { Chewy.massacre } - subject { described_class.new(type, index: index, delete: delete, fields: fields) } - let(:type) { CitiesIndex::City } - let(:index) { [] } + subject { described_class.new(index, to_index: to_index, delete: delete, fields: fields) } + let(:index) { CitiesIndex } + let(:to_index) { [] } let(:delete) { [] } let(:fields) { [] } @@ -14,9 +14,8 @@ before do stub_model(:city) stub_index(:cities) do - define_type City do - field :name, :rating - end + index_scope City + field :name, :rating end end let(:cities) { Array.new(3) { |i| City.create!(id: i + 1, name: "City#{i + 17}", rating: 42) } } @@ -24,7 +23,7 @@ specify { expect(subject.bulk_body).to eq([]) } context do - let(:index) { cities } + let(:to_index) { cities } specify do expect(subject.bulk_body).to eq([ {index: {_id: 1, data: {'name' => 'City17', 'rating' => 42}}}, @@ -44,7 +43,7 @@ end context do - let(:index) { cities.first(2) } + let(:to_index) { cities.first(2) } let(:delete) { [cities.last] } specify do expect(subject.bulk_body).to eq([ @@ -74,10 +73,9 @@ before do stub_index(:cities) do - define_type City do - root id: -> { name } do - field :rating - end + index_scope City + root id: -> { name } do + field :rating end end end @@ -90,7 +88,7 @@ end context 'indexing' do - let(:index) { [london] } + let(:to_index) { [london] } specify do expect(subject.bulk_body).to eq([ @@ -113,17 +111,15 @@ context 'crutches' do before do stub_index(:cities) do - define_type :city do - crutch :names do |collection| - collection.map { |item| [item.id, "Name#{item.id}"] }.to_h - end - - field :name, value: ->(o, c) { c.names[o.id] } + crutch :names do |collection| + collection.map { |item| [item.id, "Name#{item.id}"] }.to_h end + + field :name, value: ->(o, c) { c.names[o.id] } end end - let(:index) { [double(id: 42)] } + let(:to_index) { [double(id: 42)] } specify do expect(subject.bulk_body).to eq([ @@ -132,7 +128,7 @@ end context 'witchcraft' do - before { CitiesIndex::City.witchcraft! } + before { CitiesIndex.witchcraft! } specify do expect(subject.bulk_body).to eq([ {index: {_id: 42, data: {'name' => 'Name42'}}} @@ -144,13 +140,11 @@ context 'empty ids' do before do stub_index(:cities) do - define_type :city do - field :name - end + field :name end end - let(:index) { [{id: 1, name: 'Name0'}, double(id: '', name: 'Name1'), double(name: 'Name2')] } + let(:to_index) { [{id: 1, name: 'Name0'}, double(id: '', name: 'Name1'), double(name: 'Name2')] } let(:delete) { [double(id: '', name: 'Name3'), {name: 'Name4'}, '', 2] } specify do @@ -180,15 +174,13 @@ describe '#index_objects_by_id' do before do stub_index(:cities) do - define_type :city do - field :name - end + field :name end end - let(:index) { [double(id: 1), double(id: 2), double(id: ''), double] } + let(:to_index) { [double(id: 1), double(id: 2), double(id: ''), double] } let(:delete) { [double(id: 3)] } - specify { expect(subject.index_objects_by_id).to eq('1' => index.first, '2' => index.second) } + specify { expect(subject.index_objects_by_id).to eq('1' => to_index.first, '2' => to_index.second) } end end diff --git a/spec/chewy/type/import/bulk_request_spec.rb b/spec/chewy/index/import/bulk_request_spec.rb similarity index 91% rename from spec/chewy/type/import/bulk_request_spec.rb rename to spec/chewy/index/import/bulk_request_spec.rb index 3b62fd8b8..0869804a1 100644 --- a/spec/chewy/type/import/bulk_request_spec.rb +++ b/spec/chewy/index/import/bulk_request_spec.rb @@ -1,13 +1,13 @@ require 'spec_helper' -describe Chewy::Type::Import::BulkRequest do +describe Chewy::Index::Import::BulkRequest do before { Chewy.massacre } - subject { described_class.new(type, suffix: suffix, bulk_size: bulk_size, **bulk_options) } + subject { described_class.new(index, suffix: suffix, bulk_size: bulk_size, **bulk_options) } let(:suffix) {} let(:bulk_size) {} let(:bulk_options) { {} } - let(:type) { PlacesIndex::City } + let(:index) { PlacesIndex } describe '#initialize' do specify { expect { described_class.new(nil, bulk_size: 100) }.to raise_error(ArgumentError) } @@ -18,9 +18,8 @@ before do stub_model(:city) stub_index(:places) do - define_type City do - field :name - end + index_scope City + field :name end end diff --git a/spec/chewy/type/import/journal_builder_spec.rb b/spec/chewy/index/import/journal_builder_spec.rb similarity index 76% rename from spec/chewy/type/import/journal_builder_spec.rb rename to spec/chewy/index/import/journal_builder_spec.rb index 25b36f48c..3b8904467 100644 --- a/spec/chewy/type/import/journal_builder_spec.rb +++ b/spec/chewy/index/import/journal_builder_spec.rb @@ -1,13 +1,11 @@ require 'spec_helper' -describe Chewy::Type::Import::JournalBuilder, :orm do +describe Chewy::Index::Import::JournalBuilder, :orm do before do stub_model(:country) - stub_index 'namespace/cities' do - define_type :city - end + stub_index 'namespace/cities' stub_index 'namespace/countries' do - define_type Country + index_scope Country end Timecop.freeze(time) end @@ -15,23 +13,22 @@ let(:time) { Time.parse('2017-07-14 12:00Z') } - let(:type) { Namespace::CitiesIndex::City } - let(:index) { [] } + let(:index) { Namespace::CitiesIndex } + let(:to_index) { [] } let(:delete) { [] } - subject { described_class.new(type, index: index, delete: delete) } + subject { described_class.new(index, to_index: to_index, delete: delete) } describe '#bulk_body' do specify { expect(subject.bulk_body).to eq([]) } context do - let(:index) { [{id: 1, name: 'City'}] } + let(:to_index) { [{id: 1, name: 'City'}] } specify do expect(subject.bulk_body).to eq([{ index: { _index: 'chewy_journal', data: { 'index_name' => 'namespace/cities', - 'type_name' => 'city', 'action' => 'index', 'references' => [Base64.encode64('{"id":1,"name":"City"}')], 'created_at' => time.as_json @@ -49,7 +46,6 @@ _index: 'chewy_journal', data: { 'index_name' => 'namespace/cities', - 'type_name' => 'city', 'action' => 'delete', 'references' => [Base64.encode64('{"id":1,"name":"City"}')], 'created_at' => time.as_json @@ -60,8 +56,8 @@ end context do - let(:type) { Namespace::CountriesIndex::Country } - let(:index) { [Country.new(id: 1, name: 'City')] } + let(:index) { Namespace::CountriesIndex } + let(:to_index) { [Country.new(id: 1, name: 'City')] } let(:delete) { [Country.new(id: 2, name: 'City')] } specify do expect(subject.bulk_body).to eq([{ @@ -69,7 +65,6 @@ _index: 'chewy_journal', data: { 'index_name' => 'namespace/countries', - 'type_name' => 'country', 'action' => 'index', 'references' => [Base64.encode64('1')], 'created_at' => time.as_json @@ -80,7 +75,6 @@ _index: 'chewy_journal', data: { 'index_name' => 'namespace/countries', - 'type_name' => 'country', 'action' => 'delete', 'references' => [Base64.encode64('2')], 'created_at' => time.as_json diff --git a/spec/chewy/type/import/routine_spec.rb b/spec/chewy/index/import/routine_spec.rb similarity index 69% rename from spec/chewy/type/import/routine_spec.rb rename to spec/chewy/index/import/routine_spec.rb index ba5f4778a..40eb3d7ae 100644 --- a/spec/chewy/type/import/routine_spec.rb +++ b/spec/chewy/index/import/routine_spec.rb @@ -1,14 +1,12 @@ require 'spec_helper' # TODO: add more specs here later -describe Chewy::Type::Import::Routine do +describe Chewy::Index::Import::Routine do before { Chewy.massacre } before do stub_index(:cities) do - define_type :city do - field :name - field :object, type: 'object' - end + field :name + field :object, type: 'object' end CitiesIndex.create! end @@ -18,7 +16,7 @@ describe '#options' do specify do - expect(described_class.new(CitiesIndex::City).options).to eq( + expect(described_class.new(CitiesIndex).options).to eq( journal: nil, refresh: true, update_failover: true, @@ -29,7 +27,7 @@ specify do expect(described_class.new( - CitiesIndex::City, batch_size: 100, bulk_size: 1.megabyte, refresh: false + CitiesIndex, batch_size: 100, bulk_size: 1.megabyte, refresh: false ).options).to eq( journal: nil, refresh: false, @@ -43,7 +41,7 @@ context do before { allow(Chewy).to receive_messages(configuration: Chewy.configuration.merge(journal: true)) } specify do - expect(described_class.new(CitiesIndex::City).options).to eq( + expect(described_class.new(CitiesIndex).options).to eq( journal: true, refresh: true, update_failover: true, @@ -55,26 +53,26 @@ specify do expect(CitiesIndex.client).to receive(:bulk).with(hash_including(refresh: true)) - described_class.new(CitiesIndex::City).process(index: index) + described_class.new(CitiesIndex).process(index: index) end specify do expect(CitiesIndex.client).to receive(:bulk).with(hash_including(refresh: false)) - described_class.new(CitiesIndex::City, refresh: false).process(index: index) + described_class.new(CitiesIndex, refresh: false).process(index: index) end end describe '#parallel_options' do - specify { expect(described_class.new(CitiesIndex::City).parallel_options).to be_nil } - specify { expect(described_class.new(CitiesIndex::City, parallel: true).parallel_options).to eq({}) } - specify { expect(described_class.new(CitiesIndex::City, parallel: 3).parallel_options).to eq(in_processes: 3) } + specify { expect(described_class.new(CitiesIndex).parallel_options).to be_nil } + specify { expect(described_class.new(CitiesIndex, parallel: true).parallel_options).to eq({}) } + specify { expect(described_class.new(CitiesIndex, parallel: 3).parallel_options).to eq(in_processes: 3) } specify do - expect(described_class.new(CitiesIndex::City, parallel: {in_threads: 2}).parallel_options).to eq(in_threads: 2) + expect(described_class.new(CitiesIndex, parallel: {in_threads: 2}).parallel_options).to eq(in_threads: 2) end end describe '#stats' do - subject { described_class.new(CitiesIndex::City) } + subject { described_class.new(CitiesIndex) } specify { expect(subject.stats).to eq({}) } specify do @@ -94,7 +92,7 @@ end describe '#errors' do - subject { described_class.new(CitiesIndex::City) } + subject { described_class.new(CitiesIndex) } let(:index) { [double(id: 1, name: 'Name', object: ''), double(id: 2, name: 'Name', object: {})] } specify { expect(subject.errors).to eq([]) } diff --git a/spec/chewy/type/import_spec.rb b/spec/chewy/index/import_spec.rb similarity index 78% rename from spec/chewy/type/import_spec.rb rename to spec/chewy/index/import_spec.rb index 23dbc99e3..ca7d79e89 100644 --- a/spec/chewy/type/import_spec.rb +++ b/spec/chewy/index/import_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe Chewy::Type::Import do +describe Chewy::Index::Import do before { Chewy.massacre } before do @@ -9,14 +9,13 @@ before do stub_index(:cities) do - define_type City do - field :name - end + index_scope City + field :name end end def imported_cities - CitiesIndex::City.all.map do |city| + CitiesIndex.all.map do |city| city.attributes.except('_score', '_explanation') end end @@ -37,13 +36,13 @@ def subscribe_notification specify 'lazy (default)' do expect(CitiesIndex).to receive(:exists?).and_call_original expect(CitiesIndex).to receive(:create!).and_call_original - CitiesIndex::City.import(dummy_city) + CitiesIndex.import(dummy_city) end specify 'lazy without objects' do expect(CitiesIndex).not_to receive(:exists?) expect(CitiesIndex).not_to receive(:create!) - CitiesIndex::City.import([]) + CitiesIndex.import([]) end context 'skip' do @@ -58,7 +57,7 @@ def subscribe_notification specify do expect(CitiesIndex).not_to receive(:exists?) expect(CitiesIndex).not_to receive(:create!) - CitiesIndex::City.import(dummy_city) + CitiesIndex.import(dummy_city) end end end @@ -69,10 +68,10 @@ def subscribe_notification specify { expect(import(dummy_cities)).to eq(true) } specify { expect(import(dummy_cities.map(&:id))).to eq(true) } - specify { expect { import([]) }.not_to update_index(CitiesIndex::City) } - specify { expect { import }.to update_index(CitiesIndex::City).and_reindex(dummy_cities) } - specify { expect { import dummy_cities }.to update_index(CitiesIndex::City).and_reindex(dummy_cities) } - specify { expect { import dummy_cities.map(&:id) }.to update_index(CitiesIndex::City).and_reindex(dummy_cities) } + specify { expect { import([]) }.not_to update_index(CitiesIndex) } + specify { expect { import }.to update_index(CitiesIndex).and_reindex(dummy_cities) } + specify { expect { import dummy_cities }.to update_index(CitiesIndex).and_reindex(dummy_cities) } + specify { expect { import dummy_cities.map(&:id) }.to update_index(CitiesIndex).and_reindex(dummy_cities) } describe 'criteria-driven importing' do let(:names) { %w[name0 name1] } @@ -80,11 +79,11 @@ def subscribe_notification context 'active record', :active_record do specify do expect { import(City.where(name: names)) } - .to update_index(CitiesIndex::City).and_reindex(dummy_cities.first(2)) + .to update_index(CitiesIndex).and_reindex(dummy_cities.first(2)) end specify do expect { import(City.where(name: names).map(&:id)) } - .to update_index(CitiesIndex::City).and_reindex(dummy_cities.first(2)) + .to update_index(CitiesIndex).and_reindex(dummy_cities.first(2)) end end end @@ -92,13 +91,13 @@ def subscribe_notification specify do dummy_cities.first.destroy expect { import dummy_cities } - .to update_index(CitiesIndex::City).and_reindex(dummy_cities.from(1)).and_delete(dummy_cities.first) + .to update_index(CitiesIndex).and_reindex(dummy_cities.from(1)).and_delete(dummy_cities.first) end specify do dummy_cities.first.destroy expect { import dummy_cities.map(&:id) } - .to update_index(CitiesIndex::City).and_reindex(dummy_cities.from(1)).and_delete(dummy_cities.first) + .to update_index(CitiesIndex).and_reindex(dummy_cities.from(1)).and_delete(dummy_cities.first) end specify do @@ -123,7 +122,7 @@ def subscribe_notification specify do expect { import(dummy_cities, bulk_size: 1.2.kilobyte) } - .to update_index(CitiesIndex::City).and_reindex(dummy_cities) + .to update_index(CitiesIndex).and_reindex(dummy_cities) end context do @@ -139,23 +138,22 @@ def subscribe_notification criteria = {name: names} stub_index(:cities) do - define_type City.where(criteria) do - field :name - end + index_scope City.where(criteria) + field :name end end - specify { expect { import }.to update_index(CitiesIndex::City).and_reindex(dummy_cities.first(2)) } + specify { expect { import }.to update_index(CitiesIndex).and_reindex(dummy_cities.first(2)) } context 'active record', :active_record do specify do expect { import City.where(id: dummy_cities.first.id) } - .to update_index(CitiesIndex::City).and_reindex(dummy_cities.first).only + .to update_index(CitiesIndex).and_reindex(dummy_cities.first).only end specify do - allow(CitiesIndex::City).to receive(:import_linear).and_return(double(present?: false)) - allow(CitiesIndex::City).to receive(:import_parallel).and_return(double(present?: false)) + allow(CitiesIndex).to receive(:import_linear).and_return(double(present?: false)) + allow(CitiesIndex).to receive(:import_parallel).and_return(double(present?: false)) expects_no_query(except: /SELECT\s+1\s+AS\s+one\s+FROM/) do import City.where(id: dummy_cities.first.id) @@ -169,28 +167,27 @@ def subscribe_notification payload = subscribe_notification dummy_cities.first.destroy import dummy_cities - expect(payload).to eq(type: CitiesIndex::City, import: {delete: 1, index: 2}) + expect(payload).to eq(index: CitiesIndex, import: {delete: 1, index: 2}) end specify do payload = subscribe_notification dummy_cities.first.destroy import dummy_cities, batch_size: 2 - expect(payload).to eq(type: CitiesIndex::City, import: {delete: 1, index: 2}) + expect(payload).to eq(index: CitiesIndex, import: {delete: 1, index: 2}) end specify do payload = subscribe_notification import dummy_cities, batch_size: 2 - expect(payload).to eq(type: CitiesIndex::City, import: {index: 3}) + expect(payload).to eq(index: CitiesIndex, import: {index: 3}) end context do before do stub_index(:cities) do - define_type City do - field :name, type: 'object' - end + index_scope City + field :name, type: 'object' end end @@ -204,7 +201,7 @@ def subscribe_notification specify do payload = subscribe_notification import dummy_cities, batch_size: 2 - expect(payload).to eq(type: CitiesIndex::City, + expect(payload).to eq(index: CitiesIndex, errors: {index: {mapper_parsing_exception => %w[1 2 3]}}, import: {index: 3}) end @@ -212,7 +209,7 @@ def subscribe_notification end context 'fields' do - before { CitiesIndex::City.import!(dummy_cities.first(2)) } + before { CitiesIndex.import!(dummy_cities.first(2)) } context do before { expect(Chewy.client).to receive(:bulk).twice.and_call_original } @@ -220,7 +217,7 @@ def subscribe_notification end context do - before { CitiesIndex::City.import!(dummy_cities.last) } + before { CitiesIndex.import!(dummy_cities.last) } before { expect(Chewy.client).to receive(:bulk).once.and_call_original } specify { expect(import(dummy_cities, update_fields: [:name])).to eq(true) } end @@ -229,10 +226,8 @@ def subscribe_notification context 'fields integrational' do before do stub_index(:cities) do - define_type :city do - field :name - field :object, type: 'object' - end + field :name + field :object, type: 'object' end end @@ -267,7 +262,7 @@ def subscribe_notification } => %w[2 4]} }, import: {index: 6}, - type: CitiesIndex::City + index: CitiesIndex ) expect(imported_cities).to match_array([ {'id' => '1', 'name' => 'Name11', 'object' => {'foo' => 11}}, @@ -290,7 +285,7 @@ def subscribe_notification } => %w[2 4]} }, import: {index: 6}, - type: CitiesIndex::City + index: CitiesIndex ) expect(imported_cities).to match_array([ {'id' => '1', 'name' => 'Name11', 'object' => {'foo' => 11}}, @@ -300,7 +295,7 @@ def subscribe_notification end context do - before { CitiesIndex::City.import!(objects[4]) } + before { CitiesIndex.import!(objects[4]) } specify do payload = subscribe_notification @@ -316,7 +311,7 @@ def subscribe_notification } => %w[2 4]} }, import: {index: 6}, - type: CitiesIndex::City + index: CitiesIndex ) expect(imported_cities).to match_array([ {'id' => '1', 'name' => 'Name11', 'object' => {'foo' => 11}}, @@ -327,7 +322,7 @@ def subscribe_notification end context do - before { CitiesIndex::City.import!(old_objects[1], old_objects[3], objects[4]) } + before { CitiesIndex.import!(old_objects[1], old_objects[3], objects[4]) } specify do payload = subscribe_notification @@ -337,7 +332,7 @@ def subscribe_notification expect(payload).to eq( import: {index: 6}, - type: CitiesIndex::City + index: CitiesIndex ) expect(imported_cities).to match_array([ {'id' => '1', 'name' => 'Name11', 'object' => {'foo' => 11}}, @@ -356,7 +351,7 @@ def subscribe_notification expect(payload).to eq( import: {index: 6}, - type: CitiesIndex::City + index: CitiesIndex ) expect(imported_cities).to match_array([ {'id' => '1', 'name' => 'Name11', 'object' => {'foo' => 11}}, @@ -388,7 +383,7 @@ def subscribe_notification end context do - before { CitiesIndex::City.import!(old_objects) } + before { CitiesIndex.import!(old_objects) } specify do payload = subscribe_notification @@ -398,7 +393,7 @@ def subscribe_notification expect(payload).to eq( import: {index: 6}, - type: CitiesIndex::City + index: CitiesIndex ) expect(imported_cities).to match_array([ {'id' => '1', 'name' => 'Name11', 'object' => {'foo' => 1}}, @@ -412,7 +407,7 @@ def subscribe_notification end context do - before { CitiesIndex::City.import!(old_objects) } + before { CitiesIndex.import!(old_objects) } specify do payload = subscribe_notification @@ -428,7 +423,7 @@ def subscribe_notification } => %w[2 4]} }, import: {index: 6}, - type: CitiesIndex::City + index: CitiesIndex ) expect(imported_cities).to match_array([ {'id' => '1', 'name' => 'Name1', 'object' => {'foo' => 11}}, @@ -446,9 +441,8 @@ def subscribe_notification context do before do stub_index(:cities) do - define_type City do - field :name, type: 'object' - end + index_scope City + field :name, type: 'object' end end @@ -460,9 +454,8 @@ def subscribe_notification context do before do stub_index(:cities) do - define_type City do - field :name, type: 'object', value: -> { name == 'name1' ? name : {name: name} } - end + index_scope City + field :name, type: 'object', value: -> { name == 'name1' ? name : {name: name} } end end @@ -474,19 +467,19 @@ def subscribe_notification context 'default_import_options are set' do before do - CitiesIndex::City.default_import_options(batch_size: 500) + CitiesIndex.default_import_options(batch_size: 500) end specify do - expect(CitiesIndex::City.adapter).to receive(:import).with(any_args, hash_including(batch_size: 500)) - CitiesIndex::City.import + expect(CitiesIndex.adapter).to receive(:import).with(any_args, hash_including(batch_size: 500)) + CitiesIndex.import end end end describe '.import', :orm do def import(*args) - CitiesIndex::City.import(*args) + CitiesIndex.import(*args) end it_behaves_like 'importing' @@ -495,7 +488,7 @@ def import(*args) def import(*args) options = args.extract_options! options[:parallel] = 0 - CitiesIndex::City.import(*args, options) + CitiesIndex.import(*args, options) end it_behaves_like 'importing' @@ -503,54 +496,51 @@ def import(*args) end describe '.import!', :orm do - specify { expect { CitiesIndex::City.import! }.not_to raise_error } + specify { expect { CitiesIndex.import! }.not_to raise_error } context do before do stub_index(:cities) do - define_type City do - field :name, type: 'object' - end + index_scope City + field :name, type: 'object' end end - specify { expect { CitiesIndex::City.import!(dummy_cities) }.to raise_error Chewy::ImportFailed } + specify { expect { CitiesIndex.import!(dummy_cities) }.to raise_error Chewy::ImportFailed } end end describe '.compose' do before do stub_index(:cities) do - define_type :city do - crutch :names do |collection| - collection.map { |o| [o.name, "#{o.name}42"] }.to_h - end - field :name, value: ->(o, c) { c.names[o.name] } - field :rating + crutch :names do |collection| + collection.map { |o| [o.name, "#{o.name}42"] }.to_h end + field :name, value: ->(o, c) { c.names[o.name] } + field :rating end end specify do - expect(CitiesIndex::City.compose(double(name: 'Name', rating: 42))) + expect(CitiesIndex.compose(double(name: 'Name', rating: 42))) .to eq('name' => 'Name42', 'rating' => 42) end specify do - expect(CitiesIndex::City.compose(double(name: 'Name', rating: 42), fields: %i[name])) + expect(CitiesIndex.compose(double(name: 'Name', rating: 42), fields: %i[name])) .to eq('name' => 'Name42') end context 'witchcraft' do - before { CitiesIndex::City.witchcraft! } + before { CitiesIndex.witchcraft! } specify do - expect(CitiesIndex::City.compose(double(name: 'Name', rating: 42))) + expect(CitiesIndex.compose(double(name: 'Name', rating: 42))) .to eq('name' => 'Name42', 'rating' => 42) end specify do - expect(CitiesIndex::City.compose(double(name: 'Name', rating: 42), fields: %i[name])) + expect(CitiesIndex.compose(double(name: 'Name', rating: 42), fields: %i[name])) .to eq('name' => 'Name42') end end @@ -559,12 +549,12 @@ def import(*args) let(:crutches) { double(names: {'Name' => 'Name43'}) } specify do - expect(CitiesIndex::City.compose(double(name: 'Name', rating: 42), crutches)) + expect(CitiesIndex.compose(double(name: 'Name', rating: 42), crutches)) .to eq('name' => 'Name43', 'rating' => 42) end specify do - expect(CitiesIndex::City.compose(double(name: 'Name', rating: 42), crutches, fields: %i[name])) + expect(CitiesIndex.compose(double(name: 'Name', rating: 42), crutches, fields: %i[name])) .to eq('name' => 'Name43') end end diff --git a/spec/chewy/type/mapping_spec.rb b/spec/chewy/index/mapping_spec.rb similarity index 58% rename from spec/chewy/type/mapping_spec.rb rename to spec/chewy/index/mapping_spec.rb index a6ed5a633..feb1a376a 100644 --- a/spec/chewy/type/mapping_spec.rb +++ b/spec/chewy/index/mapping_spec.rb @@ -1,47 +1,41 @@ require 'spec_helper' -describe Chewy::Type::Mapping do - let(:product) { ProductsIndex::Product } - let(:review) { ReviewsIndex::Review } +describe Chewy::Index::Mapping do + let(:product) { ProductsIndex } + let(:review) { ReviewsIndex } before do stub_index(:products) do - define_type :product do - root do - field :name, 'surname' - field :title, type: 'text' do - field :subfield1 - end - field 'price', type: 'float' do - field :subfield2 - end - agg :named_agg do - {avg: {field: 'title.subfield1'}} - end + root do + field :name, 'surname' + field :title, type: 'text' do + field :subfield1 end - end - end - stub_index(:reviews) do - define_type :review do - field :title, :body - field :comments do - field :message - field :rating, type: 'long' + field 'price', type: 'float' do + field :subfield2 end agg :named_agg do - {avg: {field: 'comments.rating'}} + {avg: {field: 'title.subfield1'}} end end end + stub_index(:reviews) do + field :title, :body + field :comments do + field :message + field :rating, type: 'long' + end + agg :named_agg do + {avg: {field: 'comments.rating'}} + end + end end context 'no root element call' do before do stub_index(:products) do - define_type :product do - field :title, type: 'text' do - field :subfield1 - end + field :title, type: 'text' do + field :subfield1 end end end @@ -59,7 +53,7 @@ Chewy.default_root_options = previous_options end - specify { expect(product.mappings_hash).to include(_all: {enabled: false}) } + specify { expect(product.mappings_hash[:mappings]).to include(_all: {enabled: false}) } end end @@ -83,33 +77,33 @@ end describe '.mappings_hash' do - specify { expect(product.mappings_hash).to eq(product.root.mappings_hash) } + specify { expect(product.mappings_hash[:mappings]).to eq(product.root.mappings_hash) } context 'root merging' do context do before do stub_index(:products) do - define_type :product do - root other_option: 'nothing' do - field :name do - field :last_name # will be redefined in the following root flock - end - end - root other_option: 'option_value' do - field :identifier - field :name, type: 'integer' + root other_option: 'nothing' do + field :name do + field :last_name # will be redefined in the following root flock end end + root other_option: 'option_value' do + field :identifier + field :name, type: 'integer' + end end end specify do expect(product.mappings_hash).to eq( - properties: { - name: {type: 'integer'}, - identifier: {type: Chewy.default_field_type} - }, - other_option: 'option_value' + mappings: { + properties: { + name: {type: 'integer'}, + identifier: {type: Chewy.default_field_type} + }, + other_option: 'option_value' + } ) end end @@ -117,24 +111,22 @@ end describe '.supports_outdated_sync?' do - def type(&block) - stub_index(:cities) do - define_type :city, &block - end - CitiesIndex::City + def index(&block) + stub_index(:cities, &block) + CitiesIndex end - specify { expect(type.supports_outdated_sync?).to eq(false) } - specify { expect(type { field :updated_at }.supports_outdated_sync?).to eq(true) } - specify { expect(type { field :updated_at, value: -> {} }.supports_outdated_sync?).to eq(false) } + specify { expect(index.supports_outdated_sync?).to eq(false) } + specify { expect(index { field :updated_at }.supports_outdated_sync?).to eq(true) } + specify { expect(index { field :updated_at, value: -> {} }.supports_outdated_sync?).to eq(false) } specify do - expect(type do + expect(index do self.outdated_sync_field = :version field :updated_at end.supports_outdated_sync?).to eq(false) end specify do - expect(type do + expect(index do self.outdated_sync_field = :version field :version end.supports_outdated_sync?).to eq(true) diff --git a/spec/chewy/type/observe_spec.rb b/spec/chewy/index/observe_spec.rb similarity index 58% rename from spec/chewy/type/observe_spec.rb rename to spec/chewy/index/observe_spec.rb index 024d9fd0b..7d9671442 100644 --- a/spec/chewy/type/observe_spec.rb +++ b/spec/chewy/index/observe_spec.rb @@ -1,26 +1,24 @@ require 'spec_helper' -describe Chewy::Type::Observe do +describe Chewy::Index::Observe do describe '.update_index' do before do - stub_index(:dummies) do - define_type :dummy - end + stub_index(:dummies) end let(:backreferenced) { Array.new(3) { |i| double(id: i) } } specify do - expect { DummiesIndex::Dummy.update_index(backreferenced) } + expect { DummiesIndex.update_index(backreferenced) } .to raise_error Chewy::UndefinedUpdateStrategy end specify do - expect { DummiesIndex::Dummy.update_index([]) } - .not_to update_index('dummies#dummy') + expect { DummiesIndex.update_index([]) } + .not_to update_index('dummies') end specify do - expect { DummiesIndex::Dummy.update_index(nil) } - .not_to update_index('dummies#dummy') + expect { DummiesIndex.update_index(nil) } + .not_to update_index('dummies') end end @@ -29,13 +27,13 @@ before do stub_model(:city) do - update_index(->(city) { "cities##{city.class.name.underscore}" }) { self } - update_index('countries#country') { changes['country_id'] || previous_changes['country_id'] || country } + update_index(-> { 'cities' }, :self) + update_index('countries') { changes['country_id'] || previous_changes['country_id'] || country } end stub_model(:country) do - update_index('cities#city', if: -> { update_condition }) { cities } - update_index(-> { "countries##{self.class.name.underscore}" }, :self) + update_index('cities', if: -> { update_condition }) { cities } + update_index(-> { 'countries' }, :self) attr_accessor :update_condition end @@ -43,11 +41,11 @@ Country.has_many :cities stub_index(:cities) do - define_type City + index_scope City end stub_index(:countries) do - define_type Country + index_scope Country end end @@ -56,16 +54,16 @@ let!(:country2) { Chewy.strategy(:atomic) { Country.create!(id: 2, update_condition: update_condition) } } let!(:city) { Chewy.strategy(:atomic) { City.create!(id: 1, country: country1) } } - specify { expect { city.save! }.to update_index('cities#city').and_reindex(city).only } - specify { expect { city.save! }.to update_index('countries#country').and_reindex(country1).only } + specify { expect { city.save! }.to update_index('cities').and_reindex(city).only } + specify { expect { city.save! }.to update_index('countries').and_reindex(country1).only } - specify { expect { city.update!(country: nil) }.to update_index('cities#city').and_reindex(city).only } - specify { expect { city.update!(country: nil) }.to update_index('countries#country').and_reindex(country1).only } + specify { expect { city.update!(country: nil) }.to update_index('cities').and_reindex(city).only } + specify { expect { city.update!(country: nil) }.to update_index('countries').and_reindex(country1).only } - specify { expect { city.update!(country: country2) }.to update_index('cities#city').and_reindex(city).only } + specify { expect { city.update!(country: country2) }.to update_index('cities').and_reindex(city).only } specify do expect { city.update!(country: country2) } - .to update_index('countries#country').and_reindex(country1, country2).only + .to update_index('countries').and_reindex(country1, country2).only end end @@ -77,25 +75,25 @@ end end - specify { expect { country.save! }.to update_index('cities#city').and_reindex(country.cities).only } - specify { expect { country.save! }.to update_index('countries#country').and_reindex(country).only } + specify { expect { country.save! }.to update_index('cities').and_reindex(country.cities).only } + specify { expect { country.save! }.to update_index('countries').and_reindex(country).only } context 'conditional update' do let(:update_condition) { false } - specify { expect { country.save! }.not_to update_index('cities#city') } + specify { expect { country.save! }.not_to update_index('cities') } end end end context 'transactions', :active_record do context do - before { stub_model(:city) { update_index 'cities#city', :self } } - before { stub_index(:cities) { define_type City } } + before { stub_model(:city) { update_index 'cities', :self } } + before { stub_index(:cities) { index_scope City } } specify do Chewy.strategy(:urgent) do ActiveRecord::Base.transaction do - expect { City.create! }.not_to update_index('cities#city') + expect { City.create! }.not_to update_index('cities') end end end @@ -103,13 +101,13 @@ context do before { allow(Chewy).to receive_messages(use_after_commit_callbacks: false) } - before { stub_model(:city) { update_index 'cities#city', :self } } - before { stub_index(:cities) { define_type City } } + before { stub_model(:city) { update_index 'cities', :self } } + before { stub_index(:cities) { index_scope City } } specify do Chewy.strategy(:urgent) do ActiveRecord::Base.transaction do - expect { City.create! }.to update_index('cities#city') + expect { City.create! }.to update_index('cities') end end end diff --git a/spec/chewy/index/specification_spec.rb b/spec/chewy/index/specification_spec.rb index 07f997edf..68a34b8d5 100644 --- a/spec/chewy/index/specification_spec.rb +++ b/spec/chewy/index/specification_spec.rb @@ -5,44 +5,34 @@ let(:index1) do stub_index(:places) do - define_type(:city) do - field :founded_on, type: 'date' - end + field :founded_on, type: 'date' end end let(:index2) do stub_index(:places) do settings analyzer: {} - define_type(:city) do - field :founded_on, type: 'date' - end + field :founded_on, type: 'date' end end let(:index3) do stub_index(:places) do - define_type(:city) do - field :founded_on, type: 'date' - field :population, type: 'integer' - end + field :founded_on, type: 'date' + field :population, type: 'integer' end end let(:index4) do stub_index(:places) do - define_type(:city) do - field :population, type: 'integer' - field :founded_on, type: 'date' - end + field :population, type: 'integer' + field :founded_on, type: 'date' end end let(:index5) do stub_index('namespace/cities') do - define_type(:city) do - field :population, type: 'integer' - end + field :population, type: 'integer' end end diff --git a/spec/chewy/type/syncer_spec.rb b/spec/chewy/index/syncer_spec.rb similarity index 81% rename from spec/chewy/type/syncer_spec.rb rename to spec/chewy/index/syncer_spec.rb index d69d6bff9..e71617f04 100644 --- a/spec/chewy/type/syncer_spec.rb +++ b/spec/chewy/index/syncer_spec.rb @@ -1,36 +1,34 @@ require 'spec_helper' -describe Chewy::Type::Syncer, :orm do +describe Chewy::Index::Syncer, :orm do before { Chewy.massacre } before do stub_model(:city) stub_index(:cities) do - define_type City do - field :name - field :updated_at, type: 'date' - end + index_scope City + field :name + field :updated_at, type: 'date' end end shared_examples 'sync' do |options = {}| let!(:cities) { Array.new(3) { |i| City.create!(name: "Name#{i + 1}") } } - subject { described_class.new(CitiesIndex::City, **options) } + subject { described_class.new(CitiesIndex, **options) } describe '#perform' do - before { CitiesIndex::City.import!(cities) } + before { CitiesIndex.import!(cities) } specify { expect(subject.perform).to eq(0) } context do before do cities.first.destroy - sleep(1) if ActiveSupport::VERSION::STRING < '4.1.0' cities.last.update(name: 'Name5') end let!(:additional_city) { City.create!(name: 'Name4') } specify { expect(subject.perform).to eq(3) } specify do - expect { subject.perform }.to update_index(CitiesIndex::City) + expect { subject.perform }.to update_index(CitiesIndex) .and_reindex(additional_city, cities.last) .and_delete(cities.first).only end @@ -39,9 +37,8 @@ context 'does not support outdated sync' do before do stub_index(:cities) do - define_type City do - field :name - end + index_scope City + field :name end end @@ -56,7 +53,7 @@ specify { expect(subject.perform).to eq(2) } specify do - expect { subject.perform }.to update_index(CitiesIndex::City) + expect { subject.perform }.to update_index(CitiesIndex) .and_reindex(additional_city) .and_delete(cities.first).only end @@ -68,7 +65,7 @@ specify { expect(subject.missing_ids).to match_array(cities.map(&:id).map(&:to_s)) } context do - before { CitiesIndex::City.import!(cities) } + before { CitiesIndex.import!(cities) } specify { expect(subject.missing_ids).to eq([]) } context do @@ -83,12 +80,11 @@ specify { expect(subject.outdated_ids).to eq([]) } context do - before { CitiesIndex::City.import!(cities) } + before { CitiesIndex.import!(cities) } specify { expect(subject.outdated_ids).to eq([]) } context do before do - sleep(1) if ActiveSupport::VERSION::STRING < '4.1.0' cities.first.update(name: 'Name4') cities.last.update(name: 'Name5') end @@ -99,9 +95,8 @@ context 'does not support outdated sync' do before do stub_index(:cities) do - define_type City do - field :name - end + index_scope City + field :name end end diff --git a/spec/chewy/type/witchcraft_spec.rb b/spec/chewy/index/witchcraft_spec.rb similarity index 78% rename from spec/chewy/type/witchcraft_spec.rb rename to spec/chewy/index/witchcraft_spec.rb index aa8ac1d5a..0d4a28c4d 100644 --- a/spec/chewy/type/witchcraft_spec.rb +++ b/spec/chewy/index/witchcraft_spec.rb @@ -1,25 +1,23 @@ require 'spec_helper' -describe Chewy::Type::Witchcraft do +describe Chewy::Index::Witchcraft do def self.mapping(&block) before do stub_index(:products) do - define_type :product do - witchcraft! + witchcraft! - instance_exec(&block) - end + instance_exec(&block) end end end describe '#cauldron' do - let(:type) { ProductsIndex::Product } + let(:index) { ProductsIndex } let(:object) {} context 'empty mapping' do mapping {} - specify { expect(type.cauldron.brew(object)).to eq({}) } + specify { expect(index.cauldron.brew(object)).to eq({}) } end context do @@ -32,18 +30,18 @@ def self.mapping(&block) context do let(:object) { double(attributes) } - specify { expect(type.cauldron.brew(object)).to eq(attributes.as_json) } + specify { expect(index.cauldron.brew(object)).to eq(attributes.as_json) } end context do let(:object) { attributes } - specify { expect(type.cauldron.brew(object)).to eq(attributes.as_json) } + specify { expect(index.cauldron.brew(object)).to eq(attributes.as_json) } end context do let(:object) { double(attributes) } specify do - expect(type.cauldron(fields: %i[name tags]).brew(object)) + expect(index.cauldron(fields: %i[name tags]).brew(object)) .to eq('name' => 'Name', 'tags' => %w[Ruby RoR]) end end @@ -51,7 +49,7 @@ def self.mapping(&block) context do let(:object) { attributes } specify do - expect(type.cauldron(fields: %i[name tags]).brew(object)) + expect(index.cauldron(fields: %i[name tags]).brew(object)) .to eq('name' => 'Name', 'tags' => %w[Ruby RoR]) end end @@ -69,7 +67,7 @@ def self.mapping(&block) context do let(:object) { double(attributes) } - specify { expect(type.cauldron.brew(object)).to eq(attributes.merge(tags: %i[Ruby RoR]).as_json) } + specify { expect(index.cauldron.brew(object)).to eq(attributes.merge(tags: %i[Ruby RoR]).as_json) } end end @@ -82,7 +80,7 @@ def self.mapping(&block) context do let(:object) { double(attributes) } let(:crutches) { double(names: ['Other']) } - specify { expect(type.cauldron.brew(object, crutches)).to eq({name: 'Other'}.as_json) } + specify { expect(index.cauldron.brew(object, crutches)).to eq({name: 'Other'}.as_json) } end end @@ -102,7 +100,7 @@ def self.mapping(&block) ]) end specify do - expect(type.cauldron.brew(object)).to eq({queries: [ + expect(index.cauldron.brew(object)).to eq({queries: [ {title: 'Title1', body: 'This Body1'}, {title: 'Title2', body: 'This Body2'} ]}.as_json) @@ -124,7 +122,7 @@ def self.mapping(&block) ]) end specify do - expect(type.cauldron.brew(object)).to eq({queries: [ + expect(index.cauldron.brew(object)).to eq({queries: [ {title: 'Title1', body: 'This Body1'}, {title: 'Title2', body: 'This Body2'} ]}.as_json) @@ -146,7 +144,7 @@ def self.mapping(&block) ]) end specify do - expect(type.cauldron.brew(object)).to eq({queries: [ + expect(index.cauldron.brew(object)).to eq({queries: [ {title: 'Title1', body: 'This Body1'}, {title: 'Title2', body: 'This Body2'} ]}.as_json) @@ -172,7 +170,7 @@ def self.mapping(&block) ]) end specify do - expect(type.cauldron.brew(object, double(second: 'Crutch'))).to eq({queries: [ + expect(index.cauldron.brew(object, double(second: 'Crutch'))).to eq({queries: [ {fields: [ {first: 'First1', second: 'Value1Second1'}, {first: 'First2', second: 'Value1Crutch'} @@ -193,7 +191,7 @@ def self.mapping(&block) double(not_present: nil) end specify do - expect(type.cauldron.brew(object)).to eq({not_present: nil}.as_json) + expect(index.cauldron.brew(object)).to eq({not_present: nil}.as_json) end end end @@ -208,7 +206,7 @@ def self.mapping(&block) context do let(:object) { double(attributes) } - specify { expect(type.cauldron.brew(object)).to eq(attributes.as_json) } + specify { expect(index.cauldron.brew(object)).to eq(attributes.as_json) } end end @@ -224,7 +222,7 @@ def self.custom_value(field) context do let(:object) { double(attributes) } - specify { expect(type.cauldron.brew(object)).to eq(attributes.as_json) } + specify { expect(index.cauldron.brew(object)).to eq(attributes.as_json) } end end @@ -235,12 +233,12 @@ def self.custom_value(field) context do let(:object) { double(last_name: 'Name') } - specify { expect(type.cauldron.brew(object)).to eq({name: 'Name'}.as_json) } + specify { expect(index.cauldron.brew(object)).to eq({name: 'Name'}.as_json) } end context do let(:object) { {'last_name' => 'Name'} } - specify { expect(type.cauldron.brew(object)).to eq({name: 'Name'}.as_json) } + specify { expect(index.cauldron.brew(object)).to eq({name: 'Name'}.as_json) } end end end diff --git a/spec/chewy/index/wrapper_spec.rb b/spec/chewy/index/wrapper_spec.rb new file mode 100644 index 000000000..d8db65896 --- /dev/null +++ b/spec/chewy/index/wrapper_spec.rb @@ -0,0 +1,100 @@ +require 'spec_helper' + +describe Chewy::Index::Wrapper do + before do + stub_class(:city) + stub_index(:cities) do + index_scope City + end + end + + let(:cities_index) { CitiesIndex } + + describe '.build' do + specify do + expect(cities_index.build({}).attributes) + .to eq('id' => nil, '_score' => nil, '_explanation' => nil) + end + specify do + expect(cities_index.build('_source' => {name: 'Martin'}).attributes) + .to eq('id' => nil, '_score' => nil, '_explanation' => nil, 'name' => 'Martin') + end + specify do + expect(cities_index.build('_id' => 42).attributes) + .to eq('id' => 42, '_score' => nil, '_explanation' => nil) + end + specify do + expect(cities_index.build('_id' => 42, '_source' => {'id' => 43}).attributes) + .to eq('id' => 43, '_score' => nil, '_explanation' => nil) + end + specify do + expect(cities_index.build('_score' => 42, '_explanation' => {foo: 'bar'}).attributes) + .to eq('id' => nil, '_score' => 42, '_explanation' => {foo: 'bar'}) + end + specify do + expect(cities_index.build('_score' => 42, 'borogoves' => {foo: 'bar'})._data) + .to eq('_score' => 42, 'borogoves' => {foo: 'bar'}) + end + end + + describe '#initialize' do + subject(:city) { cities_index.new(name: 'Martin', age: 42) } + + it do + is_expected.to respond_to(:name) + .and respond_to(:age) + .and have_attributes( + name: 'Martin', + age: 42 + ) + end + + it { expect { city.population }.to raise_error(NoMethodError) } + + context 'highlight' do + subject(:city) do + cities_index.new(name: 'Martin', age: 42) + .tap do |city| + city._data = { + 'highlight' => {'name' => ['Martin']} + } + end + end + + it do + is_expected.to respond_to(:name_highlight) + .and respond_to(:name_highlights) + .and have_attributes( + name: 'Martin', + name_highlight: 'Martin', + name_highlights: ['Martin'] + ) + end + end + end + + describe '#==' do + specify { expect(cities_index.new(id: 42)).to eq(cities_index.new(id: 42)) } + specify { expect(cities_index.new(id: 42, age: 55)).to eq(cities_index.new(id: 42, age: 54)) } + specify { expect(cities_index.new(id: 42)).not_to eq(cities_index.new(id: 43)) } + specify { expect(cities_index.new(id: 42, age: 55)).not_to eq(cities_index.new(id: 43, age: 55)) } + specify { expect(cities_index.new(age: 55)).to eq(cities_index.new(age: 55)) } + specify { expect(cities_index.new(age: 55)).not_to eq(cities_index.new(age: 54)) } + + specify { expect(cities_index.new(id: '42')).to eq(City.new.tap { |m| allow(m).to receive_messages(id: 42) }) } + specify { expect(cities_index.new(id: 42)).not_to eq(City.new.tap { |m| allow(m).to receive_messages(id: 43) }) } + specify { expect(cities_index.new(id: 42)).not_to eq(Class.new) } + + context 'models', :orm do + before do + stub_model(:city) + stub_index(:cities) do + index_scope City + end + end + specify { expect(cities_index.new(id: '42')).to eq(City.new.tap { |m| allow(m).to receive_messages(id: 42) }) } + specify { expect(cities_index.new(id: 42)).not_to eq(City.new.tap { |m| allow(m).to receive_messages(id: 43) }) } + specify { expect(cities_index.new(id: 42)).not_to eq(Class.new) } + end + end +end diff --git a/spec/chewy/index_spec.rb b/spec/chewy/index_spec.rb index 7b6f7a457..57565861b 100644 --- a/spec/chewy/index_spec.rb +++ b/spec/chewy/index_spec.rb @@ -2,9 +2,7 @@ describe Chewy::Index do before do - stub_index(:dummies) do - define_type :dummy - end + stub_index(:dummies) end describe '.import', :orm do @@ -13,11 +11,11 @@ stub_model(:country) stub_index(:cities) do - define_type City + index_scope City end stub_index(:countries) do - define_type Country + index_scope Country end end @@ -30,20 +28,13 @@ end specify do - expect { CitiesIndex.import city: cities.first }.to update_index(CitiesIndex).and_reindex(cities.first).only - expect { CountriesIndex.import city: cities.first }.to update_index(CountriesIndex).and_reindex(countries) - end - - specify do - expect { CitiesIndex.import city: cities.first, country: countries.last } - .to update_index(CitiesIndex).and_reindex(cities.first).only - expect { CountriesIndex.import city: cities.first, country: countries.last } - .to update_index(CountriesIndex).and_reindex(countries.last).only + expect { CitiesIndex.import cities.first }.to update_index(CitiesIndex).and_reindex(cities.first).only + expect { CountriesIndex.import countries.last }.to update_index(CountriesIndex).and_reindex(countries.last).only end specify do expect(CitiesIndex.client).to receive(:bulk).with(hash_including(refresh: false)).once - CitiesIndex.import city: cities.first, refresh: false + CitiesIndex.import cities.first, refresh: false end end @@ -98,18 +89,18 @@ specify { expect(Class.new(Chewy::Index).prefix).to eq('testing') } end - describe '.define_type' do - specify { expect(DummiesIndex.type_hash['dummy']).to eq(DummiesIndex::Dummy) } + describe '.index_scope' do + specify { expect(DummiesIndex.adapter.name).to eq('Default') } context do - before { stub_index(:dummies) { define_type :dummy, name: :borogoves } } - specify { expect(DummiesIndex.type_hash['borogoves']).to eq(DummiesIndex::Borogoves) } + before { stub_index(:dummies) { index_scope :dummy, name: :borogoves } } + specify { expect(DummiesIndex.adapter.name).to eq('Borogoves') } end context do before { stub_class(:city) } - before { stub_index(:dummies) { define_type City, name: :country } } - specify { expect(DummiesIndex.type_hash['country']).to eq(DummiesIndex::Country) } + before { stub_index(:dummies) { index_scope City, name: :country } } + specify { expect(DummiesIndex.adapter.name).to eq('Country') } end context do @@ -120,16 +111,16 @@ expect do Kernel.eval <<-DUMMY_CITY_INDEX class DummyCityIndex2 < Chewy::Index - define_type City - define_type Country + index_scope City + index_scope Country end DUMMY_CITY_INDEX - end.to raise_error(/Multiple types are deprecated/) + end.to raise_error(/Index scope is already defined/) expect do Kernel.eval <<-DUMMY_CITY_INDEX class DummyCityIndex2 < Chewy::Index - define_type City::Nothing + index_scope City::Nothing end DUMMY_CITY_INDEX end.to raise_error(NameError) @@ -137,24 +128,6 @@ class DummyCityIndex2 < Chewy::Index end end - describe '.type_hash' do - specify { expect(DummiesIndex.type_hash['dummy']).to eq(DummiesIndex::Dummy) } - specify { expect(DummiesIndex.type_hash).to have_key 'dummy' } - specify { expect(DummiesIndex.type_hash['dummy']).to be < Chewy::Type } - specify { expect(DummiesIndex.type_hash['dummy'].type_name).to eq('dummy') } - end - - describe '.type' do - specify { expect(DummiesIndex.type('dummy')).to eq(DummiesIndex::Dummy) } - specify { expect { DummiesIndex.type('not-the-dummy') }.to raise_error(Chewy::UndefinedType) } - end - - specify { expect(DummiesIndex.type_names).to eq(DummiesIndex.type_hash.keys) } - - describe '.types' do - specify { expect(DummiesIndex.types).to eq(DummiesIndex.type_hash.values) } - end - describe '.settings' do before do allow(Chewy).to receive_messages(config: Chewy::Config.send(:new)) @@ -194,20 +167,18 @@ def self.colors(*colors) filter(terms: {colors: colors.flatten(1).map(&:to_s)}) end - define_type :city do - def self.by_id; end - field :colors - end + def self.by_id; end + field :colors end end specify { expect(described_class.scopes).to eq([]) } - specify { expect(PlacesIndex.scopes).to match_array(%i[by_rating colors]) } + specify { expect(PlacesIndex.scopes).to match_array(%i[by_rating colors by_id]) } context do before do Chewy.massacre - PlacesIndex::City.import!( + PlacesIndex.import!( double(colors: ['red']), double(colors: %w[red green]), double(colors: %w[green yellow]) @@ -215,16 +186,12 @@ def self.by_id; end end specify do - # This `blank?`` call is for the messed scopes bug reproduction. See #573 - PlacesIndex::City.blank? expect(PlacesIndex.colors(:green).map(&:colors)) .to contain_exactly(%w[red green], %w[green yellow]) end specify do - # This `blank?` call is for the messed scopes bug reproduction. See #573 - PlacesIndex::City.blank? - expect(PlacesIndex::City.colors(:green).map(&:colors)) + expect(PlacesIndex.colors(:green).map(&:colors)) .to contain_exactly(%w[red green], %w[green yellow]) end end @@ -243,12 +210,10 @@ def self.by_id; end describe '.mappings_hash' do specify { expect(stub_index(:documents).mappings_hash).to eq({}) } - specify { expect(stub_index(:documents) { define_type :document }.mappings_hash).to eq({}) } + specify { expect(stub_index(:documents) { index_scope :document }.mappings_hash).to eq({}) } specify do expect(stub_index(:documents) do - define_type :document do - field :date, type: 'date' - end + field :date, type: 'date' end.mappings_hash).to eq(mappings: {properties: {date: {type: 'date'}}}) end end @@ -264,17 +229,13 @@ def self.by_id; end end specify do expect(stub_index(:documents) do - define_type :document do - field :name - end + field :name end.specification_hash.keys).to eq([:mappings]) end specify do expect(stub_index(:documents) do settings number_of_shards: 1 - define_type :document do - field :name - end + field :name end.specification_hash.keys).to match_array(%i[mappings settings]) end end @@ -288,25 +249,21 @@ def self.by_id; end context 'index call inside index', :orm do before do stub_index(:cities) do - define_type :city do - field :country_name, value: (lambda do |city| - CountriesIndex::Country.filter(term: {_id: city.country_id}).first.name - end) - end + field :country_name, value: (lambda do |city| + CountriesIndex.filter(term: {_id: city.country_id}).first.name + end) end stub_index(:countries) do - define_type :country do - field :name - end + field :name end - CountriesIndex::Country.import!(double(id: 1, name: 'Country')) + CountriesIndex.import!(double(id: 1, name: 'Country')) end specify do - expect { CitiesIndex::City.import!(double(country_id: 1)) } - .to update_index(CitiesIndex::City).and_reindex(country_name: 'Country') + expect { CitiesIndex.import!(double(country_id: 1)) } + .to update_index(CitiesIndex).and_reindex(country_name: 'Country') end end end diff --git a/spec/chewy/journal_spec.rb b/spec/chewy/journal_spec.rb index abae9fa6b..95b618cc7 100644 --- a/spec/chewy/journal_spec.rb +++ b/spec/chewy/journal_spec.rb @@ -13,14 +13,12 @@ end stub_index("#{namespace}cities") do - define_type City do - default_import_options journal: true - end + index_scope City + default_import_options journal: true end stub_index("#{namespace}countries") do - define_type Country do - default_import_options journal: true - end + index_scope Country + default_import_options journal: true end Chewy.massacre @@ -67,63 +65,54 @@ def timestamp(time) expected_journal = [ { 'index_name' => "#{namespace}cities", - 'type_name' => 'city', 'action' => 'index', 'references' => ['1'].map(&Base64.method(:encode64)), 'created_at' => time.utc.as_json }, { 'index_name' => "#{namespace}cities", - 'type_name' => 'city', 'action' => 'index', 'references' => ['2'].map(&Base64.method(:encode64)), 'created_at' => time.utc.as_json }, { 'index_name' => "#{namespace}countries", - 'type_name' => 'country', 'action' => 'index', 'references' => ['1'].map(&Base64.method(:encode64)), 'created_at' => time.utc.as_json }, { 'index_name' => "#{namespace}countries", - 'type_name' => 'country', 'action' => 'index', 'references' => ['2'].map(&Base64.method(:encode64)), 'created_at' => time.utc.as_json }, { 'index_name' => "#{namespace}countries", - 'type_name' => 'country', 'action' => 'index', 'references' => ['3'].map(&Base64.method(:encode64)), 'created_at' => time.utc.as_json }, { 'index_name' => "#{namespace}cities", - 'type_name' => 'city', 'action' => 'index', 'references' => %w[1 2].map(&Base64.method(:encode64)), 'created_at' => import_time.utc.as_json }, { 'index_name' => "#{namespace}countries", - 'type_name' => 'country', 'action' => 'index', 'references' => %w[1 2 3].map(&Base64.method(:encode64)), 'created_at' => import_time.utc.as_json }, { 'index_name' => "#{namespace}cities", - 'type_name' => 'city', 'action' => 'index', 'references' => ['1'].map(&Base64.method(:encode64)), 'created_at' => update_time.utc.as_json }, { 'index_name' => "#{namespace}countries", - 'type_name' => 'country', 'action' => 'delete', 'references' => ['2'].map(&Base64.method(:encode64)), 'created_at' => destroy_time.utc.as_json @@ -166,14 +155,12 @@ def timestamp(time) end stub_index(:cities) do - define_type City do - default_import_options journal: true - end + index_scope City + default_import_options journal: true end stub_index(:countries) do - define_type Country do - default_import_options journal: true - end + index_scope Country + default_import_options journal: true end end @@ -235,7 +222,7 @@ def timestamp(time) let!(:journal_entries) do record = Chewy::Stash::Journal.entries(time).first Array.new(count_of_checks) do |i| - Chewy::Stash::Journal::Journal.new( + Chewy::Stash::Journal.new( record.attributes.merge( 'created_at' => time.to_i + i, 'references' => [i.to_s] diff --git a/spec/chewy/minitest/helpers_spec.rb b/spec/chewy/minitest/helpers_spec.rb index 6a65d56c8..59bc2b90f 100644 --- a/spec/chewy/minitest/helpers_spec.rb +++ b/spec/chewy/minitest/helpers_spec.rb @@ -18,54 +18,52 @@ def assert_includes(haystack, needle, _comment) before do stub_index(:dummies) do - define_type :dummy do - root value: ->(_o) { {} } - end + root value: ->(_o) { {} } end end context 'assert_indexes' do specify 'doesn\'t fail when index updates correctly' do expect do - assert_indexes DummiesIndex::Dummy do - DummiesIndex::Dummy.bulk body: [{index: {_id: 42, data: {}}}, {index: {_id: 41, data: {}}}] + assert_indexes DummiesIndex do + DummiesIndex.bulk body: [{index: {_id: 42, data: {}}}, {index: {_id: 41, data: {}}}] end end.to_not raise_error end specify 'fails when index doesn\'t update' do expect do - assert_indexes DummiesIndex::Dummy do + assert_indexes DummiesIndex do end end.to raise_error(RSpec::Expectations::ExpectationNotMetError) end specify 'SearchIndexReceiver catches the indexes' do - receiver = assert_indexes DummiesIndex::Dummy do - DummiesIndex::Dummy.bulk body: [{index: {_id: 42, data: {}}}, {index: {_id: 41, data: {}}}] + receiver = assert_indexes DummiesIndex do + DummiesIndex.bulk body: [{index: {_id: 42, data: {}}}, {index: {_id: 41, data: {}}}] end expect(receiver).to be_a(SearchIndexReceiver) expect( - receiver.indexes_for(DummiesIndex::Dummy) + receiver.indexes_for(DummiesIndex) .map { |index| index[:_id] } ).to match_array([41, 42]) end specify 'Real index is bypassed when asserting' do - expect(DummiesIndex::Dummy).not_to receive(:bulk) + expect(DummiesIndex).not_to receive(:bulk) - assert_indexes DummiesIndex::Dummy do - DummiesIndex::Dummy.bulk body: [{index: {_id: 42, data: {}}}, {index: {_id: 41, data: {}}}] + assert_indexes DummiesIndex do + DummiesIndex.bulk body: [{index: {_id: 42, data: {}}}, {index: {_id: 41, data: {}}}] end end specify 'Real index is allowed when asserting' do - expect(DummiesIndex::Dummy).to receive(:bulk) + expect(DummiesIndex).to receive(:bulk) - assert_indexes DummiesIndex::Dummy, bypass_actual_index: false do - DummiesIndex::Dummy.bulk body: [{index: {_id: 42, data: {}}}, {index: {_id: 41, data: {}}}] + assert_indexes DummiesIndex, bypass_actual_index: false do + DummiesIndex.bulk body: [{index: {_id: 42, data: {}}}, {index: {_id: 41, data: {}}}] end end end diff --git a/spec/chewy/minitest/search_index_receiver_spec.rb b/spec/chewy/minitest/search_index_receiver_spec.rb index 180f109a4..213d46de8 100644 --- a/spec/chewy/minitest/search_index_receiver_spec.rb +++ b/spec/chewy/minitest/search_index_receiver_spec.rb @@ -26,97 +26,93 @@ def parse_request(request) before do stub_index(:dummies) do - define_type :fizz do - root value: ->(_o) { {} } - end + root value: ->(_o) { {} } end stub_index(:dummies2) do - define_type :buzz do - root value: ->(_o) { {} } - end + root value: ->(_o) { {} } end end context 'catch' do specify 'archives more than one type' do - receiver.catch search_request(2), DummiesIndex::Fizz - receiver.catch search_request(3), Dummies2Index::Buzz - expect(receiver.indexes.keys).to match_array([DummiesIndex::Fizz, Dummies2Index::Buzz]) + receiver.catch search_request(2), DummiesIndex + receiver.catch search_request(3), Dummies2Index + expect(receiver.indexes.keys).to match_array([DummiesIndex, Dummies2Index]) end end context 'indexes_for' do before do - receiver.catch search_request(2), DummiesIndex::Fizz - receiver.catch search_request(3), Dummies2Index::Buzz + receiver.catch search_request(2), DummiesIndex + receiver.catch search_request(3), Dummies2Index end specify 'returns indexes for a specific type' do - expect(parse_request(receiver.indexes_for(DummiesIndex::Fizz))).to match_array([1, 2]) + expect(parse_request(receiver.indexes_for(DummiesIndex))).to match_array([1, 2]) end specify 'returns only indexes for all types' do index_responses = receiver.indexes - expect(index_responses.keys).to match_array([DummiesIndex::Fizz, Dummies2Index::Buzz]) + expect(index_responses.keys).to match_array([DummiesIndex, Dummies2Index]) expect(parse_request(index_responses.values.flatten)).to match_array([1, 2, 1, 2, 3]) end end context 'deletes_for' do before do - receiver.catch search_request(2, verb: :delete), DummiesIndex::Fizz - receiver.catch search_request(3, verb: :delete), Dummies2Index::Buzz + receiver.catch search_request(2, verb: :delete), DummiesIndex + receiver.catch search_request(3, verb: :delete), Dummies2Index end specify 'returns deletes for a specific type' do - expect(receiver.deletes_for(Dummies2Index::Buzz)).to match_array([1, 2, 3]) + expect(receiver.deletes_for(Dummies2Index)).to match_array([1, 2, 3]) end specify 'returns only deletes for all types' do deletes = receiver.deletes - expect(deletes.keys).to match_array([DummiesIndex::Fizz, Dummies2Index::Buzz]) + expect(deletes.keys).to match_array([DummiesIndex, Dummies2Index]) expect(deletes.values.flatten).to match_array([1, 2, 1, 2, 3]) end end context 'indexed?' do before do - receiver.catch search_request(1), DummiesIndex::Fizz + receiver.catch search_request(1), DummiesIndex end specify 'validates that an object was indexed' do dummy = OpenStruct.new(id: 1) - expect(receiver.indexed?(dummy, DummiesIndex::Fizz)).to be(true) + expect(receiver.indexed?(dummy, DummiesIndex)).to be(true) end specify 'doesn\'t validate than unindexed objects were indexed' do dummy = OpenStruct.new(id: 2) - expect(receiver.indexed?(dummy, DummiesIndex::Fizz)).to be(false) + expect(receiver.indexed?(dummy, DummiesIndex)).to be(false) end end context 'deleted?' do before do - receiver.catch search_request(1, verb: :delete), DummiesIndex::Fizz + receiver.catch search_request(1, verb: :delete), DummiesIndex end specify 'validates than an object was deleted' do dummy = OpenStruct.new(id: 1) - expect(receiver.deleted?(dummy, DummiesIndex::Fizz)).to be(true) + expect(receiver.deleted?(dummy, DummiesIndex)).to be(true) end specify 'doesn\'t validate than undeleted objects were deleted' do dummy = OpenStruct.new(id: 2) - expect(receiver.deleted?(dummy, DummiesIndex::Fizz)).to be(false) + expect(receiver.deleted?(dummy, DummiesIndex)).to be(false) end end context 'updated_indexes' do specify 'provides a list of indices updated' do - receiver.catch search_request(2, verb: :delete), DummiesIndex::Fizz - receiver.catch search_request(3, verb: :delete), Dummies2Index::Buzz - expect(receiver.updated_indexes).to match_array([DummiesIndex::Fizz, Dummies2Index::Buzz]) + receiver.catch search_request(2, verb: :delete), DummiesIndex + receiver.catch search_request(3, verb: :delete), Dummies2Index + expect(receiver.updated_indexes).to match_array([DummiesIndex, Dummies2Index]) end end end diff --git a/spec/chewy/multi_search_spec.rb b/spec/chewy/multi_search_spec.rb index ea82eb601..8ca1e2cd2 100644 --- a/spec/chewy/multi_search_spec.rb +++ b/spec/chewy/multi_search_spec.rb @@ -13,10 +13,9 @@ def self.aggregate_by_country aggs(country: {terms: {field: :country_id}}) end - define_type City do - field :name, type: 'keyword' - field :country_id, type: 'keyword' - end + index_scope City + field :name, type: 'keyword' + field :country_id, type: 'keyword' end end @@ -78,7 +77,7 @@ def self.aggregate_by_country is_expected.to have(2).responses expect(responses[0]).to be_a(Chewy::Search::Response) expect(responses[1]).to be_a(Chewy::Search::Response) - expect(responses[1].wrappers).to all(be_a CitiesIndex::City) + expect(responses[1].wrappers).to all(be_a CitiesIndex) end end end diff --git a/spec/chewy/rake_helper_spec.rb b/spec/chewy/rake_helper_spec.rb index 19b471557..97b5c9c25 100644 --- a/spec/chewy/rake_helper_spec.rb +++ b/spec/chewy/rake_helper_spec.rb @@ -8,13 +8,12 @@ stub_model(:country) stub_index(:cities) do - define_type City do - field :name - field :updated_at, type: 'date' - end + index_scope City + field :name + field :updated_at, type: 'date' end stub_index(:countries) do - define_type Country + index_scope Country end stub_index(:users) @@ -52,17 +51,18 @@ .to update_index(CitiesIndex) expect(output.string).to match(Regexp.new(<<-OUTPUT, Regexp::MULTILINE)) \\AResetting CitiesIndex - Imported CitiesIndex::City in \\d+s, stats: index 3 - Applying journal to \\[CitiesIndex::City\\], 2 entries, stage 1 - Imported CitiesIndex::City in \\d+s, stats: index 2 - Imported Chewy::Stash::Specification::Specification in \\d+s, stats: index 1 + Imported CitiesIndex in \\d+s, stats: index 3 + Applying journal to \\[CitiesIndex\\], 2 entries, stage 1 + Imported CitiesIndex in \\d+s, stats: index 2 + Imported Chewy::Stash::Specification in \\d+s, stats: index 1 Resetting CountriesIndex - Imported CountriesIndex::Country in \\d+s, stats: index 2 - Applying journal to \\[CountriesIndex::Country\\], 1 entries, stage 1 - Imported CountriesIndex::Country in \\d+s, stats: index 1 - Imported Chewy::Stash::Specification::Specification in \\d+s, stats: index 1 + Imported CountriesIndex in \\d+s, stats: index 2 + Applying journal to \\[CountriesIndex\\], 1 entries, stage 1 + Imported CountriesIndex in \\d+s, stats: index 1 + Imported Chewy::Stash::Specification in \\d+s, stats: index 1 Resetting UsersIndex - Imported Chewy::Stash::Specification::Specification in \\d+s, stats: index 1 + Imported UsersIndex in 1s, stats:\s + Imported Chewy::Stash::Specification in \\d+s, stats: index 1 Total: \\d+s\\Z OUTPUT end @@ -73,10 +73,10 @@ .to update_index(CitiesIndex) expect(output.string).to match(Regexp.new(<<-OUTPUT, Regexp::MULTILINE)) \\AResetting CitiesIndex - Imported CitiesIndex::City in \\d+s, stats: index 3 - Applying journal to \\[CitiesIndex::City\\], 2 entries, stage 1 - Imported CitiesIndex::City in \\d+s, stats: index 2 - Imported Chewy::Stash::Specification::Specification in \\d+s, stats: index 1 + Imported CitiesIndex in \\d+s, stats: index 3 + Applying journal to \\[CitiesIndex\\], 2 entries, stage 1 + Imported CitiesIndex in \\d+s, stats: index 2 + Imported Chewy::Stash::Specification in \\d+s, stats: index 1 Total: \\d+s\\Z OUTPUT end @@ -87,7 +87,8 @@ .not_to update_index(CitiesIndex) expect(output.string).to match(Regexp.new(<<-OUTPUT, Regexp::MULTILINE)) \\AResetting UsersIndex - Imported Chewy::Stash::Specification::Specification in \\d+s, stats: index 1 + Imported UsersIndex in 1s, stats:\s + Imported Chewy::Stash::Specification in \\d+s, stats: index 1 Total: \\d+s\\Z OUTPUT end @@ -100,13 +101,14 @@ .to update_index(CitiesIndex) expect(output.string).to match(Regexp.new(<<-OUTPUT, Regexp::MULTILINE)) \\AResetting CitiesIndex - Imported CitiesIndex::City in \\d+s, stats: index 3 - Imported Chewy::Stash::Specification::Specification in \\d+s, stats: index 1 + Imported CitiesIndex in \\d+s, stats: index 3 + Imported Chewy::Stash::Specification in \\d+s, stats: index 1 Resetting CountriesIndex - Imported CountriesIndex::Country in \\d+s, stats: index 2 - Imported Chewy::Stash::Specification::Specification in \\d+s, stats: index 1 + Imported CountriesIndex in \\d+s, stats: index 2 + Imported Chewy::Stash::Specification in \\d+s, stats: index 1 Resetting UsersIndex - Imported Chewy::Stash::Specification::Specification in \\d+s, stats: index 1 + Imported UsersIndex in 1s, stats:\s + Imported Chewy::Stash::Specification in \\d+s, stats: index 1 Total: \\d+s\\Z OUTPUT end @@ -125,7 +127,8 @@ \\ASkipping CitiesIndex, the specification didn't change Skipping CountriesIndex, the specification didn't change Resetting UsersIndex - Imported Chewy::Stash::Specification::Specification in \\d+s, stats: index 1 + Imported UsersIndex in 1s, stats:\s + Imported Chewy::Stash::Specification in \\d+s, stats: index 1 Total: \\d+s\\Z OUTPUT end @@ -136,7 +139,8 @@ .not_to update_index(CitiesIndex) expect(output.string).to match(Regexp.new(<<-OUTPUT, Regexp::MULTILINE)) \\AResetting UsersIndex - Imported Chewy::Stash::Specification::Specification in \\d+s, stats: index 1 + Imported UsersIndex in 1s, stats:\s + Imported Chewy::Stash::Specification in \\d+s, stats: index 1 Total: \\d+s\\Z OUTPUT end @@ -165,6 +169,7 @@ expect(output.string).to match(Regexp.new(<<-OUTPUT, Regexp::MULTILINE)) \\ASkipping CitiesIndex, it does not exists \\(use rake chewy:reset\\[cities\\] to create and update it\\) Skipping CountriesIndex, it does not exists \\(use rake chewy:reset\\[countries\\] to create and update it\\) +Skipping UsersIndex, it does not exists \\(use rake chewy:reset\\[users\\] to create and update it\\) Total: \\d+s\\Z OUTPUT end @@ -181,9 +186,10 @@ .to update_index(CitiesIndex) expect(output.string).to match(Regexp.new(<<-OUTPUT, Regexp::MULTILINE)) \\AUpdating CitiesIndex - Imported CitiesIndex::City in \\d+s, stats: index 3 + Imported CitiesIndex in \\d+s, stats: index 3 Updating CountriesIndex - Imported CountriesIndex::Country in \\d+s, stats: index 2 + Imported CountriesIndex in \\d+s, stats: index 2 +Skipping UsersIndex, it does not exists \\(use rake chewy:reset\\[users\\] to create and update it\\) Total: \\d+s\\Z OUTPUT end @@ -194,7 +200,7 @@ .not_to update_index(CitiesIndex) expect(output.string).to match(Regexp.new(<<-OUTPUT, Regexp::MULTILINE)) \\AUpdating CountriesIndex - Imported CountriesIndex::Country in \\d+s, stats: index 2 + Imported CountriesIndex in \\d+s, stats: index 2 Total: \\d+s\\Z OUTPUT end @@ -205,7 +211,8 @@ .to update_index(CitiesIndex) expect(output.string).to match(Regexp.new(<<-OUTPUT, Regexp::MULTILINE)) \\AUpdating CitiesIndex - Imported CitiesIndex::City in \\d+s, stats: index 3 + Imported CitiesIndex in \\d+s, stats: index 3 +Skipping UsersIndex, it does not exists \\(use rake chewy:reset\\[users\\] to create and update it\\) Total: \\d+s\\Z OUTPUT end @@ -218,15 +225,19 @@ expect { described_class.sync(output: output) } .to update_index(CitiesIndex) expect(output.string).to match(Regexp.new(<<-OUTPUT, Regexp::MULTILINE)) -\\ASynchronizing CitiesIndex::City - Imported CitiesIndex::City in \\d+s, stats: index 3 +\\ASynchronizing CitiesIndex + Imported CitiesIndex in \\d+s, stats: index 3 Missing documents: \\[[^\\]]+\\] Took \\d+s -Synchronizing CountriesIndex::Country - CountriesIndex::Country doesn't support outdated synchronization - Imported CountriesIndex::Country in \\d+s, stats: index 2 +Synchronizing CountriesIndex + CountriesIndex doesn't support outdated synchronization + Imported CountriesIndex in \\d+s, stats: index 2 Missing documents: \\[[^\\]]+\\] Took \\d+s +Synchronizing UsersIndex + UsersIndex doesn't support outdated synchronization + Skipping UsersIndex, up to date + Took \\d+s Total: \\d+s\\Z OUTPUT end @@ -236,7 +247,6 @@ CitiesIndex.import(cities.first(2)) CountriesIndex.import - sleep(1) if ActiveSupport::VERSION::STRING < '4.1.0' cities.first.update(name: 'Name5') end @@ -245,14 +255,18 @@ expect { described_class.sync(output: output) } .to update_index(CitiesIndex) expect(output.string).to match(Regexp.new(<<-OUTPUT, Regexp::MULTILINE)) -\\ASynchronizing CitiesIndex::City - Imported CitiesIndex::City in \\d+s, stats: index 2 +\\ASynchronizing CitiesIndex + Imported CitiesIndex in \\d+s, stats: index 2 Missing documents: \\["#{cities.last.id}"\\] Outdated documents: \\["#{cities.first.id}"\\] Took \\d+s -Synchronizing CountriesIndex::Country - CountriesIndex::Country doesn't support outdated synchronization - Skipping CountriesIndex::Country, up to date +Synchronizing CountriesIndex + CountriesIndex doesn't support outdated synchronization + Skipping CountriesIndex, up to date + Took \\d+s +Synchronizing UsersIndex + UsersIndex doesn't support outdated synchronization + Skipping UsersIndex, up to date Took \\d+s Total: \\d+s\\Z OUTPUT @@ -263,8 +277,8 @@ expect { described_class.sync(only: CitiesIndex, output: output) } .to update_index(CitiesIndex) expect(output.string).to match(Regexp.new(<<-OUTPUT, Regexp::MULTILINE)) -\\ASynchronizing CitiesIndex::City - Imported CitiesIndex::City in \\d+s, stats: index 2 +\\ASynchronizing CitiesIndex + Imported CitiesIndex in \\d+s, stats: index 2 Missing documents: \\["#{cities.last.id}"\\] Outdated documents: \\["#{cities.first.id}"\\] Took \\d+s @@ -277,9 +291,13 @@ expect { described_class.sync(except: ['cities'], output: output) } .not_to update_index(CitiesIndex) expect(output.string).to match(Regexp.new(<<-OUTPUT, Regexp::MULTILINE)) -\\ASynchronizing CountriesIndex::Country - CountriesIndex::Country doesn't support outdated synchronization - Skipping CountriesIndex::Country, up to date +\\ASynchronizing CountriesIndex + CountriesIndex doesn't support outdated synchronization + Skipping CountriesIndex, up to date + Took \\d+s +Synchronizing UsersIndex + UsersIndex doesn't support outdated synchronization + Skipping UsersIndex, up to date Took \\d+s Total: \\d+s\\Z OUTPUT @@ -308,9 +326,9 @@ .to update_index(CitiesIndex) expect(output.string).to match(Regexp.new(<<-OUTPUT, Regexp::MULTILINE)) \\AApplying journal entries created after [+-:\\d\\s]+ - Applying journal to \\[CitiesIndex::City, CountriesIndex::Country\\], 3 entries, stage 1 - Imported CitiesIndex::City in \\d+s, stats: index 2 - Imported CountriesIndex::Country in \\d+s, stats: index 1 + Applying journal to \\[CitiesIndex, CountriesIndex\\], 3 entries, stage 1 + Imported CitiesIndex in \\d+s, stats: index 2 + Imported CountriesIndex in \\d+s, stats: index 1 Total: \\d+s\\Z OUTPUT end @@ -321,8 +339,8 @@ .not_to update_index(CitiesIndex) expect(output.string).to match(Regexp.new(<<-OUTPUT, Regexp::MULTILINE)) \\AApplying journal entries created after [+-:\\d\\s]+ - Applying journal to \\[CountriesIndex::Country\\], 1 entries, stage 1 - Imported CountriesIndex::Country in \\d+s, stats: index 1 + Applying journal to \\[CountriesIndex\\], 1 entries, stage 1 + Imported CountriesIndex in \\d+s, stats: index 1 Total: \\d+s\\Z OUTPUT end @@ -333,8 +351,8 @@ .to update_index(CitiesIndex) expect(output.string).to match(Regexp.new(<<-OUTPUT, Regexp::MULTILINE)) \\AApplying journal entries created after [+-:\\d\\s]+ - Applying journal to \\[CitiesIndex::City\\], 2 entries, stage 1 - Imported CitiesIndex::City in \\d+s, stats: index 2 + Applying journal to \\[CitiesIndex\\], 2 entries, stage 1 + Imported CitiesIndex in \\d+s, stats: index 2 Total: \\d+s\\Z OUTPUT end @@ -345,8 +363,8 @@ .not_to update_index(CitiesIndex) expect(output.string).to match(Regexp.new(<<-OUTPUT, Regexp::MULTILINE)) \\AApplying journal entries created after [+-:\\d\\s]+ - Applying journal to \\[CountriesIndex::Country\\], 1 entries, stage 1 - Imported CountriesIndex::Country in \\d+s, stats: index 1 + Applying journal to \\[CountriesIndex\\], 1 entries, stage 1 + Imported CountriesIndex in \\d+s, stats: index 1 Total: \\d+s\\Z OUTPUT end @@ -443,7 +461,7 @@ end let(:index_name) { CitiesIndex.index_name } - let(:unexisting_index) { 'wrong_index' } + let(:nonexistent_index) { 'wrong_index' } context 'with existing index' do specify do @@ -457,11 +475,11 @@ end end - context 'with unexisting index name' do + context 'with non-existent index name' do specify do output = StringIO.new - expect { described_class.update_mapping(name: unexisting_index, output: output) } - .to raise_error Elasticsearch::Transport::Transport::Errors::NotFound + expect { described_class.update_mapping(name: nonexistent_index, output: output) } + .to raise_error NameError end end end diff --git a/spec/chewy/rspec/update_index_spec.rb b/spec/chewy/rspec/update_index_spec.rb index b1cea443f..5982f4c51 100644 --- a/spec/chewy/rspec/update_index_spec.rb +++ b/spec/chewy/rspec/update_index_spec.rb @@ -3,10 +3,8 @@ describe :update_index do before do stub_index(:dummies) do - define_type :dummy end stub_index(:dummies2) do - define_type :dummy end end @@ -16,16 +14,16 @@ end specify { expect {}.not_to update_index(DummiesIndex) } - specify { expect { DummiesIndex::Dummy.bulk body: [] }.not_to update_index(DummiesIndex) } + specify { expect { DummiesIndex.bulk body: [] }.not_to update_index(DummiesIndex) } specify do - expect { expect { DummiesIndex::Dummy.bulk body: [{index: {_id: 42}}] }.not_to update_index(DummiesIndex) } + expect { expect { DummiesIndex.bulk body: [{index: {_id: 42}}] }.not_to update_index(DummiesIndex) } .to fail_with(/Expected index .* not to be updated, but it was with/) end specify do expect do - DummiesIndex::Dummy.bulk body: [{index: {_id: 42, data: {}}}, {index: {_id: 41, data: {}}}] + DummiesIndex.bulk body: [{index: {_id: 42, data: {}}}, {index: {_id: 41, data: {}}}] end.to update_index(DummiesIndex).and_reindex(41, 42).only end @@ -33,7 +31,7 @@ let(:expectation) do expect do expect do - DummiesIndex::Dummy.bulk body: [{index: {_id: 42}}, {index: {_id: 41}}, {index: {_id: 42}}] + DummiesIndex.bulk body: [{index: {_id: 42}}, {index: {_id: 41}}, {index: {_id: 42}}] end.not_to update_index(DummiesIndex) end end @@ -45,8 +43,8 @@ context 'compound matchers' do specify do expect do - DummiesIndex::Dummy.bulk body: [{index: {_id: 42, data: {}}}, {index: {_id: 41, data: {}}}] - Dummies2Index::Dummy.bulk body: [{index: {_id: 43, data: {}}}] + DummiesIndex.bulk body: [{index: {_id: 42, data: {}}}, {index: {_id: 41, data: {}}}] + Dummies2Index.bulk body: [{index: {_id: 43, data: {}}}] end.to update_index(DummiesIndex).and_reindex(41, 42).only .and update_index(Dummies2Index).and_reindex(43).only end @@ -55,7 +53,7 @@ let(:expectation) do expect do expect do - Dummies2Index::Dummy.bulk body: [{index: {_id: 43, data: {}}}] + Dummies2Index.bulk body: [{index: {_id: 43, data: {}}}] end.to update_index(DummiesIndex).and_reindex(41, 42).only .and update_index(Dummies2Index).and_reindex(43).only end @@ -67,91 +65,91 @@ context '#only' do specify do - expect { DummiesIndex::Dummy.bulk body: [{index: {_id: 42, data: {}}}, {index: {_id: 41, data: {}}}] } + expect { DummiesIndex.bulk body: [{index: {_id: 42, data: {}}}, {index: {_id: 41, data: {}}}] } .to update_index(DummiesIndex).and_reindex(41, 42).only end specify do expect do - expect { DummiesIndex::Dummy.bulk body: [{index: {_id: 42, data: {}}}, {index: {_id: 41, data: {}}}] } + expect { DummiesIndex.bulk body: [{index: {_id: 42, data: {}}}, {index: {_id: 41, data: {}}}] } .to update_index(DummiesIndex).and_reindex(41).only end.to fail_matching 'to update documents ["41"] only, but ["42"] was updated also' end specify do expect do - expect { DummiesIndex::Dummy.bulk body: [{index: {_id: 42, data: {}}}, {index: {_id: 41, data: {}}}] } + expect { DummiesIndex.bulk body: [{index: {_id: 42, data: {}}}, {index: {_id: 41, data: {}}}] } .to update_index(DummiesIndex).and_reindex(41, times: 2).only end.to fail_matching 'to update documents ["41"] only, but ["42"] was updated also' end specify do - expect { DummiesIndex::Dummy.bulk body: [{delete: {_id: 42}}, {delete: {_id: 41}}] } + expect { DummiesIndex.bulk body: [{delete: {_id: 42}}, {delete: {_id: 41}}] } .to update_index(DummiesIndex).and_delete(41, 42).only end specify do expect do - expect { DummiesIndex::Dummy.bulk body: [{delete: {_id: 42}}, {delete: {_id: 41}}] } + expect { DummiesIndex.bulk body: [{delete: {_id: 42}}, {delete: {_id: 41}}] } .to update_index(DummiesIndex).and_delete(41).only end.to fail_matching 'to delete documents ["41"] only, but ["42"] was deleted also' end specify do expect do - expect { DummiesIndex::Dummy.bulk body: [{delete: {_id: 42}}, {delete: {_id: 41}}] } + expect { DummiesIndex.bulk body: [{delete: {_id: 42}}, {delete: {_id: 41}}] } .to update_index(DummiesIndex).and_delete(41, times: 2).only end.to fail_matching 'to delete documents ["41"] only, but ["42"] was deleted also' end specify do expect do - expect { DummiesIndex::Dummy.bulk body: [{index: {_id: 42, data: {}}}, {delete: {_id: 41}}] } + expect { DummiesIndex.bulk body: [{index: {_id: 42, data: {}}}, {delete: {_id: 41}}] } .to update_index(DummiesIndex).and_reindex(42).only end.to fail_matching 'to update documents ["42"] only, but ["41"] was deleted also' end specify do expect do expect do - DummiesIndex::Dummy.bulk body: [{index: {_id: 42, data: {}}}, {index: {_id: 43, data: {}}}, - {delete: {_id: 41}}] + DummiesIndex.bulk body: [{index: {_id: 42, data: {}}}, {index: {_id: 43, data: {}}}, + {delete: {_id: 41}}] end.to update_index(DummiesIndex).and_reindex(42).only end.to fail_matching 'to update documents ["42"] only, but ["43"] was updated and ["41"] was deleted also' end specify do expect do - expect { DummiesIndex::Dummy.bulk body: [{index: {_id: 42, data: {}}}, {delete: {_id: 41}}] } + expect { DummiesIndex.bulk body: [{index: {_id: 42, data: {}}}, {delete: {_id: 41}}] } .to update_index(DummiesIndex).and_delete(41).only end.to fail_matching 'to delete documents ["41"] only, but ["42"] was updated also' end specify do expect do expect do - DummiesIndex::Dummy.bulk body: [{index: {_id: 42, data: {}}}, {delete: {_id: 41}}, {delete: {_id: 43}}] + DummiesIndex.bulk body: [{index: {_id: 42, data: {}}}, {delete: {_id: 41}}, {delete: {_id: 43}}] end.to update_index(DummiesIndex).and_delete(41).only end.to fail_matching 'to delete documents ["41"] only, but ["42"] was updated and ["43"] was deleted also' end end context '#and_reindex' do - specify { expect { DummiesIndex::Dummy.bulk body: [{index: {_id: 42}}] }.to update_index(DummiesIndex) } + specify { expect { DummiesIndex.bulk body: [{index: {_id: 42}}] }.to update_index(DummiesIndex) } specify do - expect { DummiesIndex::Dummy.bulk body: [{index: {_id: 42, data: {}}}] } + expect { DummiesIndex.bulk body: [{index: {_id: 42, data: {}}}] } .to update_index(DummiesIndex).and_reindex(42) end specify do - expect { DummiesIndex::Dummy.bulk body: [{index: {_id: 42, data: {}}}, {index: {_id: 43, data: {}}}] } + expect { DummiesIndex.bulk body: [{index: {_id: 42, data: {}}}, {index: {_id: 43, data: {}}}] } .to update_index(DummiesIndex).and_reindex(double(id: 42)) end specify do - expect { DummiesIndex::Dummy.bulk body: [{index: {_id: 42, data: {}}}, {index: {_id: 43, data: {}}}] } + expect { DummiesIndex.bulk body: [{index: {_id: 42, data: {}}}, {index: {_id: 43, data: {}}}] } .to update_index(DummiesIndex).and_reindex(double(id: 42), double(id: 43)) end specify do - expect { DummiesIndex::Dummy.bulk body: [{index: {_id: 42, data: {}}}, {index: {_id: 43, data: {}}}] } + expect { DummiesIndex.bulk body: [{index: {_id: 42, data: {}}}, {index: {_id: 43, data: {}}}] } .to update_index(DummiesIndex).and_reindex([double(id: 42), 43]) end specify do expect do expect do - DummiesIndex::Dummy.bulk body: [{index: {_id: 42, data: {}}}, {index: {_id: 43, data: {}}}] + DummiesIndex.bulk body: [{index: {_id: 42, data: {}}}, {index: {_id: 43, data: {}}}] end.to update_index(DummiesIndex).and_reindex([44, 43]) end.to fail_matching 'Expected document with id `44` to be reindexed, but it was not' end @@ -160,7 +158,7 @@ let(:expectation) do expect do expect do - DummiesIndex::Dummy.bulk body: [{index: {_id: 42, data: {}}}, {index: {_id: 43, data: {}}}] + DummiesIndex.bulk body: [{index: {_id: 42, data: {}}}, {index: {_id: 43, data: {}}}] end.to update_index(DummiesIndex).and_reindex(44, double(id: 47)) end end @@ -172,15 +170,15 @@ context ':times' do specify do expect do - DummiesIndex::Dummy.bulk body: [{index: {_id: 42, data: {}}}, {index: {_id: 43, data: {}}}] - DummiesIndex::Dummy.bulk body: [{index: {_id: 43, data: {}}}, {index: {_id: 44, data: {}}}] + DummiesIndex.bulk body: [{index: {_id: 42, data: {}}}, {index: {_id: 43, data: {}}}] + DummiesIndex.bulk body: [{index: {_id: 43, data: {}}}, {index: {_id: 44, data: {}}}] end.to update_index(DummiesIndex).and_reindex(42, 44, times: 1).and_reindex(43, times: 2) end specify do expect do expect do - DummiesIndex::Dummy.bulk body: [{index: {_id: 43, data: {a: '1'}}}] + DummiesIndex.bulk body: [{index: {_id: 43, data: {a: '1'}}}] end.to update_index(DummiesIndex).and_reindex(42, times: 3) end.to fail_matching('Expected document with id `42` to be reindexed, but it was not') end @@ -189,8 +187,8 @@ let(:expectation) do expect do expect do - DummiesIndex::Dummy.bulk body: [{index: {_id: 42, data: {}}}, {index: {_id: 43, data: {}}}] - DummiesIndex::Dummy.bulk body: [{index: {_id: 43, data: {}}}, {index: {_id: 44, data: {}}}] + DummiesIndex.bulk body: [{index: {_id: 42, data: {}}}, {index: {_id: 43, data: {}}}] + DummiesIndex.bulk body: [{index: {_id: 43, data: {}}}, {index: {_id: 44, data: {}}}] end.to update_index(DummiesIndex).and_reindex(42, times: 3).and_reindex(44, 43, times: 4) end end @@ -205,20 +203,20 @@ context ':with' do specify do expect do - DummiesIndex::Dummy.bulk body: [{index: {_id: 42, data: {a: '1'}}}, {index: {_id: 42, data: {'a' => 2}}}] + DummiesIndex.bulk body: [{index: {_id: 42, data: {a: '1'}}}, {index: {_id: 42, data: {'a' => 2}}}] end.to update_index(DummiesIndex).and_reindex(42, with: {a: 2}) end specify do expect do - DummiesIndex::Dummy.bulk body: [{index: {_id: 42, data: {a: '1'}}}, {index: {_id: 42, data: {'b' => 2}}}] + DummiesIndex.bulk body: [{index: {_id: 42, data: {a: '1'}}}, {index: {_id: 42, data: {'b' => 2}}}] end.to update_index(DummiesIndex).and_reindex(42, with: {a: '1', b: 2}) end specify do expect do expect do - DummiesIndex::Dummy.bulk body: [{index: {_id: 43, data: {a: '1'}}}] + DummiesIndex.bulk body: [{index: {_id: 43, data: {a: '1'}}}] end.to update_index(DummiesIndex).and_reindex(42, with: {a: 1}) end.to fail_matching('Expected document with id `42` to be reindexed, but it was not') end @@ -232,7 +230,7 @@ ].each do |(data, with)| specify do expect do - DummiesIndex::Dummy.bulk body: [{index: {_id: 42, data: data}}] + DummiesIndex.bulk body: [{index: {_id: 42, data: data}}] end.to update_index(DummiesIndex).and_reindex(42, with: with) end end @@ -247,7 +245,7 @@ specify do expect do expect do - DummiesIndex::Dummy.bulk body: [{index: {_id: 42, data: data}}] + DummiesIndex.bulk body: [{index: {_id: 42, data: data}}] end.to update_index(DummiesIndex).and_reindex(42, with: with) end.to fail_matching('Expected document with id `42` to be reindexed') end @@ -257,7 +255,7 @@ let(:expectation) do expect do expect do - DummiesIndex::Dummy.bulk body: [{index: {_id: 43, data: {a: '1'}}}, {index: {_id: 42, data: {'a' => 2}}}] + DummiesIndex.bulk body: [{index: {_id: 43, data: {a: '1'}}}, {index: {_id: 42, data: {'a' => 2}}}] end.to update_index(DummiesIndex).and_reindex(43, times: 2, with: {a: 2}) end end @@ -271,26 +269,26 @@ context '#and_delete' do specify do - expect { DummiesIndex::Dummy.bulk body: [{index: {_id: 42, data: {}}}, {delete: {_id: 43}}] } + expect { DummiesIndex.bulk body: [{index: {_id: 42, data: {}}}, {delete: {_id: 43}}] } .to update_index(DummiesIndex).and_reindex(42).and_delete(double(id: 43)) end specify do - expect { DummiesIndex::Dummy.bulk body: [{delete: {_id: 42}}, {delete: {_id: 43}}] } + expect { DummiesIndex.bulk body: [{delete: {_id: 42}}, {delete: {_id: 43}}] } .to update_index(DummiesIndex).and_delete(42).and_delete(double(id: 43)) end specify do - expect { DummiesIndex::Dummy.bulk body: [{delete: {_id: 42}}, {delete: {_id: 43}}] } + expect { DummiesIndex.bulk body: [{delete: {_id: 42}}, {delete: {_id: 43}}] } .to update_index(DummiesIndex).and_delete(42, double(id: 43)) end specify do - expect { DummiesIndex::Dummy.bulk body: [{delete: {_id: 42}}, {delete: {_id: 43}}] } + expect { DummiesIndex.bulk body: [{delete: {_id: 42}}, {delete: {_id: 43}}] } .to update_index(DummiesIndex).and_delete([43, double(id: 42)]) end context do let(:expectation) do expect do - expect { DummiesIndex::Dummy.bulk body: [{index: {_id: 42, data: {}}}, {delete: {_id: 43}}] } + expect { DummiesIndex.bulk body: [{index: {_id: 42, data: {}}}, {delete: {_id: 43}}] } .to update_index(DummiesIndex).and_reindex(43).and_delete(double(id: 42)) end end @@ -302,7 +300,7 @@ context do let(:expectation) do expect do - expect { DummiesIndex::Dummy.bulk body: [{delete: {_id: 42, data: {}}}, {delete: {_id: 42}}] } + expect { DummiesIndex.bulk body: [{delete: {_id: 42, data: {}}}, {delete: {_id: 42}}] } .to update_index(DummiesIndex).and_delete(44, times: 2).and_delete(double(id: 42), times: 3) end end diff --git a/spec/chewy/search/loader_spec.rb b/spec/chewy/search/loader_spec.rb index 4cf04b07f..1bfde065a 100644 --- a/spec/chewy/search/loader_spec.rb +++ b/spec/chewy/search/loader_spec.rb @@ -8,17 +8,15 @@ stub_model(:country) stub_index(:cities) do - define_type City do - field :name - field :rating, type: 'integer' - end + index_scope City + field :name + field :rating, type: 'integer' end stub_index(:countries) do - define_type Country do - field :name - field :rating, type: 'integer' - end + index_scope Country + field :name + field :rating, type: 'integer' end end @@ -33,18 +31,18 @@ let(:options) { {} } subject { described_class.new(indexes: [CitiesIndex, CountriesIndex], **options) } - describe '#derive_type' do - specify { expect(subject.derive_type('cities', 'city')).to eq(CitiesIndex::City) } - specify { expect(subject.derive_type('cities_suffix', 'city')).to eq(CitiesIndex::City) } + describe '#derive_index' do + specify { expect(subject.derive_index('cities')).to eq(CitiesIndex) } + specify { expect(subject.derive_index('cities_suffix')).to eq(CitiesIndex) } - specify { expect { subject.derive_type('whatever', 'city') }.to raise_error(Chewy::UnderivableType) } - specify { expect { subject.derive_type('citiessuffix', 'city') }.to raise_error(Chewy::UnderivableType) } + specify { expect { subject.derive_index('whatever') }.to raise_error(Chewy::UndefinedIndex) } + specify { expect { subject.derive_index('citiessuffix') }.to raise_error(Chewy::UndefinedIndex) } context do before { CitiesIndex.index_name :boro_goves } - specify { expect(subject.derive_type('boro_goves', 'city')).to eq(CitiesIndex::City) } - specify { expect(subject.derive_type('boro_goves_suffix', 'city')).to eq(CitiesIndex::City) } + specify { expect(subject.derive_index('boro_goves')).to eq(CitiesIndex) } + specify { expect(subject.derive_index('boro_goves_suffix')).to eq(CitiesIndex) } end end @@ -60,7 +58,7 @@ end context do - let(:options) { {country: {scope: -> { where('rating > 2') }}} } + let(:options) { {countries: {scope: -> { where('rating > 2') }}} } specify { expect(subject.load(hits)).to eq([*cities, nil, countries.last]) } end end @@ -68,21 +66,17 @@ context 'objects' do before do stub_index(:cities) do - define_type :city do - field :name - field :rating, type: 'integer' - end + field :name + field :rating, type: 'integer' end stub_index(:countries) do - define_type :country do - field :name - field :rating, type: 'integer' - end + field :name + field :rating, type: 'integer' end end - specify { expect(subject.load(hits).map(&:class).uniq).to eq([CitiesIndex::City, CountriesIndex::Country]) } + specify { expect(subject.load(hits).map(&:class).uniq).to eq([CitiesIndex, CountriesIndex]) } specify { expect(subject.load(hits).map(&:rating)).to eq([*cities, *countries].map(&:rating)) } end end diff --git a/spec/chewy/search/pagination/kaminari_examples.rb b/spec/chewy/search/pagination/kaminari_examples.rb index f60f132df..ccb033bfd 100644 --- a/spec/chewy/search/pagination/kaminari_examples.rb +++ b/spec/chewy/search/pagination/kaminari_examples.rb @@ -5,10 +5,8 @@ before do stub_index(:products) do - define_type(:product) do - field :name - field :age, type: 'integer' - end + field :name + field :age, type: 'integer' end end @@ -25,7 +23,7 @@ context do let(:data) { Array.new(10) { |i| {id: i.next.to_s, name: "Name#{i.next}", age: 10 * i.next}.stringify_keys! } } - before { ProductsIndex::Product.import!(data.map { |h| double(h) }) } + before { ProductsIndex.import!(data.map { |h| double(h) }) } before { allow(::Kaminari.config).to receive_messages(default_per_page: 3) } describe '#per, #page' do diff --git a/spec/chewy/search/pagination/kaminari_spec.rb b/spec/chewy/search/pagination/kaminari_spec.rb index 30ac7effc..c75eacaa8 100644 --- a/spec/chewy/search/pagination/kaminari_spec.rb +++ b/spec/chewy/search/pagination/kaminari_spec.rb @@ -5,7 +5,7 @@ describe '#objects' do let(:data) { Array.new(12) { |i| {id: i.next.to_s, name: "Name#{i.next}", age: 10 * i.next}.stringify_keys! } } - before { ProductsIndex::Product.import!(data.map { |h| double(h) }) } + before { ProductsIndex.import!(data.map { |h| double(h) }) } before { allow(::Kaminari.config).to receive_messages(default_per_page: 17) } specify { expect(search.objects.class).to eq(Kaminari::PaginatableArray) } diff --git a/spec/chewy/search/parameters/indices_spec.rb b/spec/chewy/search/parameters/indices_spec.rb index 649e7d3a5..a58276b38 100644 --- a/spec/chewy/search/parameters/indices_spec.rb +++ b/spec/chewy/search/parameters/indices_spec.rb @@ -2,14 +2,8 @@ describe Chewy::Search::Parameters::Indices do before do - stub_index(:first) do - define_type :one - end - - stub_index(:second) do - define_type :three - end - + stub_index(:first) + stub_index(:second) stub_index(:third) end diff --git a/spec/chewy/search/request_spec.rb b/spec/chewy/search/request_spec.rb index d46dc9759..e077716e4 100644 --- a/spec/chewy/search/request_spec.rb +++ b/spec/chewy/search/request_spec.rb @@ -5,23 +5,17 @@ before do stub_index(:products) do - define_type :product do - field :id, type: :integer - field :name - field :age, type: :integer - end + field :id, type: :integer + field :name + field :age, type: :integer end stub_index(:cities) do - define_type :city do - field :id, type: :integer - end + field :id, type: :integer end stub_index(:countries) do - define_type :country do - field :id, type: :integer - end + field :id, type: :integer end end @@ -383,15 +377,13 @@ stub_model(:country) stub_index(:places) do - define_type City do - field :rating, type: 'integer' - end + index_scope City + field :rating, type: 'integer' end stub_index(:countries) do - define_type Country do - field :rating, type: 'integer' - end + index_scope Country + field :rating, type: 'integer' end end @@ -409,7 +401,7 @@ describe '#load' do specify { expect(subject.load(only: 'city')).to eq([*cities]) } - specify { expect(subject.load(only: 'city').map(&:class).uniq).to eq([PlacesIndex::City]) } + specify { expect(subject.load(only: 'city').map(&:class).uniq).to eq([PlacesIndex]) } specify { expect(subject.load(only: 'city').objects).to eq([*cities]) } end end @@ -508,9 +500,9 @@ let(:cities) { Array.new(3) { |i| {id: (i.next + 9).to_i}.stringify_keys! } } let(:countries) { Array.new(3) { |i| {id: (i.next + 12).to_i}.stringify_keys! } } before do - ProductsIndex::Product.import!(products.map { |h| double(h) }) - CountriesIndex::Country.import!(countries.map { |h| double(h) }) - CitiesIndex::City.import!(cities.map { |h| double(h) }) + ProductsIndex.import!(products.map { |h| double(h) }) + CountriesIndex.import!(countries.map { |h| double(h) }) + CitiesIndex.import!(cities.map { |h| double(h) }) end specify { expect(subject[0]._data).to be_a Hash } @@ -647,7 +639,7 @@ context do before { expect(Chewy.client).to receive(:search).once.and_call_original } - specify { expect(subject.first).to be_a(ProductsIndex::Product).and have_attributes(id: 9) } + specify { expect(subject.first).to be_a(ProductsIndex).and have_attributes(id: 9) } specify { expect(subject.first(3).map(&:id)).to eq([9, 8, 7]) } specify { expect(subject.first(10).map(&:id)).to have(9).items } specify { expect(subject.limit(5).first(10).map(&:id)).to have(9).items } @@ -660,7 +652,7 @@ expect(Chewy.client).not_to receive(:search) end - specify { expect(subject.first).to be_a(ProductsIndex::Product).and have_attributes(id: 9) } + specify { expect(subject.first).to be_a(ProductsIndex).and have_attributes(id: 9) } specify { expect(subject.first(3).map(&:id)).to eq([9, 8, 7]) } specify { expect(subject.first(10).map(&:id)).to have(9).items } @@ -681,8 +673,8 @@ end describe '#find' do - specify { expect(subject.find('1')).to be_a(ProductsIndex::Product).and have_attributes(id: 1) } - specify { expect(subject.find { |w| w.id == 2 }).to be_a(ProductsIndex::Product).and have_attributes(id: 2) } + specify { expect(subject.find('1')).to be_a(ProductsIndex).and have_attributes(id: 1) } + specify { expect(subject.find { |w| w.id == 2 }).to be_a(ProductsIndex).and have_attributes(id: 2) } specify { expect(subject.limit(2).find('1', '3', '7').map(&:id)).to contain_exactly(1, 3, 7) } specify { expect(subject.find(1, 3, 7).map(&:id)).to contain_exactly(1, 3, 7) } specify do @@ -722,7 +714,7 @@ before { expect(Chewy.client).to receive(:scroll).once.and_call_original } specify { expect(subject.find((1..9).to_a)).to have(9).items } - specify { expect(subject.find((1..9).to_a)).to all be_a(Chewy::Type) } + specify { expect(subject.find((1..9).to_a)).to all be_a(Chewy::Index) } end end diff --git a/spec/chewy/search/response_spec.rb b/spec/chewy/search/response_spec.rb index ae5721c2f..7b291288d 100644 --- a/spec/chewy/search/response_spec.rb +++ b/spec/chewy/search/response_spec.rb @@ -8,16 +8,14 @@ stub_model(:country) stub_index(:cities) do - define_type City do - field :name - field :rating, type: 'integer' - end + index_scope City + field :name + field :rating, type: 'integer' end stub_index(:countries) do - define_type Country do - field :name - field :rating, type: 'integer' - end + index_scope Country + field :name + field :rating, type: 'integer' end end @@ -139,7 +137,7 @@ specify { expect(subject.wrappers).to have(4).items } specify do expect(subject.wrappers.map(&:class).uniq) - .to contain_exactly(CitiesIndex::City, CountriesIndex::Country) + .to contain_exactly(CitiesIndex, CountriesIndex) end specify { expect(subject.wrappers.map(&:_data)).to eq(subject.hits) } @@ -168,7 +166,7 @@ '_source' => {'id' => 2, 'rating' => 0}} ]}} end - specify { expect(subject.wrappers.first).to be_a(CitiesIndex::City) } + specify { expect(subject.wrappers.first).to be_a(CitiesIndex) } specify { expect(subject.wrappers.first.id).to eq(2) } specify { expect(subject.wrappers.first.rating).to eq(0) } specify { expect(subject.wrappers.first._score).to eq(1.3) } @@ -185,7 +183,7 @@ '_explanation' => {foo: 'bar'}} ]}} end - specify { expect(subject.wrappers.first).to be_a(CountriesIndex::Country) } + specify { expect(subject.wrappers.first).to be_a(CountriesIndex) } specify { expect(subject.wrappers.first.id).to eq('2') } specify { expect(subject.wrappers.first.rating).to be_nil } specify { expect(subject.wrappers.first._score).to eq(1.2) } diff --git a/spec/chewy/search/scrolling_spec.rb b/spec/chewy/search/scrolling_spec.rb index aa0b64174..db714a43d 100644 --- a/spec/chewy/search/scrolling_spec.rb +++ b/spec/chewy/search/scrolling_spec.rb @@ -8,16 +8,14 @@ stub_model(:country) stub_index(:cities) do - define_type City do - field :name - field :rating, type: 'integer' - end + index_scope City + field :name + field :rating, type: 'integer' end stub_index(:countries) do - define_type Country do - field :name - field :rating, type: 'integer' - end + index_scope Country + field :name + field :rating, type: 'integer' end end @@ -153,7 +151,7 @@ end specify do expect(request.scroll_wrappers(batch_size: 2).map(&:class).uniq) - .to eq([CitiesIndex::City, CountriesIndex::Country]) + .to eq([CitiesIndex, CountriesIndex]) end end diff --git a/spec/chewy/search_spec.rb b/spec/chewy/search_spec.rb index de87a7b67..3abf43326 100644 --- a/spec/chewy/search_spec.rb +++ b/spec/chewy/search_spec.rb @@ -4,47 +4,34 @@ before { Chewy.massacre } before do - stub_index(:products) do - define_type :product - end + stub_index(:products) end - let(:product) { ProductsIndex::Product } - describe '.all' do specify { expect(ProductsIndex.all).to be_a(Chewy::Search::Request) } - specify { expect(product.all).to be_a(Chewy::Search::Request) } context do before { allow(Chewy).to receive_messages(search_class: Chewy::Search::Request) } specify { expect(ProductsIndex.all).to be_a(Chewy::Search::Request) } - specify { expect(product.all).to be_a(Chewy::Search::Request) } end end describe '.search_string' do specify do - expect(ProductsIndex.client).to receive(:search).with(hash_including(q: 'hello')).twice + expect(ProductsIndex.client).to receive(:search).with(hash_including(q: 'hello')).once ProductsIndex.search_string('hello') - product.search_string('hello') end specify do - expect(ProductsIndex.client).to receive(:search).with(hash_including(explain: true)).twice + expect(ProductsIndex.client).to receive(:search).with(hash_including(explain: true)).once ProductsIndex.search_string('hello', explain: true) - product.search_string('hello', explain: true) end specify do - expect(ProductsIndex.client).to receive(:search).with(hash_including(index: ['products'])) + expect(ProductsIndex.client).to receive(:search).with(hash_including(index: ['products'])).once ProductsIndex.search_string('hello') end - - specify do - expect(ProductsIndex.client).to receive(:search).with(hash_including(index: ['products'])) - product.search_string('hello') - end end context 'named scopes' do @@ -61,10 +48,9 @@ def self.by_name(index) filter { match name: "Name#{index}" } end - define_type City do - field :name, type: 'keyword' - field :rating, type: :integer - end + index_scope City + field :name, type: 'keyword' + field :rating, type: :integer end stub_index(:countries) do @@ -76,10 +62,9 @@ def self.by_name(index) filter { match name: "Name#{index}" } end - define_type Country do - field :name, type: 'keyword' - field :rating, type: :integer - end + index_scope Country + field :name, type: 'keyword' + field :rating, type: :integer end end @@ -94,40 +79,40 @@ def self.by_name(index) specify { expect(CitiesIndex.indices(CountriesIndex).by_rating(1).map(&:rating)).to eq([1, 1]) } specify do expect(CitiesIndex.indices(CountriesIndex).by_rating(1).map(&:class)) - .to match_array([CitiesIndex::City, CountriesIndex::Country]) + .to match_array([CitiesIndex, CountriesIndex]) end specify { expect(CitiesIndex.indices(CountriesIndex).by_rating(1).by_name(2).map(&:rating)).to eq([1]) } specify do expect(CitiesIndex.indices(CountriesIndex).by_rating(1).by_name(2).map(&:class)) - .to eq([CitiesIndex::City]) + .to eq([CitiesIndex]) end specify { expect(CitiesIndex.indices(CountriesIndex).by_name(3).map(&:rating)).to eq([2, 1]) } specify do expect(CitiesIndex.indices(CountriesIndex).by_name(3).map(&:class)) - .to eq([CitiesIndex::City, CountriesIndex::Country]) + .to eq([CitiesIndex, CountriesIndex]) end specify { expect(CitiesIndex.indices(CountriesIndex).order(:name).by_rating(1).map(&:rating)).to eq([1, 1]) } specify do expect(CitiesIndex.indices(CountriesIndex).order(:name).by_rating(1).map(&:class)) - .to match_array([CitiesIndex::City, CountriesIndex::Country]) + .to match_array([CitiesIndex, CountriesIndex]) end specify { expect(CitiesIndex.by_rating(2).map(&:rating)).to eq([2]) } - specify { expect(CitiesIndex.by_rating(2).map(&:class)).to eq([CitiesIndex::City]) } + specify { expect(CitiesIndex.by_rating(2).map(&:class)).to eq([CitiesIndex]) } specify { expect(CitiesIndex.by_rating(2).by_name(3).map(&:rating)).to eq([2]) } - specify { expect(CitiesIndex.by_rating(2).by_name(3).map(&:class)).to eq([CitiesIndex::City]) } + specify { expect(CitiesIndex.by_rating(2).by_name(3).map(&:class)).to eq([CitiesIndex]) } specify { expect(CitiesIndex.by_name(3).map(&:rating)).to eq([2]) } specify { expect(CitiesIndex.by_name(3).map(&:rating)).to eq([2]) } specify { expect(CitiesIndex.order(:name).by_name(3).map(&:rating)).to eq([2]) } specify { expect(CitiesIndex.order(:name).by_name(3).map(&:rating)).to eq([2]) } specify { expect(CitiesIndex.order(:name).by_rating(2).map(&:rating)).to eq([2]) } - specify { expect(CitiesIndex.order(:name).by_rating(2).map(&:class)).to eq([CitiesIndex::City]) } + specify { expect(CitiesIndex.order(:name).by_rating(2).map(&:class)).to eq([CitiesIndex]) } specify { expect(CountriesIndex.by_rating(3).map(&:rating)).to eq([3]) } - specify { expect(CountriesIndex.by_rating(3).map(&:class)).to eq([CountriesIndex::Country]) } + specify { expect(CountriesIndex.by_rating(3).map(&:class)).to eq([CountriesIndex]) } specify { expect(CountriesIndex.by_rating(3).by_name(5).map(&:rating)).to eq([3]) } - specify { expect(CountriesIndex.by_rating(3).by_name(5).map(&:class)).to eq([CountriesIndex::Country]) } + specify { expect(CountriesIndex.by_rating(3).by_name(5).map(&:class)).to eq([CountriesIndex]) } specify { expect(CountriesIndex.order(:name).by_rating(3).map(&:rating)).to eq([3]) } - specify { expect(CountriesIndex.order(:name).by_rating(3).map(&:class)).to eq([CountriesIndex::Country]) } + specify { expect(CountriesIndex.order(:name).by_rating(3).map(&:class)).to eq([CountriesIndex]) } end end diff --git a/spec/chewy/stash_spec.rb b/spec/chewy/stash_spec.rb index e1198a5c4..2d2774ff5 100644 --- a/spec/chewy/stash_spec.rb +++ b/spec/chewy/stash_spec.rb @@ -10,12 +10,10 @@ def fetch_deleted_number(response) before do stub_model(:city) stub_index(:cities) do - define_type City + index_scope City end - stub_index(:countries) do - define_type :country - end - stub_index(:users) { define_type :user } + stub_index(:countries) + stub_index(:users) stub_index(:borogoves) end @@ -28,7 +26,7 @@ def fetch_deleted_number(response) CountriesIndex.import!([id: 2, name: 'Country'], journal: true) end Timecop.travel(Time.now + 2.minutes) do - UsersIndex::User.import!([id: 3, name: 'User'], journal: true) + UsersIndex.import!([id: 3, name: 'User'], journal: true) end end @@ -80,24 +78,8 @@ def fetch_deleted_number(response) describe '.for' do specify { expect(described_class.for(UsersIndex).map(&:index_name)).to eq(['users']) } - specify do - expect(described_class.for(CitiesIndex, CountriesIndex).map(&:type_name)).to contain_exactly('city', 'country') - end specify do expect(described_class.for(CitiesIndex, UsersIndex).map(&:index_name)).to contain_exactly('cities', 'users') end end - - describe '#type' do - let(:index_name) { 'users' } - let(:type_name) { 'city' } - subject { described_class::Journal.new('index_name' => index_name, 'type_name' => type_name).type } - - specify { expect { subject }.to raise_error(Chewy::UnderivableType) } - - context do - let(:index_name) { 'cities' } - it { is_expected.to eq(CitiesIndex::City) } - end - end end diff --git a/spec/chewy/strategy/active_job_spec.rb b/spec/chewy/strategy/active_job_spec.rb index 1782cdeb9..71d9563d1 100644 --- a/spec/chewy/strategy/active_job_spec.rb +++ b/spec/chewy/strategy/active_job_spec.rb @@ -19,11 +19,11 @@ before do stub_model(:city) do - update_index('cities#city') { self } + update_index('cities') { self } end stub_index(:cities) do - define_type City + index_scope City end end @@ -32,7 +32,7 @@ specify do expect { [city, other_city].map(&:save!) } - .not_to update_index(CitiesIndex::City, strategy: :active_job) + .not_to update_index(CitiesIndex, strategy: :active_job) end specify do @@ -55,19 +55,19 @@ specify do ::ActiveJob::Base.queue_adapter = :inline expect { [city, other_city].map(&:save!) } - .to update_index(CitiesIndex::City, strategy: :active_job) + .to update_index(CitiesIndex, strategy: :active_job) .and_reindex(city, other_city).only end specify do - expect(CitiesIndex::City).to receive(:import!).with([city.id, other_city.id], suffix: '201601') - Chewy::Strategy::ActiveJob::Worker.new.perform('CitiesIndex::City', [city.id, other_city.id], suffix: '201601') + expect(CitiesIndex).to receive(:import!).with([city.id, other_city.id], suffix: '201601') + Chewy::Strategy::ActiveJob::Worker.new.perform('CitiesIndex', [city.id, other_city.id], suffix: '201601') end specify do allow(Chewy).to receive(:disable_refresh_async).and_return(true) - expect(CitiesIndex::City).to receive(:import!).with([city.id, other_city.id], suffix: '201601', refresh: false) - Chewy::Strategy::ActiveJob::Worker.new.perform('CitiesIndex::City', [city.id, other_city.id], suffix: '201601') + expect(CitiesIndex).to receive(:import!).with([city.id, other_city.id], suffix: '201601', refresh: false) + Chewy::Strategy::ActiveJob::Worker.new.perform('CitiesIndex', [city.id, other_city.id], suffix: '201601') end end end diff --git a/spec/chewy/strategy/atomic_spec.rb b/spec/chewy/strategy/atomic_spec.rb index d479be6bc..adca4dd4d 100644 --- a/spec/chewy/strategy/atomic_spec.rb +++ b/spec/chewy/strategy/atomic_spec.rb @@ -5,11 +5,11 @@ before do stub_model(:country) do - update_index('countries#country') { self } + update_index('countries') { self } end stub_index(:countries) do - define_type Country + index_scope Country end end @@ -18,35 +18,34 @@ specify do expect { [country, other_country].map(&:save!) } - .to update_index(CountriesIndex::Country, strategy: :atomic) + .to update_index(CountriesIndex, strategy: :atomic) .and_reindex(country, other_country).only end specify do expect { [country, other_country].map(&:destroy) } - .to update_index(CountriesIndex::Country, strategy: :atomic) + .to update_index(CountriesIndex, strategy: :atomic) .and_delete(country, other_country).only end context do before do stub_index(:countries) do - define_type Country do - root id: -> { country_code } do - end + index_scope Country + root id: -> { country_code } do end end end specify do expect { [country, other_country].map(&:save!) } - .to update_index(CountriesIndex::Country, strategy: :atomic) + .to update_index(CountriesIndex, strategy: :atomic) .and_reindex('HL', 'WD').only end specify do expect { [country, other_country].map(&:destroy) } - .to update_index(CountriesIndex::Country, strategy: :atomic) + .to update_index(CountriesIndex, strategy: :atomic) .and_delete('HL', 'WD').only end @@ -55,7 +54,7 @@ country.save! other_country.destroy end - .to update_index(CountriesIndex::Country, strategy: :atomic) + .to update_index(CountriesIndex, strategy: :atomic) .and_reindex('HL').and_delete('WD').only end end diff --git a/spec/chewy/strategy/sidekiq_spec.rb b/spec/chewy/strategy/sidekiq_spec.rb index 8fe94e143..943ac6f7e 100644 --- a/spec/chewy/strategy/sidekiq_spec.rb +++ b/spec/chewy/strategy/sidekiq_spec.rb @@ -13,11 +13,11 @@ before { ::Sidekiq::Worker.clear_all } before do stub_model(:city) do - update_index('cities#city') { self } + update_index('cities') { self } end stub_index(:cities) do - define_type City + index_scope City end end @@ -26,27 +26,27 @@ specify do expect { [city, other_city].map(&:save!) } - .not_to update_index(CitiesIndex::City, strategy: :sidekiq) + .not_to update_index(CitiesIndex, strategy: :sidekiq) end specify do expect(::Sidekiq::Client).to receive(:push).with(hash_including('queue' => 'low')).and_call_original ::Sidekiq::Testing.inline! do expect { [city, other_city].map(&:save!) } - .to update_index(CitiesIndex::City, strategy: :sidekiq) + .to update_index(CitiesIndex, strategy: :sidekiq) .and_reindex(city, other_city).only end end specify do - expect(CitiesIndex::City).to receive(:import!).with([city.id, other_city.id], suffix: '201601') - Chewy::Strategy::Sidekiq::Worker.new.perform('CitiesIndex::City', [city.id, other_city.id], suffix: '201601') + expect(CitiesIndex).to receive(:import!).with([city.id, other_city.id], suffix: '201601') + Chewy::Strategy::Sidekiq::Worker.new.perform('CitiesIndex', [city.id, other_city.id], suffix: '201601') end specify do allow(Chewy).to receive(:disable_refresh_async).and_return(true) - expect(CitiesIndex::City).to receive(:import!).with([city.id, other_city.id], suffix: '201601', refresh: false) - Chewy::Strategy::Sidekiq::Worker.new.perform('CitiesIndex::City', [city.id, other_city.id], suffix: '201601') + expect(CitiesIndex).to receive(:import!).with([city.id, other_city.id], suffix: '201601', refresh: false) + Chewy::Strategy::Sidekiq::Worker.new.perform('CitiesIndex', [city.id, other_city.id], suffix: '201601') end end end diff --git a/spec/chewy/strategy_spec.rb b/spec/chewy/strategy_spec.rb index 64c5c3399..25a2344bd 100644 --- a/spec/chewy/strategy_spec.rb +++ b/spec/chewy/strategy_spec.rb @@ -55,11 +55,11 @@ context 'nesting', :orm do before do stub_model(:city) do - update_index('cities#city') { self } + update_index('cities') { self } end stub_index(:cities) do - define_type City + index_scope City end end @@ -70,12 +70,12 @@ around { |example| Chewy.strategy(:bypass) { example.run } } specify do - expect(CitiesIndex::City).not_to receive(:import!) + expect(CitiesIndex).not_to receive(:import!) [city, other_city].map(&:save!) end specify do - expect(CitiesIndex::City).to receive(:import!).with([city.id, other_city.id]).once + expect(CitiesIndex).to receive(:import!).with([city.id, other_city.id]).once Chewy.strategy(:atomic) { [city, other_city].map(&:save!) } end end @@ -84,41 +84,39 @@ around { |example| Chewy.strategy(:urgent) { example.run } } specify do - expect(CitiesIndex::City).to receive(:import!).at_least(2).times + expect(CitiesIndex).to receive(:import!).at_least(2).times [city, other_city].map(&:save!) end specify do - expect(CitiesIndex::City).to receive(:import!).with([city.id, other_city.id]).once + expect(CitiesIndex).to receive(:import!).with([city.id, other_city.id]).once Chewy.strategy(:atomic) { [city, other_city].map(&:save!) } end context 'hash passed to urgent' do before do - stub_index(:cities) do - define_type :city - end + stub_index(:cities) stub_model(:city) do - update_index('cities#city') { {name: name} } + update_index('cities') { {name: name} } end end specify do [city, other_city].map(&:save!) - expect(CitiesIndex::City.total_count).to eq(4) + expect(CitiesIndex.total_count).to eq(4) end context do before do stub_model(:city) do - update_index('cities#city') { {id: id.to_s, name: name} } + update_index('cities') { {id: id.to_s, name: name} } end end specify do [city, other_city].map(&:save!) - expect(CitiesIndex::City.total_count).to eq(2) + expect(CitiesIndex.total_count).to eq(2) end end end diff --git a/spec/chewy/type/actions_spec.rb b/spec/chewy/type/actions_spec.rb deleted file mode 100644 index 853027cb7..000000000 --- a/spec/chewy/type/actions_spec.rb +++ /dev/null @@ -1,50 +0,0 @@ -require 'spec_helper' - -describe Chewy::Type::Actions, :orm do - before { Chewy.massacre } - - before do - stub_model(:city) - stub_index(:cities) do - define_type City do - field :name - field :updated_at, type: 'date' - end - end - end - - let!(:cities) { Array.new(3) { |i| City.create!(name: "Name#{i + 1}") } } - before { CitiesIndex::City.import } - - describe '.reset' do - specify do - expect { CitiesIndex::City.reset }.to update_index(CitiesIndex::City) - end - end - - describe '.sync' do - before do - cities.first.destroy - sleep(1) if ActiveSupport::VERSION::STRING < '4.1.0' - cities.last.update(name: 'Name5') - end - let!(:additional_city) { City.create!(name: 'Name4') } - - specify do - expect(CitiesIndex::City.sync).to match( - count: 3, - missing: contain_exactly(cities.first.id.to_s, additional_city.id.to_s), - outdated: [cities.last.id.to_s] - ) - end - specify do - expect { CitiesIndex::City.sync }.to update_index(CitiesIndex::City) - .and_reindex(additional_city, cities.last) - .and_delete(cities.first).only - end - end - - describe '.journal' do - specify { expect(CitiesIndex::City.journal).to be_a(Chewy::Journal) } - end -end diff --git a/spec/chewy/type/wrapper_spec.rb b/spec/chewy/type/wrapper_spec.rb deleted file mode 100644 index 434aaa1f1..000000000 --- a/spec/chewy/type/wrapper_spec.rb +++ /dev/null @@ -1,100 +0,0 @@ -require 'spec_helper' - -describe Chewy::Type::Wrapper do - before do - stub_class(:city) - stub_index(:cities) do - define_type City - end - end - - let(:city_type) { CitiesIndex::City } - - describe '.build' do - specify do - expect(city_type.build({}).attributes) - .to eq('id' => nil, '_score' => nil, '_explanation' => nil) - end - specify do - expect(city_type.build('_source' => {name: 'Martin'}).attributes) - .to eq('id' => nil, '_score' => nil, '_explanation' => nil, 'name' => 'Martin') - end - specify do - expect(city_type.build('_id' => 42).attributes) - .to eq('id' => 42, '_score' => nil, '_explanation' => nil) - end - specify do - expect(city_type.build('_id' => 42, '_source' => {'id' => 43}).attributes) - .to eq('id' => 43, '_score' => nil, '_explanation' => nil) - end - specify do - expect(city_type.build('_score' => 42, '_explanation' => {foo: 'bar'}).attributes) - .to eq('id' => nil, '_score' => 42, '_explanation' => {foo: 'bar'}) - end - specify do - expect(city_type.build('_score' => 42, 'borogoves' => {foo: 'bar'})._data) - .to eq('_score' => 42, 'borogoves' => {foo: 'bar'}) - end - end - - describe '#initialize' do - subject(:city) { city_type.new(name: 'Martin', age: 42) } - - it do - is_expected.to respond_to(:name) - .and respond_to(:age) - .and have_attributes( - name: 'Martin', - age: 42 - ) - end - - it { expect { city.population }.to raise_error(NoMethodError) } - - context 'highlight' do - subject(:city) do - city_type.new(name: 'Martin', age: 42) - .tap do |city| - city._data = { - 'highlight' => {'name' => ['Martin']} - } - end - end - - it do - is_expected.to respond_to(:name_highlight) - .and respond_to(:name_highlights) - .and have_attributes( - name: 'Martin', - name_highlight: 'Martin', - name_highlights: ['Martin'] - ) - end - end - end - - describe '#==' do - specify { expect(city_type.new(id: 42)).to eq(city_type.new(id: 42)) } - specify { expect(city_type.new(id: 42, age: 55)).to eq(city_type.new(id: 42, age: 54)) } - specify { expect(city_type.new(id: 42)).not_to eq(city_type.new(id: 43)) } - specify { expect(city_type.new(id: 42, age: 55)).not_to eq(city_type.new(id: 43, age: 55)) } - specify { expect(city_type.new(age: 55)).to eq(city_type.new(age: 55)) } - specify { expect(city_type.new(age: 55)).not_to eq(city_type.new(age: 54)) } - - specify { expect(city_type.new(id: '42')).to eq(City.new.tap { |m| allow(m).to receive_messages(id: 42) }) } - specify { expect(city_type.new(id: 42)).not_to eq(City.new.tap { |m| allow(m).to receive_messages(id: 43) }) } - specify { expect(city_type.new(id: 42)).not_to eq(Class.new) } - - context 'models', :orm do - before do - stub_model(:city) - stub_index(:cities) do - define_type City - end - end - specify { expect(city_type.new(id: '42')).to eq(City.new.tap { |m| allow(m).to receive_messages(id: 42) }) } - specify { expect(city_type.new(id: 42)).not_to eq(City.new.tap { |m| allow(m).to receive_messages(id: 43) }) } - specify { expect(city_type.new(id: 42)).not_to eq(Class.new) } - end - end -end diff --git a/spec/chewy/type_spec.rb b/spec/chewy/type_spec.rb deleted file mode 100644 index b51831d68..000000000 --- a/spec/chewy/type_spec.rb +++ /dev/null @@ -1,58 +0,0 @@ -require 'spec_helper' - -describe Chewy::Type do - describe '.derivable_name' do - specify { expect { Class.new(Chewy::Type).derivable_name }.to raise_error NotImplementedError } - - specify do - index = Class.new(Chewy::Index) { define_type :city } - expect(index::City.derivable_name).to be_nil - end - - specify do - stub_index(:places) { define_type :city } - expect(PlacesIndex::City.derivable_name).to eq('places#city') - end - - specify do - stub_index('namespace/places') { define_type :city } - expect(Namespace::PlacesIndex::City.derivable_name).to eq('namespace/places#city') - end - end - - describe '.types' do - subject { Class.new(Chewy::Type) } - specify { expect(subject.types).to eq([subject]) } - end - - describe '.scopes' do - before do - stub_index(:places) do - def self.by_id; end - - define_type :city do - def self.by_rating; end - - def self.by_name; end - end - end - end - - specify { expect(described_class.scopes).to eq([]) } - specify { expect(PlacesIndex::City.scopes).to match_array(%i[by_rating by_name]) } - specify { expect { PlacesIndex::City.non_existing_method_call }.to raise_error(NoMethodError) } - - specify { expect(PlacesIndex::City._default_import_options).to eq({}) } - specify do - expect { PlacesIndex::City.default_import_options(invalid_option: 'Yeah!') } - .to raise_error(ArgumentError) - end - - context 'default_import_options is set' do - let(:converter) { -> {} } - before { PlacesIndex::City.default_import_options(batch_size: 500, raw_import: converter) } - - specify { expect(PlacesIndex::City._default_import_options).to eq(batch_size: 500, raw_import: converter) } - end - end -end diff --git a/spec/chewy_spec.rb b/spec/chewy_spec.rb index 1f9637535..cf1c6d037 100644 --- a/spec/chewy_spec.rb +++ b/spec/chewy_spec.rb @@ -5,144 +5,29 @@ expect(Chewy::VERSION).not_to be nil end - describe '.derive_type' do + describe '.derive_name' do before do stub_const('SomeIndex', Class.new) - stub_index(:developers) do - define_type :developer - end - - stub_index('namespace/autocomplete') do - define_type :developer - end - end - - specify do - expect { described_class.derive_type('some#developers') } - .to raise_error(Chewy::UnderivableType, /SomeIndex/) - end - specify do - expect { described_class.derive_type('borogoves#developers') } - .to raise_error(Chewy::UnderivableType, /Borogoves/) - end - specify do - expect { described_class.derive_type('developers#borogoves') } - .to raise_error(Chewy::UnderivableType, /DevelopersIndex.*borogoves/) - end - - specify { expect(described_class.derive_type(DevelopersIndex::Developer)).to eq(DevelopersIndex::Developer) } - specify { expect(described_class.derive_type('developers_index')).to eq(DevelopersIndex::Developer) } - specify { expect(described_class.derive_type('developers')).to eq(DevelopersIndex::Developer) } - specify { expect(described_class.derive_type('developers#developer')).to eq(DevelopersIndex::Developer) } - specify do - expect(described_class.derive_type('namespace/autocomplete#developer')) - .to eq(Namespace::AutocompleteIndex::Developer) - end - end - - describe '.derive_types' do - before do - stub_const('SomeIndex', Class.new) + stub_index(:developers) - stub_index(:developers) do - define_type :developer - end - - stub_index('namespace/autocomplete') do - define_type :developer - end + stub_index('namespace/autocomplete') end specify do - expect { described_class.derive_types('some#developers') } - .to raise_error(Chewy::UnderivableType, /SomeIndex/) + expect { described_class.derive_name('some') } + .to raise_error(Chewy::UndefinedIndex, /SomeIndex/) end specify do - expect { described_class.derive_types('borogoves#developers') } - .to raise_error(Chewy::UnderivableType, /Borogoves/) - end - specify do - expect { described_class.derive_types('developers#borogoves') } - .to raise_error(Chewy::UnderivableType, /DevelopersIndex.*borogoves/) + expect { described_class.derive_name('borogoves') } + .to raise_error(Chewy::UndefinedIndex, /Borogoves/) end + specify { expect(described_class.derive_name(DevelopersIndex)).to eq(DevelopersIndex) } + specify { expect(described_class.derive_name('developers_index')).to eq(DevelopersIndex) } + specify { expect(described_class.derive_name('developers')).to eq(DevelopersIndex) } specify do - expect(described_class.derive_types(Namespace::AutocompleteIndex)) - .to match_array(Namespace::AutocompleteIndex.types) - end - specify { expect(described_class.derive_types(DevelopersIndex::Developer)).to eq([DevelopersIndex::Developer]) } - - specify { expect(described_class.derive_types('developers_index')).to eq([DevelopersIndex::Developer]) } - specify { expect(described_class.derive_types('developers')).to eq([DevelopersIndex::Developer]) } - specify { expect(described_class.derive_types('developers#developer')).to eq([DevelopersIndex::Developer]) } - specify do - expect(described_class.derive_types('namespace/autocomplete')).to match_array(Namespace::AutocompleteIndex.types) - end - specify do - expect(described_class.derive_types('namespace/autocomplete#developer')) - .to eq([Namespace::AutocompleteIndex::Developer]) - end - end - - describe '.create_type' do - before { stub_index(:cities) } - - context 'Symbol' do - subject { described_class.create_type(CitiesIndex, :city) } - - it { is_expected.to be_a Class } - it { is_expected.to be < Chewy::Type } - its(:name) { should == 'CitiesIndex::City' } - its(:index) { should == CitiesIndex } - its(:type_name) { should == 'city' } - end - - context 'simple model' do - before { stub_class(:city) } - subject { described_class.create_type(CitiesIndex, City) } - - it { is_expected.to be_a Class } - it { is_expected.to be < Chewy::Type } - its(:name) { should == 'CitiesIndex::City' } - its(:index) { should == CitiesIndex } - its(:type_name) { should == 'city' } - end - - context 'model scope', :orm do - before { stub_model(:city) } - subject { described_class.create_type(CitiesIndex, City.where(rating: 1)) } - - it { is_expected.to be_a Class } - it { is_expected.to be < Chewy::Type } - its(:name) { should == 'CitiesIndex::City' } - its(:index) { should == CitiesIndex } - its(:type_name) { should == 'city' } - end - - context 'Namespaced index' do - before { stub_class(:city) } - before { stub_index('namespace/cities') } - - subject { described_class.create_type(Namespace::CitiesIndex, City) } - - it { is_expected.to be_a Class } - it { is_expected.to be < Chewy::Type } - its(:name) { should == 'Namespace::CitiesIndex::City' } - its(:index) { should == Namespace::CitiesIndex } - its(:type_name) { should == 'city' } - end - - context 'Namespaced model' do - before { stub_class('namespace/city') } - - subject { described_class.create_type(CitiesIndex, Namespace::City) } - - it { is_expected.to be_a Class } - it { is_expected.to be < Chewy::Type } - its(:name) { should == 'CitiesIndex::City' } - its(:index) { should == CitiesIndex } - its(:type_name) { should == 'city' } + expect(described_class.derive_name('namespace/autocomplete')).to eq(Namespace::AutocompleteIndex) end end