redi_search 1.0.0 → 1.0.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 0666cc15e7fc7d132026337c916b5d233114d65378ff172d9a48ae81ed1a9eae
4
- data.tar.gz: 575c1230885537ea9a31b753808894d187f00718fe8d0fca282cba426b75e0c8
3
+ metadata.gz: 28b67b5a88b3440ae45548484cc556a2cc0866328701f3a0ae8bac9aee8fe0c4
4
+ data.tar.gz: 494003de36b2acfd5b0338dc2421dd1add923d55ac0925f1ae2f798f0d56f535
5
5
  SHA512:
6
- metadata.gz: 0f1486c6d2a7b1a726d6c956398e693317df4efffa0c73e45b180a3acb2ac36b69284e68e3230862e6948015b1ceee0a1ac518dab6182c86e4b2c710f587cd16
7
- data.tar.gz: c9e179c20fc545612a00fd2f9b46e5a2eae9ce1292d4635625a80abf641d726d248ee753633dfa8e292b0dba1543af410e58cf5a10380d7854c66af2c06326a1
6
+ metadata.gz: cf672f8b0c8ac5b129c36596c24bfcbbb0eb522c7b8233d24b5b6f68ccf4465989e298c2fef25ed355a9ab476159ffd2682bf622bc10df8625b46a641005633c
7
+ data.tar.gz: 319e401ec3cf9ac165c62e84223c62e267bd80658bdf61abcbf673d87a51516a6de8f02c51441d3b355fdd03fddf479c5e6ae5aa86002e7503bc62f33e32422f
@@ -35,15 +35,8 @@ module RediSearch
35
35
  attr_reader :index, :document, :score, :replace, :language, :no_save
36
36
 
37
37
  def command
38
- [
39
- "ADD",
40
- index.name,
41
- document.document_id,
42
- score,
43
- *extract_options,
44
- "FIELDS",
45
- document.redis_attributes
46
- ].compact
38
+ ["ADD", index.name, document.document_id, score, *extract_options,
39
+ "FIELDS", document.redis_attributes].compact
47
40
  end
48
41
 
49
42
  def extract_options
@@ -10,12 +10,9 @@ module RediSearch
10
10
 
11
11
  def call!
12
12
  index.schema.alter(field_name, raw_schema)
13
+
13
14
  RediSearch.client.call!(
14
- "ALTER",
15
- index.name,
16
- "SCHEMA",
17
- "ADD",
18
- *field_schema
15
+ "ALTER", index.name, "SCHEMA", "ADD", *field_schema
19
16
  ).ok?
20
17
  end
21
18
 
@@ -0,0 +1,32 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RediSearch
4
+ class Client
5
+ class Response < SimpleDelegator
6
+ def initialize(response)
7
+ @response = response
8
+
9
+ super(response)
10
+ end
11
+
12
+ def ok?
13
+ case response
14
+ when String then response == "OK"
15
+ when Integer then response == 1
16
+ when Array then array_ok?
17
+ else response
18
+ end
19
+ end
20
+
21
+ private
22
+
23
+ attr_reader :response
24
+
25
+ def array_ok?
26
+ response.all? do |pipeline_response|
27
+ Response.new(pipeline_response).ok?
28
+ end
29
+ end
30
+ end
31
+ end
32
+ end
@@ -1,31 +1,10 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "redis"
4
+ require "redi_search/client/response"
4
5
 
5
6
  module RediSearch
6
7
  class Client
7
- class Response < SimpleDelegator
8
- def initialize(response)
9
- @response = response
10
-
11
- super(response)
12
- end
13
-
14
- def ok?
15
- if response.is_a? String
16
- response == "OK"
17
- elsif response.is_a? Array
18
- response.all? { |pipeline_response| pipeline_response == "OK" }
19
- else
20
- response
21
- end
22
- end
23
-
24
- private
25
-
26
- attr_reader :response
27
- end
28
-
29
8
  def initialize(redis_config)
30
9
  @redis = Redis.new(redis_config)
31
10
  end
@@ -52,16 +31,11 @@ module RediSearch
52
31
  Response.new(redis.call("FT.#{command}", *params))
