elastic_ar_sync 0.1.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 +7 -0
- data/MIT-LICENSE +20 -0
- data/README.md +28 -0
- data/Rakefile +27 -0
- data/lib/elastic_ar_sync.rb +15 -0
- data/lib/elastic_ar_sync/elastic/services/document_indexer.rb +21 -0
- data/lib/elastic_ar_sync/elastic/services/index_handler.rb +40 -0
- data/lib/elastic_ar_sync/elastic/syncable.rb +106 -0
- data/lib/elastic_ar_sync/elastic/worker/index_import_worker.rb +16 -0
- data/lib/elastic_ar_sync/elastic/worker/index_worker.rb +9 -0
- data/lib/elastic_ar_sync/railtie.rb +4 -0
- data/lib/elastic_ar_sync/version.rb +3 -0
- data/lib/tasks/elastic_ar_sync_tasks.rake +4 -0
- metadata +84 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: bd13df6ed68262c7b4c23444f09d48a3530ad97e5df6f682f505ec32f9304de1
|
4
|
+
data.tar.gz: 3b4e0cb0b9c5f0226eaa85aab9d4c68b10a4a1e57c1a435f877a1dbcaa61b257
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 219487c1c412620a86beabe73e1ef4db107a17d427d69e775ef301e20fe98a24cec1f9af23ee98611a777ba6b05604551ac25fb4e06fbe43b5878e6b020b0be0
|
7
|
+
data.tar.gz: bc3b782c8f7b8626383b3dee9ec539ad4d7d00d62702c6edfa91eeeab9be91edfe25b3e693fa602b859b85ed52ad746d1d037e8d6d2847119be4770b80ef3436
|
data/MIT-LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright 2020 KitakatsuTed
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
# ElasticArSync
|
2
|
+
Short description and motivation.
|
3
|
+
|
4
|
+
## Usage
|
5
|
+
How to use my plugin.
|
6
|
+
|
7
|
+
## Installation
|
8
|
+
Add this line to your application's Gemfile:
|
9
|
+
|
10
|
+
```ruby
|
11
|
+
gem 'elastic_ar_sync'
|
12
|
+
```
|
13
|
+
|
14
|
+
And then execute:
|
15
|
+
```bash
|
16
|
+
$ bundle
|
17
|
+
```
|
18
|
+
|
19
|
+
Or install it yourself as:
|
20
|
+
```bash
|
21
|
+
$ gem install elastic_ar_sync
|
22
|
+
```
|
23
|
+
|
24
|
+
## Contributing
|
25
|
+
Contribution directions go here.
|
26
|
+
|
27
|
+
## License
|
28
|
+
The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
|
data/Rakefile
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
begin
|
2
|
+
require 'bundler/setup'
|
3
|
+
rescue LoadError
|
4
|
+
puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
|
5
|
+
end
|
6
|
+
|
7
|
+
require 'rdoc/task'
|
8
|
+
|
9
|
+
RDoc::Task.new(:rdoc) do |rdoc|
|
10
|
+
rdoc.rdoc_dir = 'rdoc'
|
11
|
+
rdoc.title = 'ElasticArSync'
|
12
|
+
rdoc.options << '--line-numbers'
|
13
|
+
rdoc.rdoc_files.include('README.md')
|
14
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
15
|
+
end
|
16
|
+
|
17
|
+
require 'bundler/gem_tasks'
|
18
|
+
|
19
|
+
require 'rake/testtask'
|
20
|
+
|
21
|
+
Rake::TestTask.new(:test) do |t|
|
22
|
+
t.libs << 'test'
|
23
|
+
t.pattern = 'test/**/*_test.rb'
|
24
|
+
t.verbose = false
|
25
|
+
end
|
26
|
+
|
27
|
+
task default: :test
|
@@ -0,0 +1,15 @@
|
|
1
|
+
require "elastic_ar_sync/railtie"
|
2
|
+
|
3
|
+
module ElasticArSync
|
4
|
+
module Elastic
|
5
|
+
module Services
|
6
|
+
autoload :DocumentIndexer, 'elastic_ar_sync/elastic/services/document_indexer'
|
7
|
+
autoload :IndexHandler, 'elastic_ar_sync/elastic/services/index_handler'
|
8
|
+
end
|
9
|
+
module Worker
|
10
|
+
autoload :IndexImportWorker, 'elastic_ar_sync/elastic/worker/index_import_worker'
|
11
|
+
autoload :IndexWorker, 'elastic_ar_sync/elastic/worker/index_worker'
|
12
|
+
end
|
13
|
+
autoload :Syncable, 'elastic_ar_sync/elastic/syncable'
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
class ElasticArSync::Elastic::Services::DocumentIndexer
|
2
|
+
def index_document(klass, operation, record_id)
|
3
|
+
case operation.to_s
|
4
|
+
when /index/
|
5
|
+
record = Object.const_get(klass).find(record_id)
|
6
|
+
|
7
|
+
Elasticsearch::Model.client.index(
|
8
|
+
index: record.__elasticsearch__.index_name,
|
9
|
+
type: record.__elasticsearch__.document_type,
|
10
|
+
id: record.id,
|
11
|
+
body: record.__elasticsearch__.as_indexed_json)
|
12
|
+
when /delete/
|
13
|
+
Elasticsearch::Model.client.delete(index: Object.const_get(klass).__elasticsearch__.index_name,
|
14
|
+
type: Object.const_get(klass).__elasticsearch__.document_type,
|
15
|
+
id: record_id)
|
16
|
+
else
|
17
|
+
raise ArgumentError, "Unknown operation '#{operation.to_s}'"
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
@@ -0,0 +1,40 @@
|
|
1
|
+
class ElasticArSync::Elastic::Services::IndexHandler
|
2
|
+
def initialize(klass)
|
3
|
+
@klass = klass
|
4
|
+
end
|
5
|
+
|
6
|
+
# インデックスを作成 デフォルトは クラス名の小文字_環境名
|
7
|
+
def create_index(new_index_name)
|
8
|
+
@klass.__elasticsearch__.client.indices.create index: new_index_name, body: { settings: @klass.settings.to_hash, mappings: @klass.mapping.to_hash }
|
9
|
+
end
|
10
|
+
|
11
|
+
def delete_index(target_index)
|
12
|
+
raise 'can not delete because this index is using now' if target_index == target_alias
|
13
|
+
|
14
|
+
@klass.__elasticsearch__.client.indices.delete index: target_index rescue false
|
15
|
+
end
|
16
|
+
|
17
|
+
# DBの内容をESのインデックスに同期する
|
18
|
+
# レコード量が多いと時間がかかるのでこれだけは非同期実行
|
19
|
+
def import_all_record(target_index ,batch_size = 100)
|
20
|
+
@klass.__elasticsearch__.import(index: target_index, batch_size: batch_size)
|
21
|
+
end
|
22
|
+
|
23
|
+
# ダウンタイムなしでインデックスを切り替える
|
24
|
+
# https://techlife.cookpad.com/entry/2015/09/25/170000
|
25
|
+
def switch_alias(alias_name:, new_index_name:)
|
26
|
+
raise 'this is already assigned' if new_index_name == target_alias
|
27
|
+
|
28
|
+
actions = [{ add: { index: new_index_name, alias: alias_name } }]
|
29
|
+
old_indexes = @klass.get_aliases.keys
|
30
|
+
old_indexes.each { |old_index| actions << { remove: { index: old_index, alias: alias_name } } }
|
31
|
+
|
32
|
+
@klass.__elasticsearch__.client.indices.update_aliases(body: { actions: actions })
|
33
|
+
end
|
34
|
+
|
35
|
+
private
|
36
|
+
|
37
|
+
def target_alias
|
38
|
+
@klass.get_aliases.keys.select { |index| @klass.get_aliases[index]["aliases"].present? }.first
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,106 @@
|
|
1
|
+
module ElasticArSync
|
2
|
+
module Elastic
|
3
|
+
module Syncable
|
4
|
+
extend ActiveSupport::Concern
|
5
|
+
include ElasticArSync::Elastic::Worker
|
6
|
+
include ElasticArSync::Elastic::Services
|
7
|
+
|
8
|
+
included do
|
9
|
+
include Elasticsearch::Model
|
10
|
+
|
11
|
+
index_name "#{self.to_s.downcase.pluralize}_#{Rails.env}"
|
12
|
+
|
13
|
+
# after_commitでRDBを操作した時にESのインデックスも同期させる
|
14
|
+
# アプリのサーバーに負荷をかけ無いように非同期で実行させる
|
15
|
+
after_commit on: [:create] do
|
16
|
+
document_sync_create(self.class, id)
|
17
|
+
end
|
18
|
+
|
19
|
+
after_commit on: [:update] do
|
20
|
+
document_sync_update(self.class, id)
|
21
|
+
end
|
22
|
+
|
23
|
+
after_commit on: [:destroy] do
|
24
|
+
document_sync_delete(self.class, id)
|
25
|
+
end
|
26
|
+
|
27
|
+
def document_sync_create(klass, record_id)
|
28
|
+
ElasticArSync::Elastic::Worker::IndexWorker.perform_async(klass, :index, record_id)
|
29
|
+
end
|
30
|
+
|
31
|
+
def document_sync_update(klass, record_id)
|
32
|
+
ElasticArSync::Elastic::Worker::IndexWorker.perform_async(klass, :index, record_id)
|
33
|
+
end
|
34
|
+
|
35
|
+
def document_sync_delete(klass, record_id)
|
36
|
+
ElasticArSync::Elastic::Worker::IndexWorker.perform_async(klass, :delete, record_id)
|
37
|
+
end
|
38
|
+
|
39
|
+
def as_indexed_json(_option = {})
|
40
|
+
attributes.symbolize_keys.select { |key, _| self.class.mapping_list_keys.include?(key) }
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
module ClassMethods
|
45
|
+
def index_setup
|
46
|
+
response = create_index
|
47
|
+
switch_alias(new_index_name: response["index"])
|
48
|
+
import_all_record(target_index: response["index"])
|
49
|
+
end
|
50
|
+
# インデックスを作成 デフォルトは クラス名の小文字_環境名
|
51
|
+
def create_index
|
52
|
+
ElasticArSync::Elastic::Services::IndexHandler.new(self).create_index("#{index_name}_#{Time.zone.now.strftime('%Y%m%d%H%M')}")
|
53
|
+
end
|
54
|
+
|
55
|
+
def delete_index(target_index)
|
56
|
+
ElasticArSync::Elastic::Services::IndexHandler.new(self).delete_index(target_index)
|
57
|
+
end
|
58
|
+
|
59
|
+
# DBの内容をESのインデックスに同期する
|
60
|
+
def import_all_record(target_index: ,batch_size: 100)
|
61
|
+
ElasticArSync::Elastic::Worker::IndexImportWorker.perform_async(self, target_index, batch_size)
|
62
|
+
end
|
63
|
+
|
64
|
+
# ダウンタイムなしでインデックスを切り替える
|
65
|
+
# https://techlife.cookpad.com/entry/2015/09/25/170000
|
66
|
+
def switch_alias(new_index_name:)
|
67
|
+
ElasticArSync::Elastic::Services::IndexHandler.new(self).switch_alias(alias_name: index_name, new_index_name: new_index_name)
|
68
|
+
end
|
69
|
+
|
70
|
+
def index_config(dynamic: 'false', number_of_shards: 1, attr_mappings: default_index_mapping)
|
71
|
+
settings index: { number_of_shards: number_of_shards } do
|
72
|
+
# ES6からStringが使えないのでtextかkeywordにする。
|
73
|
+
mappings dynamic: dynamic do
|
74
|
+
attr_mappings.each do |key, value|
|
75
|
+
indexes key, type: value
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
def default_index_mapping
|
82
|
+
mapping = {}
|
83
|
+
attribute_types.each do |attribute, active_model_type|
|
84
|
+
type = active_model_type.type
|
85
|
+
type = :text if (active_model_type.type.to_sym == :string) || (type == :integer && defined_enums.symbolize_keys.keys.include?(attribute.to_sym))
|
86
|
+
type = :date if active_model_type.type == :datetime
|
87
|
+
mapping[attribute.to_sym] = type
|
88
|
+
end
|
89
|
+
mapping
|
90
|
+
end
|
91
|
+
|
92
|
+
def mapping_list_keys
|
93
|
+
mappings.to_hash[:_doc][:properties].keys
|
94
|
+
end
|
95
|
+
|
96
|
+
def get_aliases
|
97
|
+
begin
|
98
|
+
__elasticsearch__.client.indices.get_alias(index: '')
|
99
|
+
rescue Elasticsearch::Transport::Transport::Errors::NotFound
|
100
|
+
raise Elasticsearch::Transport::Transport::Errors::NotFound, "インデックスがありません alias_name: #{alias_name}"
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|
106
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
class ElasticArSync::Elastic::Worker::IndexImportWorker
|
2
|
+
include Sidekiq::Worker
|
3
|
+
sidekiq_options queue: :elasticsearch, retry: false
|
4
|
+
|
5
|
+
def perform(klass, target_index, batch_size)
|
6
|
+
Rails.logger.debug "[elastic IndexImportWorker] start import #{target_index}"
|
7
|
+
|
8
|
+
begin
|
9
|
+
ElasticArSync::Elastic::Services::IndexHandler.new(Object.const_get(klass)).import_all_record(target_index, batch_size)
|
10
|
+
rescue => e
|
11
|
+
Rails.logger.debug "[elastic IndexImportWorker] error occur #{target_index} \n #{e.message}"
|
12
|
+
end
|
13
|
+
|
14
|
+
Rails.logger.debug "[elastic IndexImportWorker] finish import #{target_index}"
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,9 @@
|
|
1
|
+
class ElasticArSync::Elastic::Worker::IndexWorker
|
2
|
+
include Sidekiq::Worker
|
3
|
+
sidekiq_options queue: :elasticsearch, retry: false
|
4
|
+
|
5
|
+
def perform(klass, operation, record_id)
|
6
|
+
Rails.logger.debug "[elasticsearch IndexWorker] operation: #{operation} #{klass} ID: #{record_id}"
|
7
|
+
ElasticArSync::Elastic::Services::DocumentIndexer.new.index_document(klass, operation, record_id)
|
8
|
+
end
|
9
|
+
end
|
metadata
ADDED
@@ -0,0 +1,84 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: elastic_ar_sync
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- KitakatsuTed
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2020-07-06 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: rails
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ">="
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: 5.1.0
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ">="
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: 5.1.0
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: sqlite3
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ">="
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
41
|
+
description: This gem has useful methods to sync rdb data with es data.
|
42
|
+
email:
|
43
|
+
- travy300637@gmail.com
|
44
|
+
executables: []
|
45
|
+
extensions: []
|
46
|
+
extra_rdoc_files: []
|
47
|
+
files:
|
48
|
+
- MIT-LICENSE
|
49
|
+
- README.md
|
50
|
+
- Rakefile
|
51
|
+
- lib/elastic_ar_sync.rb
|
52
|
+
- lib/elastic_ar_sync/elastic/services/document_indexer.rb
|
53
|
+
- lib/elastic_ar_sync/elastic/services/index_handler.rb
|
54
|
+
- lib/elastic_ar_sync/elastic/syncable.rb
|
55
|
+
- lib/elastic_ar_sync/elastic/worker/index_import_worker.rb
|
56
|
+
- lib/elastic_ar_sync/elastic/worker/index_worker.rb
|
57
|
+
- lib/elastic_ar_sync/railtie.rb
|
58
|
+
- lib/elastic_ar_sync/version.rb
|
59
|
+
- lib/tasks/elastic_ar_sync_tasks.rake
|
60
|
+
homepage: https://github.com/KitakatsuTed/elastic_ar_sync
|
61
|
+
licenses:
|
62
|
+
- MIT
|
63
|
+
metadata: {}
|
64
|
+
post_install_message:
|
65
|
+
rdoc_options: []
|
66
|
+
require_paths:
|
67
|
+
- lib
|
68
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
69
|
+
requirements:
|
70
|
+
- - ">="
|
71
|
+
- !ruby/object:Gem::Version
|
72
|
+
version: '0'
|
73
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
74
|
+
requirements:
|
75
|
+
- - ">="
|
76
|
+
- !ruby/object:Gem::Version
|
77
|
+
version: '0'
|
78
|
+
requirements: []
|
79
|
+
rubyforge_project:
|
80
|
+
rubygems_version: 2.7.6.2
|
81
|
+
signing_key:
|
82
|
+
specification_version: 4
|
83
|
+
summary: easy setup elasticsearch for ActiveRecord
|
84
|
+
test_files: []
|