Class: Redis::Commands::Search::Query

Inherits:
Object
  • Object
show all
Defined in:
lib/redis/commands/modules/search/query.rb

Overview

A fluent builder for FT.SEARCH query strings and their options.

Chainable setter methods (+filter+, paging, sort_by, ...) return self so calls can be strung together, and a predicate DSL (+tag+/+text+/+numeric+ combined with +and_+/+or_+) builds the query string itself. #to_redis_args renders the whole thing into the argument array passed to FT.SEARCH.

Examples:

query = Redis::Commands::Search::Query.new("hello")
  .paging(0, 10)
  .sort_by("price", asc: false)
query.to_redis_args # => ["hello", "SORTBY", "price", "DESC", "LIMIT", 0, 10, "DIALECT", 2]

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(base = nil) ⇒ Query

Returns a new instance of Query.

Parameters:

  • base (String, nil) (defaults to: nil)

    an optional base query string used when no predicates are added



30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
# File 'lib/redis/commands/modules/search/query.rb', line 30

def initialize(base = nil)
  @base = base
  @predicate_collection = [PredicateCollection.new(:and)]
  @filters = []
  @options = { dialect: DEFAULT_DIALECT } # Default dialect, matching redis-py
  @return_fields = []
  @return_fields_decode = {}
  @summarize_options = nil
  @highlight_options = nil
  @language = nil
  @verbatim = false
  @no_stopwords = false
  @with_payloads = false
  @slop = nil
  @in_order = false
  @no_content = false
  @limit_ids = nil
end

Instance Attribute Details

#filtersHash, ... (readonly)

Returns:

  • (Hash)

    the accumulated query options (dialect, limit, sortby, scorer, ...)

  • (Hash)

    map of return-field name => whether its value should be JSON-decoded

  • (Array<Array>)

    the FILTER clauses as [field, min, max] triples

  • (Array<Array>, nil)

    the GEOFILTER clauses as [field, lon, lat, radius, unit] tuples



23
24
25
# File 'lib/redis/commands/modules/search/query.rb', line 23

def filters
  @filters
end

#geo_filtersHash, ... (readonly)

Returns:

  • (Hash)

    the accumulated query options (dialect, limit, sortby, scorer, ...)

  • (Hash)

    map of return-field name => whether its value should be JSON-decoded

  • (Array<Array>)

    the FILTER clauses as [field, min, max] triples

  • (Array<Array>, nil)

    the GEOFILTER clauses as [field, lon, lat, radius, unit] tuples



23
24
25
# File 'lib/redis/commands/modules/search/query.rb', line 23

def geo_filters
  @geo_filters
end

#highlight_optionsArray, ...

Returns:

  • (Array)

    the fields to RETURN

  • (Hash, nil)

    the HIGHLIGHT options

  • (Hash, nil)

    the SUMMARIZE options



27
28
29
# File 'lib/redis/commands/modules/search/query.rb', line 27

def highlight_options
  @highlight_options
end

#optionsHash, ... (readonly)

Returns:

  • (Hash)

    the accumulated query options (dialect, limit, sortby, scorer, ...)

  • (Hash)

    map of return-field name => whether its value should be JSON-decoded

  • (Array<Array>)

    the FILTER clauses as [field, min, max] triples

  • (Array<Array>, nil)

    the GEOFILTER clauses as [field, lon, lat, radius, unit] tuples



23
24
25
# File 'lib/redis/commands/modules/search/query.rb', line 23

def options
  @options
end

#return_fieldsArray, ...

Returns:

  • (Array)

    the fields to RETURN

  • (Hash, nil)

    the HIGHLIGHT options

  • (Hash, nil)

    the SUMMARIZE options



27
28
29
# File 'lib/redis/commands/modules/search/query.rb', line 27

def return_fields
  @return_fields
end

#return_fields_decodeHash, ... (readonly)

Returns:

  • (Hash)

    the accumulated query options (dialect, limit, sortby, scorer, ...)

  • (Hash)

    map of return-field name => whether its value should be JSON-decoded

  • (Array<Array>)

    the FILTER clauses as [field, min, max] triples

  • (Array<Array>, nil)

    the GEOFILTER clauses as [field, lon, lat, radius, unit] tuples



23
24
25
# File 'lib/redis/commands/modules/search/query.rb', line 23

