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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: e85e58d9e5a62bdd3b7655df5cd3e598b5891483
4
- data.tar.gz: c2a6e09a1bb1fa8fc06cda7d9d41a5a2031a4afb
3
+ metadata.gz: 32d652e44a9c8d46c731096776d6a497d9cb8cf3
4
+ data.tar.gz: d64a1aae4f1bad05cebcfec376f848024ce6b93f
5
5
  SHA512:
6
- metadata.gz: 98224260289cfb9db249661a8010ac72837e735290dd7448b5deb123cfb7434bd1743ca0fd005465c9d0a57212c1de832ebc122993f5ac5a5cdb03f9e8d59e61
7
- data.tar.gz: 59ac4629c3fd231a6a246dc75af1a6ce1ab7f8ac3f8857fd589acc2cc695b770adb362305a918dbd7ad7ac9773a44e470c538927be553432313d6b8c2dd778ca
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 "shoulda", ">= 0"
13
- gem "rdoc", "~> 3.12"
14
- gem "bundler", "~> 1.0"
15
- gem "jeweler", "~> 2.0.1"
16
- gem "simplecov", ">= 0"
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
@@ -75,11 +75,9 @@ PLATFORMS
75
75
 
76
76
  DEPENDENCIES
77
77
  bundler (~> 1.0)
78
+ concurrent-ruby
78
79
  elasticsearch-rails (~> 0.1.9)
79
80
  jeweler (~> 2.0.1)
80
81
  rdoc (~> 3.12)
81
82
  shoulda
82
83
  simplecov
83
-
84
- BUNDLED WITH
85
- 1.12.5
@@ -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
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.1 ruby lib
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.1"
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-26"
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.rdoc"
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.rdoc",
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
- 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
1
+ require 'elasticsearch/model'
14
2
 
15
- update_document_attributes attributes, options
16
- else
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.1
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-26 00:00:00.000000000 Z
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.rdoc
118
+ - README.md
105
119
  files:
106
120
  - ".document"
107
121
  - Gemfile
108
122
  - Gemfile.lock
109
123
  - LICENSE.txt
110
- - README.rdoc
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
@@ -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
-