elasticsearch-persistence 0.0.0 → 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +15 -0
- data/LICENSE.txt +10 -19
- data/README.md +432 -14
- data/Rakefile +56 -0
- data/elasticsearch-persistence.gemspec +45 -17
- data/examples/sinatra/.gitignore +7 -0
- data/examples/sinatra/Gemfile +28 -0
- data/examples/sinatra/README.markdown +36 -0
- data/examples/sinatra/application.rb +238 -0
- data/examples/sinatra/config.ru +7 -0
- data/examples/sinatra/test.rb +118 -0
- data/lib/elasticsearch/persistence.rb +88 -2
- data/lib/elasticsearch/persistence/client.rb +51 -0
- data/lib/elasticsearch/persistence/repository.rb +75 -0
- data/lib/elasticsearch/persistence/repository/class.rb +71 -0
- data/lib/elasticsearch/persistence/repository/find.rb +73 -0
- data/lib/elasticsearch/persistence/repository/naming.rb +115 -0
- data/lib/elasticsearch/persistence/repository/response/results.rb +90 -0
- data/lib/elasticsearch/persistence/repository/search.rb +60 -0
- data/lib/elasticsearch/persistence/repository/serialize.rb +31 -0
- data/lib/elasticsearch/persistence/repository/store.rb +95 -0
- data/lib/elasticsearch/persistence/version.rb +1 -1
- data/test/integration/repository/custom_class_test.rb +85 -0
- data/test/integration/repository/customized_class_test.rb +82 -0
- data/test/integration/repository/default_class_test.rb +108 -0
- data/test/integration/repository/virtus_model_test.rb +114 -0
- data/test/test_helper.rb +46 -0
- data/test/unit/persistence_test.rb +32 -0
- data/test/unit/repository_class_test.rb +51 -0
- data/test/unit/repository_client_test.rb +32 -0
- data/test/unit/repository_find_test.rb +375 -0
- data/test/unit/repository_indexing_test.rb +37 -0
- data/test/unit/repository_module_test.rb +144 -0
- data/test/unit/repository_naming_test.rb +146 -0
- data/test/unit/repository_response_results_test.rb +98 -0
- data/test/unit/repository_search_test.rb +97 -0
- data/test/unit/repository_serialize_test.rb +57 -0
- data/test/unit/repository_store_test.rb +287 -0
- metadata +288 -20
@@ -0,0 +1,82 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
module Elasticsearch
|
4
|
+
module Persistence
|
5
|
+
class RepositoryCustomizedClassIntegrationTest < Elasticsearch::Test::IntegrationTestCase
|
6
|
+
|
7
|
+
module ::My
|
8
|
+
class Note
|
9
|
+
attr_reader :attributes
|
10
|
+
|
11
|
+
def initialize(attributes={})
|
12
|
+
@attributes = attributes
|
13
|
+
end
|
14
|
+
|
15
|
+
def to_hash
|
16
|
+
@attributes
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
context "A custom repository class" do
|
22
|
+
setup do
|
23
|
+
@repository = Elasticsearch::Persistence::Repository.new do
|
24
|
+
index 'my_notes'
|
25
|
+
type 'my_note'
|
26
|
+
klass My::Note
|
27
|
+
|
28
|
+
settings number_of_shards: 1 do
|
29
|
+
mapping do
|
30
|
+
indexes :title, analyzer: 'snowball'
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
create_index!
|
35
|
+
end
|
36
|
+
|
37
|
+
@repository.client.cluster.health wait_for_status: 'yellow'
|
38
|
+
end
|
39
|
+
|
40
|
+
should "save the object under a correct index and type" do
|
41
|
+
@repository.save My::Note.new(id: '1', title: 'Test')
|
42
|
+
|
43
|
+
assert_instance_of My::Note, @repository.find(1)
|
44
|
+
assert_not_nil Elasticsearch::Persistence.client.get index: 'my_notes', type: 'my_note', id: '1'
|
45
|
+
end
|
46
|
+
|
47
|
+
should "update the document" do
|
48
|
+
@repository.save Note.new(id: 1, title: 'Test')
|
49
|
+
|
50
|
+
@repository.update 1, doc: { title: 'UPDATED' }
|
51
|
+
assert_equal 'UPDATED', @repository.find(1).attributes['title']
|
52
|
+
end
|
53
|
+
|
54
|
+
should "update the document with a script" do
|
55
|
+
@repository.save Note.new(id: 1, title: 'Test')
|
56
|
+
|
57
|
+
@repository.update 1, script: 'ctx._source.title = "UPDATED"'
|
58
|
+
assert_equal 'UPDATED', @repository.find(1).attributes['title']
|
59
|
+
end
|
60
|
+
|
61
|
+
should "delete the object" do
|
62
|
+
note = My::Note.new id: 1, title: 'Test'
|
63
|
+
@repository.save note
|
64
|
+
|
65
|
+
assert_not_nil @repository.find(1)
|
66
|
+
|
67
|
+
@repository.delete(note)
|
68
|
+
assert_raise(Elasticsearch::Persistence::Repository::DocumentNotFound) { @repository.find(1) }
|
69
|
+
end
|
70
|
+
|
71
|
+
should "create the index with correct mapping" do
|
72
|
+
note = My::Note.new title: 'Testing'
|
73
|
+
@repository.save note, refresh: true
|
74
|
+
|
75
|
+
results = @repository.search query: { match: { title: 'Test' } }
|
76
|
+
assert_equal 'Testing', results.first.attributes['title']
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
@@ -0,0 +1,108 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
module Elasticsearch
|
4
|
+
module Persistence
|
5
|
+
class RepositoryDefaultClassIntegrationTest < Elasticsearch::Test::IntegrationTestCase
|
6
|
+
|
7
|
+
class ::Note
|
8
|
+
attr_reader :attributes
|
9
|
+
|
10
|
+
def initialize(attributes={})
|
11
|
+
@attributes = attributes
|
12
|
+
end
|
13
|
+
|
14
|
+
def to_hash
|
15
|
+
@attributes
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
context "The default repository class" do
|
20
|
+
setup do
|
21
|
+
@repository = Elasticsearch::Persistence::Repository.new
|
22
|
+
@repository.client.cluster.health wait_for_status: 'yellow'
|
23
|
+
end
|
24
|
+
|
25
|
+
should "save the object with a custom ID and find it" do
|
26
|
+
@repository.save Note.new(id: '1', title: 'Test')
|
27
|
+
|
28
|
+
assert_equal 'Test', @repository.find(1).attributes['title']
|
29
|
+
end
|
30
|
+
|
31
|
+
should "save the object with an auto-generated ID and find it" do
|
32
|
+
response = @repository.save Note.new(title: 'Test')
|
33
|
+
|
34
|
+
assert_equal 'Test', @repository.find(response['_id']).attributes['title']
|
35
|
+
end
|
36
|
+
|
37
|
+
should "update the document" do
|
38
|
+
@repository.save Note.new(id: 1, title: 'Test')
|
39
|
+
|
40
|
+
@repository.update 1, type: 'note', doc: { title: 'UPDATED' }
|
41
|
+
assert_equal 'UPDATED', @repository.find(1).attributes['title']
|
42
|
+
end
|
43
|
+
|
44
|
+
should "update the document with a script" do
|
45
|
+
@repository.save Note.new(id: 1, title: 'Test')
|
46
|
+
|
47
|
+
@repository.update 1, type: 'note', script: 'ctx._source.title = "UPDATED"'
|
48
|
+
assert_equal 'UPDATED', @repository.find(1).attributes['title']
|
49
|
+
end
|
50
|
+
|
51
|
+
should "save the document with an upsert" do
|
52
|
+
@repository.update 1, type: 'note', script: 'ctx._source.clicks += 1', upsert: { clicks: 1 }
|
53
|
+
assert_equal 1, @repository.find(1).attributes['clicks']
|
54
|
+
end
|
55
|
+
|
56
|
+
should "delete an object" do
|
57
|
+
note = Note.new(id: '1', title: 'Test')
|
58
|
+
|
59
|
+
@repository.save(note)
|
60
|
+
assert_not_nil @repository.find(1)
|
61
|
+
@repository.delete(note)
|
62
|
+
assert_raise(Elasticsearch::Persistence::Repository::DocumentNotFound) { @repository.find(1) }
|
63
|
+
end
|
64
|
+
|
65
|
+
should "find multiple objects" do
|
66
|
+
(1..5).each { |i| @repository.save Note.new(id: i, title: "Test #{i}") }
|
67
|
+
|
68
|
+
assert_equal 5, @repository.find(1, 2, 3, 4, 5).size
|
69
|
+
assert_equal 5, @repository.find([1, 2, 3, 4, 5]).size
|
70
|
+
end
|
71
|
+
|
72
|
+
should "pass options to save and find" do
|
73
|
+
note = Note.new(id: '1', title: 'Test')
|
74
|
+
@repository.save note, routing: 'ABC'
|
75
|
+
|
76
|
+
assert_raise Elasticsearch::Persistence::Repository::DocumentNotFound do
|
77
|
+
@repository.find(1, routing: 'DEF')
|
78
|
+
end
|
79
|
+
|
80
|
+
assert_nothing_raised do
|
81
|
+
note = @repository.find(1, routing: 'ABC')
|
82
|
+
assert_instance_of Note, note
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
should "find notes with full text search" do
|
87
|
+
@repository.save Note.new(title: 'Test')
|
88
|
+
@repository.save Note.new(title: 'Test Test')
|
89
|
+
@repository.save Note.new(title: 'Crust')
|
90
|
+
@repository.client.indices.refresh index: @repository.index_name
|
91
|
+
|
92
|
+
results = @repository.search 'test'
|
93
|
+
assert_equal 2, results.size
|
94
|
+
|
95
|
+
results = @repository.search query: { match: { title: 'Test' } }
|
96
|
+
assert_equal 2, results.size
|
97
|
+
end
|
98
|
+
|
99
|
+
should "save and find a plain hash" do
|
100
|
+
@repository.save id: 1, title: 'Hash'
|
101
|
+
result = @repository.find(1)
|
102
|
+
assert_equal 'Hash', result['_source']['title']
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
@@ -0,0 +1,114 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
require 'virtus'
|
4
|
+
|
5
|
+
module Elasticsearch
|
6
|
+
module Persistence
|
7
|
+
class RepositoryWithVirtusIntegrationTest < Elasticsearch::Test::IntegrationTestCase
|
8
|
+
|
9
|
+
class ::Page
|
10
|
+
include Virtus.model
|
11
|
+
|
12
|
+
attribute :id, String, writer: :private
|
13
|
+
attribute :title, String
|
14
|
+
attribute :views, Integer, default: 0
|
15
|
+
attribute :published, Boolean, default: false
|
16
|
+
attribute :slug, String, default: lambda { |page, attr| page.title.downcase.gsub(' ', '-') }
|
17
|
+
|
18
|
+
def set_id(id)
|
19
|
+
self.id = id
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
context "The repository with a Virtus model" do
|
24
|
+
setup do
|
25
|
+
@repository = Elasticsearch::Persistence::Repository.new do
|
26
|
+
index :pages
|
27
|
+
klass Page
|
28
|
+
|
29
|
+
def deserialize(document)
|
30
|
+
page = klass.new document['_source']
|
31
|
+
page.set_id document['_id']
|
32
|
+
page
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
should "save and find the object" do
|
38
|
+
page = Page.new title: 'Test Page'
|
39
|
+
|
40
|
+
response = @repository.save page
|
41
|
+
id = response['_id']
|
42
|
+
|
43
|
+
result = @repository.find(id)
|
44
|
+
|
45
|
+
assert_instance_of Page, result
|
46
|
+
assert_equal 'Test Page', result.title
|
47
|
+
assert_equal 0, result.views
|
48
|
+
|
49
|
+
assert_not_nil Elasticsearch::Persistence.client.get index: 'pages',
|
50
|
+
type: 'page',
|
51
|
+
id: id
|
52
|
+
end
|
53
|
+
|
54
|
+
should "update the object with a partial document" do
|
55
|
+
response = @repository.save Page.new(title: 'Test')
|
56
|
+
id = response['_id']
|
57
|
+
|
58
|
+
page = @repository.find(id)
|
59
|
+
|
60
|
+
assert_equal 'Test', page.title
|
61
|
+
|
62
|
+
@repository.update page.id, doc: { title: 'UPDATE' }
|
63
|
+
|
64
|
+
page = @repository.find(id)
|
65
|
+
assert_equal 'UPDATE', page.title
|
66
|
+
end
|
67
|
+
|
68
|
+
should "update the object with a Hash" do
|
69
|
+
response = @repository.save Page.new(title: 'Test')
|
70
|
+
id = response['_id']
|
71
|
+
|
72
|
+
page = @repository.find(id)
|
73
|
+
|
74
|
+
assert_equal 'Test', page.title
|
75
|
+
|
76
|
+
@repository.update id: page.id, title: 'UPDATE'
|
77
|
+
|
78
|
+
page = @repository.find(id)
|
79
|
+
assert_equal 'UPDATE', page.title
|
80
|
+
end
|
81
|
+
|
82
|
+
should "update the object with a script" do
|
83
|
+
response = @repository.save Page.new(title: 'Test Page')
|
84
|
+
id = response['_id']
|
85
|
+
|
86
|
+
page = @repository.find(id)
|
87
|
+
|
88
|
+
assert_not_nil page.id
|
89
|
+
assert_equal 0, page.views
|
90
|
+
|
91
|
+
@repository.update page.id, script: 'ctx._source.views += 1'
|
92
|
+
|
93
|
+
page = @repository.find(id)
|
94
|
+
assert_equal 1, page.views
|
95
|
+
|
96
|
+
@repository.update id: page.id, script: 'ctx._source.views += 1'
|
97
|
+
|
98
|
+
page = @repository.find(id)
|
99
|
+
assert_equal 2, page.views
|
100
|
+
end
|
101
|
+
|
102
|
+
should "update the object with a script and params" do
|
103
|
+
response = @repository.save Page.new(title: 'Test Page')
|
104
|
+
|
105
|
+
@repository.update id: response['_id'], script: 'ctx._source.views += count', params: { count: 3 }
|
106
|
+
|
107
|
+
page = @repository.find(response['_id'])
|
108
|
+
assert_equal 3, page.views
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
end
|
113
|
+
end
|
114
|
+
end
|
data/test/test_helper.rb
ADDED
@@ -0,0 +1,46 @@
|
|
1
|
+
RUBY_1_8 = defined?(RUBY_VERSION) && RUBY_VERSION < '1.9'
|
2
|
+
|
3
|
+
exit(0) if RUBY_1_8
|
4
|
+
|
5
|
+
require 'simplecov' and SimpleCov.start { add_filter "/test|test_/" } if ENV["COVERAGE"]
|
6
|
+
|
7
|
+
# Register `at_exit` handler for integration tests shutdown.
|
8
|
+
# MUST be called before requiring `test/unit`.
|
9
|
+
at_exit { Elasticsearch::Test::IntegrationTestCase.__run_at_exit_hooks } if ENV['SERVER']
|
10
|
+
|
11
|
+
require 'test/unit'
|
12
|
+
require 'shoulda-context'
|
13
|
+
require 'mocha/setup'
|
14
|
+
require 'turn' unless ENV["TM_FILEPATH"] || ENV["NOTURN"] || RUBY_1_8
|
15
|
+
|
16
|
+
require 'ansi'
|
17
|
+
require 'oj'
|
18
|
+
|
19
|
+
require 'elasticsearch/extensions/test/cluster'
|
20
|
+
require 'elasticsearch/extensions/test/startup_shutdown'
|
21
|
+
|
22
|
+
require 'elasticsearch/persistence'
|
23
|
+
|
24
|
+
module Elasticsearch
|
25
|
+
module Test
|
26
|
+
class IntegrationTestCase < ::Test::Unit::TestCase
|
27
|
+
extend Elasticsearch::Extensions::Test::StartupShutdown
|
28
|
+
|
29
|
+
startup { Elasticsearch::Extensions::Test::Cluster.start(nodes: 1) if ENV['SERVER'] and not Elasticsearch::Extensions::Test::Cluster.running? }
|
30
|
+
shutdown { Elasticsearch::Extensions::Test::Cluster.stop if ENV['SERVER'] && started? }
|
31
|
+
context "IntegrationTest" do; should "noop on Ruby 1.8" do; end; end if RUBY_1_8
|
32
|
+
|
33
|
+
def setup
|
34
|
+
tracer = ::Logger.new(STDERR)
|
35
|
+
tracer.formatter = lambda { |s, d, p, m| "#{m.gsub(/^.*$/) { |n| ' ' + n }.ansi(:faint)}\n" }
|
36
|
+
Elasticsearch::Persistence.client = Elasticsearch::Client.new \
|
37
|
+
host: "localhost:#{(ENV['TEST_CLUSTER_PORT'] || 9250)}",
|
38
|
+
tracer: (ENV['QUIET'] ? nil : tracer)
|
39
|
+
end
|
40
|
+
|
41
|
+
def teardown
|
42
|
+
Elasticsearch::Persistence.client.indices.delete index: '_all'
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
class Elasticsearch::Persistence::ModuleTest < Test::Unit::TestCase
|
4
|
+
context "The Persistence module" do
|
5
|
+
|
6
|
+
context "client" do
|
7
|
+
should "have a default client" do
|
8
|
+
client = Elasticsearch::Persistence.client
|
9
|
+
assert_not_nil client
|
10
|
+
assert_instance_of Elasticsearch::Transport::Client, client
|
11
|
+
end
|
12
|
+
|
13
|
+
should "allow to set a client" do
|
14
|
+
begin
|
15
|
+
Elasticsearch::Persistence.client = "Foobar"
|
16
|
+
assert_equal "Foobar", Elasticsearch::Persistence.client
|
17
|
+
ensure
|
18
|
+
Elasticsearch::Persistence.client = nil
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
should "allow to set a client with DSL" do
|
23
|
+
begin
|
24
|
+
Elasticsearch::Persistence.client "Foobar"
|
25
|
+
assert_equal "Foobar", Elasticsearch::Persistence.client
|
26
|
+
ensure
|
27
|
+
Elasticsearch::Persistence.client = nil
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
class Elasticsearch::Persistence::RepositoryClassTest < Test::Unit::TestCase
|
4
|
+
context "The default repository class" do
|
5
|
+
|
6
|
+
context "when initialized" do
|
7
|
+
should "be created from the module" do
|
8
|
+
repository = Elasticsearch::Persistence::Repository.new
|
9
|
+
assert_instance_of Elasticsearch::Persistence::Repository::Class, repository
|
10
|
+
end
|
11
|
+
|
12
|
+
should "store and access the options" do
|
13
|
+
repository = Elasticsearch::Persistence::Repository::Class.new foo: 'bar'
|
14
|
+
assert_equal 'bar', repository.options[:foo]
|
15
|
+
end
|
16
|
+
|
17
|
+
should "instance eval a passed block" do
|
18
|
+
$foo = 100
|
19
|
+
repository = Elasticsearch::Persistence::Repository::Class.new() { $foo += 1 }
|
20
|
+
assert_equal 101, $foo
|
21
|
+
end
|
22
|
+
|
23
|
+
should "call a passed block with self" do
|
24
|
+
foo = 100
|
25
|
+
repository = Elasticsearch::Persistence::Repository::Class.new do |r|
|
26
|
+
assert_instance_of Elasticsearch::Persistence::Repository::Class, r
|
27
|
+
foo += 1
|
28
|
+
end
|
29
|
+
assert_equal 101, foo
|
30
|
+
end
|
31
|
+
|
32
|
+
should "configure the index name based on options" do
|
33
|
+
repository = Elasticsearch::Persistence::Repository::Class.new index: 'foobar'
|
34
|
+
assert_equal 'foobar', repository.index_name
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
should "include the repository methods" do
|
39
|
+
repository = Elasticsearch::Persistence::Repository::Class.new
|
40
|
+
|
41
|
+
%w( index_name document_type klass
|
42
|
+
mappings settings client client=
|
43
|
+
create_index! delete_index! refresh_index!
|
44
|
+
save delete serialize deserialize
|
45
|
+
exists? find search ).each do |method|
|
46
|
+
assert_respond_to repository, method
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
end
|
51
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
class Elasticsearch::Persistence::RepositoryClientTest < Test::Unit::TestCase
|
4
|
+
context "The repository client" do
|
5
|
+
setup do
|
6
|
+
@shoulda_subject = Class.new() { include Elasticsearch::Persistence::Repository::Client }.new
|
7
|
+
end
|
8
|
+
|
9
|
+
should "have a default client" do
|
10
|
+
assert_not_nil subject.client
|
11
|
+
assert_instance_of Elasticsearch::Transport::Client, subject.client
|
12
|
+
end
|
13
|
+
|
14
|
+
should "allow to set a client" do
|
15
|
+
begin
|
16
|
+
subject.client = "Foobar"
|
17
|
+
assert_equal "Foobar", subject.client
|
18
|
+
ensure
|
19
|
+
subject.client = nil
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
should "allow to set the client with DSL" do
|
24
|
+
begin
|
25
|
+
subject.client "Foobar"
|
26
|
+
assert_equal "Foobar", subject.client
|
27
|
+
ensure
|
28
|
+
subject.client = nil
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|