def return_fields_decode
  @return_fields_decode
end

#summarize_optionsArray, ...

Returns:

  • (Array)

    the fields to RETURN

  • (Hash, nil)

    the HIGHLIGHT options

  • (Hash, nil)

    the SUMMARIZE options



27
28
29
# File 'lib/redis/commands/modules/search/query.rb', line 27

def summarize_options
  @summarize_options
end

Class Method Details

.build { ... } ⇒ Query

Build a Query by evaluating block in the context of a fresh instance.

Examples:

Redis::Commands::Search::Query.build { paging(0, 5); sort_by("name") }

Yields:

  • evaluated via instance_eval against the new Query

Returns:

  • (Query)

    the populated query



56
57
58
59
60
# File 'lib/redis/commands/modules/search/query.rb', line 56

def self.build(&block)
  instance = new
  instance.instance_eval(&block)
  instance
end

Instance Method Details

#add_predicate(predicate) ⇒ self

Add a built predicate to the current collection.

Parameters:

  • predicate (Predicate)

    the predicate to add

Returns:

  • (self)


398
399
400
401
# File 'lib/redis/commands/modules/search/query.rb', line 398

def add_predicate(predicate)
  @predicate_collection.last.add(predicate)
  self
end

#and_ { ... } ⇒ self

Open an AND group: predicates added inside block are joined with a space.

Yields:

  • builds predicates that are combined with AND

Returns:

  • (self)


390
391
392
# File 'lib/redis/commands/modules/search/query.rb', line 390

def and_(&block)
  new_collection(:and, &block)
end

#dialect(dialect_version) ⇒ self

Set the query DIALECT version.

Parameters:

  • dialect_version (Integer)

    the dialect version (defaults to 2)

Returns:

  • (self)


304
305
306
307
# File 'lib/redis/commands/modules/search/query.rb', line 304

def dialect(dialect_version)
  @options[:dialect] = dialect_version
  self
end

#evaluate { ... } ⇒ Object?

Evaluate block against this query (used to populate it via the DSL).

Yields:

  • evaluated via instance_eval against this query

Returns:

  • (Object, nil)

    the block's return value, or nil when no block is given



473
474
475
476
477
# File 'lib/redis/commands/modules/search/query.rb', line 473

def evaluate(&block)
  if block_given?
    instance_eval(&block)
  end
end

#expander(expander_name) ⇒ self

Set the query EXPANDER (custom query expander).

Parameters:

  • expander_name (String)

    the expander name

Returns:

  • (self)


189
190
191
192
# File 'lib/redis/commands/modules/search/query.rb', line 189

def expander(expander_name)
  @expander = expander_name
  self
end

#expander_valueObject

:nodoc:



370
371
372
# File 'lib/redis/commands/modules/search/query.rb', line 370

def expander_value
  @expander
end

#explain_scoreself

Return an explanation of the score of each document (+EXPLAINSCORE+).

Returns:

  • (self)


312
313
314
315
# File 'lib/redis/commands/modules/search/query.rb', line 312

def explain_score
  @options[:explainscore] = true
  self
end

#filter(field, min, max = nil) ⇒ self

Add a numeric FILTER clause.

Bounds are validated so a malformed or untrusted value can't inject query syntax, the same guarantee the numeric-predicate DSL gives. Beyond plain numbers, the RediSearch bound forms +inf+/+-inf+ and the exclusive ( prefix (e.g. "(10") are accepted.

Parameters:

  • field (String)

    the numeric field name

  • min (Numeric, String)

    the lower bound (also used as upper bound when max is nil)

  • max (Numeric, String, nil) (defaults to: nil)

    the upper bound

Returns:

  • (self)

Raises:

  • (ArgumentError)

    if a bound is neither numeric nor a valid RediSearch bound token



73
74
75
76
77
# File 'lib/redis/commands/modules/search/query.rb', line 73

def filter(field, min, max = nil)
  max ||= min
  @filters << [field, coerce_filter_bound(min), coerce_filter_bound(max)]
  self
end

#geo_filter(field, lon, lat, radius, unit = 'km') ⇒ self

Add a GEOFILTER clause restricting results to a radius around a point.

