couchbase 3.0.0.alpha.2 → 3.0.0.alpha.3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.github/workflows/tests-dev-preview.yml +52 -0
- data/.gitmodules +3 -0
- data/.idea/vcs.xml +1 -0
- data/.yardopts +1 -0
- data/README.md +1 -1
- data/Rakefile +5 -1
- data/bin/init-cluster +13 -5
- data/couchbase.gemspec +2 -1
- data/examples/managing_query_indexes.rb +1 -1
- data/examples/managing_search_indexes.rb +62 -0
- data/examples/search.rb +187 -0
- data/ext/.clang-tidy +1 -0
- data/ext/build_version.hxx.in +1 -1
- data/ext/couchbase/bucket.hxx +0 -40
- data/ext/couchbase/couchbase.cxx +2578 -1368
- data/ext/couchbase/io/http_session.hxx +27 -7
- data/ext/couchbase/io/mcbp_parser.hxx +2 -0
- data/ext/couchbase/io/mcbp_session.hxx +53 -24
- data/ext/couchbase/io/session_manager.hxx +6 -1
- data/ext/couchbase/operations.hxx +13 -0
- data/ext/couchbase/operations/bucket_create.hxx +1 -0
- data/ext/couchbase/operations/bucket_drop.hxx +1 -0
- data/ext/couchbase/operations/bucket_flush.hxx +1 -0
- data/ext/couchbase/operations/bucket_get.hxx +1 -0
- data/ext/couchbase/operations/bucket_get_all.hxx +1 -0
- data/ext/couchbase/operations/bucket_update.hxx +1 -0
- data/ext/couchbase/operations/cluster_developer_preview_enable.hxx +1 -0
- data/ext/couchbase/operations/collection_create.hxx +6 -1
- data/ext/couchbase/operations/collection_drop.hxx +1 -0
- data/ext/couchbase/operations/command.hxx +86 -11
- data/ext/couchbase/operations/document_decrement.hxx +1 -0
- data/ext/couchbase/operations/document_exists.hxx +1 -0
- data/ext/couchbase/operations/document_get.hxx +1 -0
- data/ext/couchbase/operations/document_get_and_lock.hxx +1 -0
- data/ext/couchbase/operations/document_get_and_touch.hxx +1 -0
- data/ext/couchbase/operations/document_get_projected.hxx +243 -0
- data/ext/couchbase/operations/document_increment.hxx +4 -1
- data/ext/couchbase/operations/document_insert.hxx +1 -0
- data/ext/couchbase/operations/document_lookup_in.hxx +1 -0
- data/ext/couchbase/operations/document_mutate_in.hxx +1 -0
- data/ext/couchbase/operations/document_query.hxx +13 -2
- data/ext/couchbase/operations/document_remove.hxx +1 -0
- data/ext/couchbase/operations/document_replace.hxx +1 -0
- data/ext/couchbase/operations/document_search.hxx +337 -0
- data/ext/couchbase/operations/document_touch.hxx +1 -0
- data/ext/couchbase/operations/document_unlock.hxx +1 -0
- data/ext/couchbase/operations/document_upsert.hxx +1 -0
- data/ext/couchbase/operations/query_index_build_deferred.hxx +1 -0
- data/ext/couchbase/operations/query_index_create.hxx +1 -0
- data/ext/couchbase/operations/query_index_drop.hxx +1 -0
- data/ext/couchbase/operations/query_index_get_all.hxx +1 -0
- data/ext/couchbase/operations/scope_create.hxx +1 -0
- data/ext/couchbase/operations/scope_drop.hxx +1 -0
- data/ext/couchbase/operations/scope_get_all.hxx +2 -0
- data/ext/couchbase/operations/search_index.hxx +62 -0
- data/ext/couchbase/operations/search_index_analyze_document.hxx +92 -0
- data/ext/couchbase/operations/search_index_control_ingest.hxx +78 -0
- data/ext/couchbase/operations/search_index_control_plan_freeze.hxx +80 -0
- data/ext/couchbase/operations/search_index_control_query.hxx +80 -0
- data/ext/couchbase/operations/search_index_drop.hxx +77 -0
- data/ext/couchbase/operations/search_index_get.hxx +80 -0
- data/ext/couchbase/operations/search_index_get_all.hxx +82 -0
- data/ext/couchbase/operations/search_index_get_documents_count.hxx +81 -0
- data/ext/couchbase/operations/search_index_upsert.hxx +106 -0
- data/ext/couchbase/protocol/client_opcode.hxx +10 -0
- data/ext/couchbase/protocol/cmd_get_collection_id.hxx +117 -0
- data/ext/couchbase/timeout_defaults.hxx +32 -0
- data/ext/couchbase/version.hxx +1 -1
- data/ext/test/main.cxx +5 -5
- data/lib/couchbase/binary_collection.rb +16 -12
- data/lib/couchbase/binary_collection_options.rb +4 -0
- data/lib/couchbase/cluster.rb +88 -8
- data/lib/couchbase/collection.rb +39 -15
- data/lib/couchbase/collection_options.rb +19 -2
- data/lib/couchbase/json_transcoder.rb +2 -2
- data/lib/couchbase/management/bucket_manager.rb +37 -23
- data/lib/couchbase/management/collection_manager.rb +15 -6
- data/lib/couchbase/management/query_index_manager.rb +16 -6
- data/lib/couchbase/management/search_index_manager.rb +61 -14
- data/lib/couchbase/search_options.rb +1492 -0
- data/lib/couchbase/version.rb +1 -1
- metadata +22 -2
@@ -0,0 +1,1492 @@
|
|
1
|
+
# Copyright 2020 Couchbase, Inc.
|
2
|
+
#
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
4
|
+
# you may not use this file except in compliance with the License.
|
5
|
+
# You may obtain a copy of the License at
|
6
|
+
#
|
7
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
8
|
+
#
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
12
|
+
# See the License for the specific language governing permissions and
|
13
|
+
# limitations under the License.
|
14
|
+
|
15
|
+
module Couchbase
|
16
|
+
class Cluster
|
17
|
+
class SearchQuery
|
18
|
+
# Prepare {MatchQuery} body
|
19
|
+
#
|
20
|
+
# @param [String] match
|
21
|
+
# @yieldparam [MatchQuery] query
|
22
|
+
#
|
23
|
+
# @return [MatchQuery]
|
24
|
+
def self.match(match, &block)
|
25
|
+
MatchQuery.new(match, &block)
|
26
|
+
end
|
27
|
+
|
28
|
+
# A match query analyzes the input text and uses that analyzed text to query the index.
|
29
|
+
class MatchQuery < SearchQuery
|
30
|
+
# @return [Float]
|
31
|
+
attr_accessor :boost
|
32
|
+
|
33
|
+
# @return [String]
|
34
|
+
attr_accessor :field
|
35
|
+
|
36
|
+
# @return [String]
|
37
|
+
attr_accessor :analyzer
|
38
|
+
|
39
|
+
# @return [Integer]
|
40
|
+
attr_accessor :prefix_length
|
41
|
+
|
42
|
+
# @return [Integer]
|
43
|
+
attr_accessor :fuzziness
|
44
|
+
|
45
|
+
# @param [String] match
|
46
|
+
# @yieldparam [MatchQuery] self
|
47
|
+
def initialize(match)
|
48
|
+
super()
|
49
|
+
@match = match
|
50
|
+
yield self if block_given?
|
51
|
+
end
|
52
|
+
|
53
|
+
# @return [String]
|
54
|
+
def to_json(*args)
|
55
|
+
data = {"match" => @match}
|
56
|
+
data["boost"] = boost if boost
|
57
|
+
data["field"] = field if field
|
58
|
+
data["analyzer"] = analyzer if analyzer
|
59
|
+
if fuzziness
|
60
|
+
data["fuzziness"] = fuzziness
|
61
|
+
data["prefix_length"] = prefix_length if prefix_length
|
62
|
+
end
|
63
|
+
data.to_json(*args)
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
# Prepare {MatchPhraseQuery} body
|
68
|
+
#
|
69
|
+
# @param [String] match_phrase
|
70
|
+
# @yieldparam [MatchPhraseQuery] query
|
71
|
+
#
|
72
|
+
# @return [MatchPhraseQuery]
|
73
|
+
def self.match_phrase(match_phrase, &block)
|
74
|
+
MatchPhraseQuery.new(match_phrase, &block)
|
75
|
+
end
|
76
|
+
|
77
|
+
# The input text is analyzed and a phrase query is built with the terms resulting from the analysis.
|
78
|
+
class MatchPhraseQuery < SearchQuery
|
79
|
+
# @return [Float]
|
80
|
+
attr_accessor :boost
|
81
|
+
|
82
|
+
# @return [String]
|
83
|
+
attr_accessor :field
|
84
|
+
|
85
|
+
# @return [String]
|
86
|
+
attr_accessor :analyzer
|
87
|
+
|
88
|
+
# @param [String] match_phrase
|
89
|
+
#
|
90
|
+
# @yieldparam [MatchPhraseQuery] self
|
91
|
+
def initialize(match_phrase)
|
92
|
+
super()
|
93
|
+
@match_phrase = match_phrase
|
94
|
+
yield self if block_given?
|
95
|
+
end
|
96
|
+
|
97
|
+
# @return [String]
|
98
|
+
def to_json(*args)
|
99
|
+
data = {"match_phrase" => @match_phrase}
|
100
|
+
data["boost"] = boost if boost
|
101
|
+
data["field"] = field if field
|
102
|
+
data["analyzer"] = analyzer if analyzer
|
103
|
+
data.to_json(*args)
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
# Prepare {RegexpQuery} body
|
108
|
+
#
|
109
|
+
# @param [String] regexp
|
110
|
+
# @yieldparam [RegexpQuery] query
|
111
|
+
#
|
112
|
+
# @return [RegexpQuery]
|
113
|
+
def self.regexp(regexp, &block)
|
114
|
+
RegexpQuery.new(regexp, &block)
|
115
|
+
end
|
116
|
+
|
117
|
+
# Finds documents containing terms that match the specified regular expression.
|
118
|
+
class RegexpQuery < SearchQuery
|
119
|
+
# @return [Float]
|
120
|
+
attr_accessor :boost
|
121
|
+
|
122
|
+
# @return [String]
|
123
|
+
attr_accessor :field
|
124
|
+
|
125
|
+
# @param [String] regexp
|
126
|
+
#
|
127
|
+
# @yieldparam [RegexpQuery] self
|
128
|
+
def initialize(regexp)
|
129
|
+
super()
|
130
|
+
@regexp = regexp
|
131
|
+
yield self if block_given?
|
132
|
+
end
|
133
|
+
|
134
|
+
# @return [String]
|
135
|
+
def to_json(*args)
|
136
|
+
data = {"regexp" => @regexp}
|
137
|
+
data["boost"] = boost if boost
|
138
|
+
data["field"] = field if field
|
139
|
+
data.to_json(*args)
|
140
|
+
end
|
141
|
+
end
|
142
|
+
|
143
|
+
# Prepare {QueryStringQuery} body
|
144
|
+
#
|
145
|
+
# @param [String] query_string
|
146
|
+
# @yieldparam [QueryStringQuery] query
|
147
|
+
#
|
148
|
+
# @return [QueryStringQuery]
|
149
|
+
def self.query_string(query_string, &block)
|
150
|
+
QueryStringQuery.new(query_string, &block)
|
151
|
+
end
|
152
|
+
|
153
|
+
# The query string query allows humans to describe complex queries using a simple syntax.
|
154
|
+
class QueryStringQuery < SearchQuery
|
155
|
+
# @return [Float]
|
156
|
+
attr_accessor :boost
|
157
|
+
|
158
|
+
# @param [String] query_string
|
159
|
+
#
|
160
|
+
# @yieldparam [QueryStringQuery] self
|
161
|
+
def initialize(query_string)
|
162
|
+
super()
|
163
|
+
@query_string = query_string
|
164
|
+
yield self if block_given?
|
165
|
+
end
|
166
|
+
|
167
|
+
# @return [String]
|
168
|
+
def to_json(*args)
|
169
|
+
data = {"query" => @query_string}
|
170
|
+
data["boost"] = boost if boost
|
171
|
+
data.to_json(*args)
|
172
|
+
end
|
173
|
+
end
|
174
|
+
|
175
|
+
# Prepare {WildcardQuery} body
|
176
|
+
#
|
177
|
+
# @param [String] wildcard
|
178
|
+
# @yieldparam [WildcardQuery] query
|
179
|
+
#
|
180
|
+
# @return [WildcardQuery]
|
181
|
+
def self.wildcard(wildcard, &block)
|
182
|
+
WildcardQuery.new(wildcard, &block)
|
183
|
+
end
|
184
|
+
|
185
|
+
# Interprets * and ? wildcards as found in a lot of applications, for an easy implementation of such a search feature.
|
186
|
+
class WildcardQuery < SearchQuery
|
187
|
+
# @return [Float]
|
188
|
+
attr_accessor :boost
|
189
|
+
|
190
|
+
# @return [String]
|
191
|
+
attr_accessor :field
|
192
|
+
|
193
|
+
# @param [String] wildcard
|
194
|
+
#
|
195
|
+
# @yieldparam [WildcardQuery] self
|
196
|
+
def initialize(wildcard)
|
197
|
+
super()
|
198
|
+
@wildcard = wildcard
|
199
|
+
yield self if block_given?
|
200
|
+
end
|
201
|
+
|
202
|
+
# @return [String]
|
203
|
+
def to_json(*args)
|
204
|
+
data = {"wildcard" => @wildcard}
|
205
|
+
data["boost"] = boost if boost
|
206
|
+
data["field"] = field if field
|
207
|
+
data.to_json(*args)
|
208
|
+
end
|
209
|
+
end
|
210
|
+
|
211
|
+
# Prepare {DocIdQuery} body
|
212
|
+
#
|
213
|
+
# @param [String...] doc_ids
|
214
|
+
# @yieldparam [DocIdQuery] query
|
215
|
+
#
|
216
|
+
# @return [DocIdQuery]
|
217
|
+
def self.doc_id(*doc_ids)
|
218
|
+
DocIdQuery.new(*doc_ids)
|
219
|
+
end
|
220
|
+
|
221
|
+
# Allows to restrict matches to a set of specific documents.
|
222
|
+
class DocIdQuery < SearchQuery
|
223
|
+
# @return [Float]
|
224
|
+
attr_accessor :boost
|
225
|
+
|
226
|
+
# @return [String]
|
227
|
+
attr_accessor :field
|
228
|
+
|
229
|
+
# @param [String...] doc_ids
|
230
|
+
#
|
231
|
+
# @yieldparam [DocIdQuery] self
|
232
|
+
def initialize(*doc_ids)
|
233
|
+
super()
|
234
|
+
@doc_ids = doc_ids
|
235
|
+
yield self if block_given?
|
236
|
+
end
|
237
|
+
|
238
|
+
# @return [String]
|
239
|
+
def to_json(*args)
|
240
|
+
data = {"doc_ids" => @doc_ids.flatten.uniq}
|
241
|
+
data["boost"] = boost if boost
|
242
|
+
data["field"] = field if field
|
243
|
+
data.to_json(*args)
|
244
|
+
end
|
245
|
+
end
|
246
|
+
|
247
|
+
# Prepare {BooleanFieldQuery} body
|
248
|
+
#
|
249
|
+
# @param [Boolean] value
|
250
|
+
# @yieldparam [BooleanFieldQuery] query
|
251
|
+
#
|
252
|
+
# @return [BooleanFieldQuery]
|
253
|
+
def self.boolean_field(value)
|
254
|
+
BooleanFieldQuery.new(value)
|
255
|
+
end
|
256
|
+
|
257
|
+
# Allow to match `true`/`false` in a field mapped as boolean.
|
258
|
+
class BooleanFieldQuery < SearchQuery
|
259
|
+
# @return [Float]
|
260
|
+
attr_accessor :boost
|
261
|
+
|
262
|
+
# @return [String]
|
263
|
+
attr_accessor :field
|
264
|
+
|
265
|
+
# @param [Boolean] value
|
266
|
+
#
|
267
|
+
# @yieldparam [BooleanFieldQuery] self
|
268
|
+
def initialize(value)
|
269
|
+
super()
|
270
|
+
@value = value
|
271
|
+
yield self if block_given?
|
272
|
+
end
|
273
|
+
|
274
|
+
# @return [String]
|
275
|
+
def to_json(*args)
|
276
|
+
data = {"bool" => @value}
|
277
|
+
data["boost"] = boost if boost
|
278
|
+
data["field"] = field if field
|
279
|
+
data.to_json(*args)
|
280
|
+
end
|
281
|
+
end
|
282
|
+
|
283
|
+
# Prepare {DateRangeQuery} body
|
284
|
+
#
|
285
|
+
# @yieldparam [DateRangeQuery] query
|
286
|
+
#
|
287
|
+
# @return [DateRangeQuery]
|
288
|
+
def self.date_range(&block)
|
289
|
+
DateRangeQuery.new(&block)
|
290
|
+
end
|
291
|
+
|
292
|
+
# The date range query finds documents containing a date value in the specified field within the specified range.
|
293
|
+
class DateRangeQuery < SearchQuery
|
294
|
+
# @return [Float]
|
295
|
+
attr_accessor :boost
|
296
|
+
|
297
|
+
# @return [String]
|
298
|
+
attr_accessor :field
|
299
|
+
|
300
|
+
# @return [String]
|
301
|
+
attr_accessor :date_time_parser
|
302
|
+
|
303
|
+
# Sets the lower boundary of the range.
|
304
|
+
#
|
305
|
+
# @note The lower boundary is considered inclusive by default on the server side.
|
306
|
+
#
|
307
|
+
# @param [Time, String] time_point start time. When {Time} object is passed {#date_time_parser} must be +nil+ (to use server default)
|
308
|
+
# @param [Boolean] inclusive
|
309
|
+
def start_time(time_point, inclusive = nil)
|
310
|
+
@start_time = time_point
|
311
|
+
@start_inclusive = inclusive
|
312
|
+
end
|
313
|
+
|
314
|
+
# Sets the upper boundary of the range.
|
315
|
+
#
|
316
|
+
# @note The upper boundary is considered exclusive by default on the server side.
|
317
|
+
#
|
318
|
+
# @param [Time, String] time_point end time. When {Time} object is passed {#date_time_parser} must be +nil+ (to use server default)
|
319
|
+
# @param [Boolean] inclusive
|
320
|
+
def end_time(time_point, inclusive = nil)
|
321
|
+
@end_time = time_point
|
322
|
+
@end_inclusive = inclusive
|
323
|
+
end
|
324
|
+
|
325
|
+
# @yieldparam [DateRangeQuery] self
|
326
|
+
def initialize
|
327
|
+
super
|
328
|
+
@start_time = nil
|
329
|
+
@start_inclusive = nil
|
330
|
+
@end_time = nil
|
331
|
+
@end_inclusive = nil
|
332
|
+
yield self if block_given?
|
333
|
+
end
|
334
|
+
|
335
|
+
DATE_FORMAT_RFC3339 = "%Y-%m-%dT%H:%M:%S%:z"
|
336
|
+
|
337
|
+
# @return [String]
|
338
|
+
def to_json(*args)
|
339
|
+
if @start_time.nil? && @end_time.nil?
|
340
|
+
raise ArgumentError, "either start_time or end_time must be set for DateRangeQuery"
|
341
|
+
end
|
342
|
+
|
343
|
+
data = {}
|
344
|
+
data["boost"] = boost if boost
|
345
|
+
data["field"] = field if field
|
346
|
+
data["datetime_parser"] = date_time_parser if date_time_parser
|
347
|
+
if @start_time
|
348
|
+
data["start"] = if @start_time.respond_to?(:strftime)
|
349
|
+
@start_time.strftime(DATE_FORMAT_RFC3339)
|
350
|
+
else
|
351
|
+
@start_time
|
352
|
+
end
|
353
|
+
data["inclusive_start"] = @start_inclusive unless @start_inclusive.nil?
|
354
|
+
end
|
355
|
+
if @end_time
|
356
|
+
data["end"] = if @end_time.respond_to?(:strftime)
|
357
|
+
@end_time.strftime(DATE_FORMAT_RFC3339)
|
358
|
+
else
|
359
|
+
@end_time
|
360
|
+
end
|
361
|
+
data["inclusive_end"] = @end_inclusive unless @end_inclusive.nil?
|
362
|
+
end
|
363
|
+
data.to_json(*args)
|
364
|
+
end
|
365
|
+
end
|
366
|
+
|
367
|
+
# Prepare {NumericRangeQuery} body
|
368
|
+
#
|
369
|
+
# @yieldparam [NumericRangeQuery] query
|
370
|
+
#
|
371
|
+
# @return [NumericRangeQuery]
|
372
|
+
def self.numeric_range(&block)
|
373
|
+
NumericRangeQuery.new(&block)
|
374
|
+
end
|
375
|
+
|
376
|
+
# The numeric range query finds documents containing a numeric value in the specified field within the specified range.
|
377
|
+
class NumericRangeQuery < SearchQuery
|
378
|
+
# @return [Float]
|
379
|
+
attr_accessor :boost
|
380
|
+
|
381
|
+
# @return [String]
|
382
|
+
attr_accessor :field
|
383
|
+
|
384
|
+
# Sets lower bound of the range.
|
385
|
+
#
|
386
|
+
# The lower boundary is considered inclusive by default on the server side.
|
387
|
+
#
|
388
|
+
# @param [Numeric] lower_bound
|
389
|
+
# @param [Boolean] inclusive
|
390
|
+
def min(lower_bound, inclusive = nil)
|
391
|
+
@min = lower_bound
|
392
|
+
@min_inclusive = inclusive
|
393
|
+
end
|
394
|
+
|
395
|
+
# Sets upper bound of the range.
|
396
|
+
#
|
397
|
+
# The upper boundary is considered exclusive by default on the server side.
|
398
|
+
#
|
399
|
+
# @param [Numeric] upper_bound
|
400
|
+
# @param [Boolean] inclusive
|
401
|
+
def max(upper_bound, inclusive = nil)
|
402
|
+
@max = upper_bound
|
403
|
+
@max_inclusive = inclusive
|
404
|
+
end
|
405
|
+
|
406
|
+
# @yieldparam [NumericRangeQuery] self
|
407
|
+
def initialize
|
408
|
+
super
|
409
|
+
@min = nil
|
410
|
+
@min_inclusive = nil
|
411
|
+
@max = nil
|
412
|
+
@max_inclusive = nil
|
413
|
+
yield self if block_given?
|
414
|
+
end
|
415
|
+
|
416
|
+
# @return [String]
|
417
|
+
def to_json(*args)
|
418
|
+
if @min.nil? && @max.nil?
|
419
|
+
raise ArgumentError, "either min or max must be set for NumericRangeQuery"
|
420
|
+
end
|
421
|
+
|
422
|
+
data = {}
|
423
|
+
data["boost"] = boost if boost
|
424
|
+
data["field"] = field if field
|
425
|
+
if @min
|
426
|
+
data["min"] = @min
|
427
|
+
data["inclusive_min"] = @min_inclusive unless @min_inclusive.nil?
|
428
|
+
end
|
429
|
+
if @max
|
430
|
+
data["max"] = @max
|
431
|
+
data["inclusive_max"] = @max_inclusive unless @max_inclusive.nil?
|
432
|
+
end
|
433
|
+
data.to_json(*args)
|
434
|
+
end
|
435
|
+
end
|
436
|
+
|
437
|
+
# Prepare {TermRangeQuery} body
|
438
|
+
#
|
439
|
+
# @yieldparam [TermRangeQuery] query
|
440
|
+
#
|
441
|
+
# @return [TermRangeQuery]
|
442
|
+
def self.term_range(&block)
|
443
|
+
TermRangeQuery.new(&block)
|
444
|
+
end
|
445
|
+
|
446
|
+
# The term range query finds documents containing a string value in the specified field within the specified range.
|
447
|
+
class TermRangeQuery < 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 [String] 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 [String] 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 [TermRangeQuery] 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 [String]
|
487
|
+
def to_json(*args)
|
488
|
+
if @min.nil? && @max.nil?
|
489
|
+
raise ArgumentError, "either min or max must be set for TermRangeQuery"
|
490
|
+
end
|
491
|
+
|
492
|
+
data = {}
|
493
|
+
data["boost"] = boost if boost
|
494
|
+
data["field"] = field if field
|
495
|
+
if @min
|
496
|
+
data["min"] = @min
|
497
|
+
data["inclusive_min"] = @min_inclusive unless @min_inclusive.nil?
|
498
|
+
end
|
499
|
+
if @max
|
500
|
+
data["max"] = @max
|
501
|
+
data["inclusive_max"] = @max_inclusive unless @max_inclusive.nil?
|
502
|
+
end
|
503
|
+
data.to_json(*args)
|
504
|
+
end
|
505
|
+
end
|
506
|
+
|
507
|
+
# Prepare {GeoDistanceQuery} body
|
508
|
+
#
|
509
|
+
# @yieldparam [GeoDistanceQuery] query
|
510
|
+
#
|
511
|
+
# @param [Float] latitude location latitude
|
512
|
+
# @param [Float] longitude location longitude
|
513
|
+
# @param [String] distance how big is area (number with units)
|
514
|
+
#
|
515
|
+
# @return [GeoDistanceQuery]
|
516
|
+
def self.geo_distance(longitude, latitude, distance, &block)
|
517
|
+
GeoDistanceQuery.new(longitude, latitude, distance, &block)
|
518
|
+
end
|
519
|
+
|
520
|
+
# Finds `geopoint` indexed matches around a point with the given distance.
|
521
|
+
class GeoDistanceQuery < SearchQuery
|
522
|
+
# @return [Float]
|
523
|
+
attr_accessor :boost
|
524
|
+
|
525
|
+
# @return [String]
|
526
|
+
attr_accessor :field
|
527
|
+
|
528
|
+
# @yieldparam [GeoDistanceQuery] self
|
529
|
+
# @param [Float] longitude
|
530
|
+
# @param [Float] latitude
|
531
|
+
# @param [Float] distance
|
532
|
+
def initialize(longitude, latitude, distance)
|
533
|
+
super()
|
534
|
+
@longitude = longitude
|
535
|
+
@latitude = latitude
|
536
|
+
@distance = distance
|
537
|
+
yield self if block_given?
|
538
|
+
end
|
539
|
+
|
540
|
+
# @return [String]
|
541
|
+
def to_json(*args)
|
542
|
+
data = {
|
543
|
+
"location" => [@longitude, @latitude],
|
544
|
+
"distance" => @distance
|
545
|
+
}
|
546
|
+
data["boost"] = boost if boost
|
547
|
+
data["field"] = field if field
|
548
|
+
data.to_json(*args)
|
549
|
+
end
|
550
|
+
end
|
551
|
+
|
552
|
+
# Prepare {GeoBoundingBoxQuery} body
|
553
|
+
#
|
554
|
+
# @yieldparam [GeoDistanceQuery] query
|
555
|
+
#
|
556
|
+
# @param [Float] top_left_longitude
|
557
|
+
# @param [Float] top_left_latitude
|
558
|
+
# @param [Float] bottom_right_longitude
|
559
|
+
# @param [Float] bottom_right_latitude
|
560
|
+
#
|
561
|
+
# @return [GeoBoundingBoxQuery]
|
562
|
+
def self.geo_bounding_box(top_left_longitude, top_left_latitude, bottom_right_longitude, bottom_right_latitude, &block)
|
563
|
+
GeoBoundingBoxQuery.new(top_left_longitude, top_left_latitude, bottom_right_longitude, bottom_right_latitude, &block)
|
564
|
+
end
|
565
|
+
|
566
|
+
# Finds `geopoint` indexed matches in a given bounding box.
|
567
|
+
class GeoBoundingBoxQuery < SearchQuery
|
568
|
+
# @return [Float]
|
569
|
+
attr_accessor :boost
|
570
|
+
|
571
|
+
# @return [String]
|
572
|
+
attr_accessor :field
|
573
|
+
|
574
|
+
# @yieldparam [GeoBoundingBoxQuery] self
|
575
|
+
#
|
576
|
+
# @param [Float] top_left_longitude
|
577
|
+
# @param [Float] top_left_latitude
|
578
|
+
# @param [Float] bottom_right_longitude
|
579
|
+
# @param [Float] bottom_right_latitude
|
580
|
+
def initialize(top_left_longitude, top_left_latitude, bottom_right_longitude, bottom_right_latitude)
|
581
|
+
super()
|
582
|
+
@top_left_longitude = top_left_longitude
|
583
|
+
@top_left_latitude = top_left_latitude
|
584
|
+
@bottom_right_longitude = bottom_right_longitude
|
585
|
+
@bottom_right_latitude = bottom_right_latitude
|
586
|
+
yield self if block_given?
|
587
|
+
end
|
588
|
+
|
589
|
+
# @return [String]
|
590
|
+
def to_json(*args)
|
591
|
+
data = {
|
592
|
+
"top_left" => [@top_left_longitude, @top_left_latitude],
|
593
|
+
"bottom_right" => [@bottom_right_longitude, @bottom_right_latitude]
|
594
|
+
}
|
595
|
+
data["boost"] = boost if boost
|
596
|
+
data["field"] = field if field
|
597
|
+
data.to_json(*args)
|
598
|
+
end
|
599
|
+
end
|
600
|
+
|
601
|
+
# Prepare {ConjunctionQuery} body
|
602
|
+
#
|
603
|
+
# @yieldparam [ConjunctionQuery] query
|
604
|
+
#
|
605
|
+
# @return [ConjunctionQuery]
|
606
|
+
def self.conjuncts(*queries, &block)
|
607
|
+
ConjunctionQuery.new(*queries, &block)
|
608
|
+
end
|
609
|
+
|
610
|
+
# Result documents must satisfy all of the child queries.
|
611
|
+
class ConjunctionQuery < SearchQuery
|
612
|
+
# @return [Float]
|
613
|
+
attr_accessor :boost
|
614
|
+
|
615
|
+
# @yieldparam [ConjunctionQuery] self
|
616
|
+
#
|
617
|
+
# @param [*SearchQuery] queries
|
618
|
+
def initialize(*queries)
|
619
|
+
super()
|
620
|
+
@queries = queries.flatten
|
621
|
+
yield self if block_given?
|
622
|
+
end
|
623
|
+
|
624
|
+
# @param [*SearchQuery] queries
|
625
|
+
def and_also(*queries)
|
626
|
+
@queries |= queries.flatten
|
627
|
+
end
|
628
|
+
|
629
|
+
def empty?
|
630
|
+
@queries.empty?
|
631
|
+
end
|
632
|
+
|
633
|
+
# @return [String]
|
634
|
+
def to_json(*args)
|
635
|
+
raise ArgumentError, "compound conjunction query must have sub-queries" if @queries.nil? || @queries.empty?
|
636
|
+
data = {"conjuncts" => @queries.uniq}
|
637
|
+
data["boost"] = boost if boost
|
638
|
+
data.to_json(*args)
|
639
|
+
end
|
640
|
+
end
|
641
|
+
|
642
|
+
# Prepare {ConjunctionQuery} body
|
643
|
+
#
|
644
|
+
# @yieldparam [ConjunctionQuery] query
|
645
|
+
#
|
646
|
+
# @return [ConjunctionQuery]
|
647
|
+
def self.disjuncts(*queries, &block)
|
648
|
+
DisjunctionQuery.new(*queries, &block)
|
649
|
+
end
|
650
|
+
|
651
|
+
# Result documents must satisfy a configurable min number of child queries.
|
652
|
+
class DisjunctionQuery < SearchQuery
|
653
|
+
# @return [Float]
|
654
|
+
attr_accessor :boost
|
655
|
+
|
656
|
+
# @return [Integer]
|
657
|
+
attr_accessor :min
|
658
|
+
|
659
|
+
# @yieldparam [DisjunctionQuery] self
|
660
|
+
#
|
661
|
+
# @param [*SearchQuery] queries
|
662
|
+
def initialize(*queries)
|
663
|
+
super()
|
664
|
+
@queries = queries.flatten
|
665
|
+
yield self if block_given?
|
666
|
+
end
|
667
|
+
|
668
|
+
# @param [*SearchQuery] queries
|
669
|
+
def or_else(*queries)
|
670
|
+
@queries |= queries.flatten
|
671
|
+
end
|
672
|
+
|
673
|
+
def empty?
|
674
|
+
@queries.empty?
|
675
|
+
end
|
676
|
+
|
677
|
+
# @return [String]
|
678
|
+
def to_json(*args)
|
679
|
+
raise ArgumentError, "compound disjunction query must have sub-queries" if @queries.nil? || @queries.empty?
|
680
|
+
data = {"disjuncts" => @queries.uniq}
|
681
|
+
if min
|
682
|
+
raise ArgumentError, "disjunction query has fewer sub-queries than configured minimum" if @queries.size < min
|
683
|
+
data["min"] = min
|
684
|
+
end
|
685
|
+
data["boost"] = boost if boost
|
686
|
+
data.to_json(*args)
|
687
|
+
end
|
688
|
+
end
|
689
|
+
|
690
|
+
# Prepare {BooleanQuery} body
|
691
|
+
#
|
692
|
+
# @yieldparam [BooleanQuery] query
|
693
|
+
#
|
694
|
+
# @return [BooleanQuery]
|
695
|
+
def self.booleans(&block)
|
696
|
+
BooleanQuery.new(&block)
|
697
|
+
end
|
698
|
+
|
699
|
+
# The boolean query is a useful combination of conjunction and disjunction queries.
|
700
|
+
class BooleanQuery < SearchQuery
|
701
|
+
# @return [Float]
|
702
|
+
attr_accessor :boost
|
703
|
+
|
704
|
+
# @yieldparam [BooleanQuery] self
|
705
|
+
def initialize
|
706
|
+
super()
|
707
|
+
@must = ConjunctionQuery.new
|
708
|
+
@must_not = DisjunctionQuery.new
|
709
|
+
@should = DisjunctionQuery.new
|
710
|
+
yield self if block_given?
|
711
|
+
end
|
712
|
+
|
713
|
+
# @param [Integer] min minimal value for "should" disjunction query
|
714
|
+
def should_min(min)
|
715
|
+
@should.min = min
|
716
|
+
self
|
717
|
+
end
|
718
|
+
|
719
|
+
# @param [*SearchQuery] queries
|
720
|
+
def must(*queries)
|
721
|
+
@must.and_also(*queries)
|
722
|
+
self
|
723
|
+
end
|
724
|
+
|
725
|
+
# @param [*SearchQuery] queries
|
726
|
+
def must_not(*queries)
|
727
|
+
@must_not.or_else(*queries)
|
728
|
+
self
|
729
|
+
end
|
730
|
+
|
731
|
+
# @param [*SearchQuery] queries
|
732
|
+
def should(*queries)
|
733
|
+
@should.or_else(*queries)
|
734
|
+
self
|
735
|
+
end
|
736
|
+
|
737
|
+
# @return [String]
|
738
|
+
def to_json(*args)
|
739
|
+
if @must.empty? && @must_not.empty? && @should.empty?
|
740
|
+
raise ArgumentError, "BooleanQuery must have at least one non-empty sub-query"
|
741
|
+
end
|
742
|
+
data = {}
|
743
|
+
data["must"] = @must unless @must.empty?
|
744
|
+
data["must_not"] = @must_not unless @must_not.empty?
|
745
|
+
data["should"] = @should unless @should.empty?
|
746
|
+
data["boost"] = boost if boost
|
747
|
+
data.to_json(*args)
|
748
|
+
end
|
749
|
+
end
|
750
|
+
|
751
|
+
# Prepare {TermQuery} body
|
752
|
+
#
|
753
|
+
# @yieldparam [TermQuery] query
|
754
|
+
# @param [String] term
|
755
|
+
#
|
756
|
+
# @return [TermQuery]
|
757
|
+
def self.term(term, &block)
|
758
|
+
TermQuery.new(term, &block)
|
759
|
+
end
|
760
|
+
|
761
|
+
# A query that looks for **exact** matches of the term in the index (no analyzer, no stemming). Useful to check what the actual content of the index is. It can also apply fuzziness on the term. Usual better alternative is `MatchQuery`.
|
762
|
+
class TermQuery < SearchQuery
|
763
|
+
# @return [Float]
|
764
|
+
attr_accessor :boost
|
765
|
+
|
766
|
+
# @return [String]
|
767
|
+
attr_accessor :field
|
768
|
+
|
769
|
+
# @return [Integer]
|
770
|
+
attr_accessor :fuzziness
|
771
|
+
|
772
|
+
# @return [Integer]
|
773
|
+
attr_accessor :prefix_length
|
774
|
+
|
775
|
+
# @yieldparam [TermQuery] self
|
776
|
+
#
|
777
|
+
# @param [String] term
|
778
|
+
def initialize(term)
|
779
|
+
super()
|
780
|
+
@term = term
|
781
|
+
yield self if block_given?
|
782
|
+
end
|
783
|
+
|
784
|
+
# @return [String]
|
785
|
+
def to_json(*args)
|
786
|
+
data = {"term" => @term}
|
787
|
+
data["boost"] = boost if boost
|
788
|
+
data["field"] = field if field
|
789
|
+
if fuzziness
|
790
|
+
data["fuzziness"] = fuzziness
|
791
|
+
data["prefix_length"] = prefix_length if prefix_length
|
792
|
+
end
|
793
|
+
data.to_json(*args)
|
794
|
+
end
|
795
|
+
end
|
796
|
+
|
797
|
+
# Prepare {PrefixQuery} body
|
798
|
+
#
|
799
|
+
# @yieldparam [PrefixQuery] query
|
800
|
+
# @param [String] prefix
|
801
|
+
#
|
802
|
+
# @return [PrefixQuery]
|
803
|
+
def self.prefix(prefix, &block)
|
804
|
+
PrefixQuery.new(prefix, &block)
|
805
|
+
end
|
806
|
+
|
807
|
+
# The prefix query finds documents containing terms that start with the provided prefix. Usual better alternative is `MatchQuery`.
|
808
|
+
class PrefixQuery < SearchQuery
|
809
|
+
# @return [Float]
|
810
|
+
attr_accessor :boost
|
811
|
+
|
812
|
+
# @return [String]
|
813
|
+
attr_accessor :field
|
814
|
+
|
815
|
+
# @yieldparam [PrefixQuery] self
|
816
|
+
#
|
817
|
+
# @param [String] prefix
|
818
|
+
def initialize(prefix)
|
819
|
+
super()
|
820
|
+
@prefix = prefix
|
821
|
+
yield self if block_given?
|
822
|
+
end
|
823
|
+
|
824
|
+
# @return [String]
|
825
|
+
def to_json(*args)
|
826
|
+
data = {"prefix" => @prefix}
|
827
|
+
data["boost"] = boost if boost
|
828
|
+
data["field"] = field if field
|
829
|
+
data.to_json(*args)
|
830
|
+
end
|
831
|
+
end
|
832
|
+
|
833
|
+
# Prepare {PhraseQuery} body
|
834
|
+
#
|
835
|
+
# @yieldparam [PhraseQuery] query
|
836
|
+
# @param [*String] terms
|
837
|
+
#
|
838
|
+
# @return [PhraseQuery]
|
839
|
+
def self.phrase(*terms, &block)
|
840
|
+
PhraseQuery.new(*terms, &block)
|
841
|
+
end
|
842
|
+
|
843
|
+
# A query that looks for **exact** match of several terms (in the exact order) in the index. Usual better alternative is {MatchPhraseQuery}.
|
844
|
+
class PhraseQuery < SearchQuery
|
845
|
+
# @return [Float]
|
846
|
+
attr_accessor :boost
|
847
|
+
|
848
|
+
# @return [String]
|
849
|
+
attr_accessor :field
|
850
|
+
|
851
|
+
# @yieldparam [PhraseQuery] self
|
852
|
+
#
|
853
|
+
# @param [*String] terms
|
854
|
+
def initialize(*terms)
|
855
|
+
super()
|
856
|
+
@terms = terms.flatten
|
857
|
+
yield self if block_given?
|
858
|
+
end
|
859
|
+
|
860
|
+
# @return [String]
|
861
|
+
def to_json(*args)
|
862
|
+
data = {"terms" => @terms.flatten.uniq}
|
863
|
+
data["boost"] = boost if boost
|
864
|
+
data["field"] = field if field
|
865
|
+
data.to_json(*args)
|
866
|
+
end
|
867
|
+
end
|
868
|
+
|
869
|
+
# Prepare {MatchAllQuery} body
|
870
|
+
#
|
871
|
+
# @yieldparam [MatchAllQuery] query
|
872
|
+
#
|
873
|
+
# @return [MatchAllQuery]
|
874
|
+
def self.match_all(&block)
|
875
|
+
MatchAllQuery.new(&block)
|
876
|
+
end
|
877
|
+
|
878
|
+
# A query that matches all indexed documents.
|
879
|
+
class MatchAllQuery < SearchQuery
|
880
|
+
# @return [Float]
|
881
|
+
attr_accessor :boost
|
882
|
+
|
883
|
+
# @yieldparam [MatchAllQuery] self
|
884
|
+
def initialize
|
885
|
+
super()
|
886
|
+
yield self if block_given?
|
887
|
+
end
|
888
|
+
|
889
|
+
# @return [String]
|
890
|
+
def to_json(*args)
|
891
|
+
data = {"match_all" => nil}
|
892
|
+
data["boost"] = boost if boost
|
893
|
+
data.to_json(*args)
|
894
|
+
end
|
895
|
+
end
|
896
|
+
|
897
|
+
# Prepare {MatchNoneQuery} body
|
898
|
+
#
|
899
|
+
# @yieldparam [MatchNoneQuery] query
|
900
|
+
#
|
901
|
+
# @return [MatchNoneQuery]
|
902
|
+
def self.match_none(&block)
|
903
|
+
MatchNoneQuery.new(&block)
|
904
|
+
end
|
905
|
+
|
906
|
+
# A query that matches nothing.
|
907
|
+
class MatchNoneQuery < SearchQuery
|
908
|
+
# @return [Float]
|
909
|
+
attr_accessor :boost
|
910
|
+
|
911
|
+
# @yieldparam [MatchNoneQuery] self
|
912
|
+
def initialize
|
913
|
+
super()
|
914
|
+
yield self if block_given?
|
915
|
+
end
|
916
|
+
|
917
|
+
# @return [String]
|
918
|
+
def to_json(*args)
|
919
|
+
data = {"match_none" => nil}
|
920
|
+
data["boost"] = boost if boost
|
921
|
+
data.to_json(*args)
|
922
|
+
end
|
923
|
+
end
|
924
|
+
end
|
925
|
+
|
926
|
+
class SearchSort
|
927
|
+
# @yieldparam [SearchSortScore]
|
928
|
+
# @return [SearchSortScore]
|
929
|
+
def self.score(&block)
|
930
|
+
SearchSortScore.new(&block)
|
931
|
+
end
|
932
|
+
|
933
|
+
# @yieldparam [SearchSortId]
|
934
|
+
# @return [SearchSortScore]
|
935
|
+
def self.id(&block)
|
936
|
+
SearchSortId.new(&block)
|
937
|
+
end
|
938
|
+
|
939
|
+
# @param [String] name field name
|
940
|
+
# @yieldparam [SearchSortField]
|
941
|
+
# @return [SearchSortField]
|
942
|
+
def self.field(name, &block)
|
943
|
+
SearchSortField.new(name, &block)
|
944
|
+
end
|
945
|
+
|
946
|
+
# @param [String] name field name
|
947
|
+
# @param [Float] longitude
|
948
|
+
# @param [Float] latitude
|
949
|
+
# @yieldparam [SearchSortField]
|
950
|
+
# @return [SearchSortGeoDistance]
|
951
|
+
def self.geo_distance(name, longitude, latitude, &block)
|
952
|
+
SearchSortGeoDistance.new(name, longitude, latitude, &block)
|
953
|
+
end
|
954
|
+
|
955
|
+
class SearchSortScore < SearchSort
|
956
|
+
# @return [Boolean] if descending order should be applied
|
957
|
+
attr_accessor :desc
|
958
|
+
|
959
|
+
# @yieldparam [SearchSortScore]
|
960
|
+
def initialize
|
961
|
+
yield self if block_given?
|
962
|
+
end
|
963
|
+
|
964
|
+
# @api private
|
965
|
+
def to_json(*args)
|
966
|
+
{by: :score, desc: desc}.to_json(*args)
|
967
|
+
end
|
968
|
+
end
|
969
|
+
|
970
|
+
class SearchSortId < SearchSort
|
971
|
+
# @return [Boolean] if descending order should be applied
|
972
|
+
attr_accessor :desc
|
973
|
+
|
974
|
+
# @yieldparam [SearchSortId]
|
975
|
+
def initialize
|
976
|
+
yield self if block_given?
|
977
|
+
end
|
978
|
+
|
979
|
+
# @api private
|
980
|
+
def to_json(*args)
|
981
|
+
{by: :id, desc: desc}.to_json(*args)
|
982
|
+
end
|
983
|
+
end
|
984
|
+
|
985
|
+
class SearchSortField < SearchSort
|
986
|
+
# @return [String] name of the field to sort by
|
987
|
+
attr_reader :field
|
988
|
+
|
989
|
+
# @return [Boolean] if descending order should be applied
|
990
|
+
attr_accessor :desc
|
991
|
+
|
992
|
+
# @return [:auto, :string, :number, :date]
|
993
|
+
attr_accessor :type
|
994
|
+
|
995
|
+
# @return [:first, :last] where the documents with missing field should be placed
|
996
|
+
attr_accessor :missing
|
997
|
+
|
998
|
+
# @return [:default, :min, :max]
|
999
|
+
attr_accessor :mode
|
1000
|
+
|
1001
|
+
# @param [String] field the name of the filed for ordering
|
1002
|
+
# @yieldparam [SearchSortField]
|
1003
|
+
def initialize(field)
|
1004
|
+
@field = field
|
1005
|
+
yield self if block_given?
|
1006
|
+
end
|
1007
|
+
|
1008
|
+
# @api private
|
1009
|
+
def to_json(*args)
|
1010
|
+
{by: :field, field: field, desc: desc, type: type, missing: missing, mode: mode}.to_json(*args)
|
1011
|
+
end
|
1012
|
+
end
|
1013
|
+
|
1014
|
+
class SearchSortGeoDistance < SearchSort
|
1015
|
+
# @return [String] name of the field to sort by
|
1016
|
+
attr_reader :field
|
1017
|
+
|
1018
|
+
# @return [Boolean] if descending order should be applied
|
1019
|
+
attr_accessor :desc
|
1020
|
+
|
1021
|
+
# @return [Float]
|
1022
|
+
attr_reader :longitude
|
1023
|
+
|
1024
|
+
# @return [Float]
|
1025
|
+
attr_reader :latitude
|
1026
|
+
|
1027
|
+
# @return [:meters, :miles, :centimeters, :millimeters, :kilometers, :nauticalmiles, :feet, :yards, :inch]
|
1028
|
+
attr_accessor :unit
|
1029
|
+
|
1030
|
+
# @param [String] field field name
|
1031
|
+
# @param [Float] longitude
|
1032
|
+
# @param [Float] latitude
|
1033
|
+
# @yieldparam [SearchSortGeoDistance]
|
1034
|
+
def initialize(field, longitude, latitude)
|
1035
|
+
@field = field
|
1036
|
+
@longitude = longitude
|
1037
|
+
@latitude = latitude
|
1038
|
+
yield self if block_given?
|
1039
|
+
end
|
1040
|
+
|
1041
|
+
# @api private
|
1042
|
+
def to_json(*args)
|
1043
|
+
{by: :geo_distance, field: field, desc: desc, location: [longitude, latitude], unit: unit}.to_json(*args)
|
1044
|
+
end
|
1045
|
+
end
|
1046
|
+
end
|
1047
|
+
|
1048
|
+
class SearchFacet
|
1049
|
+
# @param [String] field_name
|
1050
|
+
# @return [SearchFacetTerm]
|
1051
|
+
def self.term(field_name, &block)
|
1052
|
+
SearchFacetTerm.new(field_name, &block)
|
1053
|
+
end
|
1054
|
+
|
1055
|
+
# @param [String] field_name
|
1056
|
+
# @return [SearchFacetNumericRange]
|
1057
|
+
def self.numeric_range(field_name, &block)
|
1058
|
+
SearchFacetNumericRange.new(field_name, &block)
|
1059
|
+
end
|
1060
|
+
|
1061
|
+
# @param [String] field_name
|
1062
|
+
# @return [SearchFacetDateRange]
|
1063
|
+
def self.date_range(field_name, &block)
|
1064
|
+
SearchFacetDateRange.new(field_name, &block)
|
1065
|
+
end
|
1066
|
+
|
1067
|
+
class SearchFacetTerm
|
1068
|
+
# @return [String]
|
1069
|
+
attr_reader :field
|
1070
|
+
|
1071
|
+
# @return [Integer]
|
1072
|
+
attr_accessor :size
|
1073
|
+
|
1074
|
+
# @param [String] field name of the field
|
1075
|
+
def initialize(field)
|
1076
|
+
@field = field
|
1077
|
+
yield self if block_given?
|
1078
|
+
end
|
1079
|
+
|
1080
|
+
# @api private
|
1081
|
+
def to_json(*args)
|
1082
|
+
{field: field, size: size}.to_json(*args)
|
1083
|
+
end
|
1084
|
+
end
|
1085
|
+
|
1086
|
+
class SearchFacetNumericRange
|
1087
|
+
# @return [String]
|
1088
|
+
attr_reader :field
|
1089
|
+
|
1090
|
+
# @return [Integer]
|
1091
|
+
attr_accessor :size
|
1092
|
+
|
1093
|
+
# @param [String] field name of the field
|
1094
|
+
def initialize(field)
|
1095
|
+
@field = field
|
1096
|
+
@ranges = []
|
1097
|
+
yield self if block_given?
|
1098
|
+
end
|
1099
|
+
|
1100
|
+
# @param [String] name the name of the range
|
1101
|
+
# @param [Integer, Float, nil] min lower bound of the range (pass +nil+ if there is no lower bound)
|
1102
|
+
# @param [Integer, Float, nil] max upper bound of the range (pass +nil+ if there is no upper bound)
|
1103
|
+
def add(name, min, max)
|
1104
|
+
@ranges.append({name: name, min: min, max: max})
|
1105
|
+
end
|
1106
|
+
|
1107
|
+
# @api private
|
1108
|
+
def to_json(*args)
|
1109
|
+
{field: field, size: size, numeric_ranges: @ranges}.to_json(*args)
|
1110
|
+
end
|
1111
|
+
end
|
1112
|
+
|
1113
|
+
class SearchFacetDateRange
|
1114
|
+
# @return [String]
|
1115
|
+
attr_reader :field
|
1116
|
+
|
1117
|
+
# @return [Integer]
|
1118
|
+
attr_accessor :size
|
1119
|
+
|
1120
|
+
# @param [String] field name of the field
|
1121
|
+
def initialize(field)
|
1122
|
+
@field = field
|
1123
|
+
@ranges = []
|
1124
|
+
yield self if block_given?
|
1125
|
+
end
|
1126
|
+
|
1127
|
+
DATE_FORMAT_RFC3339 = "%Y-%m-%dT%H:%M:%S%:z"
|
1128
|
+
|
1129
|
+
# @param [String] name the name of the range
|
1130
|
+
# @param [Time, String, nil] start_time lower bound of the range (pass +nil+ if there is no lower bound)
|
1131
|
+
# @param [Time, String, nil] end_time lower bound of the range (pass +nil+ if there is no lower bound)
|
1132
|
+
def add(name, start_time, end_time)
|
1133
|
+
start_time = start_time.strftime(DATE_FORMAT_RFC3339) if start_time && start_time.respond_to?(:strftime)
|
1134
|
+
end_time = end_time.strftime(DATE_FORMAT_RFC3339) if end_time && end_time.respond_to?(:strftime)
|
1135
|
+
@ranges.append({name: name, start: start_time, end: end_time})
|
1136
|
+
end
|
1137
|
+
|
1138
|
+
# @api private
|
1139
|
+
def to_json(*args)
|
1140
|
+
{field: field, size: size, date_ranges: @ranges}.to_json(*args)
|
1141
|
+
end
|
1142
|
+
end
|
1143
|
+
end
|
1144
|
+
|
1145
|
+
class SearchOptions
|
1146
|
+
# @return [Integer] Timeout in milliseconds
|
1147
|
+
attr_accessor :timeout
|
1148
|
+
|
1149
|
+
# @return [Integer] limits the number of matches returned from the complete result set.
|
1150
|
+
attr_accessor :limit
|
1151
|
+
|
1152
|
+
# @return [Integer] indicates how many matches are skipped on the result set before starting to return the matches
|
1153
|
+
attr_accessor :skip
|
1154
|
+
|
1155
|
+
# @return [Boolean] triggers inclusion of additional search result score explanations. (Default: +false+)
|
1156
|
+
attr_accessor :explain
|
1157
|
+
|
1158
|
+
# @return [:html, :ansi] the style of highlighting in the result excerpts (if not specified, the server default will be used)
|
1159
|
+
attr_accessor :highlight_style
|
1160
|
+
|
1161
|
+
# @return [Array<String>] list of the fields to highlight
|
1162
|
+
attr_accessor :highlight_fields
|
1163
|
+
|
1164
|
+
# @return [Array<String>] list of field values which should be retrieved for result documents, provided they were stored while indexing
|
1165
|
+
attr_accessor :fields
|
1166
|
+
|
1167
|
+
# Customizes the consistency guarantees for this query
|
1168
|
+
#
|
1169
|
+
# @note overrides consistency level set by {#consistent_with}
|
1170
|
+
#
|
1171
|
+
# [+:not_bounded+] The engine will return whatever state it has at the time of query
|
1172
|
+
#
|
1173
|
+
# @param [:not_bounded] level the scan consistency to be used for this query
|
1174
|
+
#
|
1175
|
+
# @return [void]
|
1176
|
+
def scan_consistency=(level)
|
1177
|
+
@mutation_state = nil if @mutation_state
|
1178
|
+
@scan_consistency = level
|
1179
|
+
end
|
1180
|
+
|
1181
|
+
# Sets the mutation tokens this query should be consistent with
|
1182
|
+
#
|
1183
|
+
# @note overrides consistency level set by {#scan_consistency=}
|
1184
|
+
#
|
1185
|
+
# @param [MutationState] mutation_state the mutation state containing the mutation tokens
|
1186
|
+
#
|
1187
|
+
# @return [void]
|
1188
|
+
def consistent_with(mutation_state)
|
1189
|
+
@scan_consistency = nil if @scan_consistency
|
1190
|
+
@mutation_state = mutation_state
|
1191
|
+
end
|
1192
|
+
|
1193
|
+
# Ordering rules to apply to the results
|
1194
|
+
#
|
1195
|
+
# The list might contain either strings or special objects, that derive from {SearchSort}.
|
1196
|
+
#
|
1197
|
+
# In case of String, the value represents the name of the field with optional +-+ in front of the name, which will turn on descending mode for this field. One field is special is +"_score"+ which will sort results by their score.
|
1198
|
+
#
|
1199
|
+
# When nothing specified, the Server will order results by their score descending, which is equivalent of +"-_score"+.
|
1200
|
+
#
|
1201
|
+
# @return [Array<String, SearchSort>] list of ordering object
|
1202
|
+
attr_accessor :sort
|
1203
|
+
|
1204
|
+
# Facets allow to aggregate information collected on a particular result set
|
1205
|
+
#
|
1206
|
+
# @return [Hash<String, SearchFacet]
|
1207
|
+
attr_accessor :facets
|
1208
|
+
|
1209
|
+
# @return [JsonTranscoder] transcoder to use for the results
|
1210
|
+
attr_accessor :transcoder
|
1211
|
+
|
1212
|
+
# @yieldparam [SearchOptions] self
|
1213
|
+
def initialize
|
1214
|
+
@explain = false
|
1215
|
+
@transcoder = JsonTranscoder.new
|
1216
|
+
@scan_consistency = nil
|
1217
|
+
@mutation_state = nil
|
1218
|
+
yield self if block_given?
|
1219
|
+
end
|
1220
|
+
end
|
1221
|
+
|
1222
|
+
class SearchRowLocation
|
1223
|
+
# @return [String]
|
1224
|
+
attr_accessor :field
|
1225
|
+
|
1226
|
+
# @return [String]
|
1227
|
+
attr_accessor :term
|
1228
|
+
|
1229
|
+
# @return [Integer] the position of the term within the field, starting at 1
|
1230
|
+
attr_accessor :position
|
1231
|
+
|
1232
|
+
# @return [Integer] start byte offset of the term in the field
|
1233
|
+
attr_accessor :start_offset
|
1234
|
+
|
1235
|
+
# @return [Integer] end byte offset of the term in the field
|
1236
|
+
attr_accessor :end_offset
|
1237
|
+
|
1238
|
+
# @return [Array<Integer>] the positions of the term within any elements.
|
1239
|
+
attr_accessor :array_positions
|
1240
|
+
end
|
1241
|
+
|
1242
|
+
class SearchRowLocations
|
1243
|
+
# Lists all locations (any field, any term)
|
1244
|
+
#
|
1245
|
+
# @return [Array<SearchRowLocation>]
|
1246
|
+
def get_all
|
1247
|
+
@locations
|
1248
|
+
end
|
1249
|
+
|
1250
|
+
# Lists all locations for a given field (any term)
|
1251
|
+
#
|
1252
|
+
# @return [Array<SearchRowLocation>]
|
1253
|
+
def get_for_field(name)
|
1254
|
+
@locations.select { |location| location.field == name }
|
1255
|
+
end
|
1256
|
+
|
1257
|
+
# Lists all locations for a given field and term
|
1258
|
+
#
|
1259
|
+
# @return [Array<SearchRowLocation>]
|
1260
|
+
def get_for_field_and_term(name, term)
|
1261
|
+
@locations.select { |location| location.field == name && location.term == term }
|
1262
|
+
end
|
1263
|
+
|
1264
|
+
# Lists the fields in this location
|
1265
|
+
#
|
1266
|
+
# @return [Array<String>]
|
1267
|
+
def fields
|
1268
|
+
@locations.map { |location| location.field }.uniq
|
1269
|
+
end
|
1270
|
+
|
1271
|
+
# Lists all terms in this locations, considering all fields
|
1272
|
+
#
|
1273
|
+
# @return [Array<String>]
|
1274
|
+
def terms
|
1275
|
+
@locations.map { |location| location.term }.uniq
|
1276
|
+
end
|
1277
|
+
|
1278
|
+
# Lists the terms for a given field
|
1279
|
+
#
|
1280
|
+
# @return [Array<String>]
|
1281
|
+
def terms_for_field(name)
|
1282
|
+
get_for_field(name).map { |location| location.term }.uniq
|
1283
|
+
end
|
1284
|
+
|
1285
|
+
# @param [Array<SearchRowLocation>] locations
|
1286
|
+
def initialize(locations)
|
1287
|
+
@locations = locations
|
1288
|
+
end
|
1289
|
+
end
|
1290
|
+
|
1291
|
+
# An individual facet result has both metadata and details, as each facet can define ranges into which results are
|
1292
|
+
# categorized
|
1293
|
+
class SearchFacetResult
|
1294
|
+
# @return [String]
|
1295
|
+
attr_accessor :name
|
1296
|
+
|
1297
|
+
# @return [String]
|
1298
|
+
attr_accessor :field
|
1299
|
+
|
1300
|
+
# @return [Integer]
|
1301
|
+
attr_accessor :total
|
1302
|
+
|
1303
|
+
# @return [Integer]
|
1304
|
+
attr_accessor :missing
|
1305
|
+
|
1306
|
+
# @return [Integer]
|
1307
|
+
attr_accessor :other
|
1308
|
+
|
1309
|
+
class TermFacetResult < SearchFacetResult
|
1310
|
+
# @return [Array<TermFacet>]
|
1311
|
+
attr_accessor :terms
|
1312
|
+
|
1313
|
+
def type
|
1314
|
+
:term_facet
|
1315
|
+
end
|
1316
|
+
|
1317
|
+
# @yieldparam [TermFacetResult] self
|
1318
|
+
def initialize
|
1319
|
+
yield self if block_given?
|
1320
|
+
end
|
1321
|
+
|
1322
|
+
class TermFacet
|
1323
|
+
# @return [String]
|
1324
|
+
attr_reader :term
|
1325
|
+
|
1326
|
+
# @return [Integer]
|
1327
|
+
attr_reader :count
|
1328
|
+
|
1329
|
+
def initialize(term, count)
|
1330
|
+
@term = term
|
1331
|
+
@count = count
|
1332
|
+
end
|
1333
|
+
end
|
1334
|
+
end
|
1335
|
+
|
1336
|
+
class DateRangeFacetResult < SearchFacetResult
|
1337
|
+
# @return [Array<DateRangeFacet>]
|
1338
|
+
attr_accessor :date_ranges
|
1339
|
+
|
1340
|
+
def type
|
1341
|
+
:date_range_facet
|
1342
|
+
end
|
1343
|
+
|
1344
|
+
# @yieldparam [DateRangeFacetResult] self
|
1345
|
+
def initialize
|
1346
|
+
yield self if block_given?
|
1347
|
+
end
|
1348
|
+
|
1349
|
+
class DateRangeFacet
|
1350
|
+
# @return [String]
|
1351
|
+
attr_reader :name
|
1352
|
+
|
1353
|
+
# @return [Integer]
|
1354
|
+
attr_reader :count
|
1355
|
+
|
1356
|
+
# @return [String]
|
1357
|
+
attr_reader :start_time
|
1358
|
+
|
1359
|
+
# @return [String]
|
1360
|
+
attr_reader :end_time
|
1361
|
+
|
1362
|
+
def initialize(name, count, start_time, end_time)
|
1363
|
+
@name = name
|
1364
|
+
@count = count
|
1365
|
+
@start_time = start_time
|
1366
|
+
@end_time = end_time
|
1367
|
+
end
|
1368
|
+
end
|
1369
|
+
end
|
1370
|
+
|
1371
|
+
class NumericRangeFacetResult < SearchFacetResult
|
1372
|
+
# @return [Array<NumericRangeFacet>]
|
1373
|
+
attr_accessor :numeric_ranges
|
1374
|
+
|
1375
|
+
def type
|
1376
|
+
:numeric_range_facet
|
1377
|
+
end
|
1378
|
+
|
1379
|
+
# @yieldparam [NumericRangeFacetResult] self
|
1380
|
+
def initialize
|
1381
|
+
yield self if block_given?
|
1382
|
+
end
|
1383
|
+
|
1384
|
+
class NumericRangeFacet
|
1385
|
+
# @return [String]
|
1386
|
+
attr_reader :name
|
1387
|
+
|
1388
|
+
# @return [Integer]
|
1389
|
+
attr_reader :count
|
1390
|
+
|
1391
|
+
# @return [Integer, Float, nil]
|
1392
|
+
attr_reader :min
|
1393
|
+
|
1394
|
+
# @return [Integer, Float, nil]
|
1395
|
+
attr_reader :max
|
1396
|
+
|
1397
|
+
def initialize(name, count, min, max)
|
1398
|
+
@name = name
|
1399
|
+
@count = count
|
1400
|
+
@min = min
|
1401
|
+
@max = max
|
1402
|
+
end
|
1403
|
+
end
|
1404
|
+
end
|
1405
|
+
end
|
1406
|
+
|
1407
|
+
class SearchRow
|
1408
|
+
# @return [String] name of the index
|
1409
|
+
attr_accessor :index
|
1410
|
+
|
1411
|
+
# @return [String] document identifier
|
1412
|
+
attr_accessor :id
|
1413
|
+
|
1414
|
+
# @return [Float]
|
1415
|
+
attr_accessor :score
|
1416
|
+
|
1417
|
+
# @return [SearchRowLocations]
|
1418
|
+
attr_accessor :locations
|
1419
|
+
|
1420
|
+
# @return [Hash]
|
1421
|
+
attr_accessor :explanation
|
1422
|
+
|
1423
|
+
# @return [Hash<String, Array<String>>]
|
1424
|
+
attr_accessor :fragments
|
1425
|
+
|
1426
|
+
# @return [JsonTranscoder] transcoder to use for the fields
|
1427
|
+
attr_accessor :transcoder
|
1428
|
+
|
1429
|
+
def fields
|
1430
|
+
@transcoder.decode(@fields, :json) if @fields && @transcoder
|
1431
|
+
end
|
1432
|
+
|
1433
|
+
# @yieldparam [SearchRow] self
|
1434
|
+
def initialize
|
1435
|
+
@fields = nil
|
1436
|
+
yield self if block_given?
|
1437
|
+
end
|
1438
|
+
end
|
1439
|
+
|
1440
|
+
class SearchMetrics
|
1441
|
+
# @return [Integer] time spent executing the query (in milliseconds)
|
1442
|
+
attr_accessor :took
|
1443
|
+
|
1444
|
+
# @return [Integer]
|
1445
|
+
attr_accessor :total_rows
|
1446
|
+
|
1447
|
+
# @return [Float]
|
1448
|
+
attr_accessor :max_score
|
1449
|
+
|
1450
|
+
# @return [Integer]
|
1451
|
+
attr_accessor :success_partition_count
|
1452
|
+
|
1453
|
+
# @return [Integer]
|
1454
|
+
attr_accessor :error_partition_count
|
1455
|
+
|
1456
|
+
# @return [Integer]
|
1457
|
+
def total_partition_count
|
1458
|
+
success_partition_count + error_partition_count
|
1459
|
+
end
|
1460
|
+
end
|
1461
|
+
|
1462
|
+
class SearchMetaData
|
1463
|
+
# @return [SearchMetrics]
|
1464
|
+
attr_accessor :metrics
|
1465
|
+
|
1466
|
+
# @return [Hash<String, String>]
|
1467
|
+
attr_accessor :errors
|
1468
|
+
|
1469
|
+
# @yieldparam [SearchMetaData] self
|
1470
|
+
def initialize
|
1471
|
+
@metrics = SearchMetrics.new
|
1472
|
+
yield self if block_given?
|
1473
|
+
end
|
1474
|
+
end
|
1475
|
+
|
1476
|
+
class SearchResult
|
1477
|
+
# @return [Array<SearchRow>]
|
1478
|
+
attr_accessor :rows
|
1479
|
+
|
1480
|
+
# @return [Hash<String, SearchFacetResult>]
|
1481
|
+
attr_accessor :facets
|
1482
|
+
|
1483
|
+
# @return [SearchMetaData]
|
1484
|
+
attr_accessor :meta_data
|
1485
|
+
|
1486
|
+
# @yieldparam [SearchResult] self
|
1487
|
+
def initialize
|
1488
|
+
yield self if block_given?
|
1489
|
+
end
|
1490
|
+
end
|
1491
|
+
end
|
1492
|
+
end
|