couchbase 3.9.0-aarch64-linux-musl

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