chewy 0.9.0 → 0.10.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (265) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +1 -0
  3. data/.rubocop.yml +24 -2
  4. data/.rubocop_todo.yml +2 -2
  5. data/.travis.yml +38 -21
  6. data/.yardopts +5 -0
  7. data/Appraisals +55 -27
  8. data/CHANGELOG.md +57 -12
  9. data/Gemfile +14 -10
  10. data/LEGACY_DSL.md +497 -0
  11. data/README.md +249 -515
  12. data/chewy.gemspec +5 -4
  13. data/gemfiles/rails.4.0.activerecord.gemfile +14 -0
  14. data/gemfiles/rails.4.1.activerecord.gemfile +14 -0
  15. data/gemfiles/rails.4.2.activerecord.gemfile +8 -10
  16. data/gemfiles/rails.4.2.mongoid.5.1.gemfile +9 -10
  17. data/gemfiles/rails.5.0.activerecord.gemfile +8 -10
  18. data/gemfiles/rails.5.0.mongoid.6.0.gemfile +15 -0
  19. data/gemfiles/rails.5.1.activerecord.gemfile +15 -0
  20. data/gemfiles/rails.5.1.mongoid.6.1.gemfile +15 -0
  21. data/gemfiles/sequel.4.45.gemfile +11 -0
  22. data/lib/chewy.rb +77 -43
  23. data/lib/chewy/config.rb +44 -7
  24. data/lib/chewy/errors.rb +2 -2
  25. data/lib/chewy/fields/base.rb +39 -32
  26. data/lib/chewy/fields/root.rb +33 -7
  27. data/lib/chewy/index.rb +237 -149
  28. data/lib/chewy/index/actions.rb +85 -28
  29. data/lib/chewy/index/aliases.rb +2 -1
  30. data/lib/chewy/index/settings.rb +9 -5
  31. data/lib/chewy/index/specification.rb +58 -0
  32. data/lib/chewy/journal.rb +40 -92
  33. data/lib/chewy/query.rb +43 -27
  34. data/lib/chewy/query/compose.rb +13 -13
  35. data/lib/chewy/query/criteria.rb +13 -13
  36. data/lib/chewy/query/filters.rb +1 -1
  37. data/lib/chewy/query/loading.rb +1 -1
  38. data/lib/chewy/query/nodes/and.rb +2 -2
  39. data/lib/chewy/query/nodes/bool.rb +1 -1
  40. data/lib/chewy/query/nodes/equal.rb +2 -2
  41. data/lib/chewy/query/nodes/exists.rb +1 -1
  42. data/lib/chewy/query/nodes/has_relation.rb +2 -2
  43. data/lib/chewy/query/nodes/match_all.rb +1 -1
  44. data/lib/chewy/query/nodes/missing.rb +1 -1
  45. data/lib/chewy/query/nodes/not.rb +2 -2
  46. data/lib/chewy/query/nodes/or.rb +2 -2
  47. data/lib/chewy/query/nodes/prefix.rb +1 -1
  48. data/lib/chewy/query/nodes/query.rb +2 -2
  49. data/lib/chewy/query/nodes/range.rb +4 -4
  50. data/lib/chewy/query/nodes/regexp.rb +4 -4
  51. data/lib/chewy/query/nodes/script.rb +3 -3
  52. data/lib/chewy/query/pagination.rb +10 -1
  53. data/lib/chewy/railtie.rb +1 -0
  54. data/lib/chewy/rake_helper.rb +265 -48
  55. data/lib/chewy/rspec/update_index.rb +30 -22
  56. data/lib/chewy/search.rb +78 -21
  57. data/lib/chewy/search/loader.rb +83 -0
  58. data/lib/chewy/{query → search}/pagination/kaminari.rb +13 -5
  59. data/lib/chewy/search/pagination/will_paginate.rb +41 -0
  60. data/lib/chewy/search/parameters.rb +150 -0
  61. data/lib/chewy/search/parameters/aggs.rb +16 -0
  62. data/lib/chewy/search/parameters/concerns/bool_storage.rb +24 -0
  63. data/lib/chewy/search/parameters/concerns/hash_storage.rb +23 -0
  64. data/lib/chewy/search/parameters/concerns/integer_storage.rb +14 -0
  65. data/lib/chewy/search/parameters/concerns/query_storage.rb +237 -0
  66. data/lib/chewy/search/parameters/concerns/string_array_storage.rb +23 -0
  67. data/lib/chewy/search/parameters/concerns/string_storage.rb +14 -0
  68. data/lib/chewy/search/parameters/docvalue_fields.rb +12 -0
  69. data/lib/chewy/search/parameters/explain.rb +16 -0
  70. data/lib/chewy/search/parameters/filter.rb +47 -0
  71. data/lib/chewy/search/parameters/highlight.rb +16 -0
  72. data/lib/chewy/search/parameters/indices_boost.rb +52 -0
  73. data/lib/chewy/search/parameters/limit.rb +17 -0
  74. data/lib/chewy/search/parameters/load.rb +32 -0
  75. data/lib/chewy/search/parameters/min_score.rb +16 -0
  76. data/lib/chewy/search/parameters/none.rb +27 -0
  77. data/lib/chewy/search/parameters/offset.rb +17 -0
  78. data/lib/chewy/search/parameters/order.rb +64 -0
  79. data/lib/chewy/search/parameters/post_filter.rb +19 -0
  80. data/lib/chewy/search/parameters/preference.rb +16 -0
  81. data/lib/chewy/search/parameters/profile.rb +16 -0
  82. data/lib/chewy/search/parameters/query.rb +19 -0
  83. data/lib/chewy/search/parameters/request_cache.rb +27 -0
  84. data/lib/chewy/search/parameters/rescore.rb +29 -0
  85. data/lib/chewy/search/parameters/script_fields.rb +16 -0
  86. data/lib/chewy/search/parameters/search_after.rb +20 -0
  87. data/lib/chewy/search/parameters/search_type.rb +16 -0
  88. data/lib/chewy/search/parameters/source.rb +73 -0
  89. data/lib/chewy/search/parameters/storage.rb +95 -0
  90. data/lib/chewy/search/parameters/stored_fields.rb +63 -0
  91. data/lib/chewy/search/parameters/suggest.rb +16 -0
  92. data/lib/chewy/search/parameters/terminate_after.rb +16 -0
  93. data/lib/chewy/search/parameters/timeout.rb +16 -0
  94. data/lib/chewy/search/parameters/track_scores.rb +16 -0
  95. data/lib/chewy/search/parameters/types.rb +20 -0
  96. data/lib/chewy/search/parameters/version.rb +16 -0
  97. data/lib/chewy/search/query_proxy.rb +257 -0
  98. data/lib/chewy/search/request.rb +1021 -0
  99. data/lib/chewy/search/response.rb +119 -0
  100. data/lib/chewy/search/scoping.rb +50 -0
  101. data/lib/chewy/search/scrolling.rb +136 -0
  102. data/lib/chewy/stash.rb +70 -0
  103. data/lib/chewy/strategy.rb +10 -3
  104. data/lib/chewy/strategy/active_job.rb +1 -0
  105. data/lib/chewy/strategy/atomic.rb +1 -3
  106. data/lib/chewy/strategy/bypass.rb +1 -1
  107. data/lib/chewy/strategy/resque.rb +1 -0
  108. data/lib/chewy/strategy/shoryuken.rb +40 -0
  109. data/lib/chewy/strategy/sidekiq.rb +13 -3
  110. data/lib/chewy/type.rb +29 -7
  111. data/lib/chewy/type/actions.rb +26 -2
  112. data/lib/chewy/type/adapter/active_record.rb +44 -29
  113. data/lib/chewy/type/adapter/base.rb +27 -7
  114. data/lib/chewy/type/adapter/mongoid.rb +18 -7
  115. data/lib/chewy/type/adapter/object.rb +187 -26
  116. data/lib/chewy/type/adapter/orm.rb +59 -32
  117. data/lib/chewy/type/adapter/sequel.rb +32 -16
  118. data/lib/chewy/type/import.rb +145 -191
  119. data/lib/chewy/type/import/bulk_builder.rb +122 -0
  120. data/lib/chewy/type/import/bulk_request.rb +76 -0
  121. data/lib/chewy/type/import/journal_builder.rb +45 -0
  122. data/lib/chewy/type/import/routine.rb +138 -0
  123. data/lib/chewy/type/mapping.rb +11 -1
  124. data/lib/chewy/type/observe.rb +1 -1
  125. data/lib/chewy/type/syncer.rb +220 -0
  126. data/lib/chewy/type/witchcraft.rb +27 -13
  127. data/lib/chewy/type/wrapper.rb +28 -2
  128. data/lib/chewy/version.rb +1 -1
  129. data/lib/tasks/chewy.rake +84 -26
  130. data/spec/chewy/config_spec.rb +82 -1
  131. data/spec/chewy/fields/base_spec.rb +147 -112
  132. data/spec/chewy/fields/root_spec.rb +75 -18
  133. data/spec/chewy/fields/time_fields_spec.rb +2 -3
  134. data/spec/chewy/index/actions_spec.rb +180 -50
  135. data/spec/chewy/index/aliases_spec.rb +2 -2
  136. data/spec/chewy/index/settings_spec.rb +67 -38
  137. data/spec/chewy/index/specification_spec.rb +160 -0
  138. data/spec/chewy/index_spec.rb +57 -66
  139. data/spec/chewy/journal_spec.rb +149 -54
  140. data/spec/chewy/minitest/helpers_spec.rb +4 -4
  141. data/spec/chewy/minitest/search_index_receiver_spec.rb +1 -1
  142. data/spec/chewy/query/criteria_spec.rb +179 -179
  143. data/spec/chewy/query/filters_spec.rb +15 -15
  144. data/spec/chewy/query/loading_spec.rb +22 -20
  145. data/spec/chewy/query/nodes/and_spec.rb +2 -2
  146. data/spec/chewy/query/nodes/bool_spec.rb +4 -4
  147. data/spec/chewy/query/nodes/equal_spec.rb +19 -19
  148. data/spec/chewy/query/nodes/exists_spec.rb +6 -6
  149. data/spec/chewy/query/nodes/has_child_spec.rb +19 -19
  150. data/spec/chewy/query/nodes/has_parent_spec.rb +19 -19
  151. data/spec/chewy/query/nodes/missing_spec.rb +5 -5
  152. data/spec/chewy/query/nodes/not_spec.rb +3 -2
  153. data/spec/chewy/query/nodes/or_spec.rb +2 -2
  154. data/spec/chewy/query/nodes/prefix_spec.rb +5 -5
  155. data/spec/chewy/query/nodes/query_spec.rb +2 -2
  156. data/spec/chewy/query/nodes/range_spec.rb +18 -18
  157. data/spec/chewy/query/nodes/raw_spec.rb +1 -1
  158. data/spec/chewy/query/nodes/regexp_spec.rb +14 -14
  159. data/spec/chewy/query/nodes/script_spec.rb +4 -4
  160. data/spec/chewy/query/pagination/kaminari_spec.rb +3 -55
  161. data/spec/chewy/query/pagination/will_paginate_spec.rb +5 -0
  162. data/spec/chewy/query/pagination_spec.rb +25 -21
  163. data/spec/chewy/query_spec.rb +501 -560
  164. data/spec/chewy/rake_helper_spec.rb +368 -0
  165. data/spec/chewy/repository_spec.rb +4 -4
  166. data/spec/chewy/rspec/update_index_spec.rb +89 -56
  167. data/spec/chewy/runtime_spec.rb +2 -2
  168. data/spec/chewy/search/loader_spec.rb +117 -0
  169. data/spec/chewy/search/pagination/kaminari_examples.rb +71 -0
  170. data/spec/chewy/search/pagination/kaminari_spec.rb +17 -0
  171. data/spec/chewy/search/pagination/will_paginate_examples.rb +63 -0
  172. data/spec/chewy/search/pagination/will_paginate_spec.rb +17 -0
  173. data/spec/chewy/search/parameters/aggs_spec.rb +5 -0
  174. data/spec/chewy/search/parameters/bool_storage_examples.rb +53 -0
  175. data/spec/chewy/search/parameters/docvalue_fields_spec.rb +5 -0
  176. data/spec/chewy/search/parameters/explain_spec.rb +5 -0
  177. data/spec/chewy/search/parameters/filter_spec.rb +5 -0
  178. data/spec/chewy/search/parameters/hash_storage_examples.rb +59 -0
  179. data/spec/chewy/search/parameters/highlight_spec.rb +5 -0
  180. data/spec/chewy/search/parameters/indices_boost_spec.rb +83 -0
  181. data/spec/chewy/search/parameters/integer_storage_examples.rb +32 -0
  182. data/spec/chewy/search/parameters/limit_spec.rb +5 -0
  183. data/spec/chewy/search/parameters/load_spec.rb +60 -0
  184. data/spec/chewy/search/parameters/min_score_spec.rb +32 -0
  185. data/spec/chewy/search/parameters/none_spec.rb +5 -0
  186. data/spec/chewy/search/parameters/offset_spec.rb +5 -0
  187. data/spec/chewy/search/parameters/order_spec.rb +65 -0
  188. data/spec/chewy/search/parameters/post_filter_spec.rb +5 -0
  189. data/spec/chewy/search/parameters/preference_spec.rb +5 -0
  190. data/spec/chewy/search/parameters/profile_spec.rb +5 -0
  191. data/spec/chewy/search/parameters/query_spec.rb +5 -0
  192. data/spec/chewy/search/parameters/query_storage_examples.rb +388 -0
  193. data/spec/chewy/search/parameters/request_cache_spec.rb +67 -0
  194. data/spec/chewy/search/parameters/rescore_spec.rb +62 -0
  195. data/spec/chewy/search/parameters/script_fields_spec.rb +5 -0
  196. data/spec/chewy/search/parameters/search_after_spec.rb +32 -0
  197. data/spec/chewy/search/parameters/search_type_spec.rb +5 -0
  198. data/spec/chewy/search/parameters/source_spec.rb +156 -0
  199. data/spec/chewy/search/parameters/storage_spec.rb +60 -0
  200. data/spec/chewy/search/parameters/stored_fields_spec.rb +126 -0
  201. data/spec/chewy/search/parameters/string_array_storage_examples.rb +63 -0
  202. data/spec/chewy/search/parameters/string_storage_examples.rb +32 -0
  203. data/spec/chewy/search/parameters/suggest_spec.rb +5 -0
  204. data/spec/chewy/search/parameters/terminate_after_spec.rb +5 -0
  205. data/spec/chewy/search/parameters/timeout_spec.rb +5 -0
  206. data/spec/chewy/search/parameters/track_scores_spec.rb +5 -0
  207. data/spec/chewy/search/parameters/types_spec.rb +5 -0
  208. data/spec/chewy/search/parameters/version_spec.rb +5 -0
  209. data/spec/chewy/search/parameters_spec.rb +130 -0
  210. data/spec/chewy/search/query_proxy_spec.rb +68 -0
  211. data/spec/chewy/search/request_spec.rb +669 -0
  212. data/spec/chewy/search/response_spec.rb +192 -0
  213. data/spec/chewy/search/scrolling_spec.rb +169 -0
  214. data/spec/chewy/search_spec.rb +13 -6
  215. data/spec/chewy/stash_spec.rb +95 -0
  216. data/spec/chewy/strategy/active_job_spec.rb +6 -0
  217. data/spec/chewy/strategy/resque_spec.rb +6 -0
  218. data/spec/chewy/strategy/shoryuken_spec.rb +64 -0
  219. data/spec/chewy/strategy/sidekiq_spec.rb +8 -0
  220. data/spec/chewy/strategy_spec.rb +6 -6
  221. data/spec/chewy/type/actions_spec.rb +29 -10
  222. data/spec/chewy/type/adapter/active_record_spec.rb +203 -91
  223. data/spec/chewy/type/adapter/mongoid_spec.rb +112 -54
  224. data/spec/chewy/type/adapter/object_spec.rb +101 -28
  225. data/spec/chewy/type/adapter/sequel_spec.rb +149 -82
  226. data/spec/chewy/type/import/bulk_builder_spec.rb +279 -0
  227. data/spec/chewy/type/import/bulk_request_spec.rb +102 -0
  228. data/spec/chewy/type/import/journal_builder_spec.rb +95 -0
  229. data/spec/chewy/type/import/routine_spec.rb +110 -0
  230. data/spec/chewy/type/import_spec.rb +350 -271
  231. data/spec/chewy/type/mapping_spec.rb +54 -18
  232. data/spec/chewy/type/observe_spec.rb +5 -1
  233. data/spec/chewy/type/syncer_spec.rb +123 -0
  234. data/spec/chewy/type/witchcraft_spec.rb +45 -29
  235. data/spec/chewy/type/wrapper_spec.rb +63 -23
  236. data/spec/chewy/type_spec.rb +28 -7
  237. data/spec/chewy_spec.rb +75 -7
  238. data/spec/spec_helper.rb +5 -2
  239. data/spec/support/active_record.rb +5 -1
  240. data/spec/support/class_helpers.rb +0 -14
  241. data/spec/support/mongoid.rb +15 -3
  242. data/spec/support/sequel.rb +6 -1
  243. metadata +198 -37
  244. data/gemfiles/rails.3.2.activerecord.gemfile +0 -16
  245. data/gemfiles/rails.3.2.activerecord.kaminari.gemfile +0 -15
  246. data/gemfiles/rails.3.2.activerecord.will_paginate.gemfile +0 -15
  247. data/gemfiles/rails.4.2.activerecord.kaminari.gemfile +0 -16
  248. data/gemfiles/rails.4.2.activerecord.will_paginate.gemfile +0 -16
  249. data/gemfiles/rails.4.2.mongoid.4.0.gemfile +0 -16
  250. data/gemfiles/rails.4.2.mongoid.4.0.kaminari.gemfile +0 -15
  251. data/gemfiles/rails.4.2.mongoid.4.0.will_paginate.gemfile +0 -15
  252. data/gemfiles/rails.4.2.mongoid.5.1.kaminari.gemfile +0 -15
  253. data/gemfiles/rails.4.2.mongoid.5.1.will_paginate.gemfile +0 -15
  254. data/gemfiles/rails.5.0.activerecord.kaminari.gemfile +0 -16
  255. data/gemfiles/rails.5.0.activerecord.will_paginate.gemfile +0 -16
  256. data/gemfiles/sequel.4.38.gemfile +0 -14
  257. data/lib/chewy/journal/apply.rb +0 -31
  258. data/lib/chewy/journal/clean.rb +0 -24
  259. data/lib/chewy/journal/entry.rb +0 -83
  260. data/lib/chewy/journal/query.rb +0 -87
  261. data/lib/chewy/query/pagination/will_paginate.rb +0 -27
  262. data/lib/chewy/query/scoping.rb +0 -20
  263. data/spec/chewy/journal/apply_spec.rb +0 -120
  264. data/spec/chewy/journal/entry_spec.rb +0 -237
  265. data/spec/chewy/query/pagination/will_paginage_spec.rb +0 -59
