redi_search 2.0.2 → 5.0.0
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.
- checksums.yaml +4 -4
- data/.github/workflows/lint.yml +13 -11
- data/.github/workflows/tests.yml +19 -14
- data/.rubocop.yml +71 -5
- data/Appraisals +8 -4
- data/Gemfile +3 -2
- data/README.md +15 -33
- data/Rakefile +1 -0
- data/bin/console +6 -2
- data/gemfiles/{activerecord_51.gemfile → activerecord_60.gemfile} +4 -2
- data/gemfiles/{activerecord_52.gemfile → activerecord_61.gemfile} +4 -2
- data/gemfiles/activerecord_70.gemfile +15 -0
- data/lib/redi_search/add_field.rb +1 -1
- data/lib/redi_search/client/response.rb +1 -1
- data/lib/redi_search/client.rb +15 -5
- data/lib/redi_search/create.rb +2 -1
- data/lib/redi_search/document/display.rb +4 -4
- data/lib/redi_search/document/finder.rb +12 -50
- data/lib/redi_search/document.rb +11 -16
- data/lib/redi_search/hset.rb +28 -0
- data/lib/redi_search/index.rb +19 -17
- data/lib/redi_search/lazily_load.rb +2 -2
- data/lib/redi_search/log_subscriber.rb +7 -6
- data/lib/redi_search/model.rb +4 -10
- data/lib/redi_search/schema/field.rb +10 -2
- data/lib/redi_search/schema/geo_field.rb +1 -1
- data/lib/redi_search/schema/numeric_field.rb +1 -1
- data/lib/redi_search/schema/tag_field.rb +5 -1
- data/lib/redi_search/schema/text_field.rb +1 -1
- data/lib/redi_search/schema.rb +10 -5
- data/lib/redi_search/search/clauses/boolean.rb +4 -4
- data/lib/redi_search/search/clauses/or.rb +1 -1
- data/lib/redi_search/search/result.rb +2 -2
- data/lib/redi_search/search.rb +1 -1
- data/lib/redi_search/spellcheck/result.rb +4 -4
- data/lib/redi_search/spellcheck.rb +1 -1
- data/lib/redi_search/version.rb +1 -1
- data/redi_search.gemspec +3 -3
- metadata +15 -14
- data/lib/redi_search/add.rb +0 -67
data/lib/redi_search/client.rb
CHANGED
@@ -9,10 +9,12 @@ module RediSearch
|
|
9
9
|
class Client
|
10
10
|
def initialize(redis = Redis.new)
|
11
11
|
@redis = redis
|
12
|
+
@pipeline = false
|
12
13
|
end
|
13
14
|
|
14
|
-
def call!(command, *params)
|
15
|
+
def call!(command, *params, skip_ft: false)
|
15
16
|
instrument(command.downcase, query: [command, params]) do
|
17
|
+
command = "FT.#{command}" unless skip_ft
|
16
18
|
send_command(command, *params)
|
17
19
|
end
|
18
20
|
end
|
@@ -20,23 +22,31 @@ module RediSearch
|
|
20
22
|
def multi
|
21
23
|
Response.new(redis.multi do
|
22
24
|
instrument("pipeline", query: ["begin pipeline"])
|
23
|
-
yield
|
25
|
+
capture_pipeline { yield }
|
24
26
|
instrument("pipeline", query: ["finish pipeline"])
|
25
27
|
end)
|
26
28
|
end
|
27
29
|
|
28
30
|
private
|
29
31
|
|
30
|
-
attr_reader
|
32
|
+
attr_reader :redis
|
33
|
+
attr_accessor :pipeline
|
34
|
+
|
35
|
+
def capture_pipeline
|
36
|
+
self.pipeline = true
|
37
|
+
yield
|
38
|
+
self.pipeline = false
|
39
|
+
end
|
31
40
|
|
32
41
|
def send_command(command, *params)
|
33
|
-
Response.new(redis.call(
|
42
|
+
Response.new(redis.call(command, *params))
|
34
43
|
end
|
35
44
|
|
36
45
|
def instrument(action, payload, &block)
|
37
46
|
ActiveSupport::Notifications.instrument(
|
38
47
|
"action.redi_search",
|
39
|
-
{ name: "RediSearch", action: action }.
|
48
|
+
{ name: "RediSearch", action: action, inside_pipeline: pipeline }.
|
49
|
+
merge(payload),
|
40
50
|
&Proc.new(&(block || proc {}))
|
41
51
|
)
|
42
52
|
end
|
data/lib/redi_search/create.rb
CHANGED
@@ -31,7 +31,8 @@ module RediSearch
|
|
31
31
|
attr_reader :index, :schema, :options
|
32
32
|
|
33
33
|
def command
|
34
|
-
["CREATE", index.name,
|
34
|
+
["CREATE", index.name, "ON", "HASH", "PREFIX", 1, index.name,
|
35
|
+
*extract_options.compact, "SCHEMA", schema.to_a]
|
35
36
|
end
|
36
37
|
|
37
38
|
def extract_options
|
@@ -4,9 +4,9 @@ module RediSearch
|
|
4
4
|
class Document
|
5
5
|
module Display
|
6
6
|
def inspect
|
7
|
-
inspection = pretty_print_attributes.
|
7
|
+
inspection = pretty_print_attributes.filter_map do |field_name|
|
8
8
|
"#{field_name}: #{public_send(field_name)}"
|
9
|
-
end.
|
9
|
+
end.join(", ")
|
10
10
|
|
11
11
|
"#<#{self.class} #{inspection}>"
|
12
12
|
end
|
@@ -19,7 +19,7 @@ module RediSearch
|
|
19
19
|
pp_attrs.compact
|
20
20
|
end
|
21
21
|
|
22
|
-
|
22
|
+
# :nocov:
|
23
23
|
def pretty_print(printer) # rubocop:disable Metrics/MethodLength
|
24
24
|
printer.object_address_group(self) do
|
25
25
|
printer.seplist(
|
@@ -35,7 +35,7 @@ module RediSearch
|
|
35
35
|
end
|
36
36
|
end
|
37
37
|
end
|
38
|
-
|
38
|
+
# :nocov:
|
39
39
|
end
|
40
40
|
end
|
41
41
|
end
|
@@ -3,75 +3,37 @@
|
|
3
3
|
module RediSearch
|
4
4
|
class Document
|
5
5
|
class Finder
|
6
|
-
def initialize(index,
|
6
|
+
def initialize(index, document_id)
|
7
7
|
@index = index
|
8
|
-
@
|
8
|
+
@document_id = document_id
|
9
9
|
end
|
10
10
|
|
11
11
|
def find
|
12
|
-
if
|
13
|
-
parse_multi_documents
|
14
|
-
else
|
15
|
-
parse_document(document_ids.first, response)
|
16
|
-
end
|
12
|
+
Document.new(index, document_id, Hash[*response]) if response?
|
17
13
|
end
|
18
14
|
|
19
15
|
private
|
20
16
|
|
21
|
-
attr_reader :index, :
|
17
|
+
attr_reader :index, :document_id
|
22
18
|
|
23
19
|
def response
|
24
|
-
@response ||= call!(
|
20
|
+
@response ||= call!("HGETALL", prepended_document_id)
|
25
21
|
end
|
26
22
|
|
27
23
|
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
|
24
|
+
RediSearch.client.call!(*command, skip_ft: true)
|
47
25
|
end
|
48
26
|
|
49
|
-
def
|
50
|
-
if
|
51
|
-
|
27
|
+
def prepended_document_id
|
28
|
+
if document_id.to_s.start_with? index.name
|
29
|
+
document_id
|
52
30
|
else
|
53
|
-
"#{index.name}#{
|
31
|
+
"#{index.name}#{document_id}"
|
54
32
|
end
|
55
33
|
end
|
56
34
|
|
57
|
-
def
|
58
|
-
|
59
|
-
parse_document(document_id, response[index])
|
60
|
-
end.compact
|
61
|
-
end
|
62
|
-
|
63
|
-
def parse_document(document_id, document_response)
|
64
|
-
return unless document_response?(document_response)
|
65
|
-
|
66
|
-
Document.new(index, document_id, Hash[*document_response])
|
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
|
35
|
+
def response?
|
36
|
+
!response.to_a.empty?
|
75
37
|
end
|
76
38
|
end
|
77
39
|
end
|
data/lib/redi_search/document.rb
CHANGED
@@ -11,11 +11,11 @@ module RediSearch
|
|
11
11
|
def for_object(index, record, serializer: nil, only: [])
|
12
12
|
object_to_serialize = serializer&.new(record) || record
|
13
13
|
|
14
|
-
field_values = index.schema.fields.map do |field|
|
15
|
-
next unless only.empty? || only.include?(field
|
14
|
+
field_values = index.schema.fields.map(&:name).filter_map do |field|
|
15
|
+
next unless only.empty? || only.include?(field)
|
16
16
|
|
17
17
|
[field.to_s, object_to_serialize.public_send(field)]
|
18
|
-
end.
|
18
|
+
end.to_h
|
19
19
|
|
20
20
|
new(index, object_to_serialize.id, field_values)
|
21
21
|
end
|
@@ -23,10 +23,6 @@ module RediSearch
|
|
23
23
|
def get(index, document_id)
|
24
24
|
Finder.new(index, document_id).find
|
25
25
|
end
|
26
|
-
|
27
|
-
def mget(index, *document_ids)
|
28
|
-
Finder.new(index, *document_ids).find
|
29
|
-
end
|
30
26
|
end
|
31
27
|
|
32
28
|
attr_reader :attributes, :score
|
@@ -40,17 +36,20 @@ module RediSearch
|
|
40
36
|
load_attributes
|
41
37
|
end
|
42
38
|
|
43
|
-
def del
|
44
|
-
|
45
|
-
call!(*command.compact).ok?
|
39
|
+
def del
|
40
|
+
RediSearch.client.call!("DEL", document_id, skip_ft: true).ok?
|
46
41
|
end
|
47
42
|
|
48
43
|
def schema_fields
|
49
|
-
@schema_fields ||= index.schema.fields.map
|
44
|
+
@schema_fields ||= index.schema.fields.map do |field|
|
45
|
+
field.name.to_s
|
46
|
+
end
|
50
47
|
end
|
51
48
|
|
52
49
|
def redis_attributes
|
53
|
-
attributes.
|
50
|
+
attributes.flat_map do |field, value|
|
51
|
+
[field, index.schema[field.to_sym].serialize(value)]
|
52
|
+
end
|
54
53
|
end
|
55
54
|
|
56
55
|
def document_id
|
@@ -73,10 +72,6 @@ module RediSearch
|
|
73
72
|
|
74
73
|
attr_reader :index
|
75
74
|
|
76
|
-
def call!(*command)
|
77
|
-
RediSearch.client.call!(*command)
|
78
|
-
end
|
79
|
-
|
80
75
|
def load_attributes
|
81
76
|
attributes.each do |field, value|
|
82
77
|
next unless schema_fields.include? field.to_s
|
@@ -0,0 +1,28 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RediSearch
|
4
|
+
class Hset
|
5
|
+
def initialize(index, document)
|
6
|
+
@index = index
|
7
|
+
@document = document
|
8
|
+
end
|
9
|
+
|
10
|
+
def call!
|
11
|
+
RediSearch.client.call!(*command, skip_ft: true)
|
12
|
+
end
|
13
|
+
|
14
|
+
def call
|
15
|
+
call!
|
16
|
+
rescue Redis::CommandError
|
17
|
+
false
|
18
|
+
end
|
19
|
+
|
20
|
+
private
|
21
|
+
|
22
|
+
attr_reader :index, :document
|
23
|
+
|
24
|
+
def command
|
25
|
+
["HSET", document.document_id, document.redis_attributes].compact
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
data/lib/redi_search/index.rb
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require "redi_search/
|
3
|
+
require "redi_search/hset"
|
4
4
|
require "redi_search/create"
|
5
5
|
require "redi_search/schema"
|
6
6
|
require "redi_search/search"
|
@@ -33,34 +33,36 @@ module RediSearch
|
|
33
33
|
Create.new(self, schema, options).call!
|
34
34
|
end
|
35
35
|
|
36
|
-
def drop
|
37
|
-
drop!
|
36
|
+
def drop(keep_docs: false)
|
37
|
+
drop!(keep_docs: keep_docs)
|
38
38
|
rescue Redis::CommandError
|
39
39
|
false
|
40
40
|
end
|
41
41
|
|
42
|
-
def drop!
|
43
|
-
|
42
|
+
def drop!(keep_docs: false)
|
43
|
+
command = ["DROPINDEX", name]
|
44
|
+
command << "DD" unless keep_docs
|
45
|
+
client.call!(*command.compact).ok?
|
44
46
|
end
|
45
47
|
|
46
|
-
def add(document
|
47
|
-
|
48
|
+
def add(document)
|
49
|
+
Hset.new(self, document).call
|
48
50
|
end
|
49
51
|
|
50
|
-
def add!(document
|
51
|
-
|
52
|
+
def add!(document)
|
53
|
+
Hset.new(self, document).call!
|
52
54
|
end
|
53
55
|
|
54
|
-
def add_multiple(documents
|
56
|
+
def add_multiple(documents)
|
55
57
|
client.multi do
|
56
58
|
documents.each do |document|
|
57
|
-
add(document
|
59
|
+
add(document)
|
58
60
|
end
|
59
|
-
end.
|
61
|
+
end.all? { |response| response >= 0 }
|
60
62
|
end
|
61
63
|
|
62
|
-
def del(document
|
63
|
-
document.del
|
64
|
+
def del(document)
|
65
|
+
document.del
|
64
66
|
end
|
65
67
|
|
66
68
|
def exist?
|
@@ -78,14 +80,14 @@ module RediSearch
|
|
78
80
|
end
|
79
81
|
|
80
82
|
def fields
|
81
|
-
schema.fields.map
|
83
|
+
schema.fields.map { |field| field.name.to_s }
|
82
84
|
end
|
83
85
|
|
84
|
-
def reindex(documents, recreate: false
|
86
|
+
def reindex(documents, recreate: false)
|
85
87
|
drop if recreate
|
86
88
|
create unless exist?
|
87
89
|
|
88
|
-
add_multiple documents
|
90
|
+
add_multiple documents
|
89
91
|
end
|
90
92
|
|
91
93
|
def document_count
|
@@ -30,7 +30,7 @@ module RediSearch
|
|
30
30
|
end
|
31
31
|
end
|
32
32
|
|
33
|
-
|
33
|
+
# :nocov:
|
34
34
|
def pretty_print(printer)
|
35
35
|
execute_and_rescue_inspection do
|
36
36
|
return super(inspect) unless valid?
|
@@ -38,7 +38,7 @@ module RediSearch
|
|
38
38
|
printer.pp(documents)
|
39
39
|
end
|
40
40
|
end
|
41
|
-
|
41
|
+
# :nocov:
|
42
42
|
|
43
43
|
private
|
44
44
|
|
@@ -12,12 +12,12 @@ module RediSearch
|
|
12
12
|
Thread.current[:redi_search_runtime] ||= 0
|
13
13
|
end
|
14
14
|
|
15
|
-
|
15
|
+
# :nocov:
|
16
16
|
def self.reset_runtime
|
17
17
|
rt, self.runtime = runtime, 0
|
18
18
|
rt
|
19
19
|
end
|
20
|
-
|
20
|
+
# :nocov:
|
21
21
|
|
22
22
|
def action(event)
|
23
23
|
self.class.runtime += event.duration
|
@@ -39,9 +39,9 @@ module RediSearch
|
|
39
39
|
def action_color(action)
|
40
40
|
case action.to_sym
|
41
41
|
when :search, :spellcheck then YELLOW
|
42
|
-
when :create, :
|
43
|
-
when :
|
44
|
-
when :
|
42
|
+
when :create, :hset then GREEN
|
43
|
+
when :dropindex, :del then RED
|
44
|
+
when :hgetall, :info then CYAN
|
45
45
|
when :pipeline then MAGENTA
|
46
46
|
when :explaincli then BLUE
|
47
47
|
end
|
@@ -52,6 +52,7 @@ module RediSearch
|
|
52
52
|
event.payload[:query].flatten.map.with_index do |arg, i|
|
53
53
|
arg = "FT.#{arg}" if prepend_ft?(arg, i)
|
54
54
|
arg = arg.inspect if inspect_arg?(event.payload, arg)
|
55
|
+
arg = " #{arg}" if event.payload[:inside_pipeline]
|
55
56
|
arg
|
56
57
|
end.join(" ")
|
57
58
|
end
|
@@ -61,7 +62,7 @@ module RediSearch
|
|
61
62
|
end
|
62
63
|
|
63
64
|
def prepend_ft?(arg, index)
|
64
|
-
index.zero? && !multiword?(arg)
|
65
|
+
index.zero? && !multiword?(arg) && %w(HSET HGETALL DEL).exclude?(arg.to_s)
|
65
66
|
end
|
66
67
|
|
67
68
|
def inspect_arg?(payload, arg)
|
data/lib/redi_search/model.rb
CHANGED
@@ -48,11 +48,11 @@ module RediSearch
|
|
48
48
|
redi_search_index.spellcheck(term, distance: distance)
|
49
49
|
end
|
50
50
|
|
51
|
-
def reindex(recreate: false, only: []
|
51
|
+
def reindex(recreate: false, only: [])
|
52
52
|
search_import.find_in_batches.all? do |group|
|
53
53
|
redi_search_index.reindex(
|
54
54
|
group.map { |record| record.redi_search_document(only: only) },
|
55
|
-
recreate: recreate
|
55
|
+
recreate: recreate
|
56
56
|
)
|
57
57
|
end
|
58
58
|
end
|
@@ -67,17 +67,11 @@ module RediSearch
|
|
67
67
|
end
|
68
68
|
|
69
69
|
def redi_search_delete_document
|
70
|
-
|
71
|
-
|
72
|
-
self.class.redi_search_index.del(
|
73
|
-
redi_search_document, delete_document: true
|
74
|
-
)
|
70
|
+
self.class.redi_search_index.del(redi_search_document)
|
75
71
|
end
|
76
72
|
|
77
73
|
def redi_search_add_document
|
78
|
-
|
79
|
-
|
80
|
-
self.class.redi_search_index.add(redi_search_document, replace: true)
|
74
|
+
self.class.redi_search_index.add(redi_search_document)
|
81
75
|
end
|
82
76
|
end
|
83
77
|
end
|
@@ -3,6 +3,14 @@
|
|
3
3
|
module RediSearch
|
4
4
|
class Schema
|
5
5
|
class Field
|
6
|
+
def name
|
7
|
+
@name&.to_sym
|
8
|
+
end
|
9
|
+
|
10
|
+
def serialize(value)
|
11
|
+
value
|
12
|
+
end
|
13
|
+
|
6
14
|
private
|
7
15
|
|
8
16
|
FALSES = [
|
@@ -10,10 +18,10 @@ module RediSearch
|
|
10
18
|
].freeze
|
11
19
|
|
12
20
|
def boolean_options_string
|
13
|
-
boolean_options.
|
21
|
+
boolean_options.filter_map do |option|
|
14
22
|
option.to_s.upcase.split("_").join unless
|
15
23
|
FALSES.include?(send(option))
|
16
|
-
end
|
24
|
+
end
|
17
25
|
end
|
18
26
|
end
|
19
27
|
end
|