elasticsearch-rails-dynamic-json-support 0.0.1 → 0.0.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Gemfile +6 -5
- data/Gemfile.lock +1 -3
- data/README.md +121 -0
- data/VERSION +1 -1
- data/elasticsearch-rails-dynamic-json-support.gemspec +10 -5
- data/lib/elasticsearch-rails-dynamic-json-support.rb +3 -22
- data/lib/elasticsearch/model/cascade_update.rb +107 -0
- data/lib/elasticsearch/model/indexing_decorator.rb +23 -0
- metadata +20 -4
- data/README.rdoc +0 -19
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 32d652e44a9c8d46c731096776d6a497d9cb8cf3
|
4
|
+
data.tar.gz: d64a1aae4f1bad05cebcfec376f848024ce6b93f
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 1ea410e65b98eaf8d25323a3bb7fccb7fa5d32ea0a24623a7e1c64812048f86d85f2f483d8cded1eaf6403fb63d712e8677059c57a3cd12468199d61197ac445
|
7
|
+
data.tar.gz: 39525f858190479557b85197654eec43d7b0fea400585bfc005430637dd155940a277654e5b84bff26c347d5d0471515b859d9aeeccdacb31fb9584fca087656
|
data/Gemfile
CHANGED
@@ -9,9 +9,10 @@ source "https://rubygems.org"
|
|
9
9
|
gem 'elasticsearch-rails', "~> 0.1.9"
|
10
10
|
|
11
11
|
group :development do
|
12
|
-
gem
|
13
|
-
gem
|
14
|
-
gem
|
15
|
-
gem
|
16
|
-
gem
|
12
|
+
gem 'shoulda', '>= 0'
|
13
|
+
gem 'rdoc', '~> 3.12'
|
14
|
+
gem 'bundler', '~> 1.0'
|
15
|
+
gem 'jeweler', '~> 2.0.1'
|
16
|
+
gem 'simplecov', '>= 0'
|
17
|
+
gem 'concurrent-ruby'
|
17
18
|
end
|
data/Gemfile.lock
CHANGED
data/README.md
ADDED
@@ -0,0 +1,121 @@
|
|
1
|
+
# Introduction
|
2
|
+
|
3
|
+
ElasticsearchRails DynamicJsonSupport - Small little change to help your model update easier in elasticsearch.
|
4
|
+
|
5
|
+
## Problem it solved
|
6
|
+
|
7
|
+
In ElasicSearch, if you define your model the following way:
|
8
|
+
|
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
|
+
|
34
|
+
There're 2 issues with the implementation above:
|
35
|
+
|
36
|
+
1. when the `content` is updated, this change would never be synced to the the
|
37
|
+
`article` field in ElasticSearch, since the key is `artile` instead
|
38
|
+
2. when the corresponding review is updated, the `article` is never updated,
|
39
|
+
since it's not awared of the change.
|
40
|
+
|
41
|
+
This library aims to solve these 2 issues, with the following convention:
|
42
|
+
|
43
|
+
- `es_json_changes(changed_attributes)` - for a given set of changed attributes,
|
44
|
+
it would response with the update to be processed by this record
|
45
|
+
- `Elasticsearch::Model::CascadeUpdate` - class, when included, providing:
|
46
|
+
- class methods:
|
47
|
+
- `es_register_attributes { key => lambda }`: to register a hash of `key,
|
48
|
+
lambda` pairs, to be used for rendering the json. if lambda is nil,
|
49
|
+
default to the public method of this resource named `key`.
|
50
|
+
- `key_name, relationship_name: nil, reverse_relationship_name: nil, &blk`:
|
51
|
+
to register an association of key, lambda. relationship_name default to
|
52
|
+
key if not given, and reverse_relationship_name default to the singular
|
53
|
+
form of the class name if not given. After this registration, whenever the
|
54
|
+
corresponding resource is updated, it would trigger this resource (which
|
55
|
+
is related to the resource) to update.
|
56
|
+
|
57
|
+
Example usage (which solves the 2 issues above) is as given below:
|
58
|
+
|
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: nil, title: nil, article: lambda {|rec| rec.content }, created_at: nil, updated_at: nil
|
68
|
+
es_register_assoc(:reviews) { |review| review.as_indexed_json }
|
69
|
+
|
70
|
+
def elasticsearch_json_changes(changed_attributes)
|
71
|
+
keys_to_update = changed_attributes.keys.map {|k| key_map k}
|
72
|
+
self.as_indexed_json.select { |k,_| keys_to_update.include? k.to_s }
|
73
|
+
end
|
74
|
+
|
75
|
+
private
|
76
|
+
def key_map(key)
|
77
|
+
key = key.to_s
|
78
|
+
case key
|
79
|
+
when 'content'
|
80
|
+
'article'
|
81
|
+
else
|
82
|
+
key
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
# app/models/review.rb
|
88
|
+
class Review < ActiveRecord::Base
|
89
|
+
# Make sure to register Article
|
90
|
+
Article
|
91
|
+
|
92
|
+
belongs_to :article
|
93
|
+
|
94
|
+
def as_indexed_json(options = {})
|
95
|
+
as_json
|
96
|
+
end
|
97
|
+
end
|
98
|
+
```
|
99
|
+
|
100
|
+
# Issues
|
101
|
+
|
102
|
+
- Tests.. since I don't have time to write test case for this project yet.
|
103
|
+
- Due to the `lazy-loading` of model in rails, you need to specifically specify
|
104
|
+
the model in rails. Like in the `Review` class above, it's reference to
|
105
|
+
`Article`
|
106
|
+
|
107
|
+
== Contributing to elasticsearch-rails-dynamic-json-support
|
108
|
+
|
109
|
+
* Check out the latest master to make sure the feature hasn't been implemented or the bug hasn't been fixed yet.
|
110
|
+
* Check out the issue tracker to make sure someone already hasn't requested it and/or contributed it.
|
111
|
+
* Fork the project.
|
112
|
+
* Start a feature/bugfix branch.
|
113
|
+
* Commit and push until you are happy with your contribution.
|
114
|
+
* Make sure to add tests for it. This is important so I don't break it in a future version unintentionally.
|
115
|
+
* Please try not to mess with the Rakefile, version, or history. If you want to have your own version, or is otherwise necessary, that is fine, but please isolate to its own commit so I can cherry-pick around it.
|
116
|
+
|
117
|
+
== Copyright
|
118
|
+
|
119
|
+
Copyright (c) 2016 Song Yangyu. See LICENSE.txt for
|
120
|
+
further details.
|
121
|
+
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.0.
|
1
|
+
0.0.2
|
@@ -2,32 +2,34 @@
|
|
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.2 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.2"
|
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-08-
|
14
|
+
s.date = "2016-08-30"
|
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 = [
|
18
18
|
"LICENSE.txt",
|
19
|
-
"README.
|
19
|
+
"README.md"
|
20
20
|
]
|
21
21
|
s.files = [
|
22
22
|
".document",
|
23
23
|
"Gemfile",
|
24
24
|
"Gemfile.lock",
|
25
25
|
"LICENSE.txt",
|
26
|
-
"README.
|
26
|
+
"README.md",
|
27
27
|
"Rakefile",
|
28
28
|
"VERSION",
|
29
29
|
"elasticsearch-rails-dynamic-json-support.gemspec",
|
30
30
|
"lib/elasticsearch-rails-dynamic-json-support.rb",
|
31
|
+
"lib/elasticsearch/model/cascade_update.rb",
|
32
|
+
"lib/elasticsearch/model/indexing_decorator.rb",
|
31
33
|
"test/helper.rb",
|
32
34
|
"test/test_elasticsearch-rails-dynamic-json-support.rb"
|
33
35
|
]
|
@@ -46,6 +48,7 @@ Gem::Specification.new do |s|
|
|
46
48
|
s.add_development_dependency(%q<bundler>.freeze, ["~> 1.0"])
|
47
49
|
s.add_development_dependency(%q<jeweler>.freeze, ["~> 2.0.1"])
|
48
50
|
s.add_development_dependency(%q<simplecov>.freeze, [">= 0"])
|
51
|
+
s.add_development_dependency(%q<concurrent-ruby>.freeze, [">= 0"])
|
49
52
|
else
|
50
53
|
s.add_dependency(%q<elasticsearch-rails>.freeze, ["~> 0.1.9"])
|
51
54
|
s.add_dependency(%q<shoulda>.freeze, [">= 0"])
|
@@ -53,6 +56,7 @@ Gem::Specification.new do |s|
|
|
53
56
|
s.add_dependency(%q<bundler>.freeze, ["~> 1.0"])
|
54
57
|
s.add_dependency(%q<jeweler>.freeze, ["~> 2.0.1"])
|
55
58
|
s.add_dependency(%q<simplecov>.freeze, [">= 0"])
|
59
|
+
s.add_dependency(%q<concurrent-ruby>.freeze, [">= 0"])
|
56
60
|
end
|
57
61
|
else
|
58
62
|
s.add_dependency(%q<elasticsearch-rails>.freeze, ["~> 0.1.9"])
|
@@ -61,6 +65,7 @@ Gem::Specification.new do |s|
|
|
61
65
|
s.add_dependency(%q<bundler>.freeze, ["~> 1.0"])
|
62
66
|
s.add_dependency(%q<jeweler>.freeze, ["~> 2.0.1"])
|
63
67
|
s.add_dependency(%q<simplecov>.freeze, [">= 0"])
|
68
|
+
s.add_dependency(%q<concurrent-ruby>.freeze, [">= 0"])
|
64
69
|
end
|
65
70
|
end
|
66
71
|
|
@@ -1,23 +1,4 @@
|
|
1
|
-
|
2
|
-
module Model
|
3
|
-
module Indexing
|
4
|
-
module InstanceMethods
|
5
|
-
def update_document options = {}
|
6
|
-
if changed_attributes = self.instance_variable_get(:@__changed_attributes)
|
7
|
-
attributes = if target.respond_to? :elasticsearch_json_changes
|
8
|
-
target.elasticsearch_json_changes changed_attributes
|
9
|
-
elsif respond_to?(:as_indexed_json)
|
10
|
-
self.as_indexed_json.select { |k,v| changed_attributes.keys.map(&:to_s).include? k.to_s }
|
11
|
-
else
|
12
|
-
changed_attributes
|
13
|
-
end
|
1
|
+
require 'elasticsearch/model'
|
14
2
|
|
15
|
-
|
16
|
-
|
17
|
-
index_document(options)
|
18
|
-
end
|
19
|
-
end
|
20
|
-
end
|
21
|
-
end
|
22
|
-
end
|
23
|
-
end
|
3
|
+
require_relative 'elasticsearch/model/indexing_decorator'
|
4
|
+
require_relative 'elasticsearch/model/cascade_update'
|
@@ -0,0 +1,107 @@
|
|
1
|
+
module Elasticsearch
|
2
|
+
module Model
|
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
|
+
extend ActiveSupport::Concern
|
24
|
+
|
25
|
+
# update, just for a specific key, or set of keys
|
26
|
+
def es_partial_update(*keys)
|
27
|
+
keys = keys.map(&:to_s)
|
28
|
+
|
29
|
+
res_json = {}
|
30
|
+
keys.each do |key|
|
31
|
+
res_json[key] = self.class.es_register_assoc(key).to_json(self)
|
32
|
+
end
|
33
|
+
|
34
|
+
self.__elasticsearch__.update_document_attributes res_json
|
35
|
+
end
|
36
|
+
|
37
|
+
def as_indexed_json(options = {})
|
38
|
+
self.class.to_indexed_json(self, options)
|
39
|
+
end
|
40
|
+
|
41
|
+
class_methods do
|
42
|
+
|
43
|
+
# attributes is a pair of <key, block>
|
44
|
+
def es_register_attributes attributes
|
45
|
+
attributes.each do |k, v|
|
46
|
+
k = k.to_s
|
47
|
+
json_attribute_registry[k] = v || lambda { |record| record.public_send k }
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
# default `reverse_relationship_name` to be singular case if not given
|
52
|
+
def es_register_assoc key_name, relationship_name: nil, reverse_relationship_name: nil, &blk
|
53
|
+
key_name = key_name.to_s
|
54
|
+
return json_relationship_registry[key_name] unless blk
|
55
|
+
|
56
|
+
relationship_name = key_name unless relationship_name
|
57
|
+
reverse_relationship_name = self.name.downcase unless reverse_relationship_name
|
58
|
+
|
59
|
+
reflection = reflect_on_association relationship_name
|
60
|
+
reflected_class = reflection.class_name.constantize
|
61
|
+
|
62
|
+
actor = Actor.new relationship_name, get_singularity(reflection), blk
|
63
|
+
json_relationship_registry[key_name] = actor
|
64
|
+
|
65
|
+
reflected_class.class_eval do
|
66
|
+
after_commit do
|
67
|
+
Array.wrap(self.public_send reverse_relationship_name).each do |record|
|
68
|
+
record.es_partial_update(key_name)
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
def to_indexed_json(record, options = {})
|
75
|
+
result = {}
|
76
|
+
json_attribute_registry.each do |k, blk|
|
77
|
+
result[k] = blk[record]
|
78
|
+
end
|
79
|
+
|
80
|
+
json_relationship_registry.each do |k, actor|
|
81
|
+
result[k] = actor.to_json(record)
|
82
|
+
end
|
83
|
+
|
84
|
+
result
|
85
|
+
end
|
86
|
+
|
87
|
+
private
|
88
|
+
def json_relationship_registry
|
89
|
+
@@__json_actor_registry_ ||= {}
|
90
|
+
end
|
91
|
+
|
92
|
+
def json_attribute_registry
|
93
|
+
@@__json_attribute_registry ||= {}
|
94
|
+
end
|
95
|
+
|
96
|
+
def get_singularity(reflection)
|
97
|
+
case reflection
|
98
|
+
when ActiveRecord::Reflection::HasManyReflection, ActiveRecord::Reflection::HasManyThroughAssociation
|
99
|
+
:plural
|
100
|
+
else
|
101
|
+
:singular
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module Elasticsearch
|
2
|
+
module Model
|
3
|
+
module Indexing
|
4
|
+
module InstanceMethods
|
5
|
+
def update_document options = {}
|
6
|
+
if changed_attributes = self.instance_variable_get(:@__changed_attributes)
|
7
|
+
attributes = if target.respond_to? :elasticsearch_json_changes
|
8
|
+
target.elasticsearch_json_changes changed_attributes
|
9
|
+
elsif respond_to?(:as_indexed_json)
|
10
|
+
self.as_indexed_json.select { |k,v| changed_attributes.keys.map(&:to_s).include? k.to_s }
|
11
|
+
else
|
12
|
+
changed_attributes
|
13
|
+
end
|
14
|
+
|
15
|
+
update_document_attributes attributes, options
|
16
|
+
else
|
17
|
+
index_document(options)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
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.2
|
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-08-
|
11
|
+
date: 2016-08-30 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: elasticsearch-rails
|
@@ -94,6 +94,20 @@ dependencies:
|
|
94
94
|
- - ">="
|
95
95
|
- !ruby/object:Gem::Version
|
96
96
|
version: '0'
|
97
|
+
- !ruby/object:Gem::Dependency
|
98
|
+
name: concurrent-ruby
|
99
|
+
requirement: !ruby/object:Gem::Requirement
|
100
|
+
requirements:
|
101
|
+
- - ">="
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
version: '0'
|
104
|
+
type: :development
|
105
|
+
prerelease: false
|
106
|
+
version_requirements: !ruby/object:Gem::Requirement
|
107
|
+
requirements:
|
108
|
+
- - ">="
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: '0'
|
97
111
|
description: Enhance elasticsearch-rails with `elasticsearch_json_changes` to translate
|
98
112
|
the attribute changes into document updates
|
99
113
|
email: flyfy1@gmail.com
|
@@ -101,17 +115,19 @@ executables: []
|
|
101
115
|
extensions: []
|
102
116
|
extra_rdoc_files:
|
103
117
|
- LICENSE.txt
|
104
|
-
- README.
|
118
|
+
- README.md
|
105
119
|
files:
|
106
120
|
- ".document"
|
107
121
|
- Gemfile
|
108
122
|
- Gemfile.lock
|
109
123
|
- LICENSE.txt
|
110
|
-
- README.
|
124
|
+
- README.md
|
111
125
|
- Rakefile
|
112
126
|
- VERSION
|
113
127
|
- elasticsearch-rails-dynamic-json-support.gemspec
|
114
128
|
- lib/elasticsearch-rails-dynamic-json-support.rb
|
129
|
+
- lib/elasticsearch/model/cascade_update.rb
|
130
|
+
- lib/elasticsearch/model/indexing_decorator.rb
|
115
131
|
- test/helper.rb
|
116
132
|
- test/test_elasticsearch-rails-dynamic-json-support.rb
|
117
133
|
homepage: http://github.com/flyfy1/elasticsearch-rails-dynamic-json-support
|
data/README.rdoc
DELETED
@@ -1,19 +0,0 @@
|
|
1
|
-
= elasticsearch-rails-dynamic-json-support
|
2
|
-
|
3
|
-
Description goes here.
|
4
|
-
|
5
|
-
== Contributing to elasticsearch-rails-dynamic-json-support
|
6
|
-
|
7
|
-
* Check out the latest master to make sure the feature hasn't been implemented or the bug hasn't been fixed yet.
|
8
|
-
* Check out the issue tracker to make sure someone already hasn't requested it and/or contributed it.
|
9
|
-
* Fork the project.
|
10
|
-
* Start a feature/bugfix branch.
|
11
|
-
* Commit and push until you are happy with your contribution.
|
12
|
-
* Make sure to add tests for it. This is important so I don't break it in a future version unintentionally.
|
13
|
-
* Please try not to mess with the Rakefile, version, or history. If you want to have your own version, or is otherwise necessary, that is fine, but please isolate to its own commit so I can cherry-pick around it.
|
14
|
-
|
15
|
-
== Copyright
|
16
|
-
|
17
|
-
Copyright (c) 2016 Song Yangyu. See LICENSE.txt for
|
18
|
-
further details.
|
19
|
-
|