53
32
  end
54
33
 
55
- def instrument(action, payload)
56
- block =
57
- if block_given?
58
- Proc.new
59
- else
60
- proc {}
61
- end
62
-
34
+ def instrument(action, payload, &block)
63
35
  ActiveSupport::Notifications.instrument(
64
- "#{action}.redi_search", { name: "RediSearch" }.merge(payload), &block
36
+ "#{action}.redi_search",
37
+ { name: "RediSearch" }.merge(payload),
38
+ &Proc.new(&(block || proc {}))
65
39
  )
66
40
  end
67
41
  end
@@ -18,11 +18,7 @@ module RediSearch
18
18
 
19
19
  def call!
20
20
  RediSearch.client.call!(
21
- "CREATE",
22
- index.name,
23
- *extract_options.compact,
24
- "SCHEMA",
25
- schema.to_a
21
+ "CREATE", index.name, *extract_options.compact, "SCHEMA", schema.to_a
26
22
  ).ok?
27
23
  end
28
24
 
@@ -0,0 +1,41 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RediSearch
4
+ class Document
5
+ module Display
6
+ #:nocov:
7
+ def inspect
8
+ inspection = pretty_print_attributes.map do |field_name|
9
+ "#{field_name}: #{public_send(field_name)}"
10
+ end.compact.join(", ")
11
+
12
+ "#<#{self.class} #{inspection}>"
13
+ end
14
+
15
+ def pretty_print(printer) # rubocop:disable Metrics/MethodLength
16
+ printer.object_address_group(self) do
17
+ printer.seplist(
18
+ pretty_print_attributes , proc { printer.text "," }
19
+ ) do |field_name|
20
+ printer.breakable " "
21
+ printer.group(1) do
22
+ printer.text field_name
23
+ printer.text ":"
24
+ printer.breakable
25
+ printer.pp public_send(field_name)
26
+ end
27
+ end
28
+ end
29
+ 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
+ #:nocov:
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,70 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RediSearch
4
+ class Document
5
+ class Finder
6
+ def initialize(index, *document_ids)
7
+ @index = index
8
+ @document_ids = Array.wrap(document_ids)
9
+ end
10
+
11
+ def find
12
+ if multi?
13
+ parse_multi_documents
14
+ else
15
+ parse_document(document_ids.first, response)
16
+ end
17
+ end
18
+
19
+ private
20
+
21
+ attr_reader :index, :document_ids
22
+
23
+ def response
24
+ @response ||= call!(get_command, index.name, *prepended_document_ids)
25
+ end
26
+
27
+ def call!(*command)
28
+ RediSearch.client.call!(*command)
29
+ end
30
+
31
+ def get_command
32
+ if multi?
33
+ "MGET"
34
+ else
35
+ "GET"
36
+ end
37
+ end
38
+
39
+ def multi?
40
+ document_ids.size > 1
41
+ end
42
+
43
+ def prepended_document_ids
44
+ document_ids.map do |document_id|
45
+ prepend_document_id(document_id)
46
+ end
47
+ end
48
+
49
+ def prepend_document_id(id)
50
+ if id.to_s.start_with? index.name
51
+ id
52
+ else
53
+ "#{index.name}#{id}"
54
+ end
55
+ end
56
+
57
+ def parse_multi_documents
58
+ document_ids.map.with_index do |document_id, index|
59
+ parse_document(document_id, response[index])
60
+ end.compact
61
+ end
62
+
63
+ def parse_document(document_id, document_response)
64
+ return if document_response.blank?
65
+
66
+ Document.new(index, document_id, Hash[*document_response])
67
+ end
68
+ end
69
+ end
70
+ end
@@ -1,7 +1,12 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "redi_search/document/display"
4
+ require "redi_search/document/finder"
5
+
3
6
  module RediSearch
4
7
  class Document
8
+ include Display
9
+
5
10
  class << self
6
11
  def for_object(index, record, serializer: nil, only: [])
7
12
  object_to_serialize = serializer&.new(record) || record
@@ -16,34 +21,11 @@ module RediSearch
16
21
  end
