redi_search 6.3.0 → 6.3.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (50) hide show
  1. checksums.yaml +4 -4
  2. data/.ruby-version +1 -1
  3. data/lib/redi_search/aggregate/clauses/apply.rb +24 -0
  4. data/lib/redi_search/aggregate/clauses/filter.rb +22 -0
  5. data/lib/redi_search/aggregate/clauses/group_by.rb +79 -0
  6. data/lib/redi_search/aggregate/clauses/limit.rb +28 -0
  7. data/lib/redi_search/aggregate/clauses/load.rb +22 -0
  8. data/lib/redi_search/aggregate/clauses/sort_by.rb +43 -0
  9. data/lib/redi_search/aggregate/clauses/verbatim.rb +17 -0
  10. data/lib/redi_search/aggregate/reducers/average.rb +26 -0
  11. data/lib/redi_search/aggregate/reducers/count.rb +23 -0
  12. data/lib/redi_search/aggregate/reducers/distinct_count.rb +26 -0
  13. data/lib/redi_search/aggregate/reducers/distinctish_count.rb +26 -0
  14. data/lib/redi_search/aggregate/reducers/max.rb +26 -0
  15. data/lib/redi_search/aggregate/reducers/min.rb +26 -0
  16. data/lib/redi_search/aggregate/reducers/quantile.rb +30 -0
  17. data/lib/redi_search/aggregate/reducers/stdev.rb +26 -0
  18. data/lib/redi_search/aggregate/reducers/sum.rb +26 -0
  19. data/lib/redi_search/aggregate/reducers/to_list.rb +26 -0
  20. data/lib/redi_search/aggregate.rb +104 -0
  21. data/lib/redi_search/application_clause.rb +37 -0
  22. data/lib/redi_search/client.rb +5 -4
  23. data/lib/redi_search/index.rb +4 -0
  24. data/lib/redi_search/log_subscriber.rb +4 -3
  25. data/lib/redi_search/model.rb +5 -1
  26. data/lib/redi_search/schema/tag_field.rb +8 -0
  27. data/lib/redi_search/search/clauses/highlight.rb +3 -1
  28. data/lib/redi_search/search/clauses/in_order.rb +2 -0
  29. data/lib/redi_search/search/clauses/language.rb +1 -0
  30. data/lib/redi_search/search/clauses/limit.rb +1 -0
  31. data/lib/redi_search/search/clauses/no_content.rb +2 -0
  32. data/lib/redi_search/search/clauses/no_stop_words.rb +2 -0
  33. data/lib/redi_search/search/clauses/return.rb +1 -0
  34. data/lib/redi_search/search/clauses/slop.rb +1 -0
  35. data/lib/redi_search/search/clauses/sort_by.rb +1 -0
  36. data/lib/redi_search/search/clauses/timeout.rb +24 -0
  37. data/lib/redi_search/search/clauses/verbatim.rb +2 -0
  38. data/lib/redi_search/search/clauses/with_payloads.rb +17 -0
  39. data/lib/redi_search/search/clauses/with_scores.rb +2 -0
  40. data/lib/redi_search/search/clauses/with_sort_keys.rb +17 -0
  41. data/lib/redi_search/search/clauses.rb +36 -55
  42. data/lib/redi_search/search/{clauses → queries}/and.rb +1 -1
  43. data/lib/redi_search/search/{clauses → queries}/boolean.rb +4 -4
  44. data/lib/redi_search/search/{clauses → queries}/or.rb +1 -1
  45. data/lib/redi_search/search/{clauses → queries}/where.rb +2 -2
  46. data/lib/redi_search/search/queries.rb +39 -0
  47. data/lib/redi_search/search.rb +7 -6
  48. data/lib/redi_search/version.rb +1 -1
  49. metadata +30 -8
  50. data/lib/redi_search/search/clauses/application_clause.rb +0 -31
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: d005b7e0b289004277bf5526a4270aa7dd707df16520ae0e2b9df577b12601c2
4
- data.tar.gz: 9b3954997aa84e9e25037c6b332c00139d7d112dad9fffbbe32281f9da0fdfe1
3
+ metadata.gz: 6fbc68e53dd39c963d1bb380482967016fb5a511021104e16ce1cc8741cb8023
4
+ data.tar.gz: 4899d79c5a505ebf6a306e23cf1c943e511aaaf59855705395b36d7264da7f6d
5
5
  SHA512:
