couchbase 3.5.0-x86_64-darwin-20

Sign up to get free protection for your applications and to get access to all the features.
Files changed (126) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE.txt +202 -0
  3. data/README.md +154 -0
  4. data/ext/extconf.rb +0 -0
  5. data/lib/active_support/cache/couchbase_store.rb +339 -0
  6. data/lib/couchbase/3.0/libcouchbase.bundle +0 -0
  7. data/lib/couchbase/3.1/libcouchbase.bundle +0 -0
  8. data/lib/couchbase/3.2/libcouchbase.bundle +0 -0
  9. data/lib/couchbase/3.3/libcouchbase.bundle +0 -0
  10. data/lib/couchbase/analytics_options.rb +107 -0
  11. data/lib/couchbase/authenticator.rb +64 -0
  12. data/lib/couchbase/binary_collection.rb +128 -0
  13. data/lib/couchbase/binary_collection_options.rb +24 -0
  14. data/lib/couchbase/bucket.rb +144 -0
  15. data/lib/couchbase/cluster.rb +460 -0
  16. data/lib/couchbase/cluster_registry.rb +49 -0
  17. data/lib/couchbase/collection.rb +705 -0
  18. data/lib/couchbase/collection_options.rb +399 -0
  19. data/lib/couchbase/config_profiles.rb +55 -0
  20. data/lib/couchbase/configuration.rb +56 -0
  21. data/lib/couchbase/datastructures/couchbase_list.rb +160 -0
  22. data/lib/couchbase/datastructures/couchbase_map.rb +194 -0
  23. data/lib/couchbase/datastructures/couchbase_queue.rb +134 -0
  24. data/lib/couchbase/datastructures/couchbase_set.rb +128 -0
  25. data/lib/couchbase/datastructures.rb +24 -0
  26. data/lib/couchbase/diagnostics.rb +181 -0
  27. data/lib/couchbase/errors.rb +376 -0
  28. data/lib/couchbase/json_transcoder.rb +39 -0
  29. data/lib/couchbase/key_value_scan.rb +117 -0
  30. data/lib/couchbase/libcouchbase.rb +6 -0
  31. data/lib/couchbase/logger.rb +85 -0
  32. data/lib/couchbase/management/analytics_index_manager.rb +1127 -0
  33. data/lib/couchbase/management/bucket_manager.rb +443 -0
  34. data/lib/couchbase/management/collection_manager.rb +470 -0
  35. data/lib/couchbase/management/collection_query_index_manager.rb +222 -0
  36. data/lib/couchbase/management/query_index_manager.rb +617 -0
  37. data/lib/couchbase/management/scope_search_index_manager.rb +198 -0
  38. data/lib/couchbase/management/search_index_manager.rb +424 -0
  39. data/lib/couchbase/management/user_manager.rb +468 -0
  40. data/lib/couchbase/management/view_index_manager.rb +237 -0
  41. data/lib/couchbase/management.rb +29 -0
  42. data/lib/couchbase/mutation_state.rb +63 -0
  43. data/lib/couchbase/options.rb +2837 -0
  44. data/lib/couchbase/protostellar/binary_collection.rb +55 -0
  45. data/lib/couchbase/protostellar/bucket.rb +51 -0
  46. data/lib/couchbase/protostellar/client.rb +99 -0
  47. data/lib/couchbase/protostellar/cluster.rb +163 -0
  48. data/lib/couchbase/protostellar/collection.rb +152 -0
  49. data/lib/couchbase/protostellar/connect_options.rb +63 -0
  50. data/lib/couchbase/protostellar/error_handling.rb +203 -0
  51. data/lib/couchbase/protostellar/generated/admin/bucket/v1/bucket_pb.rb +61 -0
  52. data/lib/couchbase/protostellar/generated/admin/bucket/v1/bucket_services_pb.rb +35 -0
  53. data/lib/couchbase/protostellar/generated/admin/collection/v1/collection_pb.rb +57 -0
  54. data/lib/couchbase/protostellar/generated/admin/collection/v1/collection_services_pb.rb +36 -0
  55. data/lib/couchbase/protostellar/generated/admin/query/v1/query_pb.rb +61 -0
  56. data/lib/couchbase/protostellar/generated/admin/query/v1/query_services_pb.rb +37 -0
  57. data/lib/couchbase/protostellar/generated/admin/search/v1/search_pb.rb +72 -0
  58. data/lib/couchbase/protostellar/generated/admin/search/v1/search_services_pb.rb +44 -0
  59. data/lib/couchbase/protostellar/generated/analytics/v1/analytics_pb.rb +52 -0
  60. data/lib/couchbase/protostellar/generated/analytics/v1/analytics_services_pb.rb +30 -0
  61. data/lib/couchbase/protostellar/generated/internal/hooks/v1/hooks_pb.rb +70 -0
  62. data/lib/couchbase/protostellar/generated/internal/hooks/v1/hooks_services_pb.rb +36 -0
  63. data/lib/couchbase/protostellar/generated/kv/v1/kv_pb.rb +97 -0
  64. data/lib/couchbase/protostellar/generated/kv/v1/kv_services_pb.rb +46 -0
  65. data/lib/couchbase/protostellar/generated/query/v1/query_pb.rb +57 -0
  66. data/lib/couchbase/protostellar/generated/query/v1/query_services_pb.rb +30 -0
  67. data/lib/couchbase/protostellar/generated/routing/v1/routing_pb.rb +52 -0
  68. data/lib/couchbase/protostellar/generated/routing/v1/routing_services_pb.rb +30 -0
  69. data/lib/couchbase/protostellar/generated/search/v1/search_pb.rb +99 -0
  70. data/lib/couchbase/protostellar/generated/search/v1/search_services_pb.rb +30 -0
  71. data/lib/couchbase/protostellar/generated/transactions/v1/transactions_pb.rb +57 -0
  72. data/lib/couchbase/protostellar/generated/transactions/v1/transactions_services_pb.rb +36 -0
  73. data/lib/couchbase/protostellar/generated/view/v1/view_pb.rb +51 -0
  74. data/lib/couchbase/protostellar/generated/view/v1/view_services_pb.rb +30 -0
  75. data/lib/couchbase/protostellar/generated.rb +9 -0
  76. data/lib/couchbase/protostellar/management/bucket_manager.rb +67 -0
  77. data/lib/couchbase/protostellar/management/collection_manager.rb +94 -0
  78. data/lib/couchbase/protostellar/management/collection_query_index_manager.rb +124 -0
  79. data/lib/couchbase/protostellar/management/query_index_manager.rb +112 -0
  80. data/lib/couchbase/protostellar/management.rb +24 -0
  81. data/lib/couchbase/protostellar/request.rb +78 -0
  82. data/lib/couchbase/protostellar/request_behaviour.rb +42 -0
  83. data/lib/couchbase/protostellar/request_generator/admin/bucket.rb +124 -0
  84. data/lib/couchbase/protostellar/request_generator/admin/collection.rb +94 -0
  85. data/lib/couchbase/protostellar/request_generator/admin/query.rb +130 -0
  86. data/lib/couchbase/protostellar/request_generator/admin.rb +24 -0
  87. data/lib/couchbase/protostellar/request_generator/kv.rb +474 -0
  88. data/lib/couchbase/protostellar/request_generator/query.rb +133 -0
  89. data/lib/couchbase/protostellar/request_generator/search.rb +387 -0
  90. data/lib/couchbase/protostellar/request_generator.rb +26 -0
  91. data/lib/couchbase/protostellar/response_converter/admin/bucket.rb +55 -0
  92. data/lib/couchbase/protostellar/response_converter/admin/collection.rb +42 -0
  93. data/lib/couchbase/protostellar/response_converter/admin/query.rb +59 -0
  94. data/lib/couchbase/protostellar/response_converter/admin.rb +24 -0
  95. data/lib/couchbase/protostellar/response_converter/kv.rb +151 -0
  96. data/lib/couchbase/protostellar/response_converter/query.rb +84 -0
  97. data/lib/couchbase/protostellar/response_converter/search.rb +136 -0
  98. data/lib/couchbase/protostellar/response_converter.rb +26 -0
  99. data/lib/couchbase/protostellar/retry/action.rb +38 -0
  100. data/lib/couchbase/protostellar/retry/orchestrator.rb +60 -0
  101. data/lib/couchbase/protostellar/retry/reason.rb +67 -0
  102. data/lib/couchbase/protostellar/retry/strategies/best_effort.rb +49 -0
  103. data/lib/couchbase/protostellar/retry/strategies.rb +26 -0
  104. data/lib/couchbase/protostellar/retry.rb +28 -0
  105. data/lib/couchbase/protostellar/scope.rb +57 -0
  106. data/lib/couchbase/protostellar/timeout_defaults.rb +30 -0
  107. data/lib/couchbase/protostellar/timeouts.rb +83 -0
  108. data/lib/couchbase/protostellar.rb +29 -0
  109. data/lib/couchbase/query_options.rb +120 -0
  110. data/lib/couchbase/railtie.rb +45 -0
  111. data/lib/couchbase/raw_binary_transcoder.rb +37 -0
  112. data/lib/couchbase/raw_json_transcoder.rb +38 -0
  113. data/lib/couchbase/raw_string_transcoder.rb +40 -0
  114. data/lib/couchbase/scope.rb +256 -0
  115. data/lib/couchbase/search_options.rb +1622 -0
  116. data/lib/couchbase/subdoc.rb +290 -0
  117. data/lib/couchbase/transcoder_flags.rb +62 -0
  118. data/lib/couchbase/utils/generic_logger_adapter.rb +38 -0
  119. data/lib/couchbase/utils/stdlib_logger_adapter.rb +65 -0
  120. data/lib/couchbase/utils/time.rb +69 -0
  121. data/lib/couchbase/utils.rb +21 -0
  122. data/lib/couchbase/version.rb +23 -0
  123. data/lib/couchbase/view_options.rb +65 -0
  124. data/lib/couchbase.rb +28 -0
  125. data/lib/rails/generators/couchbase/config/config_generator.rb +27 -0
  126. metadata +191 -0
