Class: Tantiny::Query

Inherits:
Object
  • Object
show all
Defined in:
lib/tantiny/query.rb

Constant Summary collapse

TYPES =
%i[
  all empty term fuzzy_term
  phrase regex range facet
  smart prefix
].freeze
DEFAULT_BOOST =
1.0
DEFAULT_FUZZY_DISTANCE =
1

Class Method Summary collapse

Instance Method Summary collapse

Class Method Details

.all_query(_index = nil) ⇒ Object



27
28
29
# File 'lib/tantiny/query.rb', line 27

def all_query(_index = nil)
  __new_all_query
end

.conjunction(*queries) ⇒ Object



17
18
19
20
# File 'lib/tantiny/query.rb', line 17

def conjunction(*queries)
  # @type var queries: Array[untyped]
  queries.one? ? queries.first : __conjunction(queries)
end

.disjunction(*queries) ⇒ Object



22
23
24
25
# File 'lib/tantiny/query.rb', line 22

def disjunction(*queries)
  # @type var queries: Array[untyped]
  queries.one? ? queries.first : __disjunction(queries)
end

.empty_query(_index = nil) ⇒ Object



31
32
33
# File 'lib/tantiny/query.rb', line 31

def empty_query(_index = nil)
  __new_empty_query
end

.facet_query(index, field, path, **options) ⇒ Object



87
88
89
90
# File 'lib/tantiny/query.rb', line 87

def facet_query(index, field, path, **options)
  allowed_fields = index.schema.facet_fields
  construct_query(index, :facet, allowed_fields, field, [path], **options)
end

.fuzzy_term_query(index, fields, term, distance = DEFAULT_FUZZY_DISTANCE, **options) ⇒ Object



40
41
42
43
44
# File 'lib/tantiny/query.rb', line 40

def fuzzy_term_query(index, fields, term, distance = DEFAULT_FUZZY_DISTANCE, **options)
  params = [term.to_s, distance.to_i]
  allowed_fields = text_and_strings(index)
  construct_query(index, :fuzzy_term, allowed_fields, fields, params, **options)
end

.phrase_query(index, fields, phrase, **options) ⇒ Object



46
47
48
49
50
51
52
53
54
# File 'lib/tantiny/query.rb', line 46

def phrase_query(index, fields, phrase, **options)
  queries = [*fields].map do |f|
    terms = index.schema.tokenizer_for(f).terms(phrase)
    allowed_fields = index.schema.text_fields
    construct_query(index, :phrase, allowed_fields, f, [terms], **options)
  end

  queries.empty? ? empty_query : disjunction(*queries)
end

.prefix_query(index, fields, prefix, **options) ⇒ Object



61
62
63
# File 'lib/tantiny/query.rb', line 61

def prefix_query(index, fields, prefix, **options)
  regex_query(index, fields, Regexp.escape(prefix) + ".*", **options)
end

.range_query(index, fields, range, **options) ⇒ Object



65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
# File 'lib/tantiny/query.rb', line 65

def range_query(index, fields, range, **options)
  schema = index.schema

  case range.first
  when Integer
    allowed_fields = schema.integer_fields
    from, to = [range.min, range.max]
  when Float
    allowed_fields = schema.double_fields
    from, to = [range.first, range.last]
  when Date, DateTime
    # @type var range: Range[Date | DateTime]
    allowed_fields = schema.date_fields
    from, to = [Helpers.timestamp(range.first), Helpers.timestamp(range.last)]
  else
    raise UnsupportedRange.new(range.first.class)
  end

  # @type var allowed_fields: Array[Symbol]
  construct_query(index, :range, allowed_fields, fields, [from, to], **options)
end

.regex_query(index, fields, regex, **options) ⇒ Object



56
57
58
59
# File 'lib/tantiny/query.rb', line 56

def regex_query(index, fields, regex, **options)
  allowed_fields = text_and_strings(index)
  construct_query(index, :regex, allowed_fields, fields, [regex.to_s], **options)
end

.smart_query(index, fields, query_string, **options) ⇒ Object



92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
# File 'lib/tantiny/query.rb', line 92

def smart_query(index, fields, query_string, **options)
  fuzzy_distance = options[:fuzzy_distance]
  boost_factor = options.fetch(:boost, DEFAULT_BOOST)

  field_queries = [*fields].map do |field|
    terms = index.schema.tokenizer_for(field).terms(query_string)

    # See: https://github.com/soutaro/steep/issues/272
    # @type block: nil | Query
    next if terms.empty?

    term_queries = terms.map do |term|
      if fuzzy_distance.nil?
        term_query(index, field, term)
      else
        fuzzy_term_query(index, field, term, fuzzy_distance)
      end
    end

    # @type var terms: untyped
    # @type var term_queries: untyped
    last_term_query = prefix_query(index, field, terms.last) | term_queries.last

    conjunction(last_term_query, *term_queries[0...-1])
  end.compact

  disjunction(*field_queries).boost(boost_factor)
end

.term_query(index, fields, term, **options) ⇒ Object



35
36
37
38
# File 'lib/tantiny/query.rb', line 35

def term_query(index, fields, term, **options)
  allowed_fields = text_and_strings(index)
  construct_query(index, :term, allowed_fields, fields, [term.to_s], **options)
end

Instance Method Details

#!Object



155
156
157
# File 'lib/tantiny/query.rb', line 155

def !
  __negation
end

#&(other) ⇒ Object

Raises:

  • (ArgumentError)


149
150
151
152
153
# File 'lib/tantiny/query.rb', line 149

def &(other)
  raise ArgumentError.new("Not a #{self.class}.") unless other.is_a?(self.class)

  self.class.conjunction(self, other)
end

#boost(boost_factor) ⇒ Object



159
160
161
162
163
# File 'lib/tantiny/query.rb', line 159

def boost(boost_factor)
  return self if boost_factor == DEFAULT_BOOST

  __boost(boost_factor.to_f)
end

#|(other) ⇒ Object

Raises:

  • (ArgumentError)


143
144
145
146
147
# File 'lib/tantiny/query.rb', line 143

def |(other)
  raise ArgumentError.new("Not a #{self.class}.") unless other.is_a?(self.class)

  self.class.disjunction(self, other)
end