redisearch-rails 0.2.0 → 0.5.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
+
[![Build Status](https://travis-ci.org/eventBrain/redisearch-rails.svg?branch=master)](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
|