redi_search 1.0.3 → 2.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (53) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/lint.yml +20 -0
  3. data/.github/workflows/tests.yml +44 -0
  4. data/.rubocop.yml +67 -189
  5. data/Appraisals +4 -8
  6. data/Gemfile +3 -4
  7. data/README.md +79 -74
  8. data/Rakefile +15 -3
  9. data/bin/console +29 -0
  10. data/gemfiles/{rails_6.gemfile → activerecord_51.gemfile} +1 -5
  11. data/gemfiles/{rails_51.gemfile → activerecord_52.gemfile} +1 -5
  12. data/lib/redi_search.rb +8 -6
  13. data/lib/redi_search/add.rb +9 -5
  14. data/lib/redi_search/{alter.rb → add_field.rb} +13 -5
  15. data/lib/redi_search/client.rb +8 -6
  16. data/lib/redi_search/client/response.rb +8 -8
  17. data/lib/redi_search/configuration.rb +1 -11
  18. data/lib/redi_search/create.rb +6 -4
  19. data/lib/redi_search/document.rb +5 -4
  20. data/lib/redi_search/document/display.rb +9 -9
  21. data/lib/redi_search/document/finder.rb +10 -2
  22. data/lib/redi_search/index.rb +9 -9
  23. data/lib/redi_search/lazily_load.rb +7 -13
  24. data/lib/redi_search/log_subscriber.rb +23 -52
  25. data/lib/redi_search/model.rb +43 -39
  26. data/lib/redi_search/schema.rb +3 -3
  27. data/lib/redi_search/schema/field.rb +2 -3
  28. data/lib/redi_search/schema/tag_field.rb +1 -1
  29. data/lib/redi_search/schema/text_field.rb +1 -1
  30. data/lib/redi_search/search.rb +15 -26
  31. data/lib/redi_search/search/clauses.rb +6 -7
  32. data/lib/redi_search/search/clauses/application_clause.rb +20 -5
  33. data/lib/redi_search/search/clauses/boolean.rb +8 -6
  34. data/lib/redi_search/search/clauses/highlight.rb +18 -2
  35. data/lib/redi_search/search/clauses/limit.rb +7 -5
  36. data/lib/redi_search/search/clauses/return.rb +1 -1
  37. data/lib/redi_search/search/clauses/slop.rb +1 -1
  38. data/lib/redi_search/search/clauses/sort_by.rb +1 -1
  39. data/lib/redi_search/search/clauses/where.rb +13 -3
  40. data/lib/redi_search/search/result.rb +27 -11
  41. data/lib/redi_search/search/term.rb +7 -6
  42. data/lib/redi_search/spellcheck.rb +3 -4
  43. data/lib/redi_search/spellcheck/result.rb +1 -1
  44. data/lib/redi_search/validatable.rb +49 -0
  45. data/lib/redi_search/validations/inclusion.rb +26 -0
  46. data/lib/redi_search/validations/numericality.rb +45 -0
  47. data/lib/redi_search/validations/presence.rb +29 -0
  48. data/lib/redi_search/version.rb +1 -1
  49. data/redi_search.gemspec +1 -3
  50. metadata +14 -45
  51. data/.travis.yml +0 -31
  52. data/bin/test +0 -7
  53. data/gemfiles/rails_52.gemfile +0 -17
@@ -1,12 +1,12 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "delegate"
4
+ require "forwardable"
3
5
  require "redis"
4
- require "active_support"
5
- require "active_model"
6
- require "active_support/core_ext/object"
7
- require "active_support/core_ext/module/delegation"
6
+ require "active_support/lazy_load_hooks"
8
7
 
9
8
  require "redi_search/configuration"
9
+ require "redi_search/client"
10
10
 
11
11
  require "redi_search/model"
12
12
  require "redi_search/index"
@@ -29,10 +29,12 @@ module RediSearch
29
29
  yield(configuration)
30
30
  end
31
31
 
32
- delegate :client, to: :configuration
32
+ def client
33
+ @client ||= Client.new(Redis.new(configuration.redis_config.to_h))
34
+ end
33
35
 
34
36
  def env
35
- @env ||= ENV["RAILS_ENV"] || ENV["RACK_ENV"] || "development"
37
+ ENV["RAILS_ENV"] || ENV["RACK_ENV"] || "development"
36
38
  end
37
39
  end
38
40
  end