17
22
 
18
23
  def get(index, document_id)
19
- response = RediSearch.client.call!(
20
- "GET", index.name, prepend_document_id(index, document_id)
21
- )
22
-
23
- return if response.blank?
24
-
25
- new(index, document_id, Hash[*response])
24
+ Finder.new(index, document_id).find
26
25
  end
27
26
 
28
27
  def mget(index, *document_ids)
29
- unique_document_ids = document_ids.map do |id|
30
- prepend_document_id(index, id)
31
- end
32
- document_ids.zip(
33
- RediSearch.client.call!("MGET", index.name, *unique_document_ids)
34
- ).map do |document|
35
- next if document[1].blank?
36
-
37
- new(index, document[0], Hash[*document[1]])
38
- end.compact
39
- end
40
-
41
- def prepend_document_id(index, document_id)
42
- if document_id.to_s.starts_with? index.name
43
- document_id
44
- else
45
- "#{index.name}#{document_id}"
46
- end
28
+ Finder.new(index, *document_ids).find
47
29
  end
48
30
  end
49
31
 
@@ -55,53 +37,12 @@ module RediSearch
55
37
  @attributes = fields
56
38
  @score = score
57
39
 
58
- attributes.each do |field, value|
59
- next unless schema_fields.include? field
60
-
61
- instance_variable_set(:"@#{field}", value)
62
- define_singleton_method(field) { value }
63
- end
40
+ load_attributes
64
41
  end
65
42
 
66
43
  def del(delete_document: false)
67
- client.call!(
68
- "DEL", index.name, document_id, ("DD" if delete_document)
69
- ) == 1
70
- end
71
-
72
- #:nocov:
73
- def inspect
74
- inspection = pretty_print_attributes.map do |field_name|
75
- "#{field_name}: #{public_send(field_name)}"
76
- end.compact.join(", ")
77
-
78
- "#<#{self.class} #{inspection}>"
79
- end
80
-
81
- def pretty_print(printer) # rubocop:disable Metrics/MethodLength
82
- printer.object_address_group(self) do
83
- printer.seplist(
84
- pretty_print_attributes , proc { printer.text "," }
85
- ) do |field_name|
86
- printer.breakable " "
87
- printer.group(1) do
88
- printer.text field_name
89
- printer.text ":"
90
- printer.breakable
91
- printer.pp public_send(field_name)
92
- end
93
- end
94
- end
95
- end
96
-
97
- def pretty_print_attributes
98
- pp_attrs = attributes.keys.dup
99
- pp_attrs.push("document_id")
100
- pp_attrs.push("score") if score.present?
101
-
102
- pp_attrs.compact
44
+ call!("DEL", index.name, document_id, ("DD" if delete_document)).ok?
103
45
  end
104
- #:nocov:
105
46
 
106
47
  def schema_fields
107
48
  @schema_fields ||= index.schema.fields.map(&:to_s)
@@ -112,7 +53,11 @@ module RediSearch
112
53
  end
113
54
 
114
55
  def document_id
115
- self.class.prepend_document_id(index, @document_id)
56
+ if @document_id.to_s.start_with? index.name
57
+ @document_id
58
+ else
59
+ "#{index.name}#{@document_id}"
60
+ end
116
61
  end
117
62
 
118
63
  def document_id_without_index
@@ -127,8 +72,17 @@ module RediSearch
127
72
 
128
73
  attr_reader :index
129
74
 
130
- def client
131
- RediSearch.client
75
+ def call!(*command)
76
+ RediSearch.client.call!(*command)
77
+ end
78
+
79
+ def load_attributes
80
+ attributes.each do |field, value|
81
+ next unless schema_fields.include? field
82
+
83
+ instance_variable_set(:"@#{field}", value)
84
+ define_singleton_method(field) { value }
85
+ end
132
86
  end
133
87
  end
134
88
  end
@@ -19,24 +19,26 @@ module RediSearch
19
19
  def to_a
20
20
  execute unless loaded?
21
21
 
22
- @documents
22
+ documents
23
23
  end
24
24
 
25
25
  alias load to_a
26
26
 