6
- metadata.gz: dfcbe3552fef2dcb9abda2ad3e51c2bf35a2d2a16a631f249d1f1a89e5ed3aab5f0815387ab5a472f13db9c1d8a430f114840e81147e2b44160b1890be0f6c94
7
- data.tar.gz: 3e2c1b66bb0053f42d886ab765468a213ab576b31d7d95a08f8bd216dcb8a933abbd76670207b2c9b366e988622a0aa1ece981901bb368916087c85a75b0726f
6
+ metadata.gz: bd8bdd6ca738503cc4a8008d43f2225ad3c14c869bfed0c42f13de5b104bb9d4093d46c479008c24d6fbf988a13e9494cff4cc202a6fa6e44a1f8d8d3c70f40d
7
+ data.tar.gz: 5fad9402eb86bc138a4b98e239b31ad48a5b5f5cc0f9c5db019c037955482f26cda1a74bce4b816f78c13503bd2da168cf9dfceb20f2ea9c92e7770ce85b8979
data/.ruby-version CHANGED
@@ -1 +1 @@
1
- 3.2.0-preview3
1
+ 3.2.1
@@ -0,0 +1,24 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RediSearch
4
+ class Aggregate
5
+ module Clauses
6
+ class Apply < ApplicationClause
7
+ clause_term :expression, presence: true
8
+ clause_term :as, presence: true
9
+ clause_order 5
10
+
11
+ def initialize(expression:, as:)
12
+ @expression = expression
13
+ @as = as
14
+ end
15
+
16
+ def clause
17
+ validate!
18
+
19
+ ["APPLY", expression, "AS", as]
20
+ end
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RediSearch
4
+ class Aggregate
5
+ module Clauses
6
+ class Filter < ApplicationClause
7
+ clause_term :expression, presence: true
8
+ clause_order 6
9
+
10
+ def initialize(expression:)
11
+ @expression = expression
12
+ end
13
+
14
+ def clause
15
+ validate!
16
+
17
+ ["FILTER", expression]
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,79 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RediSearch
4
+ class Aggregate
5
+ module Clauses
6
+ class GroupBy < ApplicationClause
7
+ clause_term :fields, presence: true
8
+ clause_order 3
9
+
10
+ def initialize(fields:)
11
+ @fields = fields.map(&:to_s).map do |field|
12
+ field.prepend("@") unless field.start_with?("@")
13
+ end
14
+ @reducer = nil
15
+ end
16
+
17
+ def clause
18
+ validate!
19
+
20
+ ["GROUPBY", fields.count, *fields, *reducer&.clause].compact
21
+ end
22
+
23
+ def count(as: nil)
24
+ self.reducer = Reducers::Count.new(as: as).tap(&:validate!)
25
+ end
26
+
27
+ def distinct_count(property:, as: nil)
28
+ self.reducer = Reducers::DistinctCount.
29
+ new(property: property, as: as).tap(&:validate!)
30
+ end
31
+
32
+ def distinctish_count(property:, as: nil)
33
+ self.reducer = Reducers::DistinctishCount.
34
+ new(property: property, as: as).tap(&:validate!)
35
+ end
36
+
37
+ def sum(property:, as: nil)
38
+ self.reducer = Reducers::Sum.
39
+ new(property: property, as: as).tap(&:validate!)
40
+ end
41
+
42
+ def min(property:, as: nil)
43
+ self.reducer = Reducers::Min.
44
+ new(property: property, as: as).tap(&:validate!)
45
+ end
46
+
47
+ def max(property:, as: nil)
48
+ self.reducer = Reducers::Max.
49
+ new(property: property, as: as).tap(&:validate!)
50
+ end
51
+
52
+ def average(property:, as: nil)
53
+ self.reducer = Reducers::Average.
54
+ new(property: property, as: as).tap(&:validate!)
55
+ end
56
+
57
+ def stdev(property:, as: nil)
58
+ self.reducer = Reducers::Stdev.
59
+ new(property: property, as: as).tap(&:validate!)
60
+ end
61
+
62
+ def quantile(property:, quantile:, as: nil)
63
+ self.reducer = Reducers::Quantile.new(
64
+ property: property, quantile: quantile, as: as
65
+ ).tap(&:validate!)
66
+ end
67
+
68
+ def to_list(property:, as: nil)
69
+ self.reducer = Reducers::ToList.
70
+ new(property: property, as: as).tap(&:validate!)
71
+ end
72
+
73
+ private
74
+
75
+ attr_accessor :reducer
76
+ end
77
+ end
78
+ end
79
+ end
@@ -0,0 +1,28 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RediSearch
4
+ class Aggregate
5
+ module Clauses
6
+ class Limit < ApplicationClause
7
+ clause_term :total, presence: true, numericality: {
8
+ within: 0..Float::INFINITY, only_integer: true
9
+ }
10
+ clause_term :offset, presence: true, numericality: {
11
+ within: 0..Float::INFINITY, only_integer: true
12
+ }
13
+ clause_order 7
14
+
15
+ def initialize(total:, offset: 0)
16
+ @total = total
17
+ @offset = offset
18
+ end
19
+
20
+ def clause
21
+ validate!
22
+
23
+ ["LIMIT", offset, total]
24
+ end
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RediSearch
4
+ class Aggregate
5
+ module Clauses
6
+ class Load < ApplicationClause
7
+ clause_term :fields, presence: true
8
+ clause_order 2
9
+
10
+ def initialize(fields:)
11
+ @fields = fields
12
+ end
13
+
14
+ def clause
15
+ validate!
16
+
17
+ ["LOAD", fields.size, *fields]
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,43 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RediSearch
4
+ class Aggregate
5
+ module Clauses
6
+ class SortBy < ApplicationClause
7
+ class Sortable < ApplicationClause
8
+ clause_term :field, presence: true
9
+ clause_term :order, presence: true,
10
+ inclusion: { within: %i(asc desc) }
11
+
12
+ def initialize(field, order = :asc)
13
+ @field = field.to_s
14
+ @field.prepend("@") unless @field.start_with?("@")
15
+ @order = order.to_sym
16
+ end
17
+
18
+ def clause
19
+ [field, order.to_s.upcase]
20
+ end
21
+ end
22
+
23
+ clause_term :fields, presence: true
24
+ clause_order 4
25
+
26
+ def initialize(fields:)
27
+ @fields = fields.flat_map do |field|
28
+ if field.is_a?(Hash) then field.map { |k, v| Sortable.new(k, v) }
29
+ else
30
+ Sortable.new(field)
31
+ end
32
+ end
33
+ end
34
+
35
+ def clause
36
+ validate! && fields.each(&:validate!)
37
+
38
+ ["SORTBY", fields.count * 2, *fields.flat_map(&:clause)]
39
+ end
40
+ end
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RediSearch
4
+ class Aggregate
5
+ module Clauses
6
+ class Verbatim < ApplicationClause
7
+ clause_order 1
8
+
9
+ def clause
10
+ validate!
11
+
12
+ ["VERBATIM"]
13
+ end
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RediSearch
4
+ class Aggregate
5
+ module Reducers
6
+ class Average < ApplicationClause
7
+ clause_term :property, presence: true
8
+ clause_term :as
9
+
10
+ def initialize(property:, as:)
11
+ @property = property.to_s
12
+ @property.prepend("@") unless @property.start_with?("@")
13
+ @as = as
14
+ end
15
+
16
+ def clause
17
+ validate!
18
+
19
+ command = ["REDUCE", "AVG", 1, property]
20
+ command += ["AS", as] if as
21
+ command
22
+ end
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RediSearch
4
+ class Aggregate
5
+ module Reducers
6
+ class Count < ApplicationClause
7
+ clause_term :as
8
+
9
+ def initialize(as:)
10
+ @as = as
11
+ end
12
+
13
+ def clause
14
+ validate!
15
+
16
+ command = ["REDUCE", "COUNT", 0]
17
+ command += ["AS", as] if as
18
+ command
19
+ end
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RediSearch
4
+ class Aggregate
5
+ module Reducers
6
+ class DistinctCount < ApplicationClause
7
+ clause_term :property, presence: true
8
+ clause_term :as
9
+
10
+ def initialize(property:, as:)
11
+ @property = property.to_s
12
+ @property.prepend("@") unless @property.start_with?("@")
13
+ @as = as
14
+ end
15
+
16
+ def clause
17
+ validate!
18
+
19
+ command = ["REDUCE", "COUNT_DISTINCT", 1, property]
20
+ command += ["AS", as] if as
21
+ command
22
+ end
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RediSearch
4
+ class Aggregate
5
+ module Reducers
6
+ class DistinctishCount < ApplicationClause
7
+ clause_term :property, presence: true
8
+ clause_term :as
9
+
10
+ def initialize(property:, as:)
11
+ @property = property.to_s
12
+ @property.prepend("@") unless @property.start_with?("@")
13
+ @as = as
14
+ end
15
+
16
+ def clause
17
+ validate!
18
+
19
+ command = ["REDUCE", "COUNT_DISTINCTISH", 1, property]
20
+ command += ["AS", as] if as
21
+ command
22
+ end
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RediSearch
4
+ class Aggregate
5
+ module Reducers
6
+ class Max < ApplicationClause
7
+ clause_term :property, presence: true
8
+ clause_term :as
9
+
10
+ def initialize(property:, as:)
11
+ @property = property.to_s
12
+ @property.prepend("@") unless @property.start_with?("@")
13
+ @as = as
14
+ end
15
+
16
+ def clause
17
+ validate!
18
+
19
+ command = ["REDUCE", "MAX", 1, property]
20
+ command += ["AS", as] if as
21
+ command
22
+ end
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RediSearch
4
+ class Aggregate
5
+ module Reducers
6
+ class Min < ApplicationClause
7
+ clause_term :property, presence: true
8
+ clause_term :as
9
+
10
+ def initialize(property:, as:)
11
+ @property = property.to_s
12
+ @property.prepend("@") unless @property.start_with?("@")
13
+ @as = as
14
+ end
15
+
16
+ def clause
17
+ validate!
18
+
19
+ command = ["REDUCE", "MIN", 1, property]
20
+ command += ["AS", as] if as
21
+ command
22
+ end
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,30 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RediSearch
4
+ class Aggregate
5
+ module Reducers
6
+ class Quantile < ApplicationClause
7
+ clause_term :property, presence: true
8
+ clause_term :quantile, presence: true, inclusion: {
9
+ within: 0.0..1.0
10
+ }
11
+ clause_term :as
12
+
13
+ def initialize(property:, quantile:, as:)
14
+ @property = property.to_s
15
+ @property.prepend("@") unless @property.start_with?("@")
16
+ @quantile = quantile
17
+ @as = as
18
+ end
19
+
20
+ def clause
21
+ validate!
22
+
23
+ command = ["REDUCE", "QUANTILE", 2, property, quantile]
24
+ command += ["AS", as] if as
25
+ command
26
+ end
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RediSearch
4
+ class Aggregate
5
+ module Reducers
6
+ class Stdev < ApplicationClause
7
+ clause_term :property, presence: true
8
+ clause_term :as
9
+
10
+ def initialize(property:, as:)
11
+ @property = property.to_s
12
+ @property.prepend("@") unless @property.start_with?("@")
13
+ @as = as
14
+ end
15
+
16
+ def clause
17
+ validate!
18
+
19
+ command = ["REDUCE", "STDEV", 1, property]
20
+ command += ["AS", as] if as
21
+ command
22
+ end
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RediSearch
4
+ class Aggregate
5
+ module Reducers
6
+ class Sum < ApplicationClause
7
+ clause_term :property, presence: true
8
+ clause_term :as
9
+
10
+ def initialize(property:, as:)
11
+ @property = property.to_s
12
+ @property.prepend("@") unless @property.start_with?("@")
13
+ @as = as
14
+ end
15
+
16
+ def clause
17
+ validate!
18
+
19
+ command = ["REDUCE", "SUM", 1, property]
20
+ command += ["AS", as] if as
21
+ command
22
+ end
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RediSearch
4
+ class Aggregate
5
+ module Reducers
6
+ class ToList < ApplicationClause
7
+ clause_term :property, presence: true
8
+ clause_term :as
9
+
10
+ def initialize(property:, as:)
11
+ @property = property.to_s
12
+ @property.prepend("@") unless @property.start_with?("@")
13
+ @as = as
14
+ end
15
+
16
+ def clause
17
+ validate!
18
+
19
+ command = ["REDUCE", "TOLIST", 1, property]
20
+ command += ["AS", as] if as
21
+ command
22
+ end
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,104 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RediSearch
4
+ class Aggregate
5
+ class MissingGroupByClause < StandardError
6
+ end
7
+
8
+ extend Forwardable
9
+ include LazilyLoad
10
+ include Search::Queries
11
+
12
+ attr_reader :query, :used_clauses, :index, :clauses
13
+
14
+ def_delegator :index, :model
15
+
16
+ def initialize(index, term = nil, **term_options)
17
+ @index = index
18
+ @clauses = []
19
+ @used_clauses = Set.new
20
+
21
+ @query = term && Search::Queries::And.new(self, term, nil, **term_options)
22
+ end
23
+
24
+ def verbatim
25
+ add_to_clauses(Clauses::Verbatim.new)
26
+ end
27
+
28
+ def load(*fields)
29
+ add_to_clauses(Clauses::Load.new(fields: fields))
30
+ end
31
+
32
+ def group_by(*fields)
33
+ add_to_clauses(Clauses::GroupBy.new(fields: fields))
34
+ end
35
+
36
+ def count(as: nil)
37
+ clause = clauses.reverse.find { |cl| cl.is_a?(Clauses::GroupBy) } ||
38
+ raise(MissingGroupByClause, "call group_by first")
39
+
40
+ clause.count(as: as)
41
+ self
42
+ end
43
+
44
+ def quantile(property: nil, quantile: nil, as: nil)
45
+ clause = clauses.reverse.find { |cl| cl.is_a?(Clauses::GroupBy) } ||
46
+ raise(MissingGroupByClause, "call group_by first")
47
+
48
+ clause.count(property: property, quantile: quantile, as: as)
49
+
50
+ self
51
+ end
52
+
53
+ %i(distinct_count distinctish_count sum min max average stdev to_list).
54
+ each do |reducer|
55
+ define_method(reducer) do |property, as: nil|
56
+ clause = clauses.reverse.find { |cl| cl.is_a?(Clauses::GroupBy) } ||
57
+ raise(MissingGroupByClause, "call group_by first")
58
+
59
+ clause.public_send(reducer, property: property, as: as)
60
+ self
61
+ end
62
+ end
63
+
64
+ def sort_by(*fields)
65
+ add_to_clauses(Clauses::SortBy.new(fields: fields))
66
+ end
67
+
68
+ def apply(expression, as:)
69
+ add_to_clauses(Clauses::Apply.new(expression: expression, as: as))
70
+ end
71
+
72
+ def filter(expression)
73
+ add_to_clauses(Clauses::Filter.new(expression: expression))
74
+ end
75
+
76
+ def limit(total, offset = 0)
77
+ add_to_clauses(Clauses::Limit.new(total: total, offset: offset))
78
+ end
79
+
80
+ private
81
+
82
+ attr_writer :index, :clauses
83
+
84
+ def command
85
+ ["AGGREGATE", index.name, query.to_s,
86
+ *clauses.sort_by(&:clause_order).flat_map(&:clause)]
87
+ end
88
+
89
+ def parse_response(response)
90
+ @documents = response
91
+ end
92
+
93
+ def valid?
94
+ !query.to_s.empty?
95
+ end
96
+
97
+ def add_to_clauses(clause)
98
+ clause.validate! && clauses.push(clause) if
99
+ used_clauses.add?(clause.class)
100
+
101
+ self
102
+ end
103
+ end
104
+ end
@@ -0,0 +1,37 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RediSearch
4
+ class ApplicationClause
5
+ include Validatable
6
+
7
+ def clause_order
8
+ self.class.order
9
+ end
10
+
11
+ class << self
12
+ def clause_term(term, **validations)
13
+ attr_reader term
14
+
15
+ validations.each do |validation_type, options|
16
+ define_validation(term, validation_type, options)
17
+ end
18
+ end
19
+
20
+ attr_reader :order
21
+
22
+ def clause_order(number)
23
+ @order = number
24
+ end
25
+
26
+ private
27
+
28
+ def define_validation(term, type, options)
29
+ if options.is_a? Hash
30
+ public_send("validates_#{type}_of", term, **options)
31
+ else
32
+ public_send("validates_#{type}_of", term)
33
+ end
34
+ end
35
+ end
36
+ end
37
+ end