Parameters:

  • field (String)

    the geo field name

  • lon (Numeric)

    the longitude of the center

  • lat (Numeric)

    the latitude of the center

  • radius (Numeric)

    the radius

  • unit (String) (defaults to: 'km')

    the radius unit ("m", "km", "mi", "ft")

Returns:

  • (self)


87
88
89
90
91
# File 'lib/redis/commands/modules/search/query.rb', line 87

def geo_filter(field, lon, lat, radius, unit = 'km')
  @geo_filters ||= []
  @geo_filters << [field, lon, lat, radius, unit]
  self
end

#highlight(fields: nil, tags: ["<b>", "</b>"]) ⇒ self

Configure result +HIGHLIGHT+ing.

Parameters:

  • fields (String, Array<String>, nil) (defaults to: nil)

    the fields to highlight (all when nil)

  • tags (Array<String>) (defaults to: ["<b>", "</b>"])

    the opening and closing tags wrapped around matches

Returns:

  • (self)


275
276
277
278
279
280
281
# File 'lib/redis/commands/modules/search/query.rb', line 275

def highlight(fields: nil, tags: ["<b>", "</b>"])
  @highlight_options = {
    fields: Array(fields),
    tags: tags
  }
  self
end

#in_orderself

Require query terms to appear in the same order as in the query (+INORDER+).

Returns:

  • (self)


239
240
241
242
# File 'lib/redis/commands/modules/search/query.rb', line 239

def in_order
  @in_order = true
  self
end

#in_order_valueObject

:nodoc:



355
356
357
# File 'lib/redis/commands/modules/search/query.rb', line 355

def in_order_value
  @in_order
end

#language(lang) ⇒ self

Set the query LANGUAGE used for stemming.

Parameters:

  • lang (String)

    the language name, e.g. "english"

Returns:

  • (self)


172
173
174
175
# File 'lib/redis/commands/modules/search/query.rb', line 172

def language(lang)
  @language = lang
  self
end

#language_valueObject

:nodoc:



325
326
327
# File 'lib/redis/commands/modules/search/query.rb', line 325

def language_value
  @language
end

#limit_fields(*fields) ⇒ self

Restrict the search to the given fields (+INFIELDS+).

Parameters:

  • fields (Array<String>)

    the field names to search within

Returns:

  • (self)


265
266
267
268
# File 'lib/redis/commands/modules/search/query.rb', line 265

def limit_fields(*fields)
  @limit_fields = fields
  self
end

#limit_fields_valueObject

:nodoc:



365
366
367
# File 'lib/redis/commands/modules/search/query.rb', line 365

def limit_fields_value
  @limit_fields
end

#limit_ids(*ids) ⇒ self

Restrict the search to the given document ids (+INKEYS+).

Parameters:

  • ids (Array<String>)

    the document ids to limit to

Returns:

  • (self)


97
98
99
100
# File 'lib/redis/commands/modules/search/query.rb', line 97

def limit_ids(*ids)
  @limit_ids = ids
  self
end

#limit_ids_valueObject

Internal getters used by Index#search to read accumulated state - unlike the chainable setters above, these return the stored value rather than self. :nodoc:



320
321
322
# File 'lib/redis/commands/modules/search/query.rb', line 320

def limit_ids_value
  @limit_ids
end

#new_collection(type) { ... } ⇒ self

Push a new predicate collection of type, evaluate the block against it, then fold it back into the parent collection.

Parameters:

  • type (Symbol)

    :and or :or

Yields:

  • builds predicates inside the new collection

Returns:

  • (self)


409
410
411
412
413
414
415
416
# File 'lib/redis/commands/modules/search/query.rb', line 409

def new_collection(type)
  collection = PredicateCollection.new(type)
  @predicate_collection << collection
  yield if block_given?
  @predicate_collection.pop
  @predicate_collection.last.add(collection)
  self
end

#no_contentself

Return only document ids, without their contents (+NOCONTENT+).

Returns:

  • (self)


256
257
258
259
# File 'lib/redis/commands/modules/search/query.rb', line 256

def no_content
  @no_content = true
  self
end

#no_content_valueObject

:nodoc:



340
341
342
# File 'lib/redis/commands/modules/search/query.rb', line 340

def no_content_value
  @no_content
end

#no_stopwordsself

Do not filter out stopwords (+NOSTOPWORDS+).

Returns:

  • (self)