27
27
  #:nocov:
28
28
  def inspect
29
- execute unless loaded?
29
+ execute_and_rescue_inspection do
30
+ return super unless valid?
30
31
 
31
- to_a
32
+ documents
33
+ end
32
34
  end
33
35
 
34
36
  def pretty_print(printer)
35
- execute unless loaded?
37
+ execute_and_rescue_inspection do
38
+ return super(inspect) unless valid?
36
39
 
37
- printer.pp(documents)
38
- rescue Redis::CommandError => e
39
- printer.pp(e.message)
40
+ printer.pp(documents)
41
+ end
40
42
  end
41
43
  #:nocov:
42
44
 
@@ -51,15 +53,35 @@ module RediSearch
51
53
  end
52
54
 
53
55
  def execute
56
+ return unless valid?
57
+
54
58
  @loaded = true
55
59
 
56
- RediSearch.client.call!(*command).yield_self do |response|
60
+ call!(*command).yield_self do |response|
57
61
  parse_response(response)
58
62
  end
59
63
  end
60
64
 
65
+ def call!(*command)
66
+ RediSearch.client.call!(*command)
67
+ end
68
+
61
69
  def parse_response(_response)
62
70
  raise NotImplementedError, "included class did not define #{__method__}"
63
71
  end
72
+
73
+ def valid?
74
+ true
75
+ end
76
+
77
+ #:nocov:
78
+ def execute_and_rescue_inspection
79
+ execute unless loaded?
80
+
81
+ yield
82
+ rescue Redis::CommandError => e
83
+ e.message
84
+ end
85
+ #:nocov:
64
86
  end
65
87
  end
@@ -68,17 +68,19 @@ module RediSearch
68
68
  self.class.runtime += event.duration
69
69
  return unless logger.debug?
70
70
 
71
- payload = event.payload
72
- name = "#{payload[:name]} (#{event.duration.round(1)}ms)"
73
- command = command_string(payload)
71
+ command = command_string(event)
74
72
 
75
- debug " #{color(name, RED, true)} #{color(command, debug_color, true)}"
73
+ debug " #{log_name(event)} #{color(command, debug_color, true)}"
76
74
  end
77
75
 
78
- def command_string(payload)
79
- payload[:query].flatten.map.with_index do |arg, i|
76
+ def log_name(event)
77
+ color("#{event.payload[:name]} (#{event.duration.round(1)}ms)", RED, true)
78
+ end
79
+
80
+ def command_string(event)
81
+ event.payload[:query].flatten.map.with_index do |arg, i|
80
82
  arg = "FT.#{arg}" if prepend_ft?(arg, i)
81
- arg = arg.inspect if inspect_arg?(payload, arg)
83
+ arg = arg.inspect if inspect_arg?(event.payload, arg)
82
84
  arg
83
85
  end.join(" ")
84
86
  end
@@ -12,9 +12,7 @@ module RediSearch
12
12
  schema, options = options.to_a.flatten
13
13
 
14
14
  "RediSearch::Schema::#{schema.to_s.capitalize}Field".
15
- constantize.
16
- new(field_name, **options.to_h).
17
- to_a
15
+ constantize.new(field_name, **options.to_h).to_a
18
16
  end
19
17
 
20
18
  def initialize(raw)
@@ -9,16 +9,13 @@ module RediSearch
9
9
  @prior_clause = prior_clause
10
10
  @not = false
11
11
 
12
- initialize_term(term, **term_options)
12
+ initialize_term(term, **term_options) if term.present?
13
13
  end
14
14
 
15
15
  def to_s
16
16
  raise ArgumentError, "missing query terms" if term.blank?
17
17
 
18
- [
19
- prior_clause.presence,
20
- queryify_term.dup.prepend(not_operator)
21
- ].compact.join(operand)
18
+ [prior_clause.presence, queryify_term].compact.join(operand)
22
19
  end
23
20
 
24
21
  delegate :inspect, to: :to_s
@@ -26,7 +23,7 @@ module RediSearch
26
23
  def not(term, **term_options)
27
24
  @not = true
28
25
 
