redisearch-rails 0.2.0 → 0.5.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/.travis.yml +24 -3
- data/README.md +1 -0
- data/lib/redisearch-rails/batches_indexer.rb +44 -0
- data/lib/redisearch-rails/configuration.rb +5 -1
- data/lib/redisearch-rails/record_indexer.rb +37 -0
- data/lib/redisearch-rails/redisearchable/class_methods.rb +36 -15
- data/lib/redisearch-rails/redisearchable/instance_methods.rb +21 -4
- data/lib/redisearch-rails/reindex_batches_job.rb +26 -0
- data/lib/redisearch-rails/reindex_record_job.rb +30 -0
- data/lib/redisearch-rails/version.rb +1 -1
- data/lib/redisearch-rails.rb +57 -0
- data/redisearch-rails.gemspec +1 -0
- metadata +20 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: f416956f2ec55228c53f37ddaadc2f0a5200caa7ccf6399c1f991217ed2242f3
|
4
|
+
data.tar.gz: 01624db3f4e81c614550dc998e0214bfaddb61a2b301e052b05612de2e9c12db
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 452964535c36fda982b1c7dc2299df33bacaf9e58bdc93969ed5e8ebf787c5fca1bd7f6e461bf09f4006fbbc47b2e7535770b8a62b969f92b55df91d8eeaee0b
|
7
|
+
data.tar.gz: 1e2f6985c7b5bec11b1a8b5c223925c6e66995f91ec21cf558ce409de9fbeb936f30f7ced8d78605199c57efc95d26cc8b291d101e26a5340e32265400ebda11
|
data/.travis.yml
CHANGED
@@ -1,7 +1,28 @@
|
|
1
1
|
---
|
2
|
-
|
2
|
+
os: linux
|
3
|
+
|
3
4
|
language: ruby
|
4
5
|
cache: bundler
|
6
|
+
|
5
7
|
rvm:
|
6
|
-
- 2.6.
|
7
|
-
|
8
|
+
- 2.6.5
|
9
|
+
- 2.5.7
|
10
|
+
- 2.4.9
|
11
|
+
|
12
|
+
env:
|
13
|
+
- BV=1.17.2
|
14
|
+
|
15
|
+
before_install:
|
16
|
+
- gem install bundler -v $BV
|
17
|
+
- docker run -d -p 127.0.0.1:6379:6379 redislabs/redisearch:latest
|
18
|
+
- docker ps -a
|
19
|
+
|
20
|
+
jobs:
|
21
|
+
include:
|
22
|
+
# Ruby-head (we want to know how we're doing, but not fail the build)
|
23
|
+
- rvm: ruby-head
|
24
|
+
env: BV=2.1.4
|
25
|
+
|
26
|
+
allow_failures:
|
27
|
+
- rvm: ruby-head
|
28
|
+
env: BV=2.1.4
|
data/README.md
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
# Redisearch-rails
|
2
|
+
[](https://travis-ci.org/eventBrain/redisearch-rails)
|
2
3
|
|
3
4
|
Adds support for easily indexing and search ActiveRecord models using RediSearch module http://redisearch.io/
|
4
5
|
|
@@ -0,0 +1,44 @@
|
|
1
|
+
module RediSearch
|
2
|
+
class BatchesIndexer
|
3
|
+
|
4
|
+
attr_reader :klass
|
5
|
+
|
6
|
+
def initialize(klass)
|
7
|
+
@klass = klass
|
8
|
+
@index = klass.redisearch_index
|
9
|
+
end
|
10
|
+
|
11
|
+
def reindex(mode: :inline, **options)
|
12
|
+
unless [:inline, true, :async].include?(mode)
|
13
|
+
raise ArgumentError, "#{mode} its not a valid value for mode"
|
14
|
+
end
|
15
|
+
|
16
|
+
batch_size = klass.redisearch_index_options[:batch_size] || DEFAULT_BATCH_SIZE
|
17
|
+
|
18
|
+
#this make a select count
|
19
|
+
size = klass.redisearch_import.find_in_batches(batch_size: batch_size).size
|
20
|
+
case mode
|
21
|
+
when :async
|
22
|
+
for i in 1..size
|
23
|
+
start = (i-1)*batch_size
|
24
|
+
finish = (i*batch_size)-1
|
25
|
+
RediSearch::ReindexBatchesJob.perform_later(klass.name, start.to_s, finish.to_s, batch_size.to_s)
|
26
|
+
end
|
27
|
+
else
|
28
|
+
reindex_in_batches(batch_size, options)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
private
|
33
|
+
|
34
|
+
def reindex_in_batches(batch_size, **options)
|
35
|
+
klass.redisearch_import.find_in_batches(batch_size: batch_size) do |records|
|
36
|
+
klass.redisearch_index.client.multi do
|
37
|
+
records.each do |record|
|
38
|
+
record.reindex(mode: :inline, **options.deep_merge(replace: true, partial: true))
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
@@ -1,9 +1,13 @@
|
|
1
1
|
module RediSearch
|
2
2
|
class Configuration
|
3
|
-
attr_accessor :redis_config
|
3
|
+
attr_accessor :redis_config, :index_prefix, :index_suffix, :queue_name, :model_options
|
4
4
|
|
5
5
|
def initialize
|
6
6
|
@redis_config = {}
|
7
|
+
@index_prefix = nil
|
8
|
+
@index_suffix = nil
|
9
|
+
@queue_name = :redisearch
|
10
|
+
@model_options = {}
|
7
11
|
end
|
8
12
|
end
|
9
13
|
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
module RediSearch
|
2
|
+
class RecordIndexer
|
3
|
+
|
4
|
+
attr_reader :record, :index
|
5
|
+
|
6
|
+
def initialize(record)
|
7
|
+
@record = record
|
8
|
+
@index = record.class.redisearch_index
|
9
|
+
end
|
10
|
+
|
11
|
+
def reindex(mode: nil, **options)
|
12
|
+
unless [:inline, true, nil, :async].include?(mode)
|
13
|
+
raise ArgumentError, "#{mode} its not a valid value for mode"
|
14
|
+
end
|
15
|
+
|
16
|
+
mode ||= RediSearch.callbacks_value || record.class.redisearch_index_options[:callbacks] || true
|
17
|
+
|
18
|
+
case mode
|
19
|
+
when :async
|
20
|
+
RediSearch::ReindexRecordJob.perform_later(record.class.name, record.id.to_s)
|
21
|
+
else
|
22
|
+
reindex_record
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
private
|
27
|
+
|
28
|
+
def reindex_record
|
29
|
+
if record.destroyed? || !record.persisted? || !record.should_index?
|
30
|
+
document = index.generate_document(record.redisearch_document_id, {})
|
31
|
+
document.del(dd: true)
|
32
|
+
else
|
33
|
+
record.redisearch_add
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -2,21 +2,48 @@ module RediSearch
|
|
2
2
|
module RediSearchable
|
3
3
|
module ClassMethods
|
4
4
|
|
5
|
-
attr_reader :redisearch_index, :redisearch_index_serializer
|
6
|
-
|
7
5
|
def redisearch(*args, schema:, **options)
|
8
|
-
|
9
|
-
|
6
|
+
options = RediSearch.model_options.merge(options)
|
7
|
+
|
8
|
+
raise "Only call redisearch once per model" if respond_to?(:redisearch_index)
|
9
|
+
|
10
|
+
prefix = options[:prefix] || RediSearch.index_prefix
|
11
|
+
prefix = prefix.call if prefix.respond_to?(:call)
|
12
|
+
|
13
|
+
suffix = options[:suffix] || RediSearch.index_suffix
|
14
|
+
suffix = suffix.call if suffix.respond_to?(:call)
|
15
|
+
|
16
|
+
callbacks = options.key?(:callbacks) ? options[:callbacks] : :inline
|
17
|
+
unless [:inline, true, false, :async].include?(callbacks)
|
18
|
+
raise ArgumentError, "#{callbacks} its not permited value for callbacks"
|
19
|
+
end
|
20
|
+
|
21
|
+
class << self
|
22
|
+
attr_reader :redisearch_index, :redisearch_index_serializer, :redisearch_index_options
|
23
|
+
end
|
10
24
|
|
11
|
-
index_name = [prefix, model_name.plural].compact.join("_")
|
25
|
+
index_name = [prefix, model_name.plural, suffix].compact.join("_")
|
26
|
+
@redisearch_index_serializer = options[:index_serializer]
|
12
27
|
@redisearch_index = RediSearch.client.generate_index(index_name, schema)
|
13
28
|
|
29
|
+
RediSearch.models << self
|
30
|
+
|
31
|
+
@redisearch_index_options = options
|
32
|
+
|
14
33
|
scope :redisearch_import, -> { all }
|
15
34
|
|
35
|
+
# always add callbacks, even when callbacks is false
|
36
|
+
# so Model.callbacks block can be used
|
37
|
+
if respond_to?(:after_commit)
|
38
|
+
after_commit :reindex, if: -> { RediSearch.callbacks?(default: callbacks) }
|
39
|
+
elsif respond_to?(:after_save)
|
40
|
+
after_save :reindex, if: -> { RediSearch.callbacks?(default: callbacks) }
|
41
|
+
after_destroy :reindex, if: -> { RediSearch.callbacks?(default: callbacks) }
|
42
|
+
end
|
43
|
+
|
16
44
|
include InstanceMethods
|
17
45
|
extend RediSearchClassMethods
|
18
46
|
end
|
19
|
-
|
20
47
|
end
|
21
48
|
|
22
49
|
module RediSearchClassMethods
|
@@ -33,21 +60,15 @@ module RediSearch
|
|
33
60
|
end
|
34
61
|
|
35
62
|
# Reindex all
|
36
|
-
def reindex(recreate: false,
|
63
|
+
def reindex(recreate: false, mode: :inline, **options)
|
37
64
|
index = redisearch_index
|
38
65
|
|
39
66
|
index.drop if recreate
|
40
67
|
index.create unless index.exists?
|
41
68
|
|
42
|
-
|
43
|
-
redisearch_index.client.multi do
|
44
|
-
elements.each do |element|
|
45
|
-
element.redisearch_document.add(options.deep_merge(replace: true, partial: true))
|
46
|
-
end
|
47
|
-
end
|
48
|
-
end
|
49
|
-
true
|
69
|
+
RediSearch::BatchesIndexer.new(self).reindex(mode: mode)
|
50
70
|
end
|
71
|
+
|
51
72
|
end
|
52
73
|
end
|
53
74
|
end
|
@@ -6,6 +6,27 @@ module RediSearch
|
|
6
6
|
@redisearch_document ||= generate_redisearch_document
|
7
7
|
end
|
8
8
|
|
9
|
+
def redisearch_reindex(**options)
|
10
|
+
RediSearch::RecordIndexer.new(self).reindex(options)
|
11
|
+
end
|
12
|
+
alias_method :reindex, :redisearch_reindex unless method_defined?(:reindex)
|
13
|
+
|
14
|
+
def should_index?
|
15
|
+
true
|
16
|
+
end
|
17
|
+
|
18
|
+
def redisearch_add
|
19
|
+
redisearch_document.add(replace: true, partial: true)
|
20
|
+
end
|
21
|
+
|
22
|
+
def redisearch_delete
|
23
|
+
redisearch_document.del(dd: true)
|
24
|
+
end
|
25
|
+
|
26
|
+
def redisearch_document_id
|
27
|
+
[self.class.redisearch_index.name, self.id].join('_')
|
28
|
+
end
|
29
|
+
|
9
30
|
private
|
10
31
|
|
11
32
|
def generate_redisearch_document
|
@@ -16,10 +37,6 @@ module RediSearch
|
|
16
37
|
index.generate_document(redisearch_document_id, fields_values)
|
17
38
|
end
|
18
39
|
|
19
|
-
def redisearch_document_id
|
20
|
-
[self.class.redisearch_index.name, self.id].join('_')
|
21
|
-
end
|
22
|
-
|
23
40
|
def redisearch_serializer
|
24
41
|
self.class.redisearch_index_serializer.new(self) if self.class.redisearch_index_serializer
|
25
42
|
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
module RediSearch
|
2
|
+
class ReindexBatchesJob < ActiveJob::Base
|
3
|
+
queue_as { RediSearch.queue_name }
|
4
|
+
|
5
|
+
def perform(klass, start, finish, batch_size)
|
6
|
+
klass = klass.constantize
|
7
|
+
|
8
|
+
batches_relation = nil
|
9
|
+
|
10
|
+
if ActiveRecord::VERSION::STRING >= "5.0"
|
11
|
+
batches_relation = klass.redisearch_import.find_in_batches(batch_size: batch_size, start: start, finish: finish)
|
12
|
+
else
|
13
|
+
batches_relation = klass.redisearch_import.where(klass.arel_table[:id].lteq(finish.to_i)).find_in_batches(batch_size: batch_size, start: start)
|
14
|
+
end
|
15
|
+
|
16
|
+
batches_relation.each do |records|
|
17
|
+
klass.redisearch_index.client.multi do
|
18
|
+
records.each do |record|
|
19
|
+
record.reindex(mode: :inline, replace: true)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
module RediSearch
|
2
|
+
class ReindexRecordJob < ActiveJob::Base
|
3
|
+
RECORD_NOT_FOUND_CLASSES = [
|
4
|
+
"ActiveRecord::RecordNotFound"
|
5
|
+
]
|
6
|
+
|
7
|
+
queue_as { RediSearch.queue_name }
|
8
|
+
|
9
|
+
def perform(klass, id)
|
10
|
+
model = klass.constantize
|
11
|
+
|
12
|
+
record =
|
13
|
+
begin
|
14
|
+
model.redisearch_import.find(id)
|
15
|
+
rescue => e
|
16
|
+
# check by name rather than rescue directly so we don't need
|
17
|
+
# to determine which classes are defined
|
18
|
+
raise e unless RECORD_NOT_FOUND_CLASSES.include?(e.class.name)
|
19
|
+
nil
|
20
|
+
end
|
21
|
+
|
22
|
+
unless record
|
23
|
+
record = model.new
|
24
|
+
record.id = id
|
25
|
+
end
|
26
|
+
|
27
|
+
RecordIndexer.new(record).reindex(mode: :inline)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
data/lib/redisearch-rails.rb
CHANGED
@@ -6,8 +6,17 @@ require 'redi_searcher'
|
|
6
6
|
|
7
7
|
module RediSearch
|
8
8
|
autoload :RediSearchable, 'redisearch-rails/redisearchable'
|
9
|
+
autoload :BatchesIndexer, 'redisearch-rails/batches_indexer'
|
10
|
+
autoload :RecordIndexer, 'redisearch-rails/record_indexer'
|
11
|
+
|
12
|
+
#jobs
|
13
|
+
autoload :ReindexRecordJob, 'redisearch-rails/reindex_record_job'
|
14
|
+
autoload :ReindexBatchesJob, 'redisearch-rails/reindex_batches_job'
|
15
|
+
|
16
|
+
DEFAULT_BATCH_SIZE = 1_000
|
9
17
|
|
10
18
|
class << self
|
19
|
+
attr_accessor :models
|
11
20
|
attr_writer :configuration
|
12
21
|
|
13
22
|
def configuration
|
@@ -22,7 +31,55 @@ module RediSearch
|
|
22
31
|
@client ||= RediSearcher::Client.new(configuration.redis_config)
|
23
32
|
end
|
24
33
|
|
34
|
+
def enable_callbacks
|
35
|
+
self.callbacks_value = nil
|
36
|
+
end
|
37
|
+
|
38
|
+
def disable_callbacks
|
39
|
+
self.callbacks_value = false
|
40
|
+
end
|
41
|
+
|
42
|
+
def callbacks?(default: true)
|
43
|
+
if self.callbacks_value.nil?
|
44
|
+
default
|
45
|
+
else
|
46
|
+
self.callbacks_value != false
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
def callbacks_value
|
51
|
+
Thread.current[:redisearch_callbacks_enabled]
|
52
|
+
end
|
53
|
+
|
54
|
+
def callbacks_value=(value)
|
55
|
+
Thread.current[:redisearch_callbacks_enabled] = value
|
56
|
+
end
|
57
|
+
|
58
|
+
def callbacks(value)
|
59
|
+
if block_given?
|
60
|
+
previous_value = self.callbacks_value
|
61
|
+
begin
|
62
|
+
self.callbacks_value = value
|
63
|
+
result = yield
|
64
|
+
result
|
65
|
+
ensure
|
66
|
+
self.callbacks_value = previous_value
|
67
|
+
end
|
68
|
+
else
|
69
|
+
self.callbacks_value = value
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
private
|
74
|
+
|
75
|
+
def method_missing(m, *args, &block)
|
76
|
+
return configuration.send(m) if configuration.respond_to?(m)
|
77
|
+
super
|
78
|
+
end
|
25
79
|
end
|
80
|
+
|
81
|
+
@models = []
|
82
|
+
|
26
83
|
end
|
27
84
|
|
28
85
|
ActiveSupport.on_load(:active_record) do
|
data/redisearch-rails.gemspec
CHANGED
@@ -26,6 +26,7 @@ Gem::Specification.new do |spec|
|
|
26
26
|
|
27
27
|
spec.add_dependency 'activerecord', '~> 4.2'
|
28
28
|
spec.add_dependency 'activesupport', '~> 4.2'
|
29
|
+
spec.add_dependency 'activejob', '~> 4.2'
|
29
30
|
spec.add_dependency 'redi_searcher', '~> 0.1', '>= 0.1.3'
|
30
31
|
|
31
32
|
spec.add_development_dependency "bundler", "~> 1.17"
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: redisearch-rails
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.5.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Patricio Beckmann
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2020-03-
|
11
|
+
date: 2020-03-17 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activerecord
|
@@ -38,6 +38,20 @@ dependencies:
|
|
38
38
|
- - "~>"
|
39
39
|
- !ruby/object:Gem::Version
|
40
40
|
version: '4.2'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: activejob
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '4.2'
|
48
|
+
type: :runtime
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '4.2'
|
41
55
|
- !ruby/object:Gem::Dependency
|
42
56
|
name: redi_searcher
|
43
57
|
requirement: !ruby/object:Gem::Requirement
|
@@ -118,10 +132,14 @@ files:
|
|
118
132
|
- bin/console
|
119
133
|
- bin/setup
|
120
134
|
- lib/redisearch-rails.rb
|
135
|
+
- lib/redisearch-rails/batches_indexer.rb
|
121
136
|
- lib/redisearch-rails/configuration.rb
|
137
|
+
- lib/redisearch-rails/record_indexer.rb
|
122
138
|
- lib/redisearch-rails/redisearchable.rb
|
123
139
|
- lib/redisearch-rails/redisearchable/class_methods.rb
|
124
140
|
- lib/redisearch-rails/redisearchable/instance_methods.rb
|
141
|
+
- lib/redisearch-rails/reindex_batches_job.rb
|
142
|
+
- lib/redisearch-rails/reindex_record_job.rb
|
125
143
|
- lib/redisearch-rails/version.rb
|
126
144
|
- redisearch-rails.gemspec
|
127
145
|
homepage: https://github.com/Ticketplus/redisearch-rails
|