elasticsearch-rails-dynamic-json-support 0.0.6 → 0.0.7
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Gemfile +9 -1
- data/Gemfile.lock +45 -8
- data/README.md +81 -64
- data/VERSION +1 -1
- data/elasticsearch-rails-dynamic-json-support.gemspec +18 -8
- data/lib/elasticsearch/model/cascade_update.rb +97 -68
- data/spec/cascade_update_spec.rb +157 -0
- data/spec/db.rb +28 -0
- data/{test/helper.rb → spec/spec_helper.rb} +10 -14
- metadata +48 -5
- data/test/test_elasticsearch-rails-dynamic-json-support.rb +0 -7
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 116ff0923afb01ab10a9ad5f3eb9792439ccb93e
|
4
|
+
data.tar.gz: a7478fbbab3f9626369a734cef52c67344b3e6fa
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 7ff38c240e1395797a06ce9ef10d3ff2afb55a4fb56e0ebbe0a5330ca59586cc28e7964c6e9a05f2104eb22a748b35cd0aeed3278693db772c21e272acf2ec3a
|
7
|
+
data.tar.gz: 461b3a61dc3b270eab43211e7485ad6dccaee47e5b697addeea62305215877fcd73934bfb2f908746ec07a50fbfd17946f776357f5ee964fc13cac762931d174
|
data/Gemfile
CHANGED
@@ -6,13 +6,21 @@ source "https://rubygems.org"
|
|
6
6
|
# Add dependencies to develop your gem here.
|
7
7
|
# Include everything needed to run rake, tests, features, etc.
|
8
8
|
|
9
|
+
gem 'elasticsearch-model', "~> 0.1.9"
|
9
10
|
gem 'elasticsearch-rails', "~> 0.1.9"
|
10
11
|
|
11
12
|
group :development do
|
12
|
-
gem '
|
13
|
+
gem 'rspec', '>= 0'
|
13
14
|
gem 'rdoc', '~> 3.12'
|
14
15
|
gem 'bundler', '~> 1.0'
|
15
16
|
gem 'jeweler', '~> 2.0.1'
|
16
17
|
gem 'simplecov', '>= 0'
|
17
18
|
gem 'concurrent-ruby'
|
19
|
+
gem 'activerecord'
|
20
|
+
gem 'activerecord-nulldb-adapter'
|
18
21
|
end
|
22
|
+
|
23
|
+
group :test do
|
24
|
+
gem 'sqlite3'
|
25
|
+
end
|
26
|
+
|
data/Gemfile.lock
CHANGED
@@ -1,18 +1,40 @@
|
|
1
1
|
GEM
|
2
2
|
remote: https://rubygems.org/
|
3
3
|
specs:
|
4
|
-
|
4
|
+
activemodel (5.0.0)
|
5
|
+
activesupport (= 5.0.0)
|
6
|
+
activerecord (5.0.0)
|
7
|
+
activemodel (= 5.0.0)
|
8
|
+
activesupport (= 5.0.0)
|
9
|
+
arel (~> 7.0)
|
10
|
+
activerecord-nulldb-adapter (0.3.4)
|
11
|
+
activerecord (>= 2.0.0)
|
12
|
+
activesupport (5.0.0)
|
5
13
|
concurrent-ruby (~> 1.0, >= 1.0.2)
|
6
14
|
i18n (~> 0.7)
|
7
15
|
minitest (~> 5.1)
|
8
16
|
tzinfo (~> 1.1)
|
9
17
|
addressable (2.4.0)
|
18
|
+
arel (7.1.1)
|
10
19
|
builder (3.2.2)
|
11
20
|
concurrent-ruby (1.0.2)
|
12
21
|
descendants_tracker (0.0.4)
|
13
22
|
thread_safe (~> 0.3, >= 0.3.1)
|
23
|
+
diff-lcs (1.2.5)
|
14
24
|
docile (1.1.5)
|
25
|
+
elasticsearch (2.0.0)
|
26
|
+
elasticsearch-api (= 2.0.0)
|
27
|
+
elasticsearch-transport (= 2.0.0)
|
28
|
+
elasticsearch-api (2.0.0)
|
29
|
+
multi_json
|
30
|
+
elasticsearch-model (0.1.9)
|
31
|
+
activesupport (> 3)
|
32
|
+
elasticsearch (> 0.4)
|
33
|
+
hashie
|
15
34
|
elasticsearch-rails (0.1.9)
|
35
|
+
elasticsearch-transport (2.0.0)
|
36
|
+
faraday
|
37
|
+
multi_json
|
16
38
|
faraday (0.9.2)
|
17
39
|
multipart-post (>= 1.2, < 3)
|
18
40
|
git (1.3.0)
|
@@ -55,17 +77,25 @@ GEM
|
|
55
77
|
rake (11.2.2)
|
56
78
|
rdoc (3.12.2)
|
57
79
|
json (~> 1.4)
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
80
|
+
rspec (3.5.0)
|
81
|
+
rspec-core (~> 3.5.0)
|
82
|
+
rspec-expectations (~> 3.5.0)
|
83
|
+
rspec-mocks (~> 3.5.0)
|
84
|
+
rspec-core (3.5.3)
|
85
|
+
rspec-support (~> 3.5.0)
|
86
|
+
rspec-expectations (3.5.0)
|
87
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
88
|
+
rspec-support (~> 3.5.0)
|
89
|
+
rspec-mocks (3.5.0)
|
90
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
91
|
+
rspec-support (~> 3.5.0)
|
92
|
+
rspec-support (3.5.0)
|
64
93
|
simplecov (0.12.0)
|
65
94
|
docile (~> 1.1.0)
|
66
95
|
json (>= 1.8, < 3)
|
67
96
|
simplecov-html (~> 0.10.0)
|
68
97
|
simplecov-html (0.10.0)
|
98
|
+
sqlite3 (1.3.11)
|
69
99
|
thread_safe (0.3.5)
|
70
100
|
tzinfo (1.2.2)
|
71
101
|
thread_safe (~> 0.1)
|
@@ -74,10 +104,17 @@ PLATFORMS
|
|
74
104
|
ruby
|
75
105
|
|
76
106
|
DEPENDENCIES
|
107
|
+
activerecord
|
108
|
+
activerecord-nulldb-adapter
|
77
109
|
bundler (~> 1.0)
|
78
110
|
concurrent-ruby
|
111
|
+
elasticsearch-model (~> 0.1.9)
|
79
112
|
elasticsearch-rails (~> 0.1.9)
|
80
113
|
jeweler (~> 2.0.1)
|
81
114
|
rdoc (~> 3.12)
|
82
|
-
|
115
|
+
rspec
|
83
116
|
simplecov
|
117
|
+
sqlite3
|
118
|
+
|
119
|
+
BUNDLED WITH
|
120
|
+
1.12.5
|
data/README.md
CHANGED
@@ -6,30 +6,30 @@ ElasticsearchRails DynamicJsonSupport - Small little change to help your model u
|
|
6
6
|
|
7
7
|
In ElasicSearch, if you define your model the following way:
|
8
8
|
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
9
|
+
```ruby
|
10
|
+
# app/models/article.rb
|
11
|
+
class Article < ActiveRecord::Base
|
12
|
+
include Elasticsearch::Model
|
13
|
+
include Elasticsearch::Model::Callbacks
|
14
|
+
|
15
|
+
has_many :reviews
|
16
|
+
|
17
|
+
def as_indexed_json options={}
|
18
|
+
{
|
19
|
+
id: id,
|
20
|
+
title: title,
|
21
|
+
article: content,
|
22
|
+
created_at: created_at,
|
23
|
+
updated_at: updated_at,
|
24
|
+
reviews: reviews.map(&:as_json),
|
25
|
+
}
|
26
|
+
end
|
27
|
+
|
28
|
+
# app/models/review.rb
|
29
|
+
class Review < ActiveRecord::Base
|
30
|
+
belongs_to :article
|
31
|
+
end
|
32
|
+
```
|
33
33
|
|
34
34
|
There're 2 issues with the implementation above:
|
35
35
|
|
@@ -56,46 +56,63 @@ This library aims to solve these 2 issues, with the following convention:
|
|
56
56
|
|
57
57
|
Example usage (which solves the 2 issues above) is as given below:
|
58
58
|
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
59
|
+
```ruby
|
60
|
+
# app/models/article.rb
|
61
|
+
class Article < ActiveRecord::Base
|
62
|
+
include Elasticsearch::Model
|
63
|
+
include Elasticsearch::Model::Callbacks
|
64
|
+
include Elasticsearch::Model::CascadeUpdate
|
65
|
+
|
66
|
+
has_many :reviews
|
67
|
+
es_register_attributes :id, :title, article: lambda {|rec| rec.content }
|
68
|
+
|
69
|
+
# silent attribute would not be output by default
|
70
|
+
es_register_silent_attributes :created_at, :updated_at
|
71
|
+
|
72
|
+
# register association, with:
|
73
|
+
# - key_name = :reviews
|
74
|
+
# - get_assoc: default to be `#reviews` (the key_name)
|
75
|
+
# - &blk: the render_assoc, default to be: `object#as_indexed_json`.
|
76
|
+
# it would use a `map` by default for the has_many relationships
|
77
|
+
# - reverse_class: default to guess from the relationship
|
78
|
+
# - reverse_trigger: lambda {|obj, changes| do_things }
|
79
|
+
# the reverse_trigger, when the model change, to render the json
|
80
|
+
# document and update the objects. the returns is an array of the
|
81
|
+
# original object to be updated
|
82
|
+
es_register_assoc(:reviews) { |review| review.as_indexed_json }
|
83
|
+
|
84
|
+
# for the given changed_attributes, it would return the json to be fed
|
85
|
+
# into the `#update_document` method. What's given here is the default
|
86
|
+
# behaviour -- which is to find the keys, and do a matching
|
87
|
+
def elasticsearch_json_changes(changed_attributes)
|
88
|
+
keys_to_update = changed_attributes.keys.map {|k| key_map k}
|
89
|
+
self.as_indexed_json.select { |k,_| keys_to_update.include? k.to_s }
|
90
|
+
end
|
91
|
+
|
92
|
+
private
|
93
|
+
def key_map(key)
|
94
|
+
key = key.to_s
|
95
|
+
case key
|
96
|
+
when 'content'
|
97
|
+
'article'
|
98
|
+
else
|
99
|
+
key
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
# app/models/review.rb
|
105
|
+
class Review < ActiveRecord::Base
|
106
|
+
# Make sure to register Article
|
107
|
+
Article
|
108
|
+
|
109
|
+
belongs_to :article
|
110
|
+
|
111
|
+
def as_indexed_json(options = {})
|
112
|
+
as_json
|
113
|
+
end
|
114
|
+
end
|
115
|
+
```
|
99
116
|
|
100
117
|
# More features
|
101
118
|
- Exclusion of keys: simply call
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.0.
|
1
|
+
0.0.7
|
@@ -2,16 +2,16 @@
|
|
2
2
|
# DO NOT EDIT THIS FILE DIRECTLY
|
3
3
|
# Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
|
4
4
|
# -*- encoding: utf-8 -*-
|
5
|
-
# stub: elasticsearch-rails-dynamic-json-support 0.0.
|
5
|
+
# stub: elasticsearch-rails-dynamic-json-support 0.0.7 ruby lib
|
6
6
|
|
7
7
|
Gem::Specification.new do |s|
|
8
8
|
s.name = "elasticsearch-rails-dynamic-json-support".freeze
|
9
|
-
s.version = "0.0.
|
9
|
+
s.version = "0.0.7"
|
10
10
|
|
11
11
|
s.required_rubygems_version = Gem::Requirement.new(">= 0".freeze) if s.respond_to? :required_rubygems_version=
|
12
12
|
s.require_paths = ["lib".freeze]
|
13
13
|
s.authors = ["Song Yangyu".freeze]
|
14
|
-
s.date = "2016-09-
|
14
|
+
s.date = "2016-09-20"
|
15
15
|
s.description = "Enhance elasticsearch-rails with `elasticsearch_json_changes` to translate the attribute changes into document updates".freeze
|
16
16
|
s.email = "flyfy1@gmail.com".freeze
|
17
17
|
s.extra_rdoc_files = [
|
@@ -31,8 +31,9 @@ Gem::Specification.new do |s|
|
|
31
31
|
"lib/elasticsearch/model/cascade_update.rb",
|
32
32
|
"lib/elasticsearch/model/importing_decorator.rb",
|
33
33
|
"lib/elasticsearch/model/indexing_decorator.rb",
|
34
|
-
"
|
35
|
-
"
|
34
|
+
"spec/cascade_update_spec.rb",
|
35
|
+
"spec/db.rb",
|
36
|
+
"spec/spec_helper.rb"
|
36
37
|
]
|
37
38
|
s.homepage = "http://github.com/flyfy1/elasticsearch-rails-dynamic-json-support".freeze
|
38
39
|
s.licenses = ["MIT".freeze]
|
@@ -43,30 +44,39 @@ Gem::Specification.new do |s|
|
|
43
44
|
s.specification_version = 4
|
44
45
|
|
45
46
|
if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
|
47
|
+
s.add_runtime_dependency(%q<elasticsearch-model>.freeze, ["~> 0.1.9"])
|
46
48
|
s.add_runtime_dependency(%q<elasticsearch-rails>.freeze, ["~> 0.1.9"])
|
47
|
-
s.add_development_dependency(%q<
|
49
|
+
s.add_development_dependency(%q<rspec>.freeze, [">= 0"])
|
48
50
|
s.add_development_dependency(%q<rdoc>.freeze, ["~> 3.12"])
|
49
51
|
s.add_development_dependency(%q<bundler>.freeze, ["~> 1.0"])
|
50
52
|
s.add_development_dependency(%q<jeweler>.freeze, ["~> 2.0.1"])
|
51
53
|
s.add_development_dependency(%q<simplecov>.freeze, [">= 0"])
|
52
54
|
s.add_development_dependency(%q<concurrent-ruby>.freeze, [">= 0"])
|
55
|
+
s.add_development_dependency(%q<activerecord>.freeze, [">= 0"])
|
56
|
+
s.add_development_dependency(%q<activerecord-nulldb-adapter>.freeze, [">= 0"])
|
53
57
|
else
|
58
|
+
s.add_dependency(%q<elasticsearch-model>.freeze, ["~> 0.1.9"])
|
54
59
|
s.add_dependency(%q<elasticsearch-rails>.freeze, ["~> 0.1.9"])
|
55
|
-
s.add_dependency(%q<
|
60
|
+
s.add_dependency(%q<rspec>.freeze, [">= 0"])
|
56
61
|
s.add_dependency(%q<rdoc>.freeze, ["~> 3.12"])
|
57
62
|
s.add_dependency(%q<bundler>.freeze, ["~> 1.0"])
|
58
63
|
s.add_dependency(%q<jeweler>.freeze, ["~> 2.0.1"])
|
59
64
|
s.add_dependency(%q<simplecov>.freeze, [">= 0"])
|
60
65
|
s.add_dependency(%q<concurrent-ruby>.freeze, [">= 0"])
|
66
|
+
s.add_dependency(%q<activerecord>.freeze, [">= 0"])
|
67
|
+
s.add_dependency(%q<activerecord-nulldb-adapter>.freeze, [">= 0"])
|
61
68
|
end
|
62
69
|
else
|
70
|
+
s.add_dependency(%q<elasticsearch-model>.freeze, ["~> 0.1.9"])
|
63
71
|
s.add_dependency(%q<elasticsearch-rails>.freeze, ["~> 0.1.9"])
|
64
|
-
s.add_dependency(%q<
|
72
|
+
s.add_dependency(%q<rspec>.freeze, [">= 0"])
|
65
73
|
s.add_dependency(%q<rdoc>.freeze, ["~> 3.12"])
|
66
74
|
s.add_dependency(%q<bundler>.freeze, ["~> 1.0"])
|
67
75
|
s.add_dependency(%q<jeweler>.freeze, ["~> 2.0.1"])
|
68
76
|
s.add_dependency(%q<simplecov>.freeze, [">= 0"])
|
69
77
|
s.add_dependency(%q<concurrent-ruby>.freeze, [">= 0"])
|
78
|
+
s.add_dependency(%q<activerecord>.freeze, [">= 0"])
|
79
|
+
s.add_dependency(%q<activerecord-nulldb-adapter>.freeze, [">= 0"])
|
70
80
|
end
|
71
81
|
end
|
72
82
|
|
@@ -1,25 +1,6 @@
|
|
1
1
|
module Elasticsearch
|
2
2
|
module Model
|
3
3
|
module CascadeUpdate
|
4
|
-
|
5
|
-
# Only for internal use. Holds the mapping from relationship, to the corresponding json
|
6
|
-
class Actor
|
7
|
-
def initialize(relationship_name, singularity, resource_to_json)
|
8
|
-
@relationship_name = relationship_name
|
9
|
-
@singularity = singularity.to_sym
|
10
|
-
@resource_to_json = resource_to_json
|
11
|
-
end
|
12
|
-
|
13
|
-
def to_json(record)
|
14
|
-
resource = record.public_send @relationship_name
|
15
|
-
if(@singularity == :plural)
|
16
|
-
resource.map {|record| @resource_to_json[record] }
|
17
|
-
else # treat it as singular otherwise
|
18
|
-
@resource_to_json[resource]
|
19
|
-
end
|
20
|
-
end
|
21
|
-
end
|
22
|
-
|
23
4
|
extend ActiveSupport::Concern
|
24
5
|
|
25
6
|
# update, just for a specific key, or set of keys
|
@@ -28,7 +9,7 @@ module Elasticsearch
|
|
28
9
|
|
29
10
|
res_json = {}
|
30
11
|
keys.each do |key|
|
31
|
-
res_json[key] = self.class.
|
12
|
+
res_json[key] = self.class.es_get_actor(key)[self]
|
32
13
|
end
|
33
14
|
|
34
15
|
self.__elasticsearch__.update_document_attributes res_json
|
@@ -40,91 +21,139 @@ module Elasticsearch
|
|
40
21
|
|
41
22
|
class_methods do
|
42
23
|
def es_to_json_when(scope_name, &condition_block)
|
43
|
-
|
44
|
-
|
24
|
+
instance_variable_set(:@_es_condition_block, condition_block)
|
25
|
+
instance_variable_set(:@_es_scope_name, scope_name)
|
26
|
+
end
|
27
|
+
|
28
|
+
def es_register_silent_attrs *attributes
|
29
|
+
__es_register_attrs __silent_attribute_registry, attributes
|
30
|
+
end
|
31
|
+
|
32
|
+
def es_get_actor(key, include_silent: true)
|
33
|
+
if include_silent
|
34
|
+
__load_attribute_registry[key] || __silent_attribute_registry[key]
|
35
|
+
else
|
36
|
+
__silent_attribute_registry[key]
|
37
|
+
end
|
45
38
|
end
|
46
39
|
|
47
40
|
# attributes is a pair of <key, block>
|
48
41
|
def es_register_attrs *attributes
|
49
|
-
|
42
|
+
__es_register_attrs __load_attribute_registry, attributes
|
43
|
+
end
|
50
44
|
|
51
|
-
|
52
|
-
|
45
|
+
# register association, with:
|
46
|
+
# - key_name = the key name of the json object
|
47
|
+
# - relationship: can be nil, String or a Proc.
|
48
|
+
# - if nil or string, then default to the key_name. In that case,
|
49
|
+
# the listening_class can be guessed intelligently if not given
|
50
|
+
# - if lambda, then this is used to fetch the relationship
|
51
|
+
# - listening_class: default to guess from the relationship, to set the
|
52
|
+
# listener upon
|
53
|
+
# - reverse_trigger: lambda {|obj, changes| do_things }
|
54
|
+
# the reverse_trigger, when the model change, to render the json
|
55
|
+
# document and update the objects. the returns is an array of the
|
56
|
+
# original object to be updated
|
57
|
+
# - slient: default to false. If silent, then it's not exported by
|
58
|
+
# default
|
59
|
+
# - &blk: the render_assoc, default to be: `object#as_indexed_json`.
|
60
|
+
# it would use a `map` by default for the has_many relationships
|
61
|
+
def es_register_assoc key_name, relationship: nil, reverse_relationship: nil,
|
62
|
+
listening_class: nil, reverse_trigger: nil,
|
63
|
+
silent: false, &blk
|
64
|
+
key_name = key_name.to_s
|
65
|
+
relationship ||= key_name
|
66
|
+
|
67
|
+
relationship_getter = case relationship
|
68
|
+
when String, Symbol
|
69
|
+
lambda {|obj| obj.public_send relationship}
|
70
|
+
when Proc
|
71
|
+
relationship
|
72
|
+
else
|
73
|
+
fail "relationsihp can only be empty, String, or Proc, #{relationsihp} given."
|
74
|
+
end
|
75
|
+
|
76
|
+
single_to_json = blk || lambda do |r|
|
77
|
+
r.respond_to?(:as_indexed_json) ? r.as_indexed_json : r.as_json
|
53
78
|
end
|
54
79
|
|
55
|
-
|
56
|
-
|
57
|
-
json_attribute_registry[k] = v || lambda { |record| record.public_send k }
|
80
|
+
resource_to_json = lambda do |resource|
|
81
|
+
resource.respond_to?(:map) ? resource.map(&single_to_json) : single_to_json[resource]
|
58
82
|
end
|
59
|
-
end
|
60
83
|
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
84
|
+
(silent ? __silent_attribute_registry : __load_attribute_registry )[key_name] = lambda do |record|
|
85
|
+
resource = relationship_getter[record]
|
86
|
+
resource_to_json[resource]
|
87
|
+
end
|
65
88
|
|
66
|
-
|
67
|
-
|
89
|
+
reflection = nil
|
90
|
+
unless listening_class
|
91
|
+
# try to guess reverse class if not given
|
92
|
+
reflection = reflect_on_association relationship
|
93
|
+
listening_class = reflection.class_name.constantize
|
94
|
+
end
|
68
95
|
|
69
|
-
|
70
|
-
reverse_relationship_name = reverse_relationship_name.to_s
|
96
|
+
reverse_relationship = self.name.demodulize.downcase unless reverse_relationship
|
71
97
|
|
72
|
-
|
98
|
+
listening_class.class_eval do
|
99
|
+
# include Elasticsearch::Model unless include? Elasticsearch::Model
|
100
|
+
before_save do |instance|
|
101
|
+
self.instance_variable_set(:@__changed_attributes__, instance.changes)
|
102
|
+
end
|
73
103
|
|
74
|
-
|
104
|
+
after_commit do |instance|
|
105
|
+
changes = instance.instance_variable_get(:@__changed_attributes__)
|
75
106
|
|
76
|
-
|
77
|
-
|
107
|
+
Array.wrap(self.public_send reverse_relationship).each do |record|
|
108
|
+
record.es_partial_update(key_name)
|
109
|
+
end if !reverse_trigger || reverse_trigger[instance, changes]
|
78
110
|
|
79
|
-
|
80
|
-
after_commit do
|
81
|
-
Array.wrap(self.public_send reverse_relationship_name).each do |record|
|
82
|
-
record.es_partial_update(key_name)
|
83
|
-
end
|
111
|
+
instance.remove_instance_variable(:@__changed_attributes__)
|
84
112
|
end
|
85
113
|
end
|
86
114
|
end
|
87
115
|
|
88
116
|
def to_indexed_json(record, options = {})
|
89
117
|
result = {}
|
90
|
-
exclude_keys = options[:
|
91
|
-
|
92
|
-
exclude_keys = exclude_keys.map(&:to_s)
|
118
|
+
exclude_keys = (options[:exclude] || []).map(&:to_s)
|
119
|
+
include_keys = (options[:include] || []).map(&:to_s)
|
93
120
|
|
94
|
-
|
95
|
-
k = k.to_s
|
121
|
+
__load_attribute_registry.each do |k, blk|
|
96
122
|
next if exclude_keys.include? k
|
97
123
|
result[k] = blk[record]
|
98
124
|
end
|
99
125
|
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
result[k] = actor.to_json(record)
|
126
|
+
__silent_attribute_registry.each do |k, blk|
|
127
|
+
next unless include_keys.include? k
|
128
|
+
result[k] = blk[record]
|
104
129
|
end
|
105
130
|
|
106
131
|
result
|
107
132
|
end
|
108
133
|
|
109
134
|
private
|
110
|
-
def
|
111
|
-
|
135
|
+
def __load_attribute_registry
|
136
|
+
instance_variable_set :@__json_attribute_registry, {} unless instance_variable_defined? :@__json_attribute_registry
|
137
|
+
instance_variable_get :@__json_attribute_registry
|
112
138
|
end
|
113
|
-
|
114
|
-
|
115
|
-
|
139
|
+
def __silent_attribute_registry
|
140
|
+
instance_variable_set :@__json_silent_attr_registry, {} unless instance_variable_defined? :@__json_silent_attr_registry
|
141
|
+
instance_variable_get :@__json_silent_attr_registry
|
116
142
|
end
|
117
143
|
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
144
|
+
# attributes is a pair of <key, block>
|
145
|
+
def __es_register_attrs register_hash, attributes
|
146
|
+
hashed_attributes = attributes.extract_options!
|
147
|
+
|
148
|
+
attributes.each do |k|
|
149
|
+
register_hash[k.to_s] = lambda { |record| record.public_send k }
|
150
|
+
end
|
151
|
+
|
152
|
+
hashed_attributes.each do |k, v|
|
153
|
+
register_hash[k.to_s] = v || lambda { |record| record.public_send k }
|
125
154
|
end
|
126
155
|
end
|
127
156
|
end
|
128
157
|
end
|
129
158
|
end
|
130
|
-
end
|
159
|
+
end
|
@@ -0,0 +1,157 @@
|
|
1
|
+
require_relative './spec_helper.rb'
|
2
|
+
|
3
|
+
RSpec.describe Elasticsearch::Model::CascadeUpdate, type: :model do
|
4
|
+
@@counter = 0
|
5
|
+
|
6
|
+
before do
|
7
|
+
@clz = Article.dup
|
8
|
+
eval("ArticleD#{@@counter} = @clz")
|
9
|
+
@clz.table_name = 'articles'
|
10
|
+
|
11
|
+
@dep_clz = Review.dup
|
12
|
+
eval("ReviewD#{@@counter} = @dep_clz")
|
13
|
+
@dep_clz.table_name = 'reviews'
|
14
|
+
|
15
|
+
@@counter += 1
|
16
|
+
|
17
|
+
review_class = @dep_clz
|
18
|
+
@clz.class_eval do
|
19
|
+
has_many :reviews, class_name: review_class.name, foreign_key: 'article_id', inverse_of: :article
|
20
|
+
end
|
21
|
+
|
22
|
+
article_class = @clz
|
23
|
+
@dep_clz.class_eval do
|
24
|
+
belongs_to :article, class_name: article_class.name, foreign_key: 'article_id', inverse_of: :reviews
|
25
|
+
end
|
26
|
+
|
27
|
+
@clz.__send__ :include, Elasticsearch::Model
|
28
|
+
@clz.__send__ :include, Elasticsearch::Model::Callbacks
|
29
|
+
@clz.__send__ :include, Elasticsearch::Model::CascadeUpdate
|
30
|
+
end
|
31
|
+
|
32
|
+
context 'attrs' do
|
33
|
+
describe 'load' do
|
34
|
+
before do
|
35
|
+
@clz.class_eval do
|
36
|
+
es_register_attrs :title, :content
|
37
|
+
end
|
38
|
+
|
39
|
+
@article = @clz.new(title: 'nani', content: 'oops')
|
40
|
+
end
|
41
|
+
|
42
|
+
it { expect(@article.as_indexed_json).to eq({"title"=>"nani", "content"=>"oops"}) }
|
43
|
+
end
|
44
|
+
|
45
|
+
describe 'silent' do
|
46
|
+
before do
|
47
|
+
@clz.class_eval do
|
48
|
+
es_register_silent_attrs :title
|
49
|
+
es_register_attrs :content, explicit_title: lambda {|o| o.title}
|
50
|
+
end
|
51
|
+
|
52
|
+
@article = @clz.new(title: 'nani', content: 'oops')
|
53
|
+
end
|
54
|
+
|
55
|
+
it { expect(@article.as_indexed_json(include: [:title])).to eq({
|
56
|
+
"explicit_title"=>"nani", "content"=>"oops", "title" => "nani"
|
57
|
+
}) }
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
context 'assoc' do
|
62
|
+
before do
|
63
|
+
@article = @clz.create!(title: 'nani', content: 'oops')
|
64
|
+
@article.reviews.create!(content: 'review 1')
|
65
|
+
@article.reviews.create!(content: 'review 2')
|
66
|
+
end
|
67
|
+
|
68
|
+
describe 'basis' do
|
69
|
+
before do
|
70
|
+
@clz.class_eval do
|
71
|
+
es_register_assoc :reviews, reverse_relationship: 'article'
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
it { expect(@article.as_indexed_json).to eq({
|
76
|
+
"reviews"=>[{"id"=>1, "article_id"=>1, "content"=>"review 1", "nonsense"=>nil},
|
77
|
+
{"id"=>2, "article_id"=>1, "content"=>"review 2", "nonsense"=>nil}]
|
78
|
+
}) }
|
79
|
+
end
|
80
|
+
|
81
|
+
describe 'blk passed in' do
|
82
|
+
before do
|
83
|
+
@clz.class_eval do
|
84
|
+
es_register_assoc :reviews, reverse_relationship: 'article' do |review|
|
85
|
+
review.as_json(only: [:content])
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
it { expect(@article.as_indexed_json).to eq({
|
91
|
+
"reviews"=>[ {"content"=>"review 1"},
|
92
|
+
{"content"=>"review 2"}]
|
93
|
+
}) }
|
94
|
+
end
|
95
|
+
|
96
|
+
describe 'reverse' do
|
97
|
+
context '#reverse_relationship' do
|
98
|
+
before do
|
99
|
+
@clz.class_eval do
|
100
|
+
es_register_attrs :title
|
101
|
+
es_register_assoc :reviews, reverse_relationship: 'article' do |review|
|
102
|
+
review.as_json(only: [:content])
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
end
|
107
|
+
|
108
|
+
it do
|
109
|
+
expect(@article.__elasticsearch__).to receive(:update_document_attributes).with(
|
110
|
+
"reviews"=>[ {"content"=>"review 1"},
|
111
|
+
{"content"=>"review 2 changed"}]
|
112
|
+
)
|
113
|
+
|
114
|
+
@review = @article.reviews[1]
|
115
|
+
@review.content = 'review 2 changed'
|
116
|
+
@review.save!
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
context '#reverse_trigger' do
|
121
|
+
before do
|
122
|
+
@clz.class_eval do
|
123
|
+
es_register_attrs :title
|
124
|
+
es_register_assoc(:reviews, reverse_relationship: 'article',
|
125
|
+
reverse_trigger: lambda {|review, changes| changes.has_key? :content }
|
126
|
+
) do |review|
|
127
|
+
review.as_json(only: [:content])
|
128
|
+
end
|
129
|
+
end
|
130
|
+
end
|
131
|
+
|
132
|
+
describe 'include changed key' do
|
133
|
+
it do
|
134
|
+
expect(@article.__elasticsearch__).to receive(:update_document_attributes).with(
|
135
|
+
"reviews"=>[ {"content"=>"review 1"},
|
136
|
+
{"content"=>"review 2 changed"}]
|
137
|
+
)
|
138
|
+
|
139
|
+
@review = @article.reviews[1]
|
140
|
+
@review.content = 'review 2 changed'
|
141
|
+
@review.save!
|
142
|
+
end
|
143
|
+
end
|
144
|
+
|
145
|
+
describe 'exclude changed key' do
|
146
|
+
it do
|
147
|
+
expect(@article.__elasticsearch__).to receive(:update_document_attributes).never
|
148
|
+
|
149
|
+
@review = @article.reviews[1]
|
150
|
+
@review.nonsense = 'change does not matter much'
|
151
|
+
@review.save!
|
152
|
+
end
|
153
|
+
end
|
154
|
+
end
|
155
|
+
end
|
156
|
+
end
|
157
|
+
end
|
data/spec/db.rb
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
require 'active_record'
|
2
|
+
require 'elasticsearch-rails-dynamic-json-support'
|
3
|
+
|
4
|
+
ActiveRecord::Base.logger = ActiveSupport::Logger.new(STDOUT)
|
5
|
+
ActiveRecord::Base.establish_connection( adapter: 'sqlite3', database: ":memory:" )
|
6
|
+
ActiveRecord::Schema.define(version: 1) do
|
7
|
+
create_table :articles do |t|
|
8
|
+
t.string :title, null: false
|
9
|
+
t.string :content, null: false
|
10
|
+
end
|
11
|
+
|
12
|
+
create_table :reviews do |t|
|
13
|
+
t.integer :article_id, null: false
|
14
|
+
t.string :content, null: false
|
15
|
+
t.string :nonsense
|
16
|
+
end
|
17
|
+
|
18
|
+
add_index :reviews, :article_id
|
19
|
+
end
|
20
|
+
|
21
|
+
class Article < ActiveRecord::Base
|
22
|
+
validates_presence_of :title
|
23
|
+
validates_presence_of :content
|
24
|
+
end
|
25
|
+
|
26
|
+
class Review < ActiveRecord::Base
|
27
|
+
validates_presence_of :content
|
28
|
+
end
|
@@ -14,21 +14,17 @@ end
|
|
14
14
|
ENV["COVERAGE"] && SimpleCov.start do
|
15
15
|
add_filter "/.rvm/"
|
16
16
|
end
|
17
|
-
require 'rubygems'
|
18
|
-
require 'bundler'
|
19
|
-
begin
|
20
|
-
Bundler.setup(:default, :development)
|
21
|
-
rescue Bundler::BundlerError => e
|
22
|
-
$stderr.puts e.message
|
23
|
-
$stderr.puts "Run `bundle install` to install missing gems"
|
24
|
-
exit e.status_code
|
25
|
-
end
|
26
|
-
require 'test/unit'
|
27
|
-
require 'shoulda'
|
28
|
-
|
29
17
|
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
|
30
18
|
$LOAD_PATH.unshift(File.dirname(__FILE__))
|
31
|
-
require 'elasticsearch-rails-dynamic-json-support'
|
32
19
|
|
33
|
-
|
20
|
+
require 'rspec'
|
21
|
+
|
22
|
+
# Requires supporting files with custom matchers and macros, etc,
|
23
|
+
# in ./support/ and its subdirectories.
|
24
|
+
Dir["#{File.dirname(__FILE__)}/support/**/*.rb"].each {|f| require f}
|
25
|
+
|
26
|
+
RSpec.configure do |config|
|
27
|
+
|
34
28
|
end
|
29
|
+
|
30
|
+
require_relative './db'
|
metadata
CHANGED
@@ -1,15 +1,29 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: elasticsearch-rails-dynamic-json-support
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.7
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Song Yangyu
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2016-09-
|
11
|
+
date: 2016-09-20 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: elasticsearch-model
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: 0.1.9
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: 0.1.9
|
13
27
|
- !ruby/object:Gem::Dependency
|
14
28
|
name: elasticsearch-rails
|
15
29
|
requirement: !ruby/object:Gem::Requirement
|
@@ -25,7 +39,7 @@ dependencies:
|
|
25
39
|
- !ruby/object:Gem::Version
|
26
40
|
version: 0.1.9
|
27
41
|
- !ruby/object:Gem::Dependency
|
28
|
-
name:
|
42
|
+
name: rspec
|
29
43
|
requirement: !ruby/object:Gem::Requirement
|
30
44
|
requirements:
|
31
45
|
- - ">="
|
@@ -108,6 +122,34 @@ dependencies:
|
|
108
122
|
- - ">="
|
109
123
|
- !ruby/object:Gem::Version
|
110
124
|
version: '0'
|
125
|
+
- !ruby/object:Gem::Dependency
|
126
|
+
name: activerecord
|
127
|
+
requirement: !ruby/object:Gem::Requirement
|
128
|
+
requirements:
|
129
|
+
- - ">="
|
130
|
+
- !ruby/object:Gem::Version
|
131
|
+
version: '0'
|
132
|
+
type: :development
|
133
|
+
prerelease: false
|
134
|
+
version_requirements: !ruby/object:Gem::Requirement
|
135
|
+
requirements:
|
136
|
+
- - ">="
|
137
|
+
- !ruby/object:Gem::Version
|
138
|
+
version: '0'
|
139
|
+
- !ruby/object:Gem::Dependency
|
140
|
+
name: activerecord-nulldb-adapter
|
141
|
+
requirement: !ruby/object:Gem::Requirement
|
142
|
+
requirements:
|
143
|
+
- - ">="
|
144
|
+
- !ruby/object:Gem::Version
|
145
|
+
version: '0'
|
146
|
+
type: :development
|
147
|
+
prerelease: false
|
148
|
+
version_requirements: !ruby/object:Gem::Requirement
|
149
|
+
requirements:
|
150
|
+
- - ">="
|
151
|
+
- !ruby/object:Gem::Version
|
152
|
+
version: '0'
|
111
153
|
description: Enhance elasticsearch-rails with `elasticsearch_json_changes` to translate
|
112
154
|
the attribute changes into document updates
|
113
155
|
email: flyfy1@gmail.com
|
@@ -129,8 +171,9 @@ files:
|
|
129
171
|
- lib/elasticsearch/model/cascade_update.rb
|
130
172
|
- lib/elasticsearch/model/importing_decorator.rb
|
131
173
|
- lib/elasticsearch/model/indexing_decorator.rb
|
132
|
-
-
|
133
|
-
-
|
174
|
+
- spec/cascade_update_spec.rb
|
175
|
+
- spec/db.rb
|
176
|
+
- spec/spec_helper.rb
|
134
177
|
homepage: http://github.com/flyfy1/elasticsearch-rails-dynamic-json-support
|
135
178
|
licenses:
|
136
179
|
- MIT
|