197
198
199
200
# File 'lib/redis/commands/modules/search/query.rb', line 197

def no_stopwords
  @no_stopwords = true
  self
end

#no_stopwords_valueObject

:nodoc:



335
336
337
# File 'lib/redis/commands/modules/search/query.rb', line 335

def no_stopwords_value
  @no_stopwords
end

#numeric(field) ⇒ NumericField

Begin a numeric-field predicate bound to this query (call +.gt+/+.lt+/+.between+ on it).

Parameters:

  • field (String)

    the numeric field name

Returns:



438
439
440
# File 'lib/redis/commands/modules/search/query.rb', line 438

def numeric(field)
  NumericField.new(field, self)
end

#or_ { ... } ⇒ self

Open an OR group: predicates added inside block are joined with |.

Yields:

  • builds predicates that are combined with OR

Returns:

  • (self)


382
383
384
# File 'lib/redis/commands/modules/search/query.rb', line 382

def or_(&block)
  new_collection(:or, &block)
end

#paging(offset, limit) ⇒ self

Set the LIMIT (paging) for the result set.

Parameters:

  • offset (Integer)

    the index of the first result to return

  • limit (Integer)

    the maximum number of results to return

Returns:

  • (self)


107
108
109
110
# File 'lib/redis/commands/modules/search/query.rb', line 107

def paging(offset, limit)
  @options[:limit] = [offset, limit]
  self
end

#return(*fields) ⇒ self

Set the fields to RETURN, replacing any previously configured ones.

Parameters:

  • fields (Array<String>)

    the field names to return

Returns:

  • (self)


138
139
140
141
142
143
144
# File 'lib/redis/commands/modules/search/query.rb', line 138

def return(*fields)
  @return_fields = fields
  # Replacing the RETURN list drops any decode flags set via #return_field; otherwise a
  # stale flag would keep decoding an overlapping field name in the new list.
  @return_fields_decode = {}
  self
end

#return_field(field, as_field: nil, decode_field: true) ⇒ self

Append a single field to RETURN, optionally aliased and/or JSON-decoded.

Parameters:

  • field (String)

    the field name to return

  • as_field (String, nil) (defaults to: nil)

    an alias to expose the field under (+AS+)

  • decode_field (Boolean) (defaults to: true)

    whether the returned value should be JSON-decoded in results

Returns:

  • (self)


152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
# File 'lib/redis/commands/modules/search/query.rb', line 152

def return_field(field, as_field: nil, decode_field: true)
  @return_fields ||= []
  @return_fields_decode ||= {}

  @return_fields << field
  # Results are keyed by the alias when one is given (Redis returns the value under AS),
  # so the decode flag must be keyed the same way or hashify_fields won't match it.
  @return_fields_decode[as_field || field] = decode_field

  if as_field
    @return_fields << "AS" << as_field
  end

  self
end

#scorer(scorer_name) ⇒ self

Set the scoring function (+SCORER+).

Parameters:

  • scorer_name (String)

    the scorer name, e.g. "BM25"

Returns:

  • (self)


214
215
216
217
# File 'lib/redis/commands/modules/search/query.rb', line 214

def scorer(scorer_name)
  @options[:scorer] = scorer_name
  self
end

#slop(value) ⇒ self

Set the allowed SLOP (number of intervening terms) for phrase matching.

Parameters:

  • value (Integer)

    the slop value

Returns:

  • (self)


231
232
233
234
# File 'lib/redis/commands/modules/search/query.rb', line 231

def slop(value)
  @slop = value
  self
end

#slop_valueObject

:nodoc:



350
351
352
# File 'lib/redis/commands/modules/search/query.rb', line 350

def slop_value
  @slop
end

#sort_by(field, order = nil, asc: true) ⇒ self

Set the SORTBY field and direction.

Supports both styles: a positional order ("ASC"/"DESC" symbol or string) and the asc: keyword. When order is given it takes precedence over asc:.

Parameters:

  • field (String)

    the field to sort by

  • order (Symbol, String, nil) (defaults to: nil)

    explicit order, e.g. :asc or "DESC"

  • asc (Boolean) (defaults to: true)

    sort ascending when true, descending otherwise (used when order is nil)

Returns:

  • (self)


121
122
123
124
125
126
127
128
129
130
131
132
# File 'lib/redis/commands/modules/search/query.rb', line 121