@@ -0,0 +1,1021 @@
1
+ module Chewy
2
+ module Search
3
+ # The main requset DSL class. Supports multiple index requests.
4
+ # Supports ES2 and ES5 search API and query DSL.
5
+ #
6
+ # @note The class tries to be as immutable as possible,
7
+ # so most of the methods return a new instance of the class.
8
+ # @see Chewy::Search
9
+ # @example
10
+ # scope = Chewy::Search::Request.new(PlacesIndex)
11
+ # # => <Chewy::Search::Request {:index=>["places"], :type=>["city", "country"]}>
12
+ # scope.limit(20)
13
+ # # => <Chewy::Search::Request {:index=>["places"], :type=>["city", "country"], :body=>{:size=>20}}>
14
+ # scope.order(:name).offset(10)
15
+ # # => <Chewy::Search::Request {:index=>["places"], :type=>["city", "country"], :body=>{:sort=>["name"], :from=>10}}>
16
+ class Request
17
+ include Enumerable
18
+ include Scoping
19
+ include Scrolling
20
+ UNDEFINED = Class.new.freeze
21
+ EVERFIELDS = %w[_index _type _id _parent].freeze
22
+ DELEGATED_METHODS = %i[
23
+ query filter post_filter order reorder docvalue_fields
24
+ track_scores request_cache explain version profile
25
+ search_type preference limit offset terminate_after
26
+ timeout min_score source stored_fields search_after
27
+ load script_fields suggest aggs aggregations none
28
+ indices_boost rescore highlight total total_count
29
+ total_entries types delete_all count exists? exist? find pluck
30
+ scroll_batches scroll_hits scroll_results scroll_wrappers
31
+ ].to_set.freeze
32
+ DEFAULT_BATCH_SIZE = 1000
33
+ DEFAULT_PLUCK_BATCH_SIZE = 10_000
34
+ DEFAULT_SCROLL = '1m'.freeze
35
+ # An array of storage names that are modifying returned fields in hits
36
+ FIELD_STORAGES = %i[
37
+ source docvalue_fields script_fields stored_fields
38
+ ].freeze
39
+ # An array of storage names that are not related to hits at all.
40
+ EXTRA_STORAGES = %i[aggs suggest].freeze
41
+ # An array of storage names that are changing the returned hist collection in any way.
42
+ WHERE_STORAGES = %i[
43
+ query filter post_filter none types min_score rescore indices_boost
44
+ ].freeze
45
+
46
+ delegate :hits, :wrappers, :objects, :records, :documents,
47
+ :object_hash, :record_hash, :document_hash,
48
+ :total, :max_score, :took, :timed_out?, to: :response
49
+ delegate :each, :size, :to_a, :[], to: :wrappers
50
+ alias_method :to_ary, :to_a
51
+ alias_method :total_count, :total
52
+ alias_method :total_entries, :total
53
+
54
+ attr_reader :_indexes, :_types
55
+
56
+ # The class is initialized with the list of chewy indexes and/or
57
+ # types, which are later used to compose requests.
58
+ #
59
+ # @example
60
+ # Chewy::Search::Request.new(PlacesIndex)
61
+ # # => <Chewy::Search::Request {:index=>["places"], :type=>["city", "country"]}>
62
+ # Chewy::Search::Request.new(PlacesIndex::City)
63
+ # # => <Chewy::Search::Request {:index=>["places"], :type=>["city"]}>
64
+ # Chewy::Search::Request.new(UsersIndex, PlacesIndex::City)
65
+ # # => <Chewy::Search::Request {:index=>["users", "places"], :type=>["city", "user"]}>
66
+ # @param indexes_or_types [Array<Chewy::Index, Chewy::Type>] indexes and types in any combinations
67
+ def initialize(*indexes_or_types)
68
+ @_types = indexes_or_types.select { |klass| klass < Chewy::Type }
69
+ @_indexes = indexes_or_types.select { |klass| klass < Chewy::Index }
70
+ @_types |= @_indexes.flat_map(&:types)
71
+ @_indexes |= @_types.map(&:index)
72
+ end
73
+
74
+ # Underlying parameter storage collection.
75
+ #
76
+ # @return [Chewy::Search::Parameters]
77
+ def parameters
78
+ @parameters ||= Parameters.new
79
+ end
80
+
81
+ # Compare two scopes or scope with a collection of wrappers.
82
+ # If other is a collection it performs the request to fetch
83
+ # data from ES.
84
+ #
85
+ # @example
86
+ # PlacesIndex.limit(10) == PlacesIndex.limit(10) # => true
87
+ # PlacesIndex.limit(10) == PlacesIndex.limit(10).to_a # => true
88
+ # PlacesIndex.limit(10) == PlacesIndex.limit(10).objects # => true
89
+ #
90
+ # PlacesIndex.limit(10) == UsersIndex.limit(10) # => false
91
+ # PlacesIndex.limit(10) == UsersIndex.limit(10).to_a # => false
92
+ #
93
+ # PlacesIndex.limit(10) == Object.new # => false
94
+ # @param other [Object] any object
95
+ # @return [true, false] the result of comparison
96
+ def ==(other)
97
+ super || other.is_a?(Chewy::Search::Request) ? compare_internals(other) : to_a == other
98
+ end
99
+
100
+ # Access to ES response wrappers providing useful methods such as
101
+ # {Chewy::Search::Response#total} or {Chewy::Search::Response#max_score}.
102
+ #
103
+ # @see Chewy::Search::Response
104
+ # @return [Chewy::Search::Response] a response object instance
105
+ def response
106
+ @response ||= Response.new(perform, loader, collection_paginator)
107
+ end
108
+
109
+ # ES request body
110
+ #
111
+ # @return [Hash] request body
112
+ def render
113
+ @render ||= render_base.merge(parameters.render)
114
+ end
115
+
116
+ # Includes the class name and the result of rendering.
117
+ #
118
+ # @return [String]
119
+ def inspect
120
+ "<#{self.class} #{render}>"
121
+ end
122
+
123
+ # @!group Chainable request modificators
124
+
125
+ # @!method query(query_hash=nil, &block)
126
+ # Adds `quer` parameter to the search request body.
127
+ #
128
+ # @see https://www.elastic.co/guide/en/elasticsearch/reference/current/search-request-query.html
129
+ # @see Chewy::Search::Parameters::Query
130
+ # @return [Chewy::Search::Request, Chewy::Search::QueryProxy]
131
+ #
132
+ # @overload query(query_hash)
133
+ # If pure hash is passed it goes straight to the `quer` parameter storage.
134
+ # Acts exactly the same way as {Chewy::Search::QueryProxy#must}.
135
+ #
136
+ # @see https://www.elastic.co/guide/en/elasticsearch/reference/current/query-dsl.html
137
+ # @example
138
+ # PlacesIndex.query(match: {name: 'Moscow'})
139
+ # # => <PlacesIndex::Query {..., :body=>{:query=>{:match=>{:name=>"Moscow"}}}}>
140
+ # @param query_hash [Hash] pure query hash
141
+ # @return [Chewy::Search::Request]
142
+ #
143
+ # @overload query
144
+ # If block is passed instead of a pure hash, `elasticsearch-dsl"
145
+ # gem will be used to process it.
146
+ # Acts exactly the same way as {Chewy::Search::QueryProxy#must} with a block.
147
+ #
148
+ # @see https://github.com/elastic/elasticsearch-ruby/tree/master/elasticsearch-dsl
149
+ # @example
150
+ # PlacesIndex.query { match name: 'Moscow' }
151
+ # # => <PlacesIndex::Query {..., :body=>{:query=>{:match=>{:name=>"Moscow"}}}}>
152
+ # @yield the block is processed by `elasticsearch-ds` gem
153
+ # @return [Chewy::Search::Request]
154
+ #
155
+ # @overload query
156
+ # If nothing is passed it returns a proxy for additional
157
+ # parameter manipulations.
158
+ #
159
+ # @see Chewy::Search::QueryProxy
160
+ # @example
161
+ # PlacesIndex.query.should(match: {name: 'Moscow'}).query.not(match: {name: 'London'})
162
+ # # => <PlacesIndex::Query {..., :body=>{:query=>{:bool=>{
163
+ # # :should=>{:match=>{:name=>"Moscow"}},
164
+ # # :must_not=>{:match=>{:name=>"London"}}}}}}>
165
+ # @return [Chewy::Search::QueryProxy]
166
+ #
167
+ # @!method filter(query_hash=nil, &block)
168
+ # Adds `filte` context of the `quer` parameter at the
169
+ # search request body.
170
+ #
171
+ # @see https://www.elastic.co/guide/en/elasticsearch/reference/current/query-filter-context.html
172
+ # @see Chewy::Search::Parameters::Filter
173
+ # @return [Chewy::Search::Request, Chewy::Search::QueryProxy]
174
+ #
175
+ # @overload filter(query_hash)
176
+ # If pure hash is passed it goes straight to the `filte` context of the `quer` parameter storage.
177
+ # Acts exactly the same way as {Chewy::Search::QueryProxy#must}.
178
+ #
179
+ # @see https://www.elastic.co/guide/en/elasticsearch/reference/current/query-dsl.html
180
+ # @example
181
+ # PlacesIndex.filter(match: {name: 'Moscow'})
182
+ # # => <PlacesIndex::Query {..., :body=>{:query=>{:bool=>{
183
+ # # :filter=>{:match=>{:name=>"Moscow"}}}}}}>
184
+ # @param query_hash [Hash] pure query hash
185
+ # @return [Chewy::Search::Request]
186
+ #
187
+ # @overload filter
188
+ # If block is passed instead of a pure hash, `elasticsearch-dsl"
189
+ # gem will be used to process it.
190
+ # Acts exactly the same way as {Chewy::Search::QueryProxy#must} with a block.
191
+ #
192
+ # @see https://github.com/elastic/elasticsearch-ruby/tree/master/elasticsearch-dsl
193
+ # @example
194
+ # PlacesIndex.filter { match name: 'Moscow' }
195
+ # # => <PlacesIndex::Query {..., :body=>{:query=>{:bool=>{
196
+ # # :filter=>{:match=>{:name=>"Moscow"}}}}}}>
197
+ # @yield the block is processed by `elasticsearch-ds` gem
198
+ # @return [Chewy::Search::Request]
199
+ #
200
+ # @overload filter
201
+ # If nothing is passed it returns a proxy for additional
202
+ # parameter manipulations.
203
+ #
204
+ # @see Chewy::Search::QueryProxy
205
+ # @example
206
+ # PlacesIndex.filter.should(match: {name: 'Moscow'}).filter.not(match: {name: 'London'})
207
+ # # => <PlacesIndex::Query {..., :body=>{:query=>{:bool=>{
208
+ # # :filter=>{:bool=>{:should=>{:match=>{:name=>"Moscow"}},
209
+ # # :must_not=>{:match=>{:name=>"London"}}}}}}}}>
210
+ # @return [Chewy::Search::QueryProxy]
211
+ #
212
+ # @!method post_filter(query_hash=nil, &block)
213
+ # Adds `post_filter` parameter to the search request body.
214
+ #
215
+ # @see https://www.elastic.co/guide/en/elasticsearch/reference/current/search-request-post-filter.html
216
+ # @see Chewy::Search::Parameters::PostFilter
217
+ # @return [Chewy::Search::Request, Chewy::Search::QueryProxy]
218
+ #
219
+ # @overload post_filter(query_hash)
220
+ # If pure hash is passed it goes straight to the `post_filter` parameter storage.
221
+ # Acts exactly the same way as {Chewy::Search::QueryProxy#must}.
222
+ #
223
+ # @see https://www.elastic.co/guide/en/elasticsearch/reference/current/query-dsl.html
224
+ # @example
225
+ # PlacesIndex.post_filter(match: {name: 'Moscow'})
226
+ # # => <PlacesIndex::Query {..., :body=>{:post_filter=>{:match=>{:name=>"Moscow"}}}}>
227
+ # @param query_hash [Hash] pure query hash
228
+ # @return [Chewy::Search::Request]
229
+ #
230
+ # @overload post_filter
231
+ # If block is passed instead of a pure hash, `elasticsearch-dsl"
232
+ # gem will be used to process it.
233
+ # Acts exactly the same way as {Chewy::Search::QueryProxy#must} with a block.
234
+ #
235
+ # @see https://github.com/elastic/elasticsearch-ruby/tree/master/elasticsearch-dsl
236
+ # @example
237
+ # PlacesIndex.post_filter { match name: 'Moscow' }
238
+ # # => <PlacesIndex::Query {..., :body=>{:post_filter=>{:match=>{:name=>"Moscow"}}}}>
239
+ # @yield the block is processed by `elasticsearch-ds` gem
240
+ # @return [Chewy::Search::Request]
241
+ #
242
+ # @overload post_filter
243
+ # If nothing is passed it returns a proxy for additional
244
+ # parameter manipulations.
245
+ #
246
+ # @see Chewy::Search::QueryProxy
247
+ # @example
248
+ # PlacesIndex.post_filter.should(match: {name: 'Moscow'}).post_filter.not(match: {name: 'London'})
249
+ # # => <PlacesIndex::Query {..., :body=>{:post_filter=>{:bool=>{
250
+ # # :should=>{:match=>{:name=>"Moscow"}},
251
+ # # :must_not=>{:match=>{:name=>"London"}}}}}}>
252
+ # @return [Chewy::Search::QueryProxy]
253
+ %i[query filter post_filter].each do |name|
254
+ define_method name do |query_hash = UNDEFINED, &block|
255
+ if block || query_hash != UNDEFINED
256
+ modify(name) { must(block || query_hash) }
257
+ else
258
+ Chewy::Search::QueryProxy.new(name, self)
259
+ end
260
+ end
261
+ end
262
+
263
+ # @!method order(*values)
264
+ # Modifies `sort` request parameter. Updates the storage on every call.
265
+ #
266
+ # @example
267
+ # PlacesIndex.order(:name, population: {order: :asc}).order(:coordinates)
268
+ # # => <PlacesIndex::Query {..., :body=>{:sort=>["name", {"population"=>{:order=>:asc}}, "coordinates"]}}>
269
+ # @see Chewy::Search::Parameters::Order
270
+ # @see https://www.elastic.co/guide/en/elasticsearch/reference/current/search-request-sort.html
271
+ # @param values [Array<Hash, String, Symbol>] sort fields and options
272
+ # @return [Chewy::Search::Request]
273
+ #
274
+ # @!method docvalue_fields(*values)
275
+ # Modifies `docvalue_fields` request parameter. Updates the storage on every call.
276
+ #
277
+ # @example
278
+ # PlacesIndex.docvalue_fields(:name).docvalue_fields(:population, :coordinates)
279
+ # # => <PlacesIndex::Query {..., :body=>{:docvalue_fields=>["name", "population", "coordinates"]}}>
280
+ # @see Chewy::Search::Parameters::DocvalueFields
281
+ # @see https://www.elastic.co/guide/en/elasticsearch/reference/current/search-request-docvalue-fields.html
282
+ # @param values [Array<String, Symbol>] field names
283
+ # @return [Chewy::Search::Request]
284
+ #
285
+ # @!method types(*values)
286
+ # Modifies `types` request parameter. Updates the storage on every call.
287
+ # Constrains types passed on the request initialization.
288
+ #
289
+ # @example
290
+ # PlacesIndex.types(:city).types(:unexistent)
291
+ # # => <PlacesIndex::Query {:index=>["places"], :type=>["city"]}>
292
+ # @see Chewy::Search::Parameters::Types
293
+ # @see https://www.elastic.co/guide/en/elasticsearch/reference/current/search-search.html
294
+ # @param values [Array<String, Symbol>] type names
295
+ # @return [Chewy::Search::Request]
296
+ %i[order docvalue_fields types].each do |name|
297
+ define_method name do |value, *values|
298
+ modify(name) { update!([value, *values]) }
299
+ end
300
+ end
301
+
302
+ # @overload reorder(*values)
303
+ # Replaces the value of the `sort` parameter with the provided value.
304
+ #
305
+ # @example
306
+ # PlacesIndex.order(:name, population: {order: :asc}).reorder(:coordinates)
307
+ # # => <PlacesIndex::Query {..., :body=>{:sort=>["coordinates"]}}>
308
+ # @see Chewy::Search::Parameters::Order
309
+ # @see https://www.elastic.co/guide/en/elasticsearch/reference/current/search-request-sort.html
310
+ # @param values [Array<Hash, String, Symbol>] sort fields and options
311
+ # @return [Chewy::Search::Request]
312
+ def reorder(value, *values)
313
+ modify(:order) { replace!([value, *values]) }
314
+ end
315
+
316
+ # @!method track_scores(value = true)
317
+ # Replaces the value of the `track_scores` parameter with the provided value.
318
+ #
319
+ # @example
320
+ # PlacesIndex.track_scores
321
+ # # => <PlacesIndex::Query {..., :body=>{:track_scores=>true}}>
322
+ # PlacesIndex.track_scores.track_scores(false)
323
+ # # => <PlacesIndex::Query {:index=>["places"], :type=>["city", "country"]}>
324
+ # @see Chewy::Search::Parameters::TrackScores
325
+ # @see https://www.elastic.co/guide/en/elasticsearch/reference/5.4/search-request-sort.html#_track_scores
326
+ # @param value [true, false]
327
+ # @return [Chewy::Search::Request]
328
+ #
329
+ # @!method explain(value = true)
330
+ # Replaces the value of the `explain` parameter with the provided value.
331
+ #
332
+ # @example
333
+ # PlacesIndex.explain
334
+ # # => <PlacesIndex::Query {..., :body=>{:explain=>true}}>
335
+ # PlacesIndex.explain.explain(false)
336
+ # # => <PlacesIndex::Query {:index=>["places"], :type=>["city", "country"]}>
337
+ # @see Chewy::Search::Parameters::Explain
338
+ # @see https://www.elastic.co/guide/en/elasticsearch/reference/5.4/search-request-explain.html
339
+ # @param value [true, false]
340
+ # @return [Chewy::Search::Request]
341
+ #
342
+ # @!method version(value = true)
343
+ # Replaces the value of the `version` parameter with the provided value.
344
+ #
345
+ # @example
346
+ # PlacesIndex.version
347
+ # # => <PlacesIndex::Query {..., :body=>{:version=>true}}>
348
+ # PlacesIndex.version.version(false)
349
+ # # => <PlacesIndex::Query {:index=>["places"], :type=>["city", "country"]}>
350
+ # @see Chewy::Search::Parameters::Version
351
+ # @see https://www.elastic.co/guide/en/elasticsearch/reference/5.4/search-request-version.html
352
+ # @param value [true, false]
353
+ # @return [Chewy::Search::Request]
354
+ #
355
+ # @!method profile(value = true)
356
+ # Replaces the value of the `profile` parameter with the provided value.
357
+ #
358
+ # @example
359
+ # PlacesIndex.profile
360
+ # # => <PlacesIndex::Query {..., :body=>{:profile=>true}}>
361
+ # PlacesIndex.profile.profile(false)
362
+ # # => <PlacesIndex::Query {:index=>["places"], :type=>["city", "country"]}>
363
+ # @see Chewy::Search::Parameters::Profile
364
+ # @see https://www.elastic.co/guide/en/elasticsearch/reference/5.4/search-profile.html
365
+ # @param value [true, false]
366
+ # @return [Chewy::Search::Request]
367
+ #
368
+ # @!method none(value = true)
369
+ # Enables `NullObject` pattern for the request, doesn't perform the
370
+ # request, `#hits` are empty, `#total` is 0, etc.
371
+ #
372
+ # @example
373
+ # PlacesIndex.none.to_a
374
+ # # => []
375
+ # PlacesIndex.none.total
376
+ # # => 0
377
+ # @see Chewy::Search::Parameters::None
378
+ # @see https://en.wikipedia.org/wiki/Null_Object_pattern
379
+ # @param value [true, false]
380
+ # @return [Chewy::Search::Request]
381
+ %i[track_scores explain version profile none].each do |name|
382
+ define_method name do |value = true|
383
+ modify(name) { replace!(value) }
384
+ end
385
+ end
386
+
387
+ # @!method request_cache(value)
388
+ # Replaces the value of the `request_cache` parameter with the provided value.
389
+ # Unlike other boolean fields, the value have to be specified explicitly
390
+ # since it overrides the index-level setting.
391
+ #
392
+ # @example
393
+ # PlacesIndex.request_cache(true)
394
+ # # => <PlacesIndex::Query {..., :body=>{:request_cache=>true}}>
395
+ # PlacesIndex.request_cache(false)
396
+ # # => <PlacesIndex::Query {..., :body=>{:request_cache=>false}}>
397
+ # @see Chewy::Search::Parameters::RequestCache
398
+ # @see https://www.elastic.co/guide/en/elasticsearch/reference/5.4/shard-request-cache.html#_enabling_and_disabling_caching_per_request
399
+ # @param value [true, false, nil]
400
+ # @return [Chewy::Search::Request]
401
+ #
402
+ # @!method search_type(value)
403
+ # Replaces the value of the `search_type` request part.
404
+ #
405
+ # @example
406
+ # PlacesIndex.search_type(:dfs_query_then_fetch)
407
+ # # => <PlacesIndex::Query {..., :body=>{:search_type=>"dfs_query_then_fetch"}}>
408
+ # @see Chewy::Search::Parameters::SearchType
409
+ # @see https://www.elastic.co/guide/en/elasticsearch/reference/5.4/search-request-search-type.html
410
+ # @param value [String, Symbol]
411
+ # @return [Chewy::Search::Request]
412
+ #
413
+ # @!method preference(value)
414
+ # Replaces the value of the `preference` request part.
415
+ #
416
+ # @example
417
+ # PlacesIndex.preference(:_primary_first)
418
+ # # => <PlacesIndex::Query {..., :body=>{:preference=>"_primary_first"}}>
419
+ # @see Chewy::Search::Parameters::Preference
420
+ # @see https://www.elastic.co/guide/en/elasticsearch/reference/5.4/search-request-preference.html
421
+ # @param value [String, Symbol]
422
+ # @return [Chewy::Search::Request]
423
+ #
424
+ # @!method timeout(value)
425
+ # Replaces the value of the `timeout` request part.
426
+ #
427
+ # @example
428
+ # PlacesIndex.timeout('1m')
429
+ # <PlacesIndex::Query {..., :body=>{:timeout=>"1m"}}>
430
+ # @see Chewy::Search::Parameters::Timeout
431
+ # @see https://www.elastic.co/guide/en/elasticsearch/reference/5.4/common-options.html#time-units
432
+ # @param value [String, Symbol]
433
+ # @return [Chewy::Search::Request]
434
+ #
435
+ # @!method limit(value)
436
+ # Replaces the value of the `size` request part.
437
+ #
438
+ # @example
439
+ # PlacesIndex.limit(10)
440
+ # <PlacesIndex::Query {..., :body=>{:size=>10}}>
441
+ # @see Chewy::Search::Parameters::Limit
442
+ # @see https://www.elastic.co/guide/en/elasticsearch/reference/5.4/search-request-from-size.html
443
+ # @param value [String, Integer]
444
+ # @return [Chewy::Search::Request]
445
+ #
446
+ # @!method offset(value)
447
+ # Replaces the value of the `from` request part.
448
+ #
449
+ # @example
450
+ # PlacesIndex.offset(10)
451
+ # <PlacesIndex::Query {..., :body=>{:from=>10}}>
452
+ # @see Chewy::Search::Parameters::Offset
453
+ # @see https://www.elastic.co/guide/en/elasticsearch/reference/5.4/search-request-from-size.html
454
+ # @param value [String, Integer]
455
+ # @return [Chewy::Search::Request]
456
+ #
457
+ # @!method terminate_after(value)
458
+ # Replaces the value of the `terminate_after` request part.
459
+ #
460
+ # @example
461
+ # PlacesIndex.terminate_after(10)
462
+ # <PlacesIndex::Query {..., :body=>{:terminate_after=>10}}>
463
+ # @see Chewy::Search::Parameters::Offset
464
+ # @see https://www.elastic.co/guide/en/elasticsearch/reference/5.4/search-request-body.html
465
+ # @param value [String, Integer]
466
+ # @return [Chewy::Search::Request]
467
+ #
468
+ # @!method min_score(value)
469
+ # Replaces the value of the `min_score` request part.
470
+ #
471
+ # @example
472
+ # PlacesIndex.min_score(2)
473
+ # <PlacesIndex::Query {..., :body=>{:min_score=>2.0}}>
474
+ # @see Chewy::Search::Parameters::Offset
475
+ # @see https://www.elastic.co/guide/en/elasticsearch/reference/5.4/search-request-min-score.html
476
+ # @param value [String, Integer, Float]
477
+ # @return [Chewy::Search::Request]
478
+ %i[request_cache search_type preference timeout limit offset terminate_after min_score].each do |name|
479
+ define_method name do |value|
480
+ modify(name) { replace!(value) }
481
+ end
482
+ end
483
+
484
+ # @!method source(*values)
485
+ # Updates `_source` request part. Accepts either an array
486
+ # of field names/templates or a hash with `includes` and `excludes`
487
+ # keys. Source also can be disabled entierly or enabled again.
488
+ #
489
+ # @example
490
+ # PlacesIndex.source(:name).source(includes: [:popularity], excludes: :description)
491
+ # # => <PlacesIndex::Query {..., :body=>{:_source=>{:includes=>["name", "popularity"], :excludes=>["description"]}}}>
492
+ # PlacesIndex.source(false)
493
+ # # => <PlacesIndex::Query {..., :body=>{:_source=>false}}>
494
+ # @see Chewy::Search::Parameters::Source
495
+ # @see https://www.elastic.co/guide/en/elasticsearch/reference/5.4/search-request-source-filtering.html
496
+ # @param values [true, false, {Symbol => Array<String, Symbol>, String, Symbol}, Array<String, Symbol>, String, Symbol]
497
+ # @return [Chewy::Search::Request]
498
+ #
499
+ # @!method stored_fields(*values)
500
+ # Updates `stored_fields` request part. Accepts an array of field
501
+ # names. Can be entierly disabled and enabled back.
502
+ #
503
+ # @example
504
+ # PlacesIndex.stored_fields(:name).stored_fields(:description)
505
+ # # => <PlacesIndex::Query {..., :body=>{:stored_fields=>["name", "description"]}}>
506
+ # PlacesIndex.stored_fields(false)
507
+ # # => <PlacesIndex::Query {..., :body=>{:stored_fields=>"_none_"}}>
508
+ # @see Chewy::Search::Parameters::StoredFields
509
+ # @see https://www.elastic.co/guide/en/elasticsearch/reference/5.4/search-request-stored-fields.html
510
+ # @param values [true, false, String, Symbol, Array<String, Symbol>]
511
+ # @return [Chewy::Search::Request]
512
+ %i[source stored_fields].each do |name|
513
+ define_method name do |value, *values|
514
+ modify(name) { update!(values.empty? ? value : [value, *values]) }
515
+ end
516
+ end
517
+
518
+ # @overload search_after(*values)
519
+ # Replaces the storage value for `search_after` request part.
520
+ #
521
+ # @example
522
+ # PlacesIndex.search_after(42, 'Moscow').search_after('London')
523
+ # # => <PlacesIndex::Query {..., :body=>{:search_after=>["London"]}}>
524
+ # @see Chewy::Search::Parameters::SearchAfter
525
+ # @see https://www.elastic.co/guide/en/elasticsearch/reference/5.4/search-request-search-after.html
526
+ # @param value [Array, Object]
527
+ # @return [Chewy::Search::Request]
528
+ def search_after(value, *values)
529
+ modify(:search_after) { replace!(values.empty? ? value : [value, *values]) }
530
+ end
531
+
532
+ # Stores ORM/ODM objects loading options. Options
533
+ # might be define per-type or be global, depends on the adapter
534
+ # loading implementation. Also, there are 2 loading options to select
535
+ # or exclude types from loading: `only` and `except` respectively.
536
+ # Options are updated on further method calls.
537
+ #
538
+ # @example
539
+ # PlaceIndex.load(only: 'city').load(scope: -> { active })
540
+ # @see Chewy::Search::Loader
541
+ # @see Chewy::Search::Response#objects
542
+ # @see Chewy::Search::Scrolling#scroll_objects
543
+ # @param options [Hash] adapter-specific loading options
544
+ def load(options = nil)
545
+ modify(:load) { update!(options) }
546
+ end
547
+
548
+ # @!method script_fields(value)
549
+ # Add a `script_fields` part to the request. Further
550
+ # call values are merged to the storage hash.
551
+ #
552
+ # @example
553
+ # PlacesIndex
554
+ # .script_fields(field1: {script: {lang: 'painless', inline: 'some script here'}})
555
+ # .script_fields(field2: {script: {lang: 'painless', inline: 'some script here'}})
556
+ # # => <PlacesIndex::Query {..., :body=>{:script_fields=>{
557
+ # # "field1"=>{:script=>{:lang=>"painless", :inline=>"some script here"}},
558
+ # # "field2"=>{:script=>{:lang=>"painless", :inline=>"some script here"}}}}}>
559
+ # @see Chewy::Search::Parameters::ScriptFields
560
+ # @see https://www.elastic.co/guide/en/elasticsearch/reference/5.4/search-request-script-fields.html
561
+ # @param value [Hash]
562
+ # @return [Chewy::Search::Request]
563
+ #
564
+ # @!method indices_boost(value)
565
+ # Add an `indices_boost` part to the request. Further
566
+ # call values are merged to the storage hash.
567
+ #
568
+ # @example
569
+ # PlacesIndex.indices_boost(index1: 1.2, index2: 1.3).indices_boost(index1: 1.5)
570
+ # # => <PlacesIndex::Query {..., :body=>{:indices_boost=>[{"index2"=>1.3}, {"index1"=>1.5}]}}>
571
+ # @see Chewy::Search::Parameters::IndicesBoost
572
+ # @see https://www.elastic.co/guide/en/elasticsearch/reference/5.4/search-request-index-boost.html
573
+ # @param value [{String, Symbol => String, Integer, Float}]
574
+ # @return [Chewy::Search::Request]
575
+ #
576
+ # @!method rescore(value)
577
+ # Add a `rescore` part to the request. Further
578
+ # call values are added to the storage array.
579
+ #
580
+ # @example
581
+ # PlacesIndex.rescore(window_size: 100, query: {}).rescore(window_size: 200, query: {})
582
+ # # => <PlacesIndex::Query {..., :body=>{:rescore=>[{:window_size=>100, :query=>{}}, {:window_size=>200, :query=>{}}]}}>
583
+ # @see Chewy::Search::Parameters::Rescore
584
+ # @see https://www.elastic.co/guide/en/elasticsearch/reference/5.4/search-request-rescore.html
585
+ # @param value [Hash, Array<Hash>]
586
+ # @return [Chewy::Search::Request]
587
+ #
588
+ # @!method highlight(value)
589
+ # Add a `highlight` configuration to the request. Further
590
+ # call values are merged to the storage hash.
591
+ #
592
+ # @example
593
+ # PlacesIndex
594
+ # .highlight(fields: {description: {type: 'plain'}})
595
+ # .highlight(pre_tags: ['<em>'], post_tags: ['</em>'])
596
+ # # => <PlacesIndex::Query {..., :body=>{:highlight=>{
597
+ # # "fields"=>{:description=>{:type=>"plain"}},
598
+ # # "pre_tags"=>["<em>"], "post_tags"=>["</em>"]}}}>
599
+ # @see Chewy::Search::Parameters::Highlight
600
+ # @see https://www.elastic.co/guide/en/elasticsearch/reference/5.4/search-request-highlighting.html
601
+ # @param value [Hash]
602
+ # @return [Chewy::Search::Request]
603
+ %i[script_fields indices_boost rescore highlight].each do |name|
604
+ define_method name do |value|
605
+ modify(name) { update!(value) }
606
+ end
607
+ end
608
+
609
+ # A dual-purpose method.
610
+ #
611
+ # @overload suggest(value)
612
+ # With the value provided it adds a new suggester
613
+ # to the suggestion hash.
614
+ #
615
+ # @example
616
+ # PlacesIndex
617
+ # .suggest(names: {text: 'tring out Elasticsearch'})
618
+ # .suggest(descriptions: {text: 'some other text'})
619
+ # # => <PlacesIndex::Query {..., :body=>{:suggest=>{
620
+ # # "names"=>{:text=>"tring out Elasticsearch"},
621
+ # # "descriptions"=>{:text=>"some other text"}}}}>
622
+ # @see Chewy::Search::Parameters::Suggest
623
+ # @see https://www.elastic.co/guide/en/elasticsearch/reference/5.4/search-suggesters.html
624
+ # @param value [Hash]
625
+ # @return [Chewy::Search::Request]
626
+ #
627
+ # @overload suggest
628
+ # Without value provided, it performs the request and
629
+ # returns {Chewy::Search::Response#suggest} contents.
630
+ #
631
+ # @example
632
+ # PlacesIndex.suggest(names: {text: 'tring out Elasticsearch'}).suggest
633
+ # @see Chewy::Search::Response#suggest
634
+ # @return [Hash]
635
+ def suggest(value = UNDEFINED)
636
+ if value == UNDEFINED
637
+ response.suggest
638
+ else
639
+ modify(:suggest) { update!(value) }
640
+ end
641
+ end
642
+
643
+ # A dual-purpose method.
644
+ #
645
+ # @overload aggs(value)
646
+ # With the value provided it adds a new aggregation
647
+ # to the aggregation hash.
648
+ #
649
+ # @example
650
+ # PlacesIndex
651
+ # .aggs(avg_population: {avg: {field: :population}})
652
+ # .aggs(avg_age: {avg: {field: :age}})
653
+ # # => <PlacesIndex::Query {..., :body=>{:aggs=>{
654
+ # # "avg_population"=>{:avg=>{:field=>:population}},
655
+ # # "avg_age"=>{:avg=>{:field=>:age}}}}}>
656
+ # @see Chewy::Search::Parameters::Aggs
657
+ # @see https://www.elastic.co/guide/en/elasticsearch/reference/current/search-aggregations.html
658
+ # @param value [Hash]
659
+ # @return [Chewy::Search::Request]
660
+ #
661
+ # @overload aggs
662
+ # Without value provided, it performs the request and
663
+ # returns {Chewy::Search::Response#aggs} contents.
664
+ #
665
+ # @example
666
+ # PlacesIndex.aggs(avg_population: {avg: {field: :population}}).aggs
667
+ # @see Chewy::Search::Response#aggs
668
+ # @return [Hash]
669
+ def aggs(value = UNDEFINED)
670
+ if value == UNDEFINED
671
+ response.aggs
672
+ else
673
+ modify(:aggs) { update!(value) }
674
+ end
675
+ end
676
+ alias_method :aggregations, :aggs
677
+
678
+ # @!group Scopes manipulation
679
+
680
+ # Merges 2 scopes by merging their parameters.
681
+ #
682
+ # @example
683
+ # scope1 = PlacesIndex.limit(10).offset(10)
684
+ # scope2 = PlacesIndex.limit(20)
685
+ # scope1.merge(scope2)
686
+ # # => <PlacesIndex::Query {..., :body=>{:size=>20, :from=>10}}>
687
+ # scope2.merge(scope1)
688
+ # # => <PlacesIndex::Query {..., :body=>{:size=>10, :from=>10}}>
689
+ # @see Chewy::Search::Parameters#merge
690
+ # @param other [Chewy::Search::Request] scope to merge
691
+ # @return [Chewy::Search::Request] new scope
692
+ def merge(other)
693
+ chain { parameters.merge!(other.parameters) }
694
+ end
695
+
696
+ # @!method and(other)
697
+ # Takes `query`, `filter`, `post_filter` from the passed scope
698
+ # and performs {Chewy::Search::QueryProxy#and} operation for each
699
+ # of them. Unlike merge, every other parameter is kept unmerged
700
+ # (values from the first scope are used in the result scope).
701
+ #
702
+ # @see Chewy::Search::QueryProxy#and
703
+ # @example
704
+ # scope1 = PlacesIndex.filter(term: {name: 'Moscow'}).query(match: {name: 'London'})
705
+ # scope2 = PlacesIndex.filter.not(term: {name: 'Berlin'}).query(match: {name: 'Washington'})
706
+ # scope1.and(scope2)
707
+ # # => <PlacesIndex::Query {..., :body=>{:query=>{:bool=>{
708
+ # # :must=>[{:match=>{:name=>"London"}}, {:match=>{:name=>"Washington"}}],
709
+ # # :filter=>{:bool=>{:must=>[{:term=>{:name=>"Moscow"}}, {:bool=>{:must_not=>{:term=>{:name=>"Berlin"}}}}]}}}}}}>
710
+ # @param other [Chewy::Search::Request] scope to merge
711
+ # @return [Chewy::Search::Request] new scope
712
+ #
713
+ # @!method or(other)
714
+ # Takes `query`, `filter`, `post_filter` from the passed scope
715
+ # and performs {Chewy::Search::QueryProxy#or} operation for each
716
+ # of them. Unlike merge, every other parameter is kept unmerged
717
+ # (values from the first scope are used in the result scope).
718
+ #
719
+ # @see Chewy::Search::QueryProxy#or
720
+ # @example
721
+ # scope1 = PlacesIndex.filter(term: {name: 'Moscow'}).query(match: {name: 'London'})
722
+ # scope2 = PlacesIndex.filter.not(term: {name: 'Berlin'}).query(match: {name: 'Washington'})
723
+ # scope1.or(scope2)
724
+ # # => <PlacesIndex::Query {..., :body=>{:query=>{:bool=>{
725
+ # # :should=>[{:match=>{:name=>"London"}}, {:match=>{:name=>"Washington"}}],
726
+ # # :filter=>{:bool=>{:should=>[{:term=>{:name=>"Moscow"}}, {:bool=>{:must_not=>{:term=>{:name=>"Berlin"}}}}]}}}}}}>
727
+ # @param other [Chewy::Search::Request] scope to merge
728
+ # @return [Chewy::Search::Request] new scope
729
+ #
730
+ # @!method not(other)
731
+ # Takes `query`, `filter`, `post_filter` from the passed scope
732
+ # and performs {Chewy::Search::QueryProxy#not} operation for each
733
+ # of them. Unlike merge, every other parameter is kept unmerged
734
+ # (values from the first scope are used in the result scope).
735
+ #
736
+ # @see Chewy::Search::QueryProxy#not
737
+ # @example
738
+ # scope1 = PlacesIndex.filter(term: {name: 'Moscow'}).query(match: {name: 'London'})
739
+ # scope2 = PlacesIndex.filter.not(term: {name: 'Berlin'}).query(match: {name: 'Washington'})
740
+ # scope1.not(scope2)
741
+ # # => <PlacesIndex::Query {..., :body=>{:query=>{:bool=>{
742
+ # # :must=>{:match=>{:name=>"London"}}, :must_not=>{:match=>{:name=>"Washington"}},
743
+ # # :filter=>{:bool=>{:must=>{:term=>{:name=>"Moscow"}}, :must_not=>{:bool=>{:must_not=>{:term=>{:name=>"Berlin"}}}}}}}}}}>
744
+ # @param other [Chewy::Search::Request] scope to merge
745
+ # @return [Chewy::Search::Request] new scope
746
+ %i[and or not].each do |name|
747
+ define_method name do |other|
748
+ %i[query filter post_filter].inject(self) do |scope, parameter_name|
749
+ scope.send(parameter_name).send(name, other.parameters[parameter_name].value)
750
+ end
751
+ end
752
+ end
753
+
754
+ # Returns a new scope containing only specified storages.
755
+ #
756
+ # @example
757
+ # PlacesIndex.limit(10).offset(10).order(:name).except(:offset, :order)
758
+ # # => <PlacesIndex::Query {..., :body=>{:size=>10}}>
759
+ # @param values [Array<String, Symbol>]
760
+ # @return [Chewy::Search::Request] new scope
761
+ def only(*values)
762
+ chain { parameters.only!(values.flatten(1)) }
763
+ end
764
+
765
+ # Returns a new scope containing all the storages except specified.
766
+ #
767
+ # @example
768
+ # PlacesIndex.limit(10).offset(10).order(:name).only(:offset, :order)
769
+ # # => <PlacesIndex::Query {..., :body=>{:from=>10, :sort=>["name"]}}>
770
+ # @param values [Array<String, Symbol>]
771
+ # @return [Chewy::Search::Request] new scope
772
+ def except(*values)
773
+ chain { parameters.except!(values.flatten(1)) }
774
+ end
775
+
776
+ # @!group Additional actions
777
+
778
+ # Returns total count of hits for the request. If the request
779
+ # was already performed - it uses the `total` value, otherwise
780
+ # it executes a fast count request.
781
+ #
782
+ # @return [Integer] total hits count
783
+ def count
784
+ if performed?
785
+ total
786
+ else
787
+ Chewy.client.count(only(WHERE_STORAGES).render)['count']
788
+ end
789
+ rescue Elasticsearch::Transport::Transport::Errors::NotFound
790
+ 0
791
+ end
792
+
793
+ # Checks if any of the document exist for this request. If
794
+ # the request was already performed - it uses the `total`,
795
+ # otherwise it executes a fast request to check existence.
796
+ #
797
+ # @return [true, false] wether hits exist or not
798
+ def exists?
799
+ if performed?
800
+ total != 0
801
+ else
802
+ limit(0).terminate_after(1).total != 0
803
+ end
804
+ end
805
+ alias_method :exist?, :exists?
806
+
807
+ # Return first wrapper object or a collection of first N wrapper
808
+ # objects if the argument is provided.
809
+ # Tries to use cached results of possible. If the amount of
810
+ # cached results is insufficient - performs a new request.
811
+ #
812
+ # @overload first
813
+ # If nothing is passed - it returns a single object.
814
+ #
815
+ # @return [Chewy::Type] result document
816
+ #
817
+ # @overload first(limit)
818
+ # If limit is provided - it returns the limit amount or less
819
+ # of wrapper objects.
820
+ #
821
+ # @param limit [Integer] amount of requested results
822
+ # @return [Array<Chewy::Type>] result document collection
823
+ def first(limit = UNDEFINED)
824
+ request_limit = limit == UNDEFINED ? 1 : limit
825
+
826
+ if performed? && (request_limit <= size || size == total)
827
+ limit == UNDEFINED ? wrappers.first : wrappers.first(limit)
828
+ else
829
+ result = except(EXTRA_STORAGES).limit(request_limit).to_a
830
+ limit == UNDEFINED ? result.first : result
831
+ end
832
+ end
833
+
834
+ # Finds documents with specified ids for the current request scope.
835
+ #
836
+ # @raise [Chewy::DocumentNotFound] in case of any document is missing
837
+ # @overload find(id)
838
+ # If single id is passed - it returns a single object.
839
+ #
840
+ # @param id [Integer, String] id of the desired document
841
+ # @return [Chewy::Type] result document
842
+ #
843
+ # @overload find(*ids)
844
+ # If several field are passed - it returns an array of wrappers.
845
+ # Respect the amount of passed ids and if it is more than the default
846
+ # batch size - uses scroll API to retrieve everything.
847
+ #
848
+ # @param ids [Array<Integer, String>] ids of the desired documents
849
+ # @return [Array<Chewy::Type>] result documents
850
+ def find(*ids)
851
+ return super if block_given?
852
+
853
+ ids = ids.flatten(1).map(&:to_s)
854
+ scope = except(EXTRA_STORAGES).filter(ids: {values: ids})
855
+
856
+ results = if ids.size > DEFAULT_BATCH_SIZE
857
+ scope.scroll_wrappers
858
+ else
859
+ scope.limit(ids.size)
860
+ end.to_a
861
+
862
+ if ids.size != results.size
863
+ missing_ids = ids - results.map(&:id).map(&:to_s)
864
+ raise Chewy::DocumentNotFound, "Could not find documents for ids: #{missing_ids.to_sentence}"
865
+ end
866
+ results.one? ? results.first : results
867
+ end
868
+
869
+ # Returns and array of values for specified fields.
870
+ # Uses `source` to restrict the list of returned fields.
871
+ # Fields `_id`, `_type` and `_index` are also supported.
872
+ #
873
+ # @overload pluck(field)
874
+ # If single field is passed - it returns and array of values.
875
+ #
876
+ # @param field [String, Symbol] field name
877
+ # @return [Array<Object>] specified field values
878
+ #
879
+ # @overload pluck(*fields)
880
+ # If several field are passed - it returns an array of arrays of values.
881
+ #
882
+ # @param fields [Array<String, Symbol>] field names
883
+ # @return [Array<Array<Object>>] specified field values
884
+ def pluck(*fields)
885
+ fields = fields.flatten(1).reject(&:blank?).map(&:to_s)
886
+
887
+ source_fields = fields - EVERFIELDS
888
+ scope = except(FIELD_STORAGES, EXTRA_STORAGES)
889
+ .source(source_fields.presence || false)
890
+
891
+ hits = raw_limit_value ? scope.hits : scope.scroll_hits(batch_size: DEFAULT_PLUCK_BATCH_SIZE)
892
+ hits.map do |hit|
893
+ if fields.one?
894
+ fetch_field(hit, fields.first)
895
+ else
896
+ fields.map do |field|
897
+ fetch_field(hit, field)
898
+ end
899
+ end
900
+ end
901
+ end
902
+
903
+ # Deletes all the documents from the specified scope it uses
904
+ # `delete_by_query` API. For ES < 5.0 it uses `delete_by_query`
905
+ # plugin, which requires additional installation effort.
906
+ #
907
+ # @see https://www.elastic.co/guide/en/elasticsearch/reference/current/docs-delete-by-query.html
908
+ # @see https://www.elastic.co/guide/en/elasticsearch/plugins/2.0/plugins-delete-by-query.html
909
+ # @note The result hash is different for different API used.
910
+ # @param refresh [true, false] field names
911
+ # @return [Hash] the result of query execution
912
+ def delete_all(refresh: true)
913
+ request_body = only(WHERE_STORAGES).render.merge(refresh: refresh)
914
+ ActiveSupport::Notifications.instrument 'delete_query.chewy',
915
+ request: request_body, indexes: _indexes, types: _types,
916
+ index: _indexes.one? ? _indexes.first : _indexes,
917
+ type: _types.one? ? _types.first : _types do
918
+ if Runtime.version < '5.0'
919
+ delete_by_query_plugin(request_body)
920
+ else
921
+ Chewy.client.delete_by_query(request_body)
922
+ end
923
+ end
924
+ end
925
+
926
+ protected
927
+
928
+ def initialize_clone(origin)
929
+ @parameters = origin.parameters.clone
930
+ reset
931
+ end
932
+
933
+ private
934
+
935
+ def compare_internals(other)
936
+ _indexes.sort_by(&:derivable_name) == other._indexes.sort_by(&:derivable_name) &&
937
+ _types.sort_by(&:derivable_name) == other._types.sort_by(&:derivable_name) &&
938
+ parameters == other.parameters
939
+ end
940
+
941
+ def modify(name, &block)
942
+ chain { parameters.modify!(name, &block) }
943
+ end
944
+
945
+ def chain(&block)
946
+ clone.tap { |r| r.instance_exec(&block) }
947
+ end
948
+
949
+ def reset
950
+ @response, @render, @render_base, @type_names, @index_names, @loader = nil
951
+ end
952
+
953
+ def perform(additional = {})
954
+ request_body = render.merge(additional)
955
+ ActiveSupport::Notifications.instrument 'search_query.chewy',
956
+ request: request_body, indexes: _indexes, types: _types,
957
+ index: _indexes.one? ? _indexes.first : _indexes,
958
+ type: _types.one? ? _types.first : _types do
959
+ begin
960
+ Chewy.client.search(request_body)
961
+ rescue Elasticsearch::Transport::Transport::Errors::NotFound
962
+ {}
963
+ end
964
+ end
965
+ end
966
+
967
+ def raw_limit_value
968
+ parameters[:limit].value
969
+ end
970
+
971
+ def raw_offset_value
972
+ parameters[:offset].value
973
+ end
974
+
975
+ def index_names
976
+ @index_names ||= _indexes.map(&:index_name).uniq
977
+ end
978
+
979
+ def type_names
980
+ @type_names ||= if parameters[:types].value.present?
981
+ _types.map(&:type_name).uniq & parameters[:types].value
982
+ else
983
+ _types.map(&:type_name).uniq
984
+ end
985
+ end
986
+
987
+ def render_base
988
+ @render_base ||= {index: index_names, type: type_names, body: {}}
989
+ end
990
+
991
+ def delete_by_query_plugin(request)
992
+ path = Elasticsearch::API::Utils.__pathify(
993
+ Elasticsearch::API::Utils.__listify(request[:index]),
994
+ Elasticsearch::API::Utils.__listify(request[:type]),
995
+ '_query'
996
+ )
997
+ Chewy.client.perform_request(Elasticsearch::API::HTTP_DELETE, path, {}, request[:body]).body
998
+ end
999
+
1000
+ def loader
1001
+ @loader ||= Loader.new(indexes: @_indexes, **parameters[:load].value)
1002
+ end
1003
+
1004
+ def fetch_field(hit, field)
1005
+ if EVERFIELDS.include?(field)
1006
+ hit[field]
1007
+ else
1008
+ hit.fetch('_source', {})[field]
1009
+ end
1010
+ end
1011
+
1012
+ def performed?
1013
+ !@response.nil?
1014
+ end
1015
+
1016
+ def collection_paginator
1017
+ method(:paginated_collection).to_proc if respond_to?(:paginated_collection, true)
1018
+ end
1019
+ end
1020
+ end
1021
+ end