chewy 0.8.4 → 5.1.0

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