elastic_record 2.0.2 → 3.0.0
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 +4 -4
- data/.gitignore +1 -0
- data/.travis.yml +15 -9
- data/Gemfile +5 -4
- data/README.md +214 -0
- data/elastic_record.gemspec +7 -7
- data/lib/elastic_record.rb +1 -0
- data/lib/elastic_record/callbacks.rb +46 -14
- data/lib/elastic_record/config.rb +1 -21
- data/lib/elastic_record/connection.rb +24 -14
- data/lib/elastic_record/errors.rb +5 -0
- data/lib/elastic_record/index.rb +11 -1
- data/lib/elastic_record/index/deferred.rb +1 -0
- data/lib/elastic_record/index/documents.rb +95 -18
- data/lib/elastic_record/index/manage.rb +0 -8
- data/lib/elastic_record/index/mapping.rb +1 -10
- data/lib/elastic_record/json.rb +29 -0
- data/lib/elastic_record/relation.rb +9 -5
- data/lib/elastic_record/relation/batches.rb +4 -40
- data/lib/elastic_record/relation/none.rb +0 -4
- data/lib/elastic_record/relation/search_methods.rb +48 -38
- data/lib/elastic_record/relation/value_methods.rb +2 -2
- data/lib/elastic_record/tasks/index.rake +2 -2
- data/test/dummy/.env.example +1 -0
- data/test/dummy/.env.test +1 -0
- data/test/dummy/app/models/project.rb +1 -1
- data/test/dummy/app/models/test_model.rb +3 -2
- data/test/dummy/app/models/widget.rb +3 -3
- data/test/dummy/config/initializers/elastic_record.rb +1 -1
- data/test/dummy/db/migrate/20151211225259_create_projects.rb +7 -0
- data/test/dummy/db/schema.rb +8 -1
- data/test/elastic_record/callbacks_test.rb +16 -2
- data/test/elastic_record/config_test.rb +1 -2
- data/test/elastic_record/connection_test.rb +52 -9
- data/test/elastic_record/index/documents_test.rb +55 -21
- data/test/elastic_record/index/mapping_test.rb +0 -10
- data/test/elastic_record/integration/active_record_test.rb +3 -3
- data/test/elastic_record/log_subscriber_test.rb +4 -4
- data/test/elastic_record/relation/batches_test.rb +5 -24
- data/test/elastic_record/relation/delegation_test.rb +4 -3
- data/test/elastic_record/relation/finder_methods_test.rb +1 -0
- data/test/elastic_record/relation/search_methods_test.rb +47 -45
- data/test/elastic_record/relation_test.rb +18 -10
- data/test/helper.rb +4 -3
- metadata +21 -12
- data/README.rdoc +0 -146
- data/test/dummy/config/database.yml +0 -15
@@ -1,6 +1,6 @@
|
|
1
1
|
module ElasticRecord
|
2
2
|
class Relation
|
3
|
-
MULTI_VALUE_METHODS = [:extending, :
|
4
|
-
SINGLE_VALUE_METHODS = [:query, :limit, :offset, :reverse_order]
|
3
|
+
MULTI_VALUE_METHODS = [:extending, :filter, :order, :select, :aggregation]
|
4
|
+
SINGLE_VALUE_METHODS = [:query, :limit, :offset, :search_type, :reverse_order]
|
5
5
|
end
|
6
6
|
end
|
@@ -33,7 +33,7 @@ namespace :index do
|
|
33
33
|
puts "Dropped #{model.name} index"
|
34
34
|
|
35
35
|
if index.has_percolator
|
36
|
-
|
36
|
+
index.delete_percolator_index
|
37
37
|
puts "Dropped #{model.name} percolator index (#{index.percolator_name})"
|
38
38
|
end
|
39
39
|
end
|
@@ -63,7 +63,7 @@ namespace :index do
|
|
63
63
|
|
64
64
|
puts " Reindexing into #{index_name}"
|
65
65
|
model.find_in_batches(batch_size: 100) do |records|
|
66
|
-
model.elastic_index.bulk_add(records, index_name)
|
66
|
+
model.elastic_index.bulk_add(records, index_name: index_name)
|
67
67
|
end
|
68
68
|
|
69
69
|
puts " Deploying index..."
|
@@ -0,0 +1 @@
|
|
1
|
+
DATABASE_URL=postgres://localhost/elastic_record_development
|
@@ -0,0 +1 @@
|
|
1
|
+
DATABASE_URL=postgres://postgres:@localhost/elastic_record_test
|
@@ -4,7 +4,7 @@ module TestModel
|
|
4
4
|
included do
|
5
5
|
extend ActiveModel::Naming
|
6
6
|
extend ActiveModel::Callbacks
|
7
|
-
define_model_callbacks :save, :destroy
|
7
|
+
define_model_callbacks :save, :update, :create, :destroy
|
8
8
|
|
9
9
|
include ActiveModel::Dirty
|
10
10
|
include ActiveModel::Validations
|
@@ -71,7 +71,8 @@ module TestModel
|
|
71
71
|
|
72
72
|
def save
|
73
73
|
@persisted = true
|
74
|
-
|
74
|
+
callback_method = new_record? ? :create : :update
|
75
|
+
run_callbacks callback_method
|
75
76
|
end
|
76
77
|
|
77
78
|
def destroy
|
@@ -8,17 +8,17 @@ class Widget
|
|
8
8
|
self.elastic_index.has_percolator = true
|
9
9
|
|
10
10
|
self.elastic_index.mapping[:properties].update(
|
11
|
-
name
|
11
|
+
'name' => {
|
12
12
|
type: 'multi_field',
|
13
13
|
fields: {
|
14
14
|
name: {type: 'string', index: 'not_analyzed'},
|
15
15
|
analyzed: {type: 'string', index: 'analyzed'}
|
16
16
|
}
|
17
17
|
},
|
18
|
-
color
|
18
|
+
'color' => {
|
19
19
|
type: 'string', index: 'not_analyzed'
|
20
20
|
},
|
21
|
-
warehouse_id
|
21
|
+
'warehouse_id' => {
|
22
22
|
type: 'string', index: 'not_analyzed'
|
23
23
|
}
|
24
24
|
)
|
data/test/dummy/db/schema.rb
CHANGED
@@ -11,6 +11,13 @@
|
|
11
11
|
#
|
12
12
|
# It's strongly recommended that you check this file into your version control system.
|
13
13
|
|
14
|
-
ActiveRecord::Schema.define(version:
|
14
|
+
ActiveRecord::Schema.define(version: 20151211225259) do
|
15
|
+
|
16
|
+
# These are extensions that must be enabled in order to support this database
|
17
|
+
enable_extension "plpgsql"
|
18
|
+
|
19
|
+
create_table "projects", force: :cascade do |t|
|
20
|
+
t.string "name", null: false
|
21
|
+
end
|
15
22
|
|
16
23
|
end
|
@@ -32,7 +32,7 @@ class ElasticRecord::CallbacksTest < MiniTest::Test
|
|
32
32
|
|
33
33
|
def test_as_search
|
34
34
|
Widget.new(id: '10', color: 'green').tap do |widget|
|
35
|
-
assert_equal({color
|
35
|
+
assert_equal({"color" => "green"}, widget.as_search)
|
36
36
|
end
|
37
37
|
|
38
38
|
Widget.new(id: '10', color: '').tap do |widget|
|
@@ -40,7 +40,21 @@ class ElasticRecord::CallbacksTest < MiniTest::Test
|
|
40
40
|
end
|
41
41
|
|
42
42
|
Widget.new(id: '10', color: false).tap do |widget|
|
43
|
-
assert_equal({color
|
43
|
+
assert_equal({"color" => false}, widget.as_search)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
def test_as_dirty_search
|
48
|
+
Widget.new(id: '10', color: 'green').tap do |widget|
|
49
|
+
assert_equal({'color' => 'green'}, widget.as_partial_update_document)
|
50
|
+
end
|
51
|
+
|
52
|
+
Widget.new(id: '10').tap do |widget|
|
53
|
+
assert_equal({}, widget.as_partial_update_document)
|
54
|
+
end
|
55
|
+
|
56
|
+
Widget.new(id: '10', color: '').tap do |widget|
|
57
|
+
assert_equal({'color' => nil}, widget.as_partial_update_document)
|
44
58
|
end
|
45
59
|
end
|
46
60
|
|
@@ -8,7 +8,7 @@ class ElasticRecord::ConfigTest < MiniTest::Test
|
|
8
8
|
def test_models
|
9
9
|
ElasticRecord::Config.model_names = %w(Widget)
|
10
10
|
|
11
|
-
assert_equal [Warehouse, Widget], ElasticRecord::Config.models
|
11
|
+
assert_equal [Warehouse, Widget, Project], ElasticRecord::Config.models
|
12
12
|
end
|
13
13
|
|
14
14
|
def test_servers
|
@@ -21,7 +21,6 @@ class ElasticRecord::ConfigTest < MiniTest::Test
|
|
21
21
|
end
|
22
22
|
end
|
23
23
|
|
24
|
-
|
25
24
|
private
|
26
25
|
|
27
26
|
def with_servers(values)
|
@@ -1,6 +1,14 @@
|
|
1
1
|
require 'helper'
|
2
2
|
|
3
3
|
class ElasticRecord::ConnectionTest < MiniTest::Test
|
4
|
+
def setup
|
5
|
+
ElasticRecord::JSON.parser = :active_support
|
6
|
+
end
|
7
|
+
|
8
|
+
def teardown
|
9
|
+
ElasticRecord::JSON.parser = :active_support
|
10
|
+
end
|
11
|
+
|
4
12
|
def test_servers
|
5
13
|
assert_equal ['foo'], ElasticRecord::Connection.new('foo').servers
|
6
14
|
assert_equal ['foo', 'bar'], ElasticRecord::Connection.new(['foo', 'bar']).servers
|
@@ -12,16 +20,17 @@ class ElasticRecord::ConnectionTest < MiniTest::Test
|
|
12
20
|
end
|
13
21
|
|
14
22
|
def test_head
|
15
|
-
|
23
|
+
stub_es_request(:head, "/success").to_return(status: 200)
|
24
|
+
|
16
25
|
assert_equal "200", connection.head("/success")
|
17
26
|
|
18
|
-
|
27
|
+
stub_es_request(:head, "/failure").to_return(status: 404)
|
19
28
|
assert_equal "404", connection.head("/failure")
|
20
29
|
end
|
21
30
|
|
22
31
|
def test_json_requests
|
23
32
|
expected = {'foo' => 'bar'}
|
24
|
-
|
33
|
+
stub_es_request(:any, "/test").to_return(status: 200, body: ElasticRecord::JSON.encode(expected))
|
25
34
|
|
26
35
|
assert_equal expected, connection.json_delete("/test")
|
27
36
|
assert_equal expected, connection.json_get("/test")
|
@@ -29,9 +38,14 @@ class ElasticRecord::ConnectionTest < MiniTest::Test
|
|
29
38
|
assert_equal expected, connection.json_put("/test")
|
30
39
|
end
|
31
40
|
|
32
|
-
def
|
41
|
+
def test_json_requests_with_oj
|
42
|
+
ElasticRecord::JSON.parser = :oj
|
43
|
+
test_json_requests
|
44
|
+
end
|
45
|
+
|
46
|
+
def test_json_request_with_valid_error_status
|
33
47
|
response_json = {'error' => 'Doing it wrong'}
|
34
|
-
|
48
|
+
stub_es_request(:get, "/error").to_return(status: 404, body: ElasticRecord::JSON.encode(response_json))
|
35
49
|
|
36
50
|
error = assert_raises ElasticRecord::ConnectionError do
|
37
51
|
connection.json_get("/error")
|
@@ -40,26 +54,55 @@ class ElasticRecord::ConnectionTest < MiniTest::Test
|
|
40
54
|
assert_equal 'Doing it wrong', error.message
|
41
55
|
end
|
42
56
|
|
43
|
-
def
|
57
|
+
def test_retry_server_exceptions
|
44
58
|
responses = [
|
45
59
|
{exception: Errno::ECONNREFUSED},
|
46
|
-
{status: ["200", "OK"], body:
|
60
|
+
{status: ["200", "OK"], body: ElasticRecord::JSON.encode('hello' => 'world')}
|
47
61
|
]
|
48
62
|
|
49
63
|
ElasticRecord::Connection.new(ElasticRecord::Config.servers, retries: 0).tap do |connection|
|
50
|
-
|
64
|
+
stub_es_request(:get, "/error").to_return(*responses)
|
51
65
|
assert_raises(Errno::ECONNREFUSED) { connection.json_get("/error") }
|
52
66
|
end
|
53
67
|
|
54
68
|
ElasticRecord::Connection.new(ElasticRecord::Config.servers, retries: 1).tap do |connection|
|
55
|
-
|
69
|
+
stub_es_request(:get, "/error").to_return(*responses)
|
70
|
+
json = connection.json_get("/error")
|
71
|
+
assert_equal({'hello' => 'world'}, json)
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
def test_retry_server_500_errors
|
76
|
+
responses = [
|
77
|
+
{status: ["500", "OK"], body: {'error' => 'temporarily_unavailable'}.to_json},
|
78
|
+
{status: ["200", "OK"], body: {'hello' => 'world'}.to_json}
|
79
|
+
]
|
80
|
+
|
81
|
+
ElasticRecord::Connection.new(ElasticRecord::Config.servers, retries: 0).tap do |connection|
|
82
|
+
stub_es_request(:get, "/error").to_return(*responses)
|
83
|
+
|
84
|
+
error = assert_raises ElasticRecord::ConnectionError do
|
85
|
+
connection.json_get("/error")
|
86
|
+
end
|
87
|
+
|
88
|
+
assert_equal '500', error.status_code
|
89
|
+
assert_equal '{"error":"temporarily_unavailable"}', error.message
|
90
|
+
end
|
91
|
+
|
92
|
+
ElasticRecord::Connection.new(ElasticRecord::Config.servers, retries: 1).tap do |connection|
|
93
|
+
stub_es_request(:get, "/error").to_return(*responses)
|
56
94
|
json = connection.json_get("/error")
|
57
95
|
assert_equal({'hello' => 'world'}, json)
|
58
96
|
end
|
59
97
|
end
|
60
98
|
|
61
99
|
private
|
100
|
+
|
62
101
|
def connection
|
63
102
|
ElasticRecord::Connection.new(ElasticRecord::Config.servers)
|
64
103
|
end
|
104
|
+
|
105
|
+
def stub_es_request(method, path)
|
106
|
+
stub_request(method, "#{connection.servers.first}#{path}")
|
107
|
+
end
|
65
108
|
end
|
@@ -7,12 +7,6 @@ class ElasticRecord::Index::DocumentsTest < MiniTest::Test
|
|
7
7
|
end
|
8
8
|
end
|
9
9
|
|
10
|
-
def setup
|
11
|
-
super
|
12
|
-
index.disable_deferring!
|
13
|
-
index.reset
|
14
|
-
end
|
15
|
-
|
16
10
|
def test_index_record
|
17
11
|
record = Widget.new(id: '5', color: 'red')
|
18
12
|
|
@@ -29,12 +23,24 @@ class ElasticRecord::Index::DocumentsTest < MiniTest::Test
|
|
29
23
|
refute index.record_exists?('xyz')
|
30
24
|
end
|
31
25
|
|
26
|
+
def test_update_document
|
27
|
+
index.index_document('abc', warehouse_id: '5', color: 'red')
|
28
|
+
index.update_document('abc', color: 'blue')
|
29
|
+
|
30
|
+
expected = {'warehouse_id' => '5', 'color' => 'blue'}
|
31
|
+
assert_equal expected, index.get('abc')['_source']
|
32
|
+
end
|
33
|
+
|
32
34
|
def test_delete_document
|
33
35
|
index.index_document('abc', color: 'red')
|
34
36
|
assert index.record_exists?('abc')
|
35
37
|
|
36
38
|
index.delete_document('abc')
|
37
39
|
refute index.record_exists?('abc')
|
40
|
+
|
41
|
+
assert_raises RuntimeError do
|
42
|
+
index.delete_document('')
|
43
|
+
end
|
38
44
|
end
|
39
45
|
|
40
46
|
def test_delete_by_query
|
@@ -47,6 +53,17 @@ class ElasticRecord::Index::DocumentsTest < MiniTest::Test
|
|
47
53
|
assert index.record_exists?('joe')
|
48
54
|
end
|
49
55
|
|
56
|
+
def test_create_scan_search
|
57
|
+
index.index_document('bob', name: 'bob')
|
58
|
+
index.index_document('joe', name: 'joe')
|
59
|
+
|
60
|
+
scan_search = index.create_scan_search('query' => {query_string: {query: 'name.analyzed:bob'}})
|
61
|
+
|
62
|
+
assert_equal 1, scan_search.total_hits
|
63
|
+
refute_nil scan_search.scroll_id
|
64
|
+
assert_equal 1, scan_search.request_more_ids.size
|
65
|
+
end
|
66
|
+
|
50
67
|
def test_bulk_add
|
51
68
|
record = Widget.new(id: 'abc', color: 'red')
|
52
69
|
|
@@ -61,12 +78,15 @@ class ElasticRecord::Index::DocumentsTest < MiniTest::Test
|
|
61
78
|
|
62
79
|
index.bulk do
|
63
80
|
index.index_document '5', color: 'green'
|
81
|
+
index.update_document '5', color: 'blue'
|
64
82
|
index.delete_document '3'
|
65
83
|
|
66
84
|
expected = [
|
67
85
|
{index: {_index: index.alias_name, _type: "widget", _id: "5"}},
|
68
86
|
{color: "green"},
|
69
|
-
{
|
87
|
+
{update: {_index: "widgets", _type: "widget", _id: "5", _retry_on_conflict: 3}},
|
88
|
+
{doc: {color: "blue"}, doc_as_upsert: true},
|
89
|
+
{delete: {_index: index.alias_name, _type: "widget", _id: "3", _retry_on_conflict: 3}}
|
70
90
|
]
|
71
91
|
assert_equal expected, index.current_bulk_batch
|
72
92
|
end
|
@@ -75,29 +95,43 @@ class ElasticRecord::Index::DocumentsTest < MiniTest::Test
|
|
75
95
|
end
|
76
96
|
|
77
97
|
def test_bulk_error
|
78
|
-
|
79
|
-
|
80
|
-
|
98
|
+
without_deferring do
|
99
|
+
begin
|
100
|
+
index.bulk do
|
101
|
+
index.index_document '5', color: 'green'
|
102
|
+
index.index_document '3', color: {'bad' => 'stuff'}
|
103
|
+
end
|
104
|
+
refute index.record_exists?('3')
|
105
|
+
assert false, 'Expected ElasticRecord::BulkError'
|
106
|
+
rescue => e
|
107
|
+
assert_match '[{"index"', e.message
|
108
|
+
end
|
81
109
|
end
|
82
|
-
assert false, 'Expected ElasticRecord::BulkError'
|
83
|
-
rescue => e
|
84
|
-
assert_match '[{"index"', e.message
|
85
110
|
end
|
86
111
|
|
87
112
|
def test_bulk_inheritence
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
113
|
+
without_deferring do
|
114
|
+
index.bulk do
|
115
|
+
InheritedWidget.elastic_index.index_document '5', color: 'green'
|
116
|
+
|
117
|
+
expected = [
|
118
|
+
{index: {_index: index.alias_name, _type: "widget", _id: "5"}},
|
119
|
+
{color: "green"}
|
120
|
+
]
|
121
|
+
assert_equal expected, index.current_bulk_batch
|
122
|
+
end
|
96
123
|
end
|
97
124
|
end
|
98
125
|
|
99
126
|
private
|
100
127
|
|
128
|
+
def without_deferring
|
129
|
+
index.disable_deferring!
|
130
|
+
yield
|
131
|
+
index.reset
|
132
|
+
index.enable_deferring!
|
133
|
+
end
|
134
|
+
|
101
135
|
def index
|
102
136
|
@index ||= Widget.elastic_index
|
103
137
|
end
|
@@ -1,19 +1,9 @@
|
|
1
1
|
require 'helper'
|
2
2
|
|
3
3
|
class ElasticRecord::Index::MappingTest < MiniTest::Test
|
4
|
-
def test_delete_mapping
|
5
|
-
index_name = index.create
|
6
|
-
refute_nil index.get_mapping(index_name)['widget']
|
7
|
-
|
8
|
-
index.delete_mapping(index_name)
|
9
|
-
|
10
|
-
assert_nil index.get_mapping(index_name)
|
11
|
-
end
|
12
|
-
|
13
4
|
def test_default_mapping
|
14
5
|
mapping = index.mapping
|
15
6
|
|
16
|
-
refute_nil mapping[:_source]
|
17
7
|
refute_nil mapping[:properties]
|
18
8
|
end
|
19
9
|
|
@@ -48,7 +48,7 @@ class ElasticRecord::Mysql2Test < MiniTest::Test
|
|
48
48
|
setup_project_database(
|
49
49
|
'adapter' => 'mysql2',
|
50
50
|
'host' => "localhost",
|
51
|
-
'database' => '
|
51
|
+
'database' => 'elastic_record_subtest',
|
52
52
|
'username' => 'root'
|
53
53
|
)
|
54
54
|
end
|
@@ -63,9 +63,9 @@ class ElasticRecord::PostgresqlTest < MiniTest::Test
|
|
63
63
|
setup_project_database(
|
64
64
|
'adapter' => 'postgresql',
|
65
65
|
'encoding' => 'unicode',
|
66
|
-
'database' => '
|
66
|
+
'database' => 'elastic_record_subtest',
|
67
67
|
'pool' => 5,
|
68
|
-
'
|
68
|
+
'host' => 'localhost',
|
69
69
|
'password' => ''
|
70
70
|
)
|
71
71
|
end
|