redi_search 1.0.3 → 2.0.2

Sign up to get free protection for your applications and to get access to all the features.
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