couchbase 3.5.2-x86_64-darwin

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