@@ -0,0 +1,1622 @@
1
+ # Copyright 2020-2021 Couchbase, Inc.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ module Couchbase
16
+ class SearchRequest
17
+ # Creates a search request, used to perform operations against the Full Text Search (FTS) Couchbase service.
18
+ #
19
+ # @overload new(search_query)
20
+ # Will run an FTS +SearchQuery+
21
+ # @param [SearchQuery] search_query
22
+ #
23
+ # @overload new(vector_search)
24
+ # Will run a +VectorSearch+
25
+ # @param [VectorSearch] vector_search
26
+ #
27
+ # @!macro uncommitted
28
+ def initialize(search)
29
+ case search
30
+ when SearchQuery
31
+ @search_query = search
32
+ when VectorSearch
33
+ @vector_search = search
34
+ else
35
+ raise Error::InvalidArgument, "Search type must be either SearchQuery or VectorSearch, #{search.class} given"
36
+ end
37
+ end
38
+
39
+ # Can be used to run a +SearchQuery+ together with an existing +VectorSearch+
40
+ # Note that a maximum of one +SearchQuery+ can be provided.
41
+ #
42
+ # @param [SearchQuery] query
43
+ #
44
+ # @return [SearchRequest] for chaining purposes
45
+ def search_query(query)
46
+ raise Error::InvalidArgument, "A SearchQuery has already been specified" unless @search_query.nil?
47
+
48
+ @search_query = query
49
+ self
50
+ end
51
+
52
+ # Can be used to run a +VectorSearch+ together with an existing +SearchQuery+
53
+ # Note that a maximum of one +VectorSearch+ can be provided.
54
+ #
55
+ # @param [VectorSearch] query
56
+ #
57
+ # @return [SearchRequest] for chaining purposes
58
+ #
59
+ # @!macro uncommitted
60
+ def vector_search(query)
61
+ raise Error::InvalidArgument, "A VectorSearch has already been specified" unless @vector_search.nil?
62
+
63
+ @vector_search = query
64
+ self
65
+ end
66
+
67
+ # @api private
68
+ def to_backend
69
+ [
70
+ (@search_query || SearchQuery.match_none).to_json,
71
+ {
72
+ vector_search: @vector_search&.to_backend,
73
+ },
74
+ ]
75
+ end
76
+ end
77
+
78
+ class SearchQuery
79
+ # @return [Hash<Symbol, #to_json>]
80
+ def to_h
81
+ {}
82
+ end
83
+
84
+ # @return [String]
85
+ def to_json(*args)
86
+ to_h.to_json(*args)
87
+ end
88
+
89
+ # Prepare {MatchQuery} body
90
+ #
91
+ # @param [String] match
92
+ # @yieldparam [MatchQuery] query
93
+ #
94
+ # @return [MatchQuery]
95
+ def self.match(match, &block)
96
+ MatchQuery.new(match, &block)
97
+ end
98
+
99
+ # A match query analyzes the input text and uses that analyzed text to query the index.
100
+ class MatchQuery < SearchQuery
101
+ # @return [Float]
102
+ attr_accessor :boost
103
+
104
+ # @return [String]
105
+ attr_accessor :field
106
+
107
+ # @return [String]
108
+ attr_accessor :analyzer
109
+
110
+ # @return [Integer]
111
+ attr_accessor :prefix_length
112
+
113
+ # @return [Integer]
114
+ attr_accessor :fuzziness
115
+
116
+ # @return [nil, :or, :and]
117
+ attr_accessor :operator
118
+
119
+ # @param [String] match
120
+ # @yieldparam [MatchQuery] self
121
+ def initialize(match)
122
+ super()
123
+ @match = match
124
+ yield self if block_given?
125
+ end
126
+
127
+ # @return [Hash<Symbol, #to_json>]
128
+ def to_h
129
+ data = {:match => @match}
130
+ data[:boost] = boost if boost
131
+ data[:field] = field if field
132
+ data[:analyzer] = analyzer if analyzer
133
+ data[:operator] = operator if operator
134
+ data[:fuzziness] = fuzziness if fuzziness
135
+ data[:prefix_length] = prefix_length if prefix_length
136
+ data
137
+ end
138
+ end
139
+
140
+ # Prepare {MatchPhraseQuery} body
141
+ #
142
+ # @param [String] match_phrase
143
+ # @yieldparam [MatchPhraseQuery] query
144
+ #
145
+ # @return [MatchPhraseQuery]
146
+ def self.match_phrase(match_phrase, &block)
147
+ MatchPhraseQuery.new(match_phrase, &block)
148
+ end
149
+
150
+ # The input text is analyzed and a phrase query is built with the terms resulting from the analysis.
151
+ class MatchPhraseQuery < SearchQuery
152
+ # @return [Float]
153
+ attr_accessor :boost
154
+
155
+ # @return [String]
156
+ attr_accessor :field
157
+
158
+ # @return [String]
159
+ attr_accessor :analyzer
160
+
161
+ # @param [String] match_phrase
162
+ #
163
+ # @yieldparam [MatchPhraseQuery] self
164
+ def initialize(match_phrase)
165
+ super()
166
+ @match_phrase = match_phrase
167
+ yield self if block_given?
168
+ end
169
+
170
+ # @return [Hash<Symbol, #to_json>]
171
+ def to_h
172
+ data = {:match_phrase => @match_phrase}
173
+ data[:boost] = boost if boost
174
+ data[:field] = field if field
175
+ data[:analyzer] = analyzer if analyzer
176
+ data
177
+ end
178
+ end
179
+
180
+ # Prepare {RegexpQuery} body
181
+ #
182
+ # @param [String] regexp
183
+ # @yieldparam [RegexpQuery] query
184
+ #
185
+ # @return [RegexpQuery]
186
+ def self.regexp(regexp, &block)
187
+ RegexpQuery.new(regexp, &block)
188
+ end
189
+
190
+ # Finds documents containing terms that match the specified regular expression.
191
+ class RegexpQuery < SearchQuery
192
+ # @return [Float]
193
+ attr_accessor :boost
194
+
195
+ # @return [String]
196
+ attr_accessor :field
197
+
198
+ # @param [String] regexp
199
+ #
200
+ # @yieldparam [RegexpQuery] self
201
+ def initialize(regexp)
202
+ super()
203
+ @regexp = regexp
204
+ yield self if block_given?
205
+ end
206
+
207
+ # @return [Hash<Symbol, #to_json>]
208
+ def to_h
209
+ data = {:regexp => @regexp}
210
+ data[:boost] = boost if boost
211
+ data[:field] = field if field
212
+ data
213
+ end
214
+ end
215
+
216
+ # Prepare {QueryStringQuery} body
217
+ #
218
+ # @param [String] query_string
219
+ # @yieldparam [QueryStringQuery] query
220
+ #
221
+ # @return [QueryStringQuery]
222
+ def self.query_string(query_string, &block)
223
+ QueryStringQuery.new(query_string, &block)
224
+ end
225
+
226
+ # The query string query allows humans to describe complex queries using a simple syntax.
227
+ class QueryStringQuery < SearchQuery
228
+ # @return [Float]
229
+ attr_accessor :boost
230
+
231
+ # @param [String] query_string
232
+ #
233
+ # @yieldparam [QueryStringQuery] self
234
+ def initialize(query_string)
235
+ super()
236
+ @query_string = query_string
237
+ yield self if block_given?
238
+ end
239
+
240
+ # @return [Hash<Symbol, #to_json>]
241
+ def to_h
242
+ data = {:query => @query_string}
243
+ data[:boost] = boost if boost
244
+ data
245
+ end
246
+ end
247
+
248
+ # Prepare {WildcardQuery} body
249
+ #
250
+ # @param [String] wildcard
251
+ # @yieldparam [WildcardQuery] query
252
+ #
253
+ # @return [WildcardQuery]
254
+ def self.wildcard(wildcard, &block)
255
+ WildcardQuery.new(wildcard, &block)
256
+ end
257
+
258
+ # Interprets * and ? wildcards as found in a lot of applications, for an easy implementation of such a search feature.
259
+ class WildcardQuery < SearchQuery
260
+ # @return [Float]
261
+ attr_accessor :boost
262
+
263
+ # @return [String]
264
+ attr_accessor :field
265
+
266
+ # @param [String] wildcard
267
+ #
268
+ # @yieldparam [WildcardQuery] self
269
+ def initialize(wildcard)
270
+ super()
271
+ @wildcard = wildcard
272
+ yield self if block_given?
273
+ end
274
+
275
+ # @return [Hash<Symbol, #to_json>]
276
+ def to_h
277
+ data = {:wildcard => @wildcard}
278
+ data[:boost] = boost if boost
279
+ data[:field] = field if field
280
+ data
281
+ end
282
+ end
283
+
284
+ # Prepare {DocIdQuery} body
285
+ #
286
+ # @param [String...] doc_ids
287
+ # @yieldparam [DocIdQuery] query
288
+ #
289
+ # @return [DocIdQuery]
290
+ def self.doc_id(*doc_ids)
291
+ DocIdQuery.new(*doc_ids)
292
+ end
293
+
294
+ # Allows to restrict matches to a set of specific documents.
295
+ class DocIdQuery < SearchQuery
296
+ # @return [Float]
297
+ attr_accessor :boost
298
+
299
+ # @param [String...] doc_ids
300
+ #
301
+ # @yieldparam [DocIdQuery] self
302
+ def initialize(*doc_ids)
303
+ super()
304
+ @doc_ids = doc_ids
305
+ yield self if block_given?
306
+ end
307
+
308
+ # @return [Hash<Symbol, #to_json>]
309
+ def to_h
310
+ data = {:ids => @doc_ids.flatten.uniq}
311
+ data[:boost] = boost if boost
312
+ data
313
+ end
314
+ end
315
+
316
+ # Prepare {BooleanFieldQuery} body
317
+ #
318
+ # @param [Boolean] value
319
+ # @yieldparam [BooleanFieldQuery] query
320
+ #
321
+ # @return [BooleanFieldQuery]
322
+ def self.boolean_field(value)
323
+ BooleanFieldQuery.new(value)
324
+ end
325
+
326
+ # Allow to match `true`/`false` in a field mapped as boolean.
327
+ class BooleanFieldQuery < SearchQuery
328
+ # @return [Float]
329
+ attr_accessor :boost
330
+
331
+ # @return [String]
332
+ attr_accessor :field
333
+
334
+ # @param [Boolean] value
335
+ #
336
+ # @yieldparam [BooleanFieldQuery] self
337
+ def initialize(value)
338
+ super()
339
+ @value = value
340
+ yield self if block_given?
341
+ end
342
+
343
+ # @return [Hash<Symbol, #to_json>]
344
+ def to_h
345
+ data = {:bool => @value}
346
+ data[:boost] = boost if boost
347
+ data[:field] = field if field
348
+ data
349
+ end
350
+ end
351
+
352
+ # Prepare {DateRangeQuery} body
353
+ #
354
+ # @yieldparam [DateRangeQuery] query
355
+ #
356
+ # @return [DateRangeQuery]
357
+ def self.date_range(&block)
358
+ DateRangeQuery.new(&block)
359
+ end
360
+
361
+ # The date range query finds documents containing a date value in the specified field within the specified range.
362
+ class DateRangeQuery < SearchQuery
363
+ # @return [Float]
364
+ attr_accessor :boost
365
+
366
+ # @return [String]
367
+ attr_accessor :field
368
+
369
+ # @return [String]
370
+ attr_accessor :date_time_parser
371
+
372
+ # Sets the lower boundary of the range.
373
+ #
374
+ # @note The lower boundary is considered inclusive by default on the server side.
375
+ #
376
+ # @param [Time, String] time_point start time. When +Time+ object is passed {#date_time_parser} must be +nil+ (to use server
377
+ # default)
378
+ # @param [Boolean] inclusive
379
+ def start_time(time_point, inclusive = nil)
380
+ @start_time = time_point
381
+ @start_inclusive = inclusive
382
+ end
383
+
384
+ # Sets the upper boundary of the range.
385
+ #
386
+ # @note The upper boundary is considered exclusive by default on the server side.
387
+ #
388
+ # @param [Time, String] time_point end time. When +Time+ object is passed {#date_time_parser} must be +nil+ (to use server default)
389
+ # @param [Boolean] inclusive
390
+ def end_time(time_point, inclusive = nil)
391
+ @end_time = time_point
392
+ @end_inclusive = inclusive
393
+ end
394
+
395
+ # @yieldparam [DateRangeQuery] self
396
+ def initialize
397
+ super
398
+ @start_time = nil
399
+ @start_inclusive = nil
400
+ @end_time = nil
401
+ @end_inclusive = nil
402
+ yield self if block_given?
403
+ end
404
+
405
+ DATE_FORMAT_RFC3339 = "%Y-%m-%dT%H:%M:%S%:z".freeze
406
+
407
+ # @return [Hash<Symbol, #to_json>]
408
+ def to_h
409
+ raise ArgumentError, "either start_time or end_time must be set for DateRangeQuery" if @start_time.nil? && @end_time.nil?
410
+
411
+ data = {}
412
+ data[:boost] = boost if boost
413
+ data[:field] = field if field
414
+ data[:datetime_parser] = date_time_parser if date_time_parser
415
+ if @start_time
416
+ data[:start] = if @start_time.respond_to?(:strftime)
417
+ @start_time.strftime(DATE_FORMAT_RFC3339)
418
+ else
419
+ @start_time
420
+ end
421
+ data[:inclusive_start] = @start_inclusive unless @start_inclusive.nil?
422
+ end
423
+ if @end_time
424
+ data[:end] = if @end_time.respond_to?(:strftime)
425
+ @end_time.strftime(DATE_FORMAT_RFC3339)
426
+ else
427
+ @end_time
428
+ end
429
+ data[:inclusive_end] = @end_inclusive unless @end_inclusive.nil?
430
+ end
431
+ data
432
+ end
433
+ end
434
+
435
+ # Prepare {NumericRangeQuery} body
436
+ #
437
+ # @yieldparam [NumericRangeQuery] query
438
+ #
439
+ # @return [NumericRangeQuery]
440
+ def self.numeric_range(&block)
441
+ NumericRangeQuery.new(&block)
442
+ end
443
+
444
+ # The numeric range query finds documents containing a numeric value in the specified field within the specified range.
445
+ class NumericRangeQuery < SearchQuery
446
+ # @return [Float]
447
+ attr_accessor :boost
448
+
449
+ # @return [String]
450
+ attr_accessor :field
451
+
452
+ # Sets lower bound of the range.
453
+ #
454
+ # The lower boundary is considered inclusive by default on the server side.
455
+ #
456
+ # @param [Numeric] lower_bound
457
+ # @param [Boolean] inclusive
458
+ def min(lower_bound, inclusive = nil)
459
+ @min = lower_bound
460
+ @min_inclusive = inclusive
461
+ end
462
+
463
+ # Sets upper bound of the range.
464
+ #
465
+ # The upper boundary is considered exclusive by default on the server side.
466
+ #
467
+ # @param [Numeric] upper_bound
468
+ # @param [Boolean] inclusive
469
+ def max(upper_bound, inclusive = nil)
470
+ @max = upper_bound
471
+ @max_inclusive = inclusive
472
+ end
473
+
474
+ # @yieldparam [NumericRangeQuery] self
475
+ def initialize
476
+ super
477
+ @min = nil
478
+ @min_inclusive = nil
479
+ @max = nil
480
+ @max_inclusive = nil
481
+ yield self if block_given?
482
+ end
483
+
484
+ # @return [Hash<Symbol, #to_json>]
485
+ def to_h
486
+ raise ArgumentError, "either min or max must be set for NumericRangeQuery" if @min.nil? && @max.nil?
487
+
488
+ data = {}
489
+ data[:boost] = boost if boost
490
+ data[:field] = field if field
491
+ if @min
492
+ data[:min] = @min
493
+ data[:inclusive_min] = @min_inclusive unless @min_inclusive.nil?
494
+ end
495
+ if @max
496
+ data[:max] = @max
497
+ data[:inclusive_max] = @max_inclusive unless @max_inclusive.nil?
498
+ end
499
+ data
500
+ end
501
+ end
502
+
503
+ # Prepare {TermRangeQuery} body
504
+ #
505
+ # @yieldparam [TermRangeQuery] query
506
+ #
507
+ # @return [TermRangeQuery]
508
+ def self.term_range(&block)
509
+ TermRangeQuery.new(&block)
510
+ end
511
+
512
+ # The term range query finds documents containing a string value in the specified field within the specified range.
513
+ class TermRangeQuery < SearchQuery
514
+ # @return [Float]
515
+ attr_accessor :boost
516
+
517
+ # @return [String]
518
+ attr_accessor :field
519
+
520
+ # Sets lower bound of the range.
521
+ #
522
+ # The lower boundary is considered inclusive by default on the server side.
523
+ #
524
+ # @param [String] lower_bound
525
+ # @param [Boolean] inclusive
526
+ def min(lower_bound, inclusive = nil)
527
+ @min = lower_bound
528
+ @min_inclusive = inclusive
529
+ end
530
+
531
+ # Sets upper bound of the range.
532
+ #
533
+ # The upper boundary is considered exclusive by default on the server side.
534
+ #
535
+ # @param [String] upper_bound
536
+ # @param [Boolean] inclusive
537
+ def max(upper_bound, inclusive = nil)
538
+ @max = upper_bound
539
+ @max_inclusive = inclusive
540
+ end
541
+
542
+ # @yieldparam [TermRangeQuery] self
543
+ def initialize
544
+ super
545
+ @min = nil
546
+ @min_inclusive = nil
547
+ @max = nil
548
+ @max_inclusive = nil
549
+ yield self if block_given?
550
+ end
551
+
552
+ # @return [Hash<Symbol, #to_json>]
553
+ def to_h
554
+ raise ArgumentError, "either min or max must be set for TermRangeQuery" if @min.nil? && @max.nil?
555
+
556
+ data = {}
557
+ data[:boost] = boost if boost
558
+ data[:field] = field if field
559
+ if @min
560
+ data[:min] = @min
561
+ data[:inclusive_min] = @min_inclusive unless @min_inclusive.nil?
562
+ end
563
+ if @max
564
+ data[:max] = @max
565
+ data[:inclusive_max] = @max_inclusive unless @max_inclusive.nil?
566
+ end
567
+ data
568
+ end
569
+ end
570
+
571
+ # Prepare {GeoDistanceQuery} body
572
+ #
573
+ # @yieldparam [GeoDistanceQuery] query
574
+ #
575
+ # @param [Float] latitude location latitude
576
+ # @param [Float] longitude location longitude
577
+ # @param [String] distance how big is area (number with units)
578
+ #
579
+ # @return [GeoDistanceQuery]
580
+ def self.geo_distance(longitude, latitude, distance, &block)
581
+ GeoDistanceQuery.new(longitude, latitude, distance, &block)
582
+ end
583
+
584
+ # Finds `geopoint` indexed matches around a point with the given distance.
585
+ class GeoDistanceQuery < SearchQuery
586
+ # @return [Float]
587
+ attr_accessor :boost
588
+
589
+ # @return [String]
590
+ attr_accessor :field
591
+
592
+ # @yieldparam [GeoDistanceQuery] self
593
+ # @param [Float] longitude
594
+ # @param [Float] latitude
595
+ # @param [Float] distance
596
+ def initialize(longitude, latitude, distance)
597
+ super()
598
+ @longitude = longitude
599
+ @latitude = latitude
600
+ @distance = distance
601
+ yield self if block_given?
602
+ end
603
+
604
+ # @return [Hash<Symbol, #to_json>]
605
+ def to_h
606
+ data = {
607
+ :location => [@longitude, @latitude],
608
+ :distance => @distance,
609
+ }
610
+ data[:boost] = boost if boost
611
+ data[:field] = field if field
612
+ data
613
+ end
614
+ end
615
+
616
+ # Prepare {GeoBoundingBoxQuery} body
617
+ #
618
+ # @yieldparam [GeoBoundingBoxQuery] query
619
+ #
620
+ # @param [Float] top_left_longitude
621
+ # @param [Float] top_left_latitude
622
+ # @param [Float] bottom_right_longitude
623
+ # @param [Float] bottom_right_latitude
624
+ #
625
+ # @return [GeoBoundingBoxQuery]
626
+ def self.geo_bounding_box(top_left_longitude, top_left_latitude, bottom_right_longitude, bottom_right_latitude, &block)
627
+ GeoBoundingBoxQuery.new(top_left_longitude, top_left_latitude, bottom_right_longitude, bottom_right_latitude, &block)
628
+ end
629
+
630
+ # Finds `geopoint` indexed matches in a given bounding box.
631
+ class GeoBoundingBoxQuery < SearchQuery
632
+ # @return [Float]
633
+ attr_accessor :boost
634
+
635
+ # @return [String]
636
+ attr_accessor :field
637
+
638
+ # @yieldparam [GeoBoundingBoxQuery] self
639
+ #
640
+ # @param [Float] top_left_longitude
641
+ # @param [Float] top_left_latitude
642
+ # @param [Float] bottom_right_longitude
643
+ # @param [Float] bottom_right_latitude
644
+ def initialize(top_left_longitude, top_left_latitude, bottom_right_longitude, bottom_right_latitude)
645
+ super()
646
+ @top_left_longitude = top_left_longitude
647
+ @top_left_latitude = top_left_latitude
648
+ @bottom_right_longitude = bottom_right_longitude
649
+ @bottom_right_latitude = bottom_right_latitude
650
+ yield self if block_given?
651
+ end
652
+
653
+ # @return [Hash<Symbol, #to_json>]
654
+ def to_h
655
+ data = {
656
+ :top_left => [@top_left_longitude, @top_left_latitude],
657
+ :bottom_right => [@bottom_right_longitude, @bottom_right_latitude],
658
+ }
659
+ data[:boost] = boost if boost
660
+ data[:field] = field if field
661
+ data
662
+ end
663
+ end
664
+
665
+ # A coordinate is a tuple of a latitude and a longitude.
666
+ #
667
+ # @see GeoPolygonQuery
668
+ # @see SearchQuery.geo_polygon
669
+ class Coordinate
670
+ # @return [Float]
671
+ attr_accessor :longitude
672
+
673
+ # @return [Float]
674
+ attr_accessor :latitude
675
+
676
+ def initialize(longitude, latitude)
677
+ @longitude = longitude
678
+ @latitude = latitude
679
+ end
680
+ end
681
+
682
+ # Prepare {GeoPolygonQuery} body
683
+ #
684
+ # @yieldparam [GeoPolygonQuery] query
685
+ #
686
+ # @param [Array<Coordinate>] coordinates list of coordinates that forms polygon
687
+ #
688
+ # @return [GeoPolygonQuery]
689
+ #
690
+ # @!macro uncommitted
691
+ def self.geo_polygon(coordinates, &block)
692
+ GeoPolygonQuery.new(coordinates, &block)
693
+ end
694
+
695
+ # A search query which allows to match inside a geo polygon.
696
+ class GeoPolygonQuery < SearchQuery
697
+ # @return [Float]
698
+ attr_accessor :boost
699
+
700
+ # @return [String]
701
+ attr_accessor :field
702
+
703
+ # @yieldparam [GeoPolygonQuery] self
704
+ #
705
+ # @param [Array<Coordinate>] coordinates list of coordinates that forms polygon
706
+ def initialize(coordinates)
707
+ super()
708
+ @coordinates = coordinates
709
+ yield self if block_given?
710
+ end
711
+
712
+ # @return [Hash<Symbol, #to_json>]
713
+ def to_h
714
+ data = {
715
+ :polygon_points => @coordinates.map { |coord| [coord.longitude, coord.latitude] },
716
+ }
717
+ data[:boost] = boost if boost
718
+ data[:field] = field if field
719
+ data
720
+ end
721
+ end
722
+
723
+ # Prepare {ConjunctionQuery} body
724
+ #
725
+ # @yieldparam [ConjunctionQuery] query
726
+ #
727
+ # @return [ConjunctionQuery]
728
+ def self.conjuncts(...)
729
+ ConjunctionQuery.new(...)
730
+ end
731
+
732
+ # Result documents must satisfy all of the child queries.
733
+ class ConjunctionQuery < SearchQuery
734
+ # @return [Float]
735
+ attr_accessor :boost
736
+
737
+ # @yieldparam [ConjunctionQuery] self
738
+ #
739
+ # @param [*SearchQuery] queries
740
+ def initialize(*queries)
741
+ super()
742
+ @queries = queries.flatten
743
+ yield self if block_given?
744
+ end
745
+
746
+ # @param [*SearchQuery] queries
747
+ def and_also(*queries)
748
+ @queries |= queries.flatten
749
+ end
750
+
751
+ def empty?
752
+ @queries.empty?
753
+ end
754
+
755
+ # @return [Hash<Symbol, #to_json>]
756
+ def to_h
757
+ raise ArgumentError, "compound conjunction query must have sub-queries" if @queries.nil? || @queries.empty?
758
+
759
+ data = {:conjuncts => @queries.uniq.map(&:to_h)}
760
+ data[:boost] = boost if boost
761
+ data
762
+ end
763
+ end
764
+
765
+ # Prepare {ConjunctionQuery} body
766
+ #
767
+ # @yieldparam [DisjunctionQuery] query
768
+ #
769
+ # @return [DisjunctionQuery]
770
+ def self.disjuncts(...)
771
+ DisjunctionQuery.new(...)
772
+ end
773
+
774
+ # Result documents must satisfy a configurable min number of child queries.
775
+ class DisjunctionQuery < SearchQuery
776
+ # @return [Float]
777
+ attr_accessor :boost
778
+
779
+ # @return [Integer]
780
+ attr_accessor :min
781
+
782
+ # @yieldparam [DisjunctionQuery] self
783
+ #
784
+ # @param [*SearchQuery] queries
785
+ def initialize(*queries)
786
+ super()
787
+ @queries = queries.flatten
788
+ yield self if block_given?
789
+ end
790
+
791
+ # @param [*SearchQuery] queries
792
+ def or_else(*queries)
793
+ @queries |= queries.flatten
794
+ end
795
+
796
+ def empty?
797
+ @queries.empty?
798
+ end
799
+
800
+ # @return [Hash<Symbol, #to_json>]
801
+ def to_h
802
+ raise ArgumentError, "compound disjunction query must have sub-queries" if @queries.nil? || @queries.empty?
803
+
804
+ data = {:disjuncts => @queries.uniq.map(&:to_h)}
805
+ if min
806
+ raise ArgumentError, "disjunction query has fewer sub-queries than configured minimum" if @queries.size < min
807
+
808
+ data[:min] = min
809
+ end
810
+ data[:boost] = boost if boost
811
+ data
812
+ end
813
+ end
814
+
815
+ # Prepare {BooleanQuery} body
816
+ #
817
+ # @yieldparam [BooleanQuery] query
818
+ #
819
+ # @return [BooleanQuery]
820
+ def self.booleans(&block)
821
+ BooleanQuery.new(&block)
822
+ end
823
+
824
+ # The boolean query is a useful combination of conjunction and disjunction queries.
825
+ class BooleanQuery < SearchQuery
826
+ # @return [Float]
827
+ attr_accessor :boost
828
+
829
+ # @yieldparam [BooleanQuery] self
830
+ def initialize
831
+ super()
832
+ @must = ConjunctionQuery.new
833
+ @must_not = DisjunctionQuery.new
834
+ @should = DisjunctionQuery.new
835
+ yield self if block_given?
836
+ end
837
+
838
+ # @param [Integer] min minimal value for "should" disjunction query
839
+ def should_min(min)
840
+ @should.min = min
841
+ self
842
+ end
843
+
844
+ # @param [*SearchQuery] queries
845
+ def must(*queries)
846
+ @must.and_also(*queries)
847
+ self
848
+ end
849
+
850
+ # @param [*SearchQuery] queries
851
+ def must_not(*queries)
852
+ @must_not.or_else(*queries)
853
+ self
854
+ end
855
+
856
+ # @param [*SearchQuery] queries
857
+ def should(*queries)
858
+ @should.or_else(*queries)
859
+ self
860
+ end
861
+
862
+ # @return [Hash<Symbol, #to_json>]
863
+ def to_h
864
+ raise ArgumentError, "BooleanQuery must have at least one non-empty sub-query" if @must.empty? && @must_not.empty? && @should.empty?
865
+
866
+ data = {}
867
+ data[:must] = @must.to_h unless @must.empty?
868
+ data[:must_not] = @must_not.to_h unless @must_not.empty?
869
+ data[:should] = @should.to_h unless @should.empty?
870
+ data[:boost] = boost if boost
871
+ data
872
+ end
873
+ end
874
+
875
+ # Prepare {TermQuery} body
876
+ #
877
+ # @yieldparam [TermQuery] query
878
+ # @param [String] term
879
+ #
880
+ # @return [TermQuery]
881
+ def self.term(term, &block)
882
+ TermQuery.new(term, &block)
883
+ end
884
+
885
+ # A query that looks for **exact** matches of the term in the index (no analyzer, no stemming). Useful to check what the actual
886
+ # content of the index is. It can also apply fuzziness on the term. Usual better alternative is `MatchQuery`.
887
+ class TermQuery < SearchQuery
888
+ # @return [Float]
889
+ attr_accessor :boost
890
+
891
+ # @return [String]
892
+ attr_accessor :field
893
+
894
+ # @return [Integer]
895
+ attr_accessor :fuzziness
896
+
897
+ # @return [Integer]
898
+ attr_accessor :prefix_length
899
+
900
+ # @yieldparam [TermQuery] self
901
+ #
902
+ # @param [String] term
903
+ def initialize(term)
904
+ super()
905
+ @term = term
906
+ yield self if block_given?
907
+ end
908
+
909
+ # @return [Hash<Symbol, #to_json>]
910
+ def to_h
911
+ data = {:term => @term}
912
+ data[:boost] = boost if boost
913
+ data[:field] = field if field
914
+ if fuzziness
915
+ data[:fuzziness] = fuzziness
916
+ data[:prefix_length] = prefix_length if prefix_length
917
+ end
918
+ data
919
+ end
920
+ end
921
+
922
+ # Prepare {PrefixQuery} body
923
+ #
924
+ # @yieldparam [PrefixQuery] query
925
+ # @param [String] prefix
926
+ #
927
+ # @return [PrefixQuery]
928
+ def self.prefix(prefix, &block)
929
+ PrefixQuery.new(prefix, &block)
930
+ end
931
+
932
+ # The prefix query finds documents containing terms that start with the provided prefix. Usual better alternative is `MatchQuery`.
933
+ class PrefixQuery < SearchQuery
934
+ # @return [Float]
935
+ attr_accessor :boost
936
+
937
+ # @return [nil, :or, :and]
938
+ attr_accessor :operator
939
+
940
+ # @return [String]
941
+ attr_accessor :field
942
+
943
+ # @yieldparam [PrefixQuery] self
944
+ #
945
+ # @param [String] prefix
946
+ def initialize(prefix)
947
+ super()
948
+ @prefix = prefix
949
+ yield self if block_given?
950
+ end
951
+
952
+ # @return [Hash<Symbol, #to_json>]
953
+ def to_h
954
+ data = {:prefix => @prefix}
955
+ data[:boost] = boost if boost
956
+ data[:field] = field if field
957
+ data
958
+ end
959
+ end
960
+
961
+ # Prepare {PhraseQuery} body
962
+ #
963
+ # Creates a new instances {PhraseQuery} passing all parameters into {PhraseQuery#initialize}.
964
+ #
965
+ # @return [PhraseQuery]
966
+ def self.phrase(...)
967
+ PhraseQuery.new(...)
968
+ end
969
+
970
+ # A query that looks for **exact** match of several terms (in the exact order) in the index. Usual better alternative is
971
+ # {MatchPhraseQuery}.
972
+ class PhraseQuery < SearchQuery
973
+ # @return [Float]
974
+ attr_accessor :boost
975
+
976
+ # @return [String]
977
+ attr_accessor :field
978
+
979
+ # @yieldparam [PhraseQuery] self
980
+ #
981
+ # @param [*String] terms
982
+ def initialize(*terms)
983
+ super()
984
+ @terms = terms.flatten
985
+ yield self if block_given?
986
+ end
987
+
988
+ # @return [Hash<Symbol, #to_json>]
989
+ def to_h
990
+ data = {:terms => @terms.flatten.uniq}
991
+ data[:boost] = boost if boost
992
+ data[:field] = field if field
993
+ data
994
+ end
995
+ end
996
+
997
+ # Prepare {MatchAllQuery} body
998
+ #
999
+ # @yieldparam [MatchAllQuery] query
1000
+ #
1001
+ # @return [MatchAllQuery]
1002
+ def self.match_all(&block)
1003
+ MatchAllQuery.new(&block)
1004
+ end
1005
+
1006
+ # A query that matches all indexed documents.
1007
+ class MatchAllQuery < SearchQuery
1008
+ # @yieldparam [MatchAllQuery] self
1009
+ def initialize
1010
+ super()
1011
+ yield self if block_given?
1012
+ end
1013
+
1014
+ # @return [Hash<Symbol, #to_json>]
1015
+ def to_h
1016
+ {:match_all => nil}
1017
+ end
1018
+ end
1019
+
1020
+ # Prepare {MatchNoneQuery} body
1021
+ #
1022
+ # @yieldparam [MatchNoneQuery] query
1023
+ #
1024
+ # @return [MatchNoneQuery]
1025
+ def self.match_none(&block)
1026
+ MatchNoneQuery.new(&block)
1027
+ end
1028
+
1029
+ # A query that matches nothing.
1030
+ class MatchNoneQuery < SearchQuery
1031
+ # @yieldparam [MatchNoneQuery] self
1032
+ def initialize
1033
+ super()
1034
+ yield self if block_given?
1035
+ end
1036
+
1037
+ # @return [Hash<Symbol, #to_json>]
1038
+ def to_h
1039
+ {:match_none => nil}
1040
+ end
1041
+ end
1042
+ end
1043
+
1044
+ # @!macro uncommitted
1045
+ class VectorSearch
1046
+ # Constructs a +VectorSearch+ instance, which allows one or more individual vector queries to be executed.
1047
+ #
1048
+ # @param [VectorQuery, Array<VectorQuery>] vector_queries vector queries/query to execute
1049
+ # @param [Options::VectorSearch] options
1050
+ def initialize(vector_queries, options = Options::VectorSearch::DEFAULT)
1051
+ @vector_queries = vector_queries.respond_to?(:each) ? vector_queries : [vector_queries]
1052
+ @options = options
1053
+ end
1054
+
1055
+ # @api private
1056
+ def to_backend
1057
+ {vector_queries: @vector_queries.map(&:to_h).to_json}.merge(@options.to_backend)
1058
+ end
1059
+ end
1060
+
1061
+ # @!macro uncommitted
1062
+ class VectorQuery
1063
+ # @return [Integer, nil]
1064
+ attr_accessor :num_candidates
1065
+
1066
+ # @return [Float, nil]
1067
+ attr_accessor :boost
1068
+
1069
+ # Constructs a +VectorQuery+ instance
1070
+ #
1071
+ # @param [String] vector_field_name the document field that contains the vector.
1072
+ # @param [Array<Float>] vector_query the vector query to run.
1073
+ #
1074
+ # @yieldparam [MatchPhraseQuery] self
1075
+ def initialize(vector_field_name, vector_query)
1076
+ @vector_field_name = vector_field_name
1077
+ @vector_query = vector_query
1078
+
1079
+ yield self if block_given?
1080
+ end
1081
+
1082
+ # @api private
1083
+ def to_h
1084
+ if !num_candidates.nil? && num_candidates < 1
1085
+ raise Error::InvalidArgument,
1086
+ "Number of candidates must be at least 1, #{num_candidates} given"
1087
+ end
1088
+
1089
+ {
1090
+ field: @vector_field_name,
1091
+ vector: @vector_query,
1092
+ k: num_candidates || 3,
1093
+ boost: boost,
1094
+ }.compact
1095
+ end
1096
+
1097
+ # @api private
1098
+ def to_json(*args)
1099
+ to_h.to_json(*args)
1100
+ end
1101
+ end
1102
+
1103
+ class SearchSort
1104
+ # @yieldparam [SearchSortScore]
1105
+ # @return [SearchSortScore]
1106
+ def self.score(&block)
1107
+ SearchSortScore.new(&block)
1108
+ end
1109
+
1110
+ # @yieldparam [SearchSortId]
1111
+ # @return [SearchSortScore]
1112
+ def self.id(&block)
1113
+ SearchSortId.new(&block)
1114
+ end
1115
+
1116
+ # @param [String] name field name
1117
+ # @yieldparam [SearchSortField]
1118
+ # @return [SearchSortField]
1119
+ def self.field(name, &block)
1120
+ SearchSortField.new(name, &block)
1121
+ end
1122
+
1123
+ # @param [String] name field name
1124
+ # @param [Float] longitude
1125
+ # @param [Float] latitude
1126
+ # @yieldparam [SearchSortField]
1127
+ # @return [SearchSortGeoDistance]
1128
+ def self.geo_distance(name, longitude, latitude, &block)
1129
+ SearchSortGeoDistance.new(name, longitude, latitude, &block)
1130
+ end
1131
+
1132
+ class SearchSortScore < SearchSort
1133
+ # @return [Boolean] if descending order should be applied
1134
+ attr_accessor :desc
1135
+
1136
+ # @yieldparam [SearchSortScore]
1137
+ def initialize
1138
+ super
1139
+ yield self if block_given?
1140
+ end
1141
+
1142
+ # @api private
1143
+ def to_json(*args)
1144
+ {by: :score, desc: desc}.to_json(*args)
1145
+ end
1146
+ end
1147
+
1148
+ class SearchSortId < SearchSort
1149
+ # @return [Boolean] if descending order should be applied
1150
+ attr_accessor :desc
1151
+
1152
+ # @yieldparam [SearchSortId]
1153
+ def initialize
1154
+ super
1155
+ yield self if block_given?
1156
+ end
1157
+
1158
+ # @api private
1159
+ def to_json(*args)
1160
+ {by: :id, desc: desc}.to_json(*args)
1161
+ end
1162
+ end
1163
+
1164
+ class SearchSortField < SearchSort
1165
+ # @return [String] name of the field to sort by
1166
+ attr_reader :field
1167
+
1168
+ # @return [Boolean] if descending order should be applied
1169
+ attr_accessor :desc
1170
+
1171
+ # @return [:auto, :string, :number, :date]
1172
+ attr_accessor :type
1173
+
1174
+ # @return [:first, :last] where the documents with missing field should be placed
1175
+ attr_accessor :missing
1176
+
1177
+ # @return [:default, :min, :max]
1178
+ attr_accessor :mode
1179
+
1180
+ # @param [String] field the name of the filed for ordering
1181
+ # @yieldparam [SearchSortField]
1182
+ def initialize(field)
1183
+ super()
1184
+ @field = field
1185
+ yield self if block_given?
1186
+ end
1187
+
1188
+ # @api private
1189
+ def to_json(*args)
1190
+ {by: :field, field: field, desc: desc, type: type, missing: missing, mode: mode}.to_json(*args)
1191
+ end
1192
+ end
1193
+
1194
+ class SearchSortGeoDistance < SearchSort
1195
+ # @return [String] name of the field to sort by
1196
+ attr_reader :field
1197
+
1198
+ # @return [Boolean] if descending order should be applied
1199
+ attr_accessor :desc
1200
+
1201
+ # @return [Float]
1202
+ attr_reader :longitude
1203
+
1204
+ # @return [Float]
1205
+ attr_reader :latitude
1206
+
1207
+ # @return [:meters, :miles, :centimeters, :millimeters, :kilometers, :nauticalmiles, :feet, :yards, :inch]
1208
+ attr_accessor :unit
1209
+
1210
+ # @param [String] field field name
1211
+ # @param [Float] longitude
1212
+ # @param [Float] latitude
1213
+ # @yieldparam [SearchSortGeoDistance]
1214
+ def initialize(field, longitude, latitude)
1215
+ super()
1216
+ @field = field
1217
+ @longitude = longitude
1218
+ @latitude = latitude
1219
+ yield self if block_given?
1220
+ end
1221
+
1222
+ # @api private
1223
+ def to_json(*args)
1224
+ {by: :geo_distance, field: field, desc: desc, location: [longitude, latitude], unit: unit}.to_json(*args)
1225
+ end
1226
+ end
1227
+ end
1228
+
1229
+ class SearchFacet
1230
+ # @param [String] field_name
1231
+ # @return [SearchFacetTerm]
1232
+ def self.term(field_name, &block)
1233
+ SearchFacetTerm.new(field_name, &block)
1234
+ end
1235
+
1236
+ # @param [String] field_name
1237
+ # @return [SearchFacetNumericRange]
1238
+ def self.numeric_range(field_name, &block)
1239
+ SearchFacetNumericRange.new(field_name, &block)
1240
+ end
1241
+
1242
+ # @param [String] field_name
1243
+ # @return [SearchFacetDateRange]
1244
+ def self.date_range(field_name, &block)
1245
+ SearchFacetDateRange.new(field_name, &block)
1246
+ end
1247
+
1248
+ class SearchFacetTerm
1249
+ # @return [String]
1250
+ attr_reader :field
1251
+
1252
+ # @return [Integer]
1253
+ attr_accessor :size
1254
+
1255
+ # @param [String] field name of the field
1256
+ def initialize(field)
1257
+ @field = field
1258
+ yield self if block_given?
1259
+ end
1260
+
1261
+ # @api private
1262
+ def to_json(*args)
1263
+ {field: field, size: size}.to_json(*args)
1264
+ end
1265
+ end
1266
+
1267
+ class SearchFacetNumericRange
1268
+ # @return [String]
1269
+ attr_reader :field
1270
+
1271
+ # @return [Integer]
1272
+ attr_accessor :size
1273
+
1274
+ # @param [String] field name of the field
1275
+ def initialize(field)
1276
+ @field = field
1277
+ @ranges = []
1278
+ yield self if block_given?
1279
+ end
1280
+
1281
+ # @param [String] name the name of the range
1282
+ # @param [Integer, Float, nil] min lower bound of the range (pass +nil+ if there is no lower bound)
1283
+ # @param [Integer, Float, nil] max upper bound of the range (pass +nil+ if there is no upper bound)
1284
+ def add(name, min, max)
1285
+ @ranges.append({name: name, min: min, max: max})
1286
+ end
1287
+
1288
+ # @api private
1289
+ def to_json(*args)
1290
+ {field: field, size: size, numeric_ranges: @ranges}.to_json(*args)
1291
+ end
1292
+ end
1293
+
1294
+ class SearchFacetDateRange
1295
+ # @return [String]
1296
+ attr_reader :field
1297
+
1298
+ # @return [Integer]
1299
+ attr_accessor :size
1300
+
1301
+ # @param [String] field name of the field
1302
+ def initialize(field)
1303
+ super()
1304
+ @field = field
1305
+ @ranges = []
1306
+ yield self if block_given?
1307
+ end
1308
+
1309
+ DATE_FORMAT_RFC3339 = "%Y-%m-%dT%H:%M:%S%:z".freeze
1310
+
1311
+ # @param [String] name the name of the range
1312
+ # @param [Time, String, nil] start_time lower bound of the range (pass +nil+ if there is no lower bound)
1313
+ # @param [Time, String, nil] end_time lower bound of the range (pass +nil+ if there is no lower bound)
1314
+ def add(name, start_time, end_time)
1315
+ start_time = start_time.strftime(DATE_FORMAT_RFC3339) if start_time.respond_to?(:strftime)
1316
+ end_time = end_time.strftime(DATE_FORMAT_RFC3339) if end_time.respond_to?(:strftime)
1317
+ @ranges.append({name: name, start: start_time, end: end_time})
1318
+ end
1319
+
1320
+ # @api private
1321
+ def to_json(*args)
1322
+ {field: field, size: size, date_ranges: @ranges}.to_json(*args)
1323
+ end
1324
+ end
1325
+ end
1326
+
1327
+ class SearchRowLocation
1328
+ # @return [String]
1329
+ attr_accessor :field
1330
+
1331
+ # @return [String]
1332
+ attr_accessor :term
1333
+
1334
+ # @return [Integer] the position of the term within the field, starting at 1
1335
+ attr_accessor :position
1336
+
1337
+ # @return [Integer] start byte offset of the term in the field
1338
+ attr_accessor :start_offset
1339
+
1340
+ # @return [Integer] end byte offset of the term in the field
1341
+ attr_accessor :end_offset
1342
+
1343
+ # @return [Array<Integer>] the positions of the term within any elements.
1344
+ attr_accessor :array_positions
1345
+
1346
+ # @yieldparam [SearchRowLocation] self
1347
+ def initialize
1348
+ yield self if block_given?
1349
+ end
1350
+ end
1351
+
1352
+ class SearchRowLocations
1353
+ # Lists all locations (any field, any term)
1354
+ #
1355
+ # @return [Array<SearchRowLocation>]
1356
+ def get_all
1357
+ @locations
1358
+ end
1359
+
1360
+ # Lists all locations for a given field (any term)
1361
+ #
1362
+ # @return [Array<SearchRowLocation>]
1363
+ def get_for_field(name)
1364
+ @locations.select { |location| location.field == name }
1365
+ end
1366
+
1367
+ # Lists all locations for a given field and term
1368
+ #
1369
+ # @return [Array<SearchRowLocation>]
1370
+ def get_for_field_and_term(name, term)
1371
+ @locations.select { |location| location.field == name && location.term == term }
1372
+ end
1373
+
1374
+ # Lists the fields in this location
1375
+ #
1376
+ # @return [Array<String>]
1377
+ def fields
1378
+ @locations.map(&:field).uniq
1379
+ end
1380
+
1381
+ # Lists all terms in this locations, considering all fields
1382
+ #
1383
+ # @return [Array<String>]
1384
+ def terms
1385
+ @locations.map(&:term).uniq
1386
+ end
1387
+
1388
+ # Lists the terms for a given field
1389
+ #
1390
+ # @return [Array<String>]
1391
+ def terms_for_field(name)
1392
+ get_for_field(name).map(&:term).uniq
1393
+ end
1394
+
1395
+ # @param [Array<SearchRowLocation>] locations
1396
+ def initialize(locations)
1397
+ super()
1398
+ @locations = locations
1399
+ end
1400
+ end
1401
+
1402
+ # An individual facet result has both metadata and details, as each facet can define ranges into which results are categorized
1403
+ class SearchFacetResult
1404
+ # @return [String]
1405
+ attr_accessor :name
1406
+
1407
+ # @return [String]
1408
+ attr_accessor :field
1409
+
1410
+ # @return [Integer]
1411
+ attr_accessor :total
1412
+
1413
+ # @return [Integer]
1414
+ attr_accessor :missing
1415
+
1416
+ # @return [Integer]
1417
+ attr_accessor :other
1418
+
1419
+ class TermFacetResult < SearchFacetResult
1420
+ # @return [Array<TermFacet>]
1421
+ attr_accessor :terms
1422
+
1423
+ def type
1424
+ :term_facet
1425
+ end
1426
+
1427
+ # @yieldparam [TermFacetResult] self
1428
+ def initialize
1429
+ super
1430
+ yield self if block_given?
1431
+ end
1432
+
1433
+ class TermFacet
1434
+ # @return [String]
1435
+ attr_reader :term
1436
+
1437
+ # @return [Integer]
1438
+ attr_reader :count
1439
+
1440
+ def initialize(term, count)
1441
+ super()
1442
+ @term = term
1443
+ @count = count
1444
+ end
1445
+ end
1446
+ end
1447
+
1448
+ class DateRangeFacetResult < SearchFacetResult
1449
+ # @return [Array<DateRangeFacet>]
1450
+ attr_accessor :date_ranges
1451
+
1452
+ def type
1453
+ :date_range_facet
1454
+ end
1455
+
1456
+ # @yieldparam [DateRangeFacetResult] self
1457
+ def initialize
1458
+ super
1459
+ yield self if block_given?
1460
+ end
1461
+
1462
+ class DateRangeFacet
1463
+ # @return [String]
1464
+ attr_reader :name
1465
+
1466
+ # @return [Integer]
1467
+ attr_reader :count
1468
+
1469
+ # @return [String]
1470
+ attr_reader :start_time
1471
+
1472
+ # @return [String]
1473
+ attr_reader :end_time
1474
+
1475
+ def initialize(name, count, start_time, end_time)
1476
+ @name = name
1477
+ @count = count
1478
+ @start_time = start_time
1479
+ @end_time = end_time
1480
+ end
1481
+ end
1482
+ end
1483
+
1484
+ class NumericRangeFacetResult < SearchFacetResult
1485
+ # @return [Array<NumericRangeFacet>]
1486
+ attr_accessor :numeric_ranges
1487
+
1488
+ def type
1489
+ :numeric_range_facet
1490
+ end
1491
+
1492
+ # @yieldparam [NumericRangeFacetResult] self
1493
+ def initialize
1494
+ super
1495
+ yield self if block_given?
1496
+ end
1497
+
1498
+ class NumericRangeFacet
1499
+ # @return [String]
1500
+ attr_reader :name
1501
+
1502
+ # @return [Integer]
1503
+ attr_reader :count
1504
+
1505
+ # @return [Integer, Float, nil]
1506
+ attr_reader :min
1507
+
1508
+ # @return [Integer, Float, nil]
1509
+ attr_reader :max
1510
+
1511
+ def initialize(name, count, min, max)
1512
+ @name = name
1513
+ @count = count
1514
+ @min = min
1515
+ @max = max
1516
+ end
1517
+ end
1518
+ end
1519
+ end
1520
+
1521
+ class SearchRow
1522
+ # @return [String] name of the index
1523
+ attr_accessor :index
1524
+
1525
+ # @return [String] document identifier
1526
+ attr_accessor :id
1527
+
1528
+ # @return [Float]
1529
+ attr_accessor :score
1530
+
1531
+ # @return [SearchRowLocations]
1532
+ attr_accessor :locations
1533
+
1534
+ # @return [Hash]
1535
+ attr_accessor :explanation
1536
+
1537
+ # @return [Hash<String => Array<String>>]
1538
+ attr_accessor :fragments
1539
+
1540
+ # @return [JsonTranscoder] transcoder to use for the fields
1541
+ attr_accessor :transcoder
1542
+
1543
+ def fields
1544
+ @transcoder.decode(@fields, :json) if @fields && @transcoder
1545
+ end
1546
+
1547
+ # @yieldparam [SearchRow] self
1548
+ def initialize
1549
+ @fields = nil
1550
+ yield self if block_given?
1551
+ end
1552
+ end
1553
+
1554
+ class SearchMetrics
1555
+ # @return [Integer] time spent executing the query (in milliseconds)
1556
+ attr_accessor :took
1557
+
1558
+ # @return [Integer]
1559
+ attr_accessor :total_rows
1560
+
1561
+ # @return [Float]
1562
+ attr_accessor :max_score
1563
+
1564
+ # @return [Integer]
1565
+ attr_accessor :success_partition_count
1566
+
1567
+ # @return [Integer]
1568
+ attr_accessor :error_partition_count
1569
+
1570
+ # @return [Integer]
1571
+ def total_partition_count
1572
+ success_partition_count + error_partition_count
1573
+ end
1574
+ end
1575
+
1576
+ class SearchMetaData
1577
+ # @return [SearchMetrics]
1578
+ attr_accessor :metrics
1579
+
1580
+ # @return [Hash<String => String>]
1581
+ attr_accessor :errors
1582
+
1583
+ # @yieldparam [SearchMetaData] self
1584
+ def initialize
1585
+ @metrics = SearchMetrics.new
1586
+ yield self if block_given?
1587
+ end
1588
+ end
1589
+
1590
+ class SearchResult
1591
+ # @return [Array<SearchRow>]
1592
+ attr_accessor :rows
1593
+
1594
+ # @return [Hash<String => SearchFacetResult>]
1595
+ attr_accessor :facets
1596
+
1597
+ # @return [SearchMetaData]
1598
+ attr_accessor :meta_data
1599
+
1600
+ def success?
1601
+ meta_data.errors.nil? || meta_data.errors.empty?
1602
+ end
1603
+
1604
+ # @yieldparam [SearchResult] self
1605
+ def initialize
1606
+ yield self if block_given?
1607
+ end
1608
+ end
1609
+
1610
+ class Cluster
1611
+ SearchQuery = Couchbase::SearchQuery
1612
+ SearchSort = Couchbase::SearchSort
1613
+ SearchFacet = Couchbase::SearchFacet
1614
+ SearchRowLocation = Couchbase::SearchRowLocation
1615
+ SearchRowLocations = Couchbase::SearchRowLocations
1616
+ SearchFacetResult = Couchbase::SearchFacetResult
1617
+ SearchRow = Couchbase::SearchRow
1618
+ SearchResult = Couchbase::SearchResult
1619
+ SearchMetaData = Couchbase::SearchMetaData
1620
+ SearchMetrics = Couchbase::SearchMetrics
1621
+ end
1622
+ end