29
- initialize_term(term, **term_options)
26
+ initialize_term(term, **term_options) if term.present?
30
27
 
31
28
  search
32
29
  end
@@ -46,25 +43,31 @@ module RediSearch
46
43
  end
47
44
 
48
45
  def initialize_term(term, **term_options)
49
- return if term.blank?
50
-
51
- @term =
52
- if term.is_a? RediSearch::Search
53
- term
54
- else
55
- Term.new(term, term_options)
56
- end
46
+ @term = if term.is_a? RediSearch::Search
47
+ term
48
+ else
49
+ Term.new(term, term_options)
50
+ end
57
51
  end
58
52
 
59
53
  def queryify_term
60
- if term.is_a?(RediSearch::Search) &&
61
- !term.term_clause.is_a?(RediSearch::Search::Clauses::Where)
62
- "(#{term.term_clause})"
63
- elsif term.is_a?(RediSearch::Search)
64
- term.term_clause
54
+ if term_is_search?
55
+ queryify_search
65
56
  else
66
57
  term
67
- end.to_s
58
+ end.to_s.dup.prepend(not_operator)
59
+ end
60
+
61
+ def term_is_search?
62
+ term.is_a? RediSearch::Search
63
+ end
64
+
65
+ def queryify_search
66
+ if !term.term_clause.is_a?(RediSearch::Search::Clauses::Where)
67
+ "(#{term.term_clause})"
68
+ else
69
+ term.term_clause
70
+ end
68
71
  end
69
72
  end
70
73
  end
@@ -53,12 +53,15 @@ module RediSearch
53
53
  condition, *options = condition.to_a
54
54
 
55
55
  @field = condition[0]
56
- @term =
57
- if condition[1].is_a? RediSearch::Search
58
- condition[1]
59
- else
60
- Term.new(condition[1], **options.to_h)
61
- end
56
+ @term = make_term(condition, options)
57
+ end
58
+
59
+ def make_term(condition, options)
60
+ if condition[1].is_a? RediSearch::Search
61
+ condition[1]
62
+ else
63
+ Term.new(condition[1], **options.to_h)
64
+ end
62
65
  end
63
66
  end
64
67
  end
@@ -66,13 +66,11 @@ module RediSearch
66
66
  end
67
67
 
68
68
  def count
69
- if @loaded
70
- to_a.size
71
- else
72
- RediSearch.client.call!(
73
- "SEARCH", index.name, term_clause, *Limit.new(total: 0).clause
74
- ).first
75
- end
69
+ return to_a.size if loaded?
70
+
71
+ call!(
72
+ "SEARCH", index.name, term_clause, *Limit.new(total: 0).clause
73
+ ).first
76
74
  end
77
75
 
78
76
  def where(**condition)
@@ -7,13 +7,7 @@ module RediSearch
7
7
  @count = count
8
8
  @used_clauses = used_clauses
9
9
 
10
- super(documents.each_slice(response_slice).map do |slice|
11
- document_id = slice[0]
12
- fields = slice.last unless no_content?
13
- score = slice[1].to_f if with_scores?
14
-
15
- Document.new(index, document_id, Hash[*fields.to_a], score)
16
- end)
10
+ super(parse_results(index, documents))
17
11
  end
18
12
 
19
13
  def count
@@ -41,6 +35,16 @@ module RediSearch
41
35
  def no_content?
42
36
  @used_clauses.include? "no_content"
43
37
  end
38
+
39
+ def parse_results(index, documents)
40
+ documents.each_slice(response_slice).map do |slice|
41
+ document_id = slice[0]
42
+ fields = slice.last unless no_content?
43
+ score = slice[1].to_f if with_scores?
44
+
45
+ Document.new(index, document_id, Hash[*fields.to_a], score)
46
+ end
47
+ end
44
48
  end
45
49
  end
46
50
  end
@@ -3,11 +3,19 @@
3
3
  module RediSearch
4
4
  class Search
5
5
  class Term
