elasticsearch-persistence 5.0.2 → 6.1.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (81) hide show
  1. checksums.yaml +5 -5
  2. data/.rspec +2 -0
  3. data/Gemfile +9 -0
  4. data/README.md +206 -338
  5. data/Rakefile +15 -12
  6. data/elasticsearch-persistence.gemspec +6 -7
  7. data/examples/notes/application.rb +3 -4
  8. data/lib/elasticsearch/persistence.rb +2 -110
  9. data/lib/elasticsearch/persistence/repository.rb +212 -53
  10. data/lib/elasticsearch/persistence/repository/dsl.rb +94 -0
  11. data/lib/elasticsearch/persistence/repository/find.rb +27 -10
  12. data/lib/elasticsearch/persistence/repository/response/results.rb +21 -8
  13. data/lib/elasticsearch/persistence/repository/search.rb +30 -18
  14. data/lib/elasticsearch/persistence/repository/serialize.rb +65 -7
  15. data/lib/elasticsearch/persistence/repository/store.rb +38 -44
  16. data/lib/elasticsearch/persistence/version.rb +1 -1
  17. data/spec/repository/find_spec.rb +179 -0
  18. data/spec/repository/response/results_spec.rb +128 -0
  19. data/spec/repository/search_spec.rb +181 -0
  20. data/spec/repository/serialize_spec.rb +53 -0
  21. data/spec/repository/store_spec.rb +327 -0
  22. data/spec/repository_spec.rb +723 -0
  23. data/spec/spec_helper.rb +32 -0
  24. metadata +26 -104
  25. data/examples/music/album.rb +0 -54
  26. data/examples/music/artist.rb +0 -70
  27. data/examples/music/artists/_form.html.erb +0 -8
  28. data/examples/music/artists/artists_controller.rb +0 -67
  29. data/examples/music/artists/artists_controller_test.rb +0 -53
  30. data/examples/music/artists/index.html.erb +0 -60
  31. data/examples/music/artists/show.html.erb +0 -54
  32. data/examples/music/assets/application.css +0 -257
  33. data/examples/music/assets/autocomplete.css +0 -48
  34. data/examples/music/assets/blank_artist.png +0 -0
  35. data/examples/music/assets/blank_cover.png +0 -0
  36. data/examples/music/assets/form.css +0 -113
  37. data/examples/music/index_manager.rb +0 -73
  38. data/examples/music/search/index.html.erb +0 -95
  39. data/examples/music/search/search_controller.rb +0 -41
  40. data/examples/music/search/search_controller_test.rb +0 -12
  41. data/examples/music/search/search_helper.rb +0 -15
  42. data/examples/music/suggester.rb +0 -69
  43. data/examples/music/template.rb +0 -430
  44. data/examples/music/vendor/assets/jquery-ui-1.10.4.custom.min.css +0 -7
  45. data/examples/music/vendor/assets/jquery-ui-1.10.4.custom.min.js +0 -6
  46. data/examples/music/vendor/assets/stylesheets/ui-lightness/images/ui-bg_highlight-soft_100_eeeeee_1x100.png +0 -0
  47. data/lib/elasticsearch/persistence/client.rb +0 -51
  48. data/lib/elasticsearch/persistence/model.rb +0 -135
  49. data/lib/elasticsearch/persistence/model/base.rb +0 -87
  50. data/lib/elasticsearch/persistence/model/errors.rb +0 -8
  51. data/lib/elasticsearch/persistence/model/find.rb +0 -180
  52. data/lib/elasticsearch/persistence/model/rails.rb +0 -47
  53. data/lib/elasticsearch/persistence/model/store.rb +0 -254
  54. data/lib/elasticsearch/persistence/model/utils.rb +0 -0
  55. data/lib/elasticsearch/persistence/repository/class.rb +0 -71
  56. data/lib/elasticsearch/persistence/repository/naming.rb +0 -115
  57. data/lib/rails/generators/elasticsearch/model/model_generator.rb +0 -21
  58. data/lib/rails/generators/elasticsearch/model/templates/model.rb.tt +0 -9
  59. data/lib/rails/generators/elasticsearch_generator.rb +0 -2
  60. data/test/integration/model/model_basic_test.rb +0 -233
  61. data/test/integration/repository/custom_class_test.rb +0 -85
  62. data/test/integration/repository/customized_class_test.rb +0 -82
  63. data/test/integration/repository/default_class_test.rb +0 -116
  64. data/test/integration/repository/virtus_model_test.rb +0 -118
  65. data/test/test_helper.rb +0 -55
  66. data/test/unit/model_base_test.rb +0 -72
  67. data/test/unit/model_find_test.rb +0 -153
  68. data/test/unit/model_gateway_test.rb +0 -101
  69. data/test/unit/model_rails_test.rb +0 -112
  70. data/test/unit/model_store_test.rb +0 -576
  71. data/test/unit/persistence_test.rb +0 -32
  72. data/test/unit/repository_class_test.rb +0 -51
  73. data/test/unit/repository_client_test.rb +0 -32
  74. data/test/unit/repository_find_test.rb +0 -388
  75. data/test/unit/repository_indexing_test.rb +0 -37
  76. data/test/unit/repository_module_test.rb +0 -146
  77. data/test/unit/repository_naming_test.rb +0 -146
  78. data/test/unit/repository_response_results_test.rb +0 -98
  79. data/test/unit/repository_search_test.rb +0 -117
  80. data/test/unit/repository_serialize_test.rb +0 -57
  81. data/test/unit/repository_store_test.rb +0 -303