@@ -1,12 +1,12 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "redi_search/validatable"
4
+
3
5
  module RediSearch
4
6
  class Add
5
- include ActiveModel::Validations
7
+ include Validatable
6
8
 
7
- validates :score, numericality: {
8
- greater_than_or_equal_to: 0.0, less_than_or_equal_to: 1.0
9
- }
9
+ validates_numericality_of :score, within: 0.0..1.0
10
10
 
11
11
  def initialize(index, document, score: 1.0, replace: {}, language: nil,
12
12
  no_save: false)
@@ -48,7 +48,11 @@ module RediSearch
48
48
  end
49
49
 
50
50
  def replace?
51
- replace.present?
51
+ if replace.respond_to?(:empty?)
52
+ !replace.empty?
53
+ else
54
+ replace
55
+ end
52
56
  end
53
57
 
54
58
  def replace_options
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module RediSearch
4
- class Alter
4
+ class AddField
5
5
  def initialize(index, field_name, schema)
6
6
  @index = index
7
7
  @field_name = field_name
@@ -9,17 +9,25 @@ module RediSearch
9
9
  end
10
10
 
11
11
  def call!
12
- index.schema.alter(field_name, raw_schema)
12
+ index.schema.add_field(field_name, raw_schema)
13
13
 
14
- RediSearch.client.call!(
15
- "ALTER", index.name, "SCHEMA", "ADD", *field_schema
16
- ).ok?
14
+ RediSearch.client.call!(*command).ok?
15
+ end
16
+
17
+ def call
18
+ call!
19
+ rescue Redis::CommandError
20
+ false
17
21
  end
18
22
 
19
23
  private
20
24
 
21
25
  attr_reader :index, :field_name, :raw_schema
22
26
 
27
+ def command
28
+ ["ALTER", index.name, "SCHEMA", "ADD", *field_schema]
29
+ end
30
+
23
31
  def field_schema
24
32
  @field_schema ||= Schema.make_field(field_name, raw_schema)
25
33
  end
@@ -1,12 +1,14 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "redis"
4
+ require "active_support/notifications"
5
+
4
6
  require "redi_search/client/response"
5
7
 
6
8
  module RediSearch
7
9
  class Client
8
- def initialize(redis_config)
9
- @redis = Redis.new(redis_config)
10
+ def initialize(redis = Redis.new)
11
+ @redis = redis
10
12
  end
11
13
 
12
14
  def call!(command, *params)
@@ -15,8 +17,8 @@ module RediSearch
15
17
  end
16
18
  end
17
19
 