6
+ include ActiveModel::Validations
7
+
8
+ validates :fuzziness, numericality: {
9
+ only_integer: true, less_than: 4, greater_than: 0, allow_blank: true
10
+ }
11
+ validates :option, inclusion: { in: %i(fuzziness optional prefix) },
12
+ allow_nil: true
13
+
6
14
  def initialize(term, **options)
7
15
  @term = term
8
16
  @options = options
9
17
 
10
- validate_options
18
+ validate!
11
19
  end
12
20
 
13
21
  def to_s
@@ -23,7 +31,7 @@ module RediSearch
23
31
  attr_accessor :term, :options
24
32
 
25
33
  def fuzziness
26
- @fuzziness ||= options[:fuzziness].to_i
34
+ @fuzziness ||= options[:fuzziness]
27
35
  end
28
36
 
29
37
  def optional_operator
@@ -38,10 +46,14 @@ module RediSearch
38
46
  "*"
39
47
  end
40
48
 
49
+ def fuzzy_operator
50
+ "%" * fuzziness.to_i
51
+ end
52
+
41
53
  def stringify_query
42
54
  @term.to_s.
43
55
  tr("`", "\`").
44
- yield_self { |str| "#{'%' * fuzziness}#{str}#{'%' * fuzziness}" }.
56
+ yield_self { |str| "#{fuzzy_operator}#{str}#{fuzzy_operator}" }.
45
57
  yield_self { |str| "#{optional_operator}#{str}" }.
46
58
  yield_self { |str| "#{str}#{prefix_operator}" }.
47
59
  yield_self { |str| "`#{str}`" }
@@ -55,17 +67,8 @@ module RediSearch
55
67
  "[#{first} #{last}]"
56
68
  end
57
69
 
58
- def validate_options
59
- unsupported_options =
60
- (options.keys.map(&:to_s) - %w(fuzziness optional prefix)).join(", ")
61
-
62
- if unsupported_options.present?
63
- raise(ArgumentError,
64
- "#{unsupported_options} are unsupported term options")
65
- end
66
-
67
- raise(ArgumentError, "fuzziness can only be between 0 and 3") if
68
- fuzziness.negative? || fuzziness > 3
70
+ def option
71
+ options.keys.first&.to_sym
69
72
  end
70
73
  end
71
74
  end
@@ -37,11 +37,7 @@ module RediSearch
37
37
 
38
38
  def to_redis
39
39
  command.map do |arg|
40
- if !arg.to_s.starts_with?(/\(-?@/) && arg.to_s.split(/\s|\|/).size > 1
41
- arg.inspect
42
- else
43
- arg
44
- end
40
+ inspect_command_arg(arg)
45
41
  end.join(" ")
46
42
  end
47
43
 
@@ -63,5 +59,17 @@ module RediSearch
63
59
  def parse_response(response)
64
60
  @documents = Result.new(index, used_clauses, response[0], response[1..-1])
65
61
  end
62
+
63
+ def inspect_command_arg(arg)
64
+ if !arg.to_s.starts_with?(/\(-?@/) && arg.to_s.split(/\s|\|/).size > 1
65
+ arg.inspect
66
+ else
67
+ arg
68
+ end
69
+ end
70
+
71
+ def valid?
72
+ term_clause.present?
73
+ end
66
74
  end
67
75
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module RediSearch
4
- VERSION = "1.0.0"
4
+ VERSION = "1.0.1"
5
5
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: redi_search
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.0
4
+ version: 1.0.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Nick Pezza
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2019-07-03 00:00:00.000000000 Z
11
+ date: 2019-07-07 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activemodel
@@ -145,9 +145,12 @@ files:
145
145
  - lib/redi_search/add.rb
146
146
  - lib/redi_search/alter.rb
147
147
  - lib/redi_search/client.rb
148
+ - lib/redi_search/client/response.rb
148
149
  - lib/redi_search/configuration.rb
149
150
  - lib/redi_search/create.rb
150
151
  - lib/redi_search/document.rb
152
+ - lib/redi_search/document/display.rb
153
+ - lib/redi_search/document/finder.rb
151
154
  - lib/redi_search/index.rb
152
155
  - lib/redi_search/lazily_load.rb
153
156
  - lib/redi_search/log_subscriber.rb