couchbase 3.5.3-arm64-darwin

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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