elasticsearch-persistence 0.1.7 → 0.1.8
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +13 -1
- data/README.md +11 -5
- data/examples/music/album.rb +0 -1
- data/lib/elasticsearch/persistence/model.rb +9 -3
- data/lib/elasticsearch/persistence/model/rails.rb +1 -1
- data/lib/elasticsearch/persistence/model/store.rb +15 -3
- data/lib/elasticsearch/persistence/version.rb +1 -1
- data/test/integration/model/model_basic_test.rb +26 -1
- data/test/integration/repository/default_class_test.rb +2 -0
- data/test/unit/model_base_test.rb +36 -12
- data/test/unit/model_rails_test.rb +17 -0
- data/test/unit/model_store_test.rb +34 -0
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 0e68271351dc49665d55b42ef03261773e0e0487
|
4
|
+
data.tar.gz: 0062f28b76d0d5e73484b10ba4480e74b576efe4
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 1610448416e545c6dc7ed1ce18760833fd0f48e3452ea9887762b303896c3dd7cd8ca28e71cff25ed9a9a6062d063944ecc06f8f7cd7483819a05665f7c06553
|
7
|
+
data.tar.gz: 02df7217562efa1277489e1e8878fff983f509f5351d0533d2231154bc556829063a4e8369afddc0c3b722f505e46d59108f3220c1c49c83eb058ef89ab0d291
|
data/CHANGELOG.md
CHANGED
@@ -1,4 +1,16 @@
|
|
1
|
-
|
1
|
+
## 0.1.8
|
2
|
+
|
3
|
+
* Added `cluster.health wait_for_status: 'yellow'` to Repository integration test
|
4
|
+
* Fixed tests for the updates to the `update` method for Persistence::Model
|
5
|
+
* Fixed timestamp tests
|
6
|
+
* Fixed typos and broken links in documentation, fixed examples
|
7
|
+
* Fixed, that `MyModel#save` does in fact persist `updated_at` attribute
|
8
|
+
* Fixed, that `options` have not been passed to gateway in MyModel#update
|
9
|
+
* Short-circuit the operation and return `false` when the model is not valid
|
10
|
+
* Fixed the problem where `document_type` configuration was not propagated to mapping
|
11
|
+
|
12
|
+
|
13
|
+
## 0.1.7
|
2
14
|
|
3
15
|
* Added an integration test for the `MyModel.all` method
|
4
16
|
* Improved the "music" example application
|
data/README.md
CHANGED
@@ -23,12 +23,18 @@ or install it from a source code checkout:
|
|
23
23
|
|
24
24
|
## Usage
|
25
25
|
|
26
|
+
The library provides two different patterns for adding persistence to your Ruby objects:
|
27
|
+
|
28
|
+
* [Repository Pattern](#the-repository-pattern)
|
29
|
+
* [ActiveRecord Pattern](#the-activerecord-pattern)
|
30
|
+
|
26
31
|
### The Repository Pattern
|
27
32
|
|
28
33
|
The `Elasticsearch::Persistence::Repository` module provides an implementation of the
|
29
34
|
[repository pattern](http://martinfowler.com/eaaCatalog/repository.html) and allows
|
30
35
|
to save, delete, find and search objects stored in Elasticsearch, as well as configure
|
31
|
-
mappings and settings for the index.
|
36
|
+
mappings and settings for the index. It's an unobtrusive and decoupled way of adding
|
37
|
+
persistence to your Ruby objects.
|
32
38
|
|
33
39
|
Let's have a simple plain old Ruby object (PORO):
|
34
40
|
|
@@ -92,7 +98,7 @@ repository.delete(note)
|
|
92
98
|
=> {"found"=>true, "_index"=>"repository", "_type"=>"note", "_id"=>"1", "_version"=>2}
|
93
99
|
```
|
94
100
|
|
95
|
-
The repository module provides a number of features and facilities to configure and customize the
|
101
|
+
The repository module provides a number of features and facilities to configure and customize the behavior:
|
96
102
|
|
97
103
|
* Configuring the Elasticsearch [client](https://github.com/elasticsearch/elasticsearch-ruby#usage) being used
|
98
104
|
* Setting the index name, document type, and object class for deserialization
|
@@ -120,7 +126,7 @@ repository = Elasticsearch::Persistence::Repository.new do
|
|
120
126
|
# Set a custom document type
|
121
127
|
type :my_note
|
122
128
|
|
123
|
-
# Specify the class to
|
129
|
+
# Specify the class to initialize when deserializing documents
|
124
130
|
klass Note
|
125
131
|
|
126
132
|
# Configure the settings and mappings for the Elasticsearch index
|
@@ -488,7 +494,7 @@ class Article
|
|
488
494
|
|
489
495
|
# Execute code after saving the model.
|
490
496
|
#
|
491
|
-
after_save { puts "
|
497
|
+
after_save { puts "Successfully saved: #{self}" }
|
492
498
|
end
|
493
499
|
```
|
494
500
|
|
@@ -567,7 +573,7 @@ Any callbacks defined in the model will be triggered during the persistence oper
|
|
567
573
|
|
568
574
|
```ruby
|
569
575
|
article.save
|
570
|
-
#
|
576
|
+
# Successfully saved: #<Article {...}>
|
571
577
|
```
|
572
578
|
|
573
579
|
The model also supports familiar `find_in_batches` and `find_each` methods to efficiently
|
data/examples/music/album.rb
CHANGED
@@ -71,7 +71,6 @@ module Elasticsearch
|
|
71
71
|
delegate :settings,
|
72
72
|
:mappings,
|
73
73
|
:mapping,
|
74
|
-
:document_type,
|
75
74
|
:document_type=,
|
76
75
|
:index_name,
|
77
76
|
:index_name=,
|
@@ -80,6 +79,13 @@ module Elasticsearch
|
|
80
79
|
:create_index!,
|
81
80
|
:refresh_index!,
|
82
81
|
to: :gateway
|
82
|
+
|
83
|
+
# forward document type to mappings when set
|
84
|
+
def document_type(type = nil)
|
85
|
+
return gateway.document_type unless type
|
86
|
+
gateway.document_type type
|
87
|
+
mapping.type = type
|
88
|
+
end
|
83
89
|
end
|
84
90
|
|
85
91
|
# Configure the repository based on the model (set up index_name, etc)
|
@@ -115,8 +121,8 @@ module Elasticsearch
|
|
115
121
|
|
116
122
|
# Set up common attributes
|
117
123
|
#
|
118
|
-
attribute :created_at,
|
119
|
-
attribute :updated_at,
|
124
|
+
attribute :created_at, Time, default: lambda { |o,a| Time.now.utc }
|
125
|
+
attribute :updated_at, Time, default: lambda { |o,a| Time.now.utc }
|
120
126
|
|
121
127
|
attr_reader :hit
|
122
128
|
end
|
@@ -53,10 +53,10 @@ module Elasticsearch
|
|
53
53
|
options.update index: self._index if self._index
|
54
54
|
options.update type: self._type if self._type
|
55
55
|
|
56
|
-
response = self.class.gateway.save(self, options)
|
57
|
-
|
58
56
|
self[:updated_at] = Time.now.utc
|
59
57
|
|
58
|
+
response = self.class.gateway.save(self, options)
|
59
|
+
|
60
60
|
@_id = response['_id']
|
61
61
|
@_index = response['_index']
|
62
62
|
@_type = response['_type']
|
@@ -99,9 +99,22 @@ module Elasticsearch
|
|
99
99
|
# p.update name: 'UPDATED'
|
100
100
|
# => {"_index"=>"people", ... "_version"=>2}
|
101
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
|
+
#
|
102
112
|
# @return [Hash] The Elasticsearch response as a Hash
|
103
113
|
#
|
104
114
|
def update(attributes={}, options={})
|
115
|
+
unless options.delete(:validate) == false
|
116
|
+
return false unless valid?
|
117
|
+
end
|
105
118
|
raise DocumentNotPersisted, "Object not persisted: #{self.inspect}" unless persisted?
|
106
119
|
|
107
120
|
run_callbacks :update do
|
@@ -109,7 +122,6 @@ module Elasticsearch
|
|
109
122
|
options.update type: self._type if self._type
|
110
123
|
|
111
124
|
attributes.update( { updated_at: Time.now.utc } )
|
112
|
-
|
113
125
|
response = self.class.gateway.update(self.id, { doc: attributes}.merge(options))
|
114
126
|
|
115
127
|
self.attributes = self.attributes.merge(attributes)
|
@@ -12,6 +12,7 @@ module Elasticsearch
|
|
12
12
|
include Elasticsearch::Persistence::Model::Rails
|
13
13
|
|
14
14
|
settings index: { number_of_shards: 1 }
|
15
|
+
document_type 'human_being'
|
15
16
|
|
16
17
|
attribute :name, String,
|
17
18
|
mapping: { fields: {
|
@@ -60,7 +61,7 @@ module Elasticsearch
|
|
60
61
|
assert_equal 'John Smith', document.name
|
61
62
|
assert_equal 'John Smith', Person.find(person.id).name
|
62
63
|
|
63
|
-
assert_not_nil Elasticsearch::Persistence.client.get index: 'people', type: '
|
64
|
+
assert_not_nil Elasticsearch::Persistence.client.get index: 'people', type: 'human_being', id: person.id
|
64
65
|
end
|
65
66
|
|
66
67
|
should "not save an invalid object" do
|
@@ -143,6 +144,30 @@ module Elasticsearch
|
|
143
144
|
assert found.updated_at > updated_at, [found.updated_at, updated_at].inspect
|
144
145
|
end
|
145
146
|
|
147
|
+
should 'update the object timestamp on save' do
|
148
|
+
person = Person.create name: 'John Smith'
|
149
|
+
person.admin = true
|
150
|
+
sleep 1
|
151
|
+
person.save
|
152
|
+
|
153
|
+
Person.gateway.refresh_index!
|
154
|
+
|
155
|
+
found = Person.find(person.id)
|
156
|
+
|
157
|
+
# Compare without milliseconds
|
158
|
+
assert_equal person.updated_at.to_i, found.updated_at.to_i
|
159
|
+
end
|
160
|
+
|
161
|
+
should "respect the version" do
|
162
|
+
person = Person.create name: 'John Smith'
|
163
|
+
|
164
|
+
person.update( { name: 'UPDATE 1' })
|
165
|
+
|
166
|
+
assert_raise Elasticsearch::Transport::Transport::Errors::Conflict do
|
167
|
+
person.update( { name: 'UPDATE 2' }, { version: 1 } )
|
168
|
+
end
|
169
|
+
end
|
170
|
+
|
146
171
|
should "find all instances" do
|
147
172
|
Person.create name: 'John Smith'
|
148
173
|
Person.create name: 'Mary Smith'
|
@@ -73,6 +73,8 @@ module Elasticsearch
|
|
73
73
|
note = Note.new(id: '1', title: 'Test')
|
74
74
|
@repository.save note, routing: 'ABC'
|
75
75
|
|
76
|
+
@repository.client.cluster.health level: 'indices', wait_for_status: 'yellow'
|
77
|
+
|
76
78
|
assert_raise Elasticsearch::Persistence::Repository::DocumentNotFound do
|
77
79
|
@repository.find(1, routing: 'DEF')
|
78
80
|
end
|
@@ -20,29 +20,53 @@ class Elasticsearch::Persistence::ModelBaseTest < Test::Unit::TestCase
|
|
20
20
|
end
|
21
21
|
|
22
22
|
should "set the ID from attributes during initialization" do
|
23
|
-
|
24
|
-
assert_equal 1,
|
23
|
+
model = DummyBaseModel.new id: 1
|
24
|
+
assert_equal 1, model.id
|
25
25
|
|
26
|
-
|
27
|
-
assert_equal 2,
|
26
|
+
model = DummyBaseModel.new 'id' => 2
|
27
|
+
assert_equal 2, model.id
|
28
28
|
end
|
29
29
|
|
30
30
|
should "set the ID using setter method" do
|
31
|
-
|
32
|
-
assert_equal 1,
|
31
|
+
model = DummyBaseModel.new id: 1
|
32
|
+
assert_equal 1, model.id
|
33
33
|
|
34
|
-
|
35
|
-
assert_equal 2,
|
34
|
+
model.id = 2
|
35
|
+
assert_equal 2, model.id
|
36
36
|
end
|
37
37
|
|
38
38
|
should "have ID in attributes" do
|
39
|
-
|
40
|
-
assert_equal 1,
|
39
|
+
model = DummyBaseModel.new id: 1, name: 'Test'
|
40
|
+
assert_equal 1, model.attributes[:id]
|
41
41
|
end
|
42
42
|
|
43
43
|
should "have the customized inspect method" do
|
44
|
-
|
45
|
-
assert_match /name\: "Test"/,
|
44
|
+
model = DummyBaseModel.new name: 'Test'
|
45
|
+
assert_match /name\: "Test"/, model.inspect
|
46
|
+
end
|
47
|
+
|
48
|
+
context "with custom document_type" do
|
49
|
+
setup do
|
50
|
+
@model = DummyBaseModel
|
51
|
+
@gateway = mock()
|
52
|
+
@mapping = mock()
|
53
|
+
@model.stubs(:gateway).returns(@gateway)
|
54
|
+
@gateway.stubs(:mapping).returns(@mapping)
|
55
|
+
@document_type = 'dummybase'
|
56
|
+
end
|
57
|
+
|
58
|
+
should "forward the argument to mapping" do
|
59
|
+
@gateway.expects(:document_type).with(@document_type).once
|
60
|
+
@mapping.expects(:type=).with(@document_type).once
|
61
|
+
@model.document_type @document_type
|
62
|
+
end
|
63
|
+
|
64
|
+
should "return the value from the gateway" do
|
65
|
+
@gateway.expects(:document_type).once.returns(@document_type)
|
66
|
+
@mapping.expects(:type=).never
|
67
|
+
returned_type = @model.document_type
|
68
|
+
assert_equal @document_type, returned_type
|
69
|
+
end
|
46
70
|
end
|
47
71
|
end
|
48
72
|
end
|
@@ -91,5 +91,22 @@ class Elasticsearch::Persistence::ModelRailsTest < Test::Unit::TestCase
|
|
91
91
|
assert_equal "2014-01-01", m.published_on.iso8601
|
92
92
|
end
|
93
93
|
|
94
|
+
context "when updating," do
|
95
|
+
should "pass the options to gateway" do
|
96
|
+
model = MyRailsModel.new name: 'Test'
|
97
|
+
model.stubs(:persisted?).returns(true)
|
98
|
+
|
99
|
+
model.class.gateway
|
100
|
+
.expects(:update)
|
101
|
+
.with do |object, options|
|
102
|
+
assert_equal 'ABC', options[:routing]
|
103
|
+
true
|
104
|
+
end
|
105
|
+
.returns({'_id' => 'abc123'})
|
106
|
+
|
107
|
+
assert model.update( { title: 'UPDATED' }, { routing: 'ABC' } )
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
94
111
|
end
|
95
112
|
end
|
@@ -311,6 +311,40 @@ class Elasticsearch::Persistence::ModelStoreTest < Test::Unit::TestCase
|
|
311
311
|
assert subject.update( {}, { script: 'EXEC' } )
|
312
312
|
end
|
313
313
|
|
314
|
+
should "not update an invalid model" do
|
315
|
+
@gateway
|
316
|
+
.expects(:update)
|
317
|
+
.never
|
318
|
+
|
319
|
+
subject.instance_eval do
|
320
|
+
def valid?; false; end;
|
321
|
+
end
|
322
|
+
|
323
|
+
assert ! subject.update(title: 'INVALID')
|
324
|
+
end
|
325
|
+
|
326
|
+
should "skip the validation with the :validate option" do
|
327
|
+
subject.expects(:persisted?).returns(true).at_least_once
|
328
|
+
subject.expects(:id).returns('abc123').at_least_once
|
329
|
+
|
330
|
+
@gateway
|
331
|
+
.expects(:update)
|
332
|
+
.with do |object, options|
|
333
|
+
assert_equal 'abc123', object
|
334
|
+
assert_equal nil, options[:id]
|
335
|
+
assert_equal 'INVALID', options[:doc][:title]
|
336
|
+
true
|
337
|
+
end
|
338
|
+
.returns({'_id' => 'abc123'})
|
339
|
+
|
340
|
+
subject.instance_eval do
|
341
|
+
def valid?; false; end;
|
342
|
+
end
|
343
|
+
|
344
|
+
assert subject.update( { title: 'INVALID' }, { validate: false } )
|
345
|
+
assert subject.persisted?
|
346
|
+
end
|
347
|
+
|
314
348
|
should "pass the options to gateway" do
|
315
349
|
subject.expects(:persisted?).returns(true)
|
316
350
|
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: elasticsearch-persistence
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.8
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Karel Minarik
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2015-
|
11
|
+
date: 2015-10-12 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: elasticsearch
|