@@ -1,47 +0,0 @@
1
- module Elasticsearch
2
- module Persistence
3
- module Model
4
-
5
- # Make the `Persistence::Model` models compatible with Ruby On Rails applications
6
- #
7
- module Rails
8
- def self.included(base)
9
- base.class_eval do
10
-
11
- def initialize(attributes={})
12
- super(__convert_rails_dates(attributes))
13
- end
14
-
15
- def update(attributes={}, options={})
16
- super(__convert_rails_dates(attributes), options)
17
- end
18
- end
19
- end
20
-
21
- # Decorates the passed in `attributes` so they extract the date & time values from Rails forms
22
- #
23
- # @example Correctly combine the date and time to a datetime string
24
- #
25
- # params = { "published_on(1i)"=>"2014",
26
- # "published_on(2i)"=>"1",
27
- # "published_on(3i)"=>"1",
28
- # "published_on(4i)"=>"12",
29
- # "published_on(5i)"=>"00"
30
- # }
31
- # MyRailsModel.new(params).published_on.iso8601
32
- # # => "2014-01-01T12:00:00+00:00"
33
- #
34
- def __convert_rails_dates(attributes={})
35
- day = attributes.select { |p| p =~ /\([1-3]/ }.reduce({}) { |sum, item| (sum[item.first.gsub(/\(.+\)/, '')] ||= '' )<< item.last+'-'; sum }
36
- time = attributes.select { |p| p =~ /\([4-6]/ }.reduce({}) { |sum, item| (sum[item.first.gsub(/\(.+\)/, '')] ||= '' )<< item.last+':'; sum }
37
- unless day.empty?
38
- attributes.update day.reduce({}) { |sum, item| sum[item.first] = item.last; sum[item.first] += ' ' + time[item.first] unless time.empty?; sum }
39
- end
40
-
41
- return attributes
42
- end; module_function :__convert_rails_dates
43
- end
44
-
45
- end
46
- end
47
- end
@@ -1,254 +0,0 @@
1
- module Elasticsearch
2
- module Persistence
3
- module Model
4
-
5
- # This module contains the storage related features of {Elasticsearch::Persistence::Model}
6
- #
7
- module Store
8
- module ClassMethods #:nodoc:
9
-
10
- # Creates a class instance, saves it, if validations pass, and returns it
11
- #
12
- # @example Create a new person
13
- #
14
- # Person.create name: 'John Smith'
15
- # # => #<Person:0x007f889e302b30 ... @id="bG7yQDAXRhCi3ZfVcx6oAA", @name="John Smith" ...>
16
- #
17
- # @return [Object] The model instance
18
- #
19
- def create(attributes, options={})
20
- object = self.new(attributes)
21
- object.run_callbacks :create do
22
- object.save(options)
23
- object
24
- end
25
- end
26
- end
27
-
28
- module InstanceMethods
29
-
30
- # Saves the model (if validations pass) and returns the response (or `false`)
31
- #
32
- # @example Save a valid model instance
33
- #
34
- # p = Person.new(name: 'John')
35
- # p.save
36
- # => {"_index"=>"people", ... "_id"=>"RzFSXFR0R8u1CZIWNs2Gvg", "_version"=>1, "created"=>true}
37
- #
38
- # @example Save an invalid model instance
39
- #
40
- # p = Person.new(name: nil)
41
- # p.save
42
- # # => false
43
- #
44
- # @return [Hash,FalseClass] The Elasticsearch response as a Hash or `false`
45
- #
46
- def save(options={})
47
- unless options.delete(:validate) == false
48
- return false unless valid?
49
- end
50
-
51
- run_callbacks :save do
52
- options.update id: self.id
53
- options.update index: self._index if self._index
54
- options.update type: self._type if self._type
55
-
56
- self[:updated_at] = Time.now.utc
57
-
58
- response = self.class.gateway.save(self, options)
59
-
60
- @_id = response['_id']
61
- @_index = response['_index']
62
- @_type = response['_type']
63
- @_version = response['_version']
64
- @persisted = true
65
-
66
- response
67
- end
68
- end
69
-
70
- # Deletes the model from Elasticsearch (if it's persisted), freezes it, and returns the response
71
- #
72
- # @example Delete a model instance
73
- #
74
- # p.destroy
75
- # => {"_index"=>"people", ... "_id"=>"RzFSXFR0R8u1CZIWNs2Gvg", "_version"=>2 ...}
76
- #
77
- # @return [Hash] The Elasticsearch response as a Hash
78
- #
79
- def destroy(options={})
80
- raise DocumentNotPersisted, "Object not persisted: #{self.inspect}" unless persisted?
81
-
82
- run_callbacks :destroy do
83
- options.update index: self._index if self._index
84
- options.update type: self._type if self._type
85
-
86
- response = self.class.gateway.delete(self.id, options)
87
-
88
- @destroyed = true
89
- @persisted = false
90
- self.freeze
91
- response
92
- end
93
- end; alias :delete :destroy
94
-
95
- # Updates the model (via Elasticsearch's "Update" API) and returns the response
96
- #
97
- # @example Update a model with partial attributes
98
- #
99
- # p.update name: 'UPDATED'
100
- # => {"_index"=>"people", ... "_version"=>2}
101
- #
102
- # @example Pass a version for concurrency control
103
- #
104
- # p.update( { name: 'UPDATED' }, { version: 2 } )
105
- # => {"_index"=>"people", ... "_version"=>3}
106
- #
107
- # @example An exception is raised when the version doesn't match
108
- #
109
- # p.update( { name: 'UPDATED' }, { version: 2 } )
110
- # => Elasticsearch::Transport::Transport::Errors::Conflict: [409] {"error" ... }
111
- #
112
- # @return [Hash] The Elasticsearch response as a Hash
113
- #
114
- def update(attributes={}, options={})
115
- unless options.delete(:validate) == false
116
- return false unless valid?
117
- end
118
- raise DocumentNotPersisted, "Object not persisted: #{self.inspect}" unless persisted?
119
-
120
- run_callbacks :update do
121
- options.update index: self._index if self._index
122
- options.update type: self._type if self._type
123
-
124
- attributes.update( { updated_at: Time.now.utc } )
125
- response = self.class.gateway.update(self.id, { doc: attributes}.merge(options))
126
-
127
- self.attributes = self.attributes.merge(attributes)
128
- @_index = response['_index']
129
- @_type = response['_type']
130
- @_version = response['_version']
131
-
132
- response
133
- end
134
- end; alias :update_attributes :update
135
-
136
- # Increments a numeric attribute (via Elasticsearch's "Update" API) and returns the response
137
- #
138
- # @example Increment the `salary` attribute by 1
139
- #
140
- # p.increment :salary
141
- #
142
- # @example Increment the `salary` attribute by 100
143
- #
144
- # p.increment :salary, 100
145
- #
146
- # @return [Hash] The Elasticsearch response as a Hash
147
- #
148
- def increment(attribute, value=1, options={})
149
- raise DocumentNotPersisted, "Object not persisted: #{self.inspect}" unless persisted?
150
-
151
- options.update index: self._index if self._index
152
- options.update type: self._type if self._type
153
-
154
- response = self.class.gateway.update(self.id, { script: "ctx._source.#{attribute} += #{value}"}.merge(options))
155
-
156
- self[attribute] += value
157
-
158
- @_index = response['_index']
159
- @_type = response['_type']
160
- @_version = response['_version']
161
-
162
- response
163
- end
164
-
165
- # Decrements a numeric attribute (via Elasticsearch's "Update" API) and returns the response
166
- #
167
- # @example Decrement the `salary` attribute by 1
168
- #
169
- # p.decrement :salary
170
- #
171
- # @example Decrement the `salary` attribute by 100
172
- #
173
- # p.decrement :salary, 100
174
- #
175
- # @return [Hash] The Elasticsearch response as a Hash
176
- #
177
- def decrement(attribute, value=1, options={})
178
- raise DocumentNotPersisted, "Object not persisted: #{self.inspect}" unless persisted?
179
-
180
- options.update index: self._index if self._index
181
- options.update type: self._type if self._type
182
-
183
- response = self.class.gateway.update(self.id, { script: "ctx._source.#{attribute} = ctx._source.#{attribute} - #{value}"}.merge(options))
184
- self[attribute] -= value
185
-
186
- @_index = response['_index']
187
- @_type = response['_type']
188
- @_version = response['_version']
189
-
190
- response
191
- end
192
-
193
- # Updates the `updated_at` attribute, saves the model and returns the response
194
- #
195
- # @example Update the `updated_at` attribute (default)
196
- #
197
- # p.touch
198
- #
199
- # @example Update a custom attribute: `saved_on`
200
- #
201
- # p.touch :saved_on
202
- #
203
- # @return [Hash] The Elasticsearch response as a Hash
204
- #
205
- def touch(attribute=:updated_at, options={})
206
- raise DocumentNotPersisted, "Object not persisted: #{self.inspect}" unless persisted?
207
- raise ArgumentError, "Object does not have '#{attribute}' attribute" unless respond_to?(attribute)
208
-
209
- run_callbacks :touch do
210
- options.update index: self._index if self._index
211
- options.update type: self._type if self._type
212
-
213
- value = Time.now.utc
214
- response = self.class.gateway.update(self.id, { doc: { attribute => value.iso8601 }}.merge(options))
215
-
216
- self[attribute] = value
217
-
218
- @_index = response['_index']
219
- @_type = response['_type']
220
- @_version = response['_version']
221
-
222
- response
223
- end
224
- end
225
-
226
- # Returns true when the model has been destroyed, false otherwise
227
- #
228
- # @return [TrueClass,FalseClass]
229
- #
230
- def destroyed?
231
- !!@destroyed
232
- end
233
-
234
- # Returns true when the model has been already saved to the database, false otherwise
235
- #
236
- # @return [TrueClass,FalseClass]
237
- #
238
- def persisted?
239
- !!@persisted && !destroyed?
240
- end
241
-
242
- # Returns true when the model has not been saved yet, false otherwise
243
- #
244
- # @return [TrueClass,FalseClass]
245
- #
246
- def new_record?
247
- !persisted? && !destroyed?
248
- end
249
- end
250
- end
251
-
252
- end
253
- end
254
- end
@@ -1,71 +0,0 @@
1
- module Elasticsearch
2
- module Persistence
3
- module Repository
4
-
5
- # The default repository class, to be used either directly, or as a gateway in a custom repository class
6
- #
7
- # @example Standalone use
8
- #
9
- # repository = Elasticsearch::Persistence::Repository::Class.new
10
- # # => #<Elasticsearch::Persistence::Repository::Class ...>
11
- # repository.save(my_object)
12
- # # => {"_index"=> ... }
13
- #
14
- # @example Shortcut use
15
- #
16
- # repository = Elasticsearch::Persistence::Repository.new
17
- # # => #<Elasticsearch::Persistence::Repository::Class ...>
18
- #
19
- # @example Configuration via a block
20
- #
21
- # repository = Elasticsearch::Persistence::Repository.new do
22
- # index 'my_notes'
23
- # end
24
- #
25
- # # => #<Elasticsearch::Persistence::Repository::Class ...>
26
- # # > repository.save(my_object)
27
- # # => {"_index"=> ... }
28
- #
29
- # @example Accessing the gateway in a custom class
30
- #
31
- # class MyRepository
32
- # include Elasticsearch::Persistence::Repository
33
- # end
34
- #
35
- # repository = MyRepository.new
36
- #
37
- # repository.gateway.client.info
38
- # # => {"status"=>200, "name"=>"Venom", ... }
39
- #
40
- class Class
41
- include Elasticsearch::Persistence::Repository::Client
42
- include Elasticsearch::Persistence::Repository::Naming
43
- include Elasticsearch::Persistence::Repository::Serialize
44
- include Elasticsearch::Persistence::Repository::Store
45
- include Elasticsearch::Persistence::Repository::Find
46
- include Elasticsearch::Persistence::Repository::Search
47
-
48
- include Elasticsearch::Model::Indexing::ClassMethods
49
-
50
- attr_reader :options
51
-
52
- def initialize(options={}, &block)
53
- @options = options
54
- index_name options.delete(:index)
55
- block.arity < 1 ? instance_eval(&block) : block.call(self) if block_given?
56
- end
57
-
58
- # Return the "host" class, if this repository is a gateway hosted in another class
59
- #
60
- # @return [nil, Class]
61
- #
62
- # @api private
63
- #
64
- def host
65
- options[:host]
66
- end
67
- end
68
-
69
- end
70
- end
71
- end
@@ -1,115 +0,0 @@
1
- module Elasticsearch
2
- module Persistence
3
- module Repository
4
-
5
- # Wraps all naming-related features of the repository (index name, the domain object class, etc)
6
- #
7
- module Naming
8
-
9
- # Get or set the class used to initialize domain objects when deserializing them
10
- #
11
- def klass name=nil
12
- @klass = name || @klass
13
- end
14
-
15
- # Set the class used to initialize domain objects when deserializing them
16
- #
17
- def klass=klass
18
- @klass = klass
19
- end
20
-
21
- # Get or set the index name used when storing and retrieving documents
22
- #
23
- def index_name name=nil
24
- @index_name = name || @index_name || begin
25
- if respond_to?(:host) && host && host.is_a?(Module)
26
- self.host.to_s.underscore.gsub(/\//, '-')
27
- else
28
- self.class.to_s.underscore.gsub(/\//, '-')
29
- end
30
- end
31
- end; alias :index :index_name
32
-
33
- # Set the index name used when storing and retrieving documents
34
- #
35
- def index_name=(name)
36
- @index_name = name
37
- end; alias :index= :index_name=
38
-
39
- # Get or set the document type used when storing and retrieving documents
40
- #
41
- def document_type name=nil
42
- @document_type = name || @document_type || (klass ? klass.to_s.underscore : nil)
43
- end; alias :type :document_type
44
-
45
- # Set the document type used when storing and retrieving documents
46
- #
47
- def document_type=(name)
48
- @document_type = name
49
- end; alias :type= :document_type=
50
-
51
- # Get the Ruby class from the Elasticsearch `_type`
52
- #
53
- # @example
54
- # repository.__get_klass_from_type 'note'
55
- # => Note
56
- #
57
- # @return [Class] The class corresponding to the passed type
58
- # @raise [NameError] if the class cannot be found
59
- #
60
- # @api private
61
- #
62
- def __get_klass_from_type(type)
63
- klass = type.classify
64
- klass.constantize
65
- rescue NameError => e
66
- raise NameError, "Attempted to get class '#{klass}' from the '#{type}' type, but no such class can be found."
67
- end
68
-
69
- # Get the Elasticsearch `_type` from the Ruby class
70
- #
71
- # @example
72
- # repository.__get_type_from_class Note
73
- # => "note"
74
- #
75
- # @return [String] The type corresponding to the passed class
76
- #
77
- # @api private
78
- #
79
- def __get_type_from_class(klass)
80
- klass.to_s.underscore
81
- end
82
-
83
- # Get a document ID from the document (assuming Hash or Hash-like object)
84
- #
85
- # @example
86
- # repository.__get_id_from_document title: 'Test', id: 'abc123'
87
- # => "abc123"
88
- #
89
- # @api private
90
- #
91
- def __get_id_from_document(document)
92
- document[:id] || document['id'] || document[:_id] || document['_id']
93
- end
94
-
95
- # Extract a document ID from the document (assuming Hash or Hash-like object)
96
- #
97
- # @note Calling this method will *remove* the `id` or `_id` key from the passed object.
98
- #
99
- # @example
100
- # options = { title: 'Test', id: 'abc123' }
101
- # repository.__extract_id_from_document options
102
- # # => "abc123"
103
- # options
104
- # # => { title: 'Test' }
105
- #
106
- # @api private
107
- #
108
- def __extract_id_from_document(document)
109
- document.delete(:id) || document.delete('id') || document.delete(:_id) || document.delete('_id')
110
- end
111
- end
112
-
113
- end
114
- end
115
- end