def sort_by(field, order = nil, asc: true)
  # Support both old style (order as symbol/string) and new style (asc as boolean)
  direction = if order.nil?
    # New style: use asc parameter
    asc ? "ASC" : "DESC"
  else
    # Old style: order is a symbol or string
    order.to_s.upcase
  end
  @options[:sortby] = [field, direction]
  self
end

#summarize(fields: nil, separator: "...", len: 20, frags: 3) ⇒ self

Configure result +SUMMARIZE+ation (fragment extraction around matches).

Parameters:

  • fields (String, Array<String>, nil) (defaults to: nil)

    the fields to summarize (all when nil)

  • separator (String) (defaults to: "...")

    the string placed between fragments

  • len (Integer) (defaults to: 20)

    the number of words per fragment

  • frags (Integer) (defaults to: 3)

    the number of fragments to extract

Returns:

  • (self)


290
291
292
293
294
295
296
297
298
# File 'lib/redis/commands/modules/search/query.rb', line 290

def summarize(fields: nil, separator: "...", len: 20, frags: 3)
  @summarize_options = {
    fields: Array(fields),
    separator: separator,
    len: len,
    frags: frags
  }
  self
end

#tag(field) ⇒ TagField

Begin a tag-field predicate bound to this query (call .eq on the result).

Parameters:

  • field (String)

    the tag field name

Returns:

  • (TagField)

    a field bound to this query



422
423
424
# File 'lib/redis/commands/modules/search/query.rb', line 422

def tag(field)
  TagField.new(field, self)
end

#text(field) ⇒ TextField

Begin a text-field predicate bound to this query (call .match on the result).

Parameters:

  • field (String)

    the text field name

Returns:

  • (TextField)

    a field bound to this query



430
431
432
# File 'lib/redis/commands/modules/search/query.rb', line 430

def text(field)
  TextField.new(field, self)
end

#timeout(milliseconds) ⇒ self

Set the query TIMEOUT.

Parameters:

  • milliseconds (Integer)

    the timeout in milliseconds

Returns:

  • (self)


248
249
250
251
# File 'lib/redis/commands/modules/search/query.rb', line 248

def timeout(milliseconds)
  @timeout = milliseconds
  self
end

#timeout_valueObject

:nodoc:



360
361
362
# File 'lib/redis/commands/modules/search/query.rb', line 360

def timeout_value
  @timeout
end

#to_redis_argsArray

Render the query and all configured options into the FT.SEARCH argument array.

Returns:

  • (Array)

    the argument array whose first element is the query string and whose remaining elements are option tokens



446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
# File 'lib/redis/commands/modules/search/query.rb', line 446

def to_redis_args
  args = [query_string]
  append_boolean_options(args)
  append_scorer(args)
  append_with_scores(args)
  append_limit_fields(args)
  append_limit_ids(args)
  append_filters(args)
  append_geo_filters(args)
  append_return_fields(args)
  append_summarize_options(args)
  append_highlight_options(args)
  append_slop(args)
  append_timeout(args)
  append_language(args)
  append_expander(args)
  append_in_order(args)
  append_sort_by(args)
  append_limit(args)
  append_dialect(args)
  args.flatten
end

#verbatimself

Disable stemming for the query (+VERBATIM+).

Returns:

  • (self)


180
181
182
183
# File 'lib/redis/commands/modules/search/query.rb', line 180

def verbatim
  @verbatim = true
  self
end

#verbatim_valueObject

:nodoc:



330
331
332
# File 'lib/redis/commands/modules/search/query.rb', line 330

def verbatim_value
  @verbatim
end

#with_payloadsself

Return the payload attached to each document (+WITHPAYLOADS+).

Returns:

  • (self)


222
223
224
225
# File 'lib/redis/commands/modules/search/query.rb', line 222

def with_payloads
  @with_payloads = true
  self
end

#with_payloads_valueObject

:nodoc:



345
346
347
# File 'lib/redis/commands/modules/search/query.rb', line 345

def with_payloads_value
  @with_payloads
end

#with_scoresself

Return the relevance score of each document (+WITHSCORES+).

Returns:

  • (self)


205
206
207
208
# File 'lib/redis/commands/modules/search/query.rb', line 205

def with_scores
  @options[:withscores] = true
  self
end