18
- def pipelined
19
- Response.new(redis.pipelined do
20
+ def multi
21
+ Response.new(redis.multi do
20
22
  instrument("pipeline", query: ["begin pipeline"])
21
23
  yield
22
24
  instrument("pipeline", query: ["finish pipeline"])
@@ -33,8 +35,8 @@ module RediSearch
33
35
 
34
36
  def instrument(action, payload, &block)
35
37
  ActiveSupport::Notifications.instrument(
36
- "#{action}.redi_search",
37
- { name: "RediSearch" }.merge(payload),
38
+ "action.redi_search",
39
+ { name: "RediSearch", action: action }.merge(payload),
38
40
  &Proc.new(&(block || proc {}))
39
41
  )
40
42
  end
@@ -3,12 +3,6 @@
3
3
  module RediSearch
4
4
  class Client
5
5
  class Response < SimpleDelegator
6
- def initialize(response)
7
- @response = response
8
-
9
- super(response)
10
- end
11
-
12
6
  def ok?
13
7
  case response
14
8
  when String then response == "OK"
@@ -18,15 +12,21 @@ module RediSearch
18
12
  end
19
13
  end
20
14
 
21
- private
15
+ def nil?
16
+ response.nil?
17
+ end
22
18
 
23
- attr_reader :response
19
+ private
24
20
 
25
21
  def array_ok?
26
22
  response.all? do |pipeline_response|
27
23
  Response.new(pipeline_response).ok?
28
24
  end
29
25
  end
26
+
27
+ def response
28
+ __getobj__
29
+ end
30
30
  end
31
31
  end
32
32
  end
@@ -1,17 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "redi_search/client"
4
-
5
3
  module RediSearch
6
4
  class Configuration
7
- attr_writer :redis_config
8
-
9
- def client
10
- @client ||= Client.new(redis_config)
11
- end
12
-
13
- def redis_config
14
- @redis_config ||= { host: "127.0.0.1", port: "6379" }
15
- end
5
+ attr_accessor :redis_config
16
6
  end
17
7
  end
@@ -17,9 +17,7 @@ module RediSearch
17
17
  end
18
18
 
19
19
  def call!
20
- RediSearch.client.call!(
21
- "CREATE", index.name, *extract_options.compact, "SCHEMA", schema.to_a
22
- ).ok?
20
+ RediSearch.client.call!(*command).ok?
23
21
  end
24
22
 
25
23
  def call
@@ -32,12 +30,16 @@ module RediSearch
32
30
 
33
31
  attr_reader :index, :schema, :options
34
32
 
33
+ def command
34
+ ["CREATE", index.name, *extract_options.compact, "SCHEMA", schema.to_a]
35
+ end
36
+
35
37
  def extract_options
36
38
  options.map do |clause, switch|
37
39
  next unless OPTION_MAPPER.key?(clause.to_sym) && switch
38
40
 
39
41
  OPTION_MAPPER[clause.to_sym]
40
- end << temporary_option
42
+ end + temporary_option
41
43
  end
42
44
 
43
45
  def temporary_option
@@ -12,7 +12,7 @@ module RediSearch
12
12
  object_to_serialize = serializer&.new(record) || record
13
13
 
14
14
  field_values = index.schema.fields.map do |field|
15
- next if only.present? && !only.include?(field.to_sym)
15
+ next unless only.empty? || only.include?(field.to_sym)
16
16
 
17
17
  [field.to_s, object_to_serialize.public_send(field)]
18
18
  end.compact.to_h
@@ -41,7 +41,8 @@ module RediSearch
41
41
  end
42
42
 
43
43
  def del(delete_document: false)
44
- call!("DEL", index.name, document_id, ("DD" if delete_document)).ok?
44
+ command = ["DEL", index.name, document_id, ("DD" if delete_document)]
45
+ call!(*command.compact).ok?
45
46
  end
46
47
 
47
48
  def schema_fields
@@ -61,7 +62,7 @@ module RediSearch
61
62
  end
62
63
 
63
64
  def document_id_without_index
64
- if @document_id.to_s.starts_with? index.name
65
+ if @document_id.to_s.start_with? index.name
65
66
  @document_id.gsub(index.name, "")
66
67
  else
67
68
  @document_id
@@ -78,7 +79,7 @@ module RediSearch
78
79
 
79
80
  def load_attributes
80
81
  attributes.each do |field, value|
81
- next unless schema_fields.include? field
82
+ next unless schema_fields.include? field.to_s
82
83
 
83
84
  instance_variable_set(:"@#{field}", value)
84
85
  define_singleton_method(field) { value }
@@ -3,7 +3,6 @@
3
3
  module RediSearch
4
4
  class Document
5
5
  module Display
6
- #:nocov:
7
6
  def inspect
8
7
  inspection = pretty_print_attributes.map do |field_name|
9
8
  "#{field_name}: #{public_send(field_name)}"
@@ -12,6 +11,15 @@ module RediSearch
12
11
  "#<#{self.class} #{inspection}>"
13
12
  end
14
13
 
14
+ def pretty_print_attributes
15
+ pp_attrs = attributes.keys.dup
16
+ pp_attrs.push("document_id")
17
+ pp_attrs.push("score") if score
18
+
19
+ pp_attrs.compact
20
+ end
21
+
22
+ #:nocov:
15
23
  def pretty_print(printer) # rubocop:disable Metrics/MethodLength
16
24
  printer.object_address_group(self) do
17
25
  printer.seplist(
@@ -27,14 +35,6 @@ module RediSearch
27
35
  end
28
36
  end
29
37
  end
30
-
31
- def pretty_print_attributes
32
- pp_attrs = attributes.keys.dup
33
- pp_attrs.push("document_id")
34
- pp_attrs.push("score") if score.present?
35
-
36
- pp_attrs.compact
37
- end
38
38
  #:nocov:
39
39
  end
40
40
  end
@@ -5,7 +5,7 @@ module RediSearch
5
5
  class Finder
6
6
  def initialize(index, *document_ids)
7
7
  @index = index
8
- @document_ids = Array.wrap(document_ids)
8
+ @document_ids = [*document_ids]
9
9
  end
10
10
 
11
11
  def find
@@ -61,10 +61,18 @@ module RediSearch
61
61
  end
62
62
 
63
63
  def parse_document(document_id, document_response)
64
- return if document_response.blank?
64
+ return unless document_response?(document_response)
65
65
 
66
66
  Document.new(index, document_id, Hash[*document_response])
67
67
  end
68
+
69
+ def document_response?(document_response)
70
+ if document_response.respond_to?(:empty?)
71
+ !document_response.empty?
72
+ else
73
+ !document_response.nil?
74
+ end
75
+ end
68
76
  end
69
77
  end
70
78
  end
@@ -5,14 +5,14 @@ require "redi_search/create"
5
5
  require "redi_search/schema"
6
6
  require "redi_search/search"
7
7
  require "redi_search/spellcheck"
8
- require "redi_search/alter"
8
+ require "redi_search/add_field"
9
9
 
10
10
  module RediSearch
11
11
  class Index
12
12
  attr_reader :name, :schema, :model
13
13
 
14
14
  def initialize(name, schema, model = nil)
15
- @name = name
15
+ @name = name.to_s
16
16
  @schema = Schema.new(schema)
17
17
  @model = model
18
18
  end
@@ -51,10 +51,10 @@ module RediSearch
51
51
  Add.new(self, document, **options).call!
52
52
  end
53
53
 
54
- def add_multiple!(documents, **options)
55
- client.pipelined do
54
+ def add_multiple(documents, **options)
55
+ client.multi do
56
56
  documents.each do |document|
57
- add!(document, **options)
57
+ add(document, **options)
58
58
  end
59
59
  end.ok?
60
60
  end
@@ -85,15 +85,15 @@ module RediSearch
85
85
  drop if recreate
86
86
  create unless exist?
87
87
 
88
- add_multiple! documents, **options
88
+ add_multiple documents, **options
89
89
  end
90
90
 
91
91
  def document_count
92
- info["num_docs"].to_i
92
+ info.num_docs.to_i
93
93
  end
94
94
 
95
- def alter(field_name, schema)
96
- Alter.new(self, field_name, schema).call!
95
+ def add_field(field_name, schema)
96
+ AddField.new(self, field_name, schema).call!
97
97
  end
98
98
 
99
99
  private
@@ -2,13 +2,11 @@
2
2
 
3
3
  module RediSearch
4
4
  module LazilyLoad
5
- extend ActiveSupport::Concern
5
+ extend Forwardable
6
6
 
7
7
  include Enumerable
8
8
 
9
- included do
10
- delegate :size, :each, to: :to_a
11
- end
9
+ def_delegators :to_a, :size, :each, :last, :[], :count, :empty?
12
10
 
13
11
  def loaded?
14
12
  @loaded = false unless defined? @loaded
@@ -24,7 +22,6 @@ module RediSearch
24
22
 
25
23
  alias load to_a
26
24
 
27
- #:nocov:
28
25
  def inspect
29
26
  execute_and_rescue_inspection do
30
27
  return super unless valid?
@@ -33,6 +30,7 @@ module RediSearch
33
30
  end
34
31
  end
35
32
 
33
+ #:nocov:
36
34
  def pretty_print(printer)
37
35
  execute_and_rescue_inspection do
38
36
  return super(inspect) unless valid?
@@ -42,12 +40,10 @@ module RediSearch
42
40
  end
43
41
  #:nocov:
44
42
 
45
- def count
46
- to_a.size
47
- end
48
-
49
43
  private
50
44
 
45
+ attr_reader :documents
46
+
51
47
  def command
52
48
  raise NotImplementedError, "included class did not define #{__method__}"
53
49
  end
@@ -57,12 +53,12 @@ module RediSearch
57
53
 
58
54
  @loaded = true
59
55
 
60
- call!(*command).yield_self do |response|
56
+ call!.yield_self do |response|
61
57
  parse_response(response)
62
58
  end
63
59
  end
64
60
 
65
- def call!(*command)
61
+ def call!
66
62
  RediSearch.client.call!(*command)
67
63
  end
68
64
 
@@ -74,7 +70,6 @@ module RediSearch
74
70
  true
75
71
  end
76
72
 
77
- #:nocov:
78
73
  def execute_and_rescue_inspection
79
74
  execute unless loaded?
80
75
 
@@ -82,6 +77,5 @@ module RediSearch
82
77
  rescue Redis::CommandError => e
83
78
  e.message
84
79
  end
85
- #:nocov:
86
80
  end
87
81
  end