elastic_record 1.1.4 → 1.1.5
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Gemfile +3 -1
- data/Rakefile +2 -2
- data/elastic_record.gemspec +2 -2
- data/lib/elastic_record.rb +1 -0
- data/lib/elastic_record/callbacks.rb +13 -5
- data/lib/elastic_record/connection.rb +7 -8
- data/lib/elastic_record/errors.rb +10 -0
- data/lib/elastic_record/index/deferred.rb +7 -1
- data/lib/elastic_record/index/documents.rb +34 -17
- data/lib/elastic_record/index/manage.rb +8 -0
- data/lib/elastic_record/index/percolator.rb +16 -10
- data/lib/elastic_record/integration/active_record.rb +7 -0
- data/lib/elastic_record/integration/cassandra_object.rb +7 -0
- data/lib/elastic_record/model.rb +5 -10
- data/lib/elastic_record/railtie.rb +10 -0
- data/lib/elastic_record/relation.rb +26 -5
- data/lib/elastic_record/relation/batches.rb +4 -1
- data/lib/elastic_record/relation/finder_methods.rb +13 -3
- data/lib/elastic_record/relation/search_methods.rb +17 -0
- data/lib/elastic_record/relation/value_methods.rb +2 -2
- data/lib/elastic_record/searches_many.rb +1 -1
- data/lib/elastic_record/searches_many/association.rb +14 -10
- data/lib/elastic_record/searches_many/builder.rb +3 -3
- data/lib/elastic_record/searches_many/collection_proxy.rb +9 -3
- data/lib/elastic_record/searches_many/reflection.rb +4 -0
- data/test/elastic_record/callbacks_test.rb +65 -1
- data/test/elastic_record/config_test.rb +3 -3
- data/test/elastic_record/connection_test.rb +1 -1
- data/test/elastic_record/index/configurator_test.rb +1 -1
- data/test/elastic_record/index/documents_test.rb +23 -4
- data/test/elastic_record/index/manage_test.rb +1 -1
- data/test/elastic_record/index/mapping_test.rb +1 -1
- data/test/elastic_record/index/percolator_test.rb +1 -1
- data/test/elastic_record/index/settings_test.rb +1 -1
- data/test/elastic_record/index/warmer_test.rb +1 -1
- data/test/elastic_record/index_test.rb +1 -1
- data/test/elastic_record/integration/active_record_test.rb +39 -0
- data/test/elastic_record/log_subscriber_test.rb +11 -0
- data/test/elastic_record/lucene_test.rb +1 -1
- data/test/elastic_record/model_test.rb +1 -1
- data/test/elastic_record/railties/controller_runtime_test.rb +1 -1
- data/test/elastic_record/relation/admin_test.rb +13 -7
- data/test/elastic_record/relation/batches_test.rb +27 -1
- data/test/elastic_record/relation/delegation_test.rb +1 -1
- data/test/elastic_record/relation/finder_methods_test.rb +23 -8
- data/test/elastic_record/relation/merging_test.rb +1 -1
- data/test/elastic_record/relation/none_test.rb +1 -1
- data/test/elastic_record/relation/search_methods_test.rb +15 -2
- data/test/elastic_record/relation_test.rb +50 -3
- data/test/elastic_record/searches_many/association_test.rb +47 -0
- data/test/elastic_record/searches_many/autosave_test.rb +11 -10
- data/test/elastic_record/searches_many/collection_proxy_test.rb +1 -1
- data/test/elastic_record/searches_many/reflection_test.rb +7 -1
- data/test/elastic_record/searches_many_test.rb +11 -11
- data/test/elastic_record/searching_test.rb +1 -1
- data/test/helper.rb +19 -12
- data/test/support/models/option.rb +24 -0
- data/test/support/models/test_model.rb +22 -1
- data/test/support/models/warehouse.rb +2 -2
- data/test/support/models/widget.rb +4 -2
- data/test/support/query_counter.rb +56 -0
- metadata +10 -5
- data/lib/elastic_record/orm/active_record.rb +0 -7
@@ -25,9 +25,12 @@ module ElasticRecord
|
|
25
25
|
search_type: 'scan'
|
26
26
|
}.update(options)
|
27
27
|
|
28
|
-
|
28
|
+
search_result = klass.elastic_index.search(as_elastic, options)
|
29
|
+
scroll_id = search_result['_scroll_id']
|
30
|
+
hit_count = 0
|
29
31
|
|
30
32
|
while (hit_ids = get_scroll_hit_ids(scroll_id, scroll_keep_alive)).any?
|
33
|
+
hit_count += hit_ids.size
|
31
34
|
hit_ids.each_slice(size, &block)
|
32
35
|
end
|
33
36
|
end
|
@@ -1,14 +1,24 @@
|
|
1
1
|
module ElasticRecord
|
2
2
|
class Relation
|
3
3
|
module FinderMethods
|
4
|
-
def find(
|
5
|
-
|
4
|
+
def find(*ids)
|
5
|
+
ids = ids.flatten
|
6
|
+
case ids.size
|
7
|
+
when 0; raise ActiveRecord::RecordNotFound.new('empty argument')
|
8
|
+
when 1; filter(arelastic.filter.ids(ids)).first!
|
9
|
+
else
|
10
|
+
filter(arelastic.filter.ids(ids))
|
11
|
+
end
|
6
12
|
end
|
7
13
|
|
8
14
|
def first
|
9
15
|
find_one self
|
10
16
|
end
|
11
17
|
|
18
|
+
def first!
|
19
|
+
first or raise ActiveRecord::RecordNotFound
|
20
|
+
end
|
21
|
+
|
12
22
|
def last
|
13
23
|
find_one reverse_order
|
14
24
|
end
|
@@ -24,4 +34,4 @@ module ElasticRecord
|
|
24
34
|
end
|
25
35
|
end
|
26
36
|
end
|
27
|
-
end
|
37
|
+
end
|
@@ -39,6 +39,23 @@ module ElasticRecord
|
|
39
39
|
clone.filter!(*args)
|
40
40
|
end
|
41
41
|
|
42
|
+
def find_by(*args)
|
43
|
+
filter(*args).first
|
44
|
+
end
|
45
|
+
|
46
|
+
def find_by!(*args)
|
47
|
+
filter(*args).first!
|
48
|
+
end
|
49
|
+
|
50
|
+
def eager_load!(*args)
|
51
|
+
self.eager_load_values += args.flatten
|
52
|
+
self
|
53
|
+
end
|
54
|
+
|
55
|
+
def eager_load(*args)
|
56
|
+
clone.eager_load! *args
|
57
|
+
end
|
58
|
+
|
42
59
|
def limit!(value)
|
43
60
|
self.limit_value = value
|
44
61
|
self
|
@@ -1,6 +1,6 @@
|
|
1
1
|
module ElasticRecord
|
2
2
|
class Relation
|
3
|
-
MULTI_VALUE_METHODS = [:extending, :facet, :filter, :order, :select]
|
3
|
+
MULTI_VALUE_METHODS = [:extending, :facet, :filter, :order, :select, :eager_load]
|
4
4
|
SINGLE_VALUE_METHODS = [:query, :limit, :offset, :reverse_order]
|
5
5
|
end
|
6
|
-
end
|
6
|
+
end
|
@@ -31,7 +31,7 @@ module ElasticRecord
|
|
31
31
|
owner.send("#{reflection.touch_column}=", Time.current)
|
32
32
|
end
|
33
33
|
end
|
34
|
-
|
34
|
+
|
35
35
|
def reader
|
36
36
|
CollectionProxy.new(self)
|
37
37
|
end
|
@@ -69,7 +69,7 @@ module ElasticRecord
|
|
69
69
|
end
|
70
70
|
|
71
71
|
def scope
|
72
|
-
search = klass.elastic_search.filter("#{reflection.belongs_to}_id" => owner.id).limit(
|
72
|
+
search = klass.elastic_search.filter("#{reflection.belongs_to}_id" => owner.id).limit(1000000)
|
73
73
|
if options[:as]
|
74
74
|
search.filter! "#{reflection.belongs_to}_type" => owner.class.name
|
75
75
|
end
|
@@ -85,19 +85,23 @@ module ElasticRecord
|
|
85
85
|
collection
|
86
86
|
end
|
87
87
|
|
88
|
+
def eager_loaded_collection(records)
|
89
|
+
unless @loaded
|
90
|
+
@collection = records
|
91
|
+
@loaded = true
|
92
|
+
end
|
93
|
+
|
94
|
+
collection
|
95
|
+
end
|
96
|
+
|
88
97
|
private
|
98
|
+
|
89
99
|
def load_persisted_collection?
|
90
100
|
!loaded? || owner.new_record?
|
91
101
|
end
|
92
102
|
|
93
103
|
def persisted_collection
|
94
|
-
@persisted_collection ||=
|
95
|
-
if reflection.counter_cache_column && (owner.send(reflection.counter_cache_column).nil? || owner.send(reflection.counter_cache_column) == 0)
|
96
|
-
[]
|
97
|
-
else
|
98
|
-
scope.to_a
|
99
|
-
end
|
100
|
-
end
|
104
|
+
@persisted_collection ||= scope.to_a
|
101
105
|
end
|
102
106
|
|
103
107
|
def merge_collections(existing_records, new_records)
|
@@ -142,4 +146,4 @@ module ElasticRecord
|
|
142
146
|
end
|
143
147
|
end
|
144
148
|
end
|
145
|
-
end
|
149
|
+
end
|
@@ -10,14 +10,14 @@ module ElasticRecord
|
|
10
10
|
@model, @name, @options = model, name, options
|
11
11
|
end
|
12
12
|
|
13
|
-
def build
|
13
|
+
def build
|
14
14
|
define_writer
|
15
15
|
define_reader
|
16
16
|
|
17
17
|
reflection = ElasticRecord::SearchesMany::Reflection.new(model, name, options)
|
18
18
|
model.searches_many_reflections = model.searches_many_reflections.merge(name => reflection)
|
19
19
|
|
20
|
-
model.add_autosave_callbacks(reflection)
|
20
|
+
model.add_autosave_callbacks(reflection) if options[:autosave]
|
21
21
|
end
|
22
22
|
|
23
23
|
def mixin
|
@@ -39,4 +39,4 @@ module ElasticRecord
|
|
39
39
|
end
|
40
40
|
end
|
41
41
|
end
|
42
|
-
end
|
42
|
+
end
|
@@ -3,12 +3,18 @@ module ElasticRecord
|
|
3
3
|
class CollectionProxy < ElasticRecord::Relation
|
4
4
|
def initialize(association)
|
5
5
|
@association = association
|
6
|
-
super association.klass
|
6
|
+
super association.klass
|
7
7
|
merge! association.scope
|
8
8
|
end
|
9
9
|
|
10
|
+
def eager_loaded(records)
|
11
|
+
@association.eager_loaded_collection(records)
|
12
|
+
end
|
13
|
+
|
10
14
|
def to_a
|
11
|
-
@association.load_collection.reject(&:destroyed?)
|
15
|
+
records = @association.load_collection.reject(&:destroyed?)
|
16
|
+
records = eager_load_associations(records) if eager_loading?
|
17
|
+
records
|
12
18
|
end
|
13
19
|
|
14
20
|
def <<(*records)
|
@@ -17,4 +23,4 @@ module ElasticRecord
|
|
17
23
|
alias_method :push, :<<
|
18
24
|
end
|
19
25
|
end
|
20
|
-
end
|
26
|
+
end
|
@@ -20,6 +20,10 @@ module ElasticRecord
|
|
20
20
|
options[:as] ? options[:as].to_s : model.name.to_s.demodulize.underscore
|
21
21
|
end
|
22
22
|
|
23
|
+
def foreign_key
|
24
|
+
options[:foreign_key] ? options[:foreign_key].to_s : "#{model.name.to_s.demodulize.underscore}_id"
|
25
|
+
end
|
26
|
+
|
23
27
|
CALLBACKS = [:before_add, :after_add, :before_remove, :after_remove]
|
24
28
|
def define_callbacks(options)
|
25
29
|
Hash[CALLBACKS.map { |callback_name| [callback_name, Array(options[callback_name.to_sym])] }]
|
@@ -1,6 +1,6 @@
|
|
1
1
|
require 'helper'
|
2
2
|
|
3
|
-
class ElasticRecord::CallbacksTest < MiniTest::
|
3
|
+
class ElasticRecord::CallbacksTest < MiniTest::Unit::TestCase
|
4
4
|
def test_added_to_index
|
5
5
|
widget = Widget.new id: '10', color: 'green'
|
6
6
|
refute Widget.elastic_index.record_exists?(widget.id)
|
@@ -43,4 +43,68 @@ class ElasticRecord::CallbacksTest < MiniTest::Spec
|
|
43
43
|
assert_equal({color: false}, widget.as_search)
|
44
44
|
end
|
45
45
|
end
|
46
|
+
|
47
|
+
class SpecialFieldsModel
|
48
|
+
include TestModel
|
49
|
+
|
50
|
+
class Author
|
51
|
+
def as_search
|
52
|
+
{name: 'Jonny'}
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
self.elastic_index.mapping[:properties].update(
|
57
|
+
author: {
|
58
|
+
type: :object
|
59
|
+
},
|
60
|
+
commenters: {
|
61
|
+
type: :nested
|
62
|
+
}
|
63
|
+
)
|
64
|
+
|
65
|
+
def author
|
66
|
+
Author.new
|
67
|
+
end
|
68
|
+
|
69
|
+
def commenters
|
70
|
+
[Author.new, Author.new]
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
def test_as_search_with_special_fields
|
75
|
+
doc = SpecialFieldsModel.new.as_search
|
76
|
+
|
77
|
+
assert_equal({name: 'Jonny'}, doc[:author])
|
78
|
+
assert_equal([{name: 'Jonny'}, {name: 'Jonny'}], doc[:commenters])
|
79
|
+
end
|
80
|
+
|
81
|
+
class DisablingModel
|
82
|
+
include TestModel
|
83
|
+
|
84
|
+
attr_accessor :called_as_search
|
85
|
+
|
86
|
+
define_attributes [:height]
|
87
|
+
|
88
|
+
self.elastic_index.mapping[:properties].update(
|
89
|
+
height: {
|
90
|
+
type: 'string', index: 'not_analyzed'
|
91
|
+
}
|
92
|
+
)
|
93
|
+
|
94
|
+
def as_search
|
95
|
+
@called_as_search = true
|
96
|
+
super
|
97
|
+
end
|
98
|
+
|
99
|
+
end
|
100
|
+
|
101
|
+
def test_disabled_skip_document
|
102
|
+
DisablingModel.elastic_index.disable!
|
103
|
+
|
104
|
+
model = DisablingModel.new id: '5', height: '9 feets'
|
105
|
+
model.save
|
106
|
+
|
107
|
+
refute Widget.elastic_index.record_exists?(model.id)
|
108
|
+
refute model.called_as_search
|
109
|
+
end
|
46
110
|
end
|
@@ -1,6 +1,6 @@
|
|
1
1
|
require 'helper'
|
2
2
|
|
3
|
-
class ElasticRecord::ConfigTest < MiniTest::
|
3
|
+
class ElasticRecord::ConfigTest < MiniTest::Unit::TestCase
|
4
4
|
def test_defaults
|
5
5
|
assert_equal '5m', ElasticRecord::Config.scroll_keep_alive
|
6
6
|
end
|
@@ -8,6 +8,6 @@ class ElasticRecord::ConfigTest < MiniTest::Spec
|
|
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, Option], ElasticRecord::Config.models
|
12
12
|
end
|
13
|
-
end
|
13
|
+
end
|
@@ -1,6 +1,6 @@
|
|
1
1
|
require 'helper'
|
2
2
|
|
3
|
-
class ElasticRecord::ConnectionTest < MiniTest::
|
3
|
+
class ElasticRecord::ConnectionTest < MiniTest::Unit::TestCase
|
4
4
|
def test_servers
|
5
5
|
assert_equal ['foo', 'bar'], ElasticRecord::Connection.new('foo,bar').servers
|
6
6
|
assert_equal ['foo', 'bar'], ElasticRecord::Connection.new(['foo', 'bar']).servers
|
@@ -1,6 +1,6 @@
|
|
1
1
|
require 'helper'
|
2
2
|
|
3
|
-
class ElasticRecord::Index::DocumentsTest < MiniTest::
|
3
|
+
class ElasticRecord::Index::DocumentsTest < MiniTest::Unit::TestCase
|
4
4
|
class InheritedWidget < Widget
|
5
5
|
def self.base_class
|
6
6
|
Widget
|
@@ -13,6 +13,15 @@ class ElasticRecord::Index::DocumentsTest < MiniTest::Spec
|
|
13
13
|
index.reset
|
14
14
|
end
|
15
15
|
|
16
|
+
def test_index_record
|
17
|
+
record = Widget.new(id: '5', color: 'red')
|
18
|
+
|
19
|
+
index.index_record(record)
|
20
|
+
|
21
|
+
assert index.record_exists?('5')
|
22
|
+
refute index.record_exists?('7')
|
23
|
+
end
|
24
|
+
|
16
25
|
def test_index_document
|
17
26
|
index.index_document('abc', color: 'red')
|
18
27
|
|
@@ -59,10 +68,20 @@ class ElasticRecord::Index::DocumentsTest < MiniTest::Spec
|
|
59
68
|
{color: "green"},
|
60
69
|
{delete: {_index: "widgets", _type: "widget", _id: "3"}}
|
61
70
|
]
|
62
|
-
assert_equal expected, index.
|
71
|
+
assert_equal expected, index.current_bulk_batch
|
63
72
|
end
|
64
73
|
|
65
|
-
assert_nil index.
|
74
|
+
assert_nil index.current_bulk_batch
|
75
|
+
end
|
76
|
+
|
77
|
+
def test_bulk_error
|
78
|
+
index.bulk do
|
79
|
+
index.index_document '5', color: 'green'
|
80
|
+
index.index_document '3', color: {'bad' => 'stuff'}
|
81
|
+
end
|
82
|
+
assert false, 'Expected ElasticRecord::BulkError'
|
83
|
+
rescue => e
|
84
|
+
assert_match '[{"index"', e.message
|
66
85
|
end
|
67
86
|
|
68
87
|
def test_bulk_inheritence
|
@@ -73,7 +92,7 @@ class ElasticRecord::Index::DocumentsTest < MiniTest::Spec
|
|
73
92
|
{index: {_index: "widgets", _type: "widget", _id: "5"}},
|
74
93
|
{color: "green"}
|
75
94
|
]
|
76
|
-
assert_equal expected, index.
|
95
|
+
assert_equal expected, index.current_bulk_batch
|
77
96
|
end
|
78
97
|
end
|
79
98
|
|
@@ -1,6 +1,6 @@
|
|
1
1
|
require 'helper'
|
2
2
|
|
3
|
-
class ElasticRecord::Index::WarmerTest < MiniTest::
|
3
|
+
class ElasticRecord::Index::WarmerTest < MiniTest::Unit::TestCase
|
4
4
|
def test_create_warmer
|
5
5
|
index.delete_warmer('green') if index.warmer_exists?('green')
|
6
6
|
refute index.warmer_exists?('green')
|
@@ -0,0 +1,39 @@
|
|
1
|
+
require 'helper'
|
2
|
+
|
3
|
+
require 'active_record'
|
4
|
+
require 'mysql2'
|
5
|
+
|
6
|
+
ActiveSupport.on_load :active_record do
|
7
|
+
ActiveRecord::Base.establish_connection(
|
8
|
+
adapter: 'mysql2',
|
9
|
+
host: "localhost",
|
10
|
+
database: "elastic_record_test",
|
11
|
+
username: "root"
|
12
|
+
)
|
13
|
+
|
14
|
+
`mysqladmin -u root -f drop elastic_record_test`
|
15
|
+
`mysqladmin -u root create elastic_record_test`
|
16
|
+
|
17
|
+
ActiveRecord::Migration.create_table :projects do |t|
|
18
|
+
t.string :name, null: false
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
require 'elastic_record/integration/active_record'
|
23
|
+
class Project < ActiveRecord::Base
|
24
|
+
include ElasticRecord::Model
|
25
|
+
|
26
|
+
self.elastic_index.mapping[:properties].update(
|
27
|
+
name: { type: 'string', index: 'not_analyzed' }
|
28
|
+
)
|
29
|
+
end
|
30
|
+
|
31
|
+
class ElasticRecord::ActiveRecordTest < MiniTest::Unit::TestCase
|
32
|
+
def test_load_elastic_record_hits
|
33
|
+
poo_product = Project.create! name: "Poo"
|
34
|
+
bear_product = Project.create! name: "Bear"
|
35
|
+
|
36
|
+
assert_equal [poo_product, bear_product], Project.load_elastic_record_hits([poo_product.id, bear_product.id])
|
37
|
+
assert_equal [bear_product, poo_product], Project.load_elastic_record_hits([bear_product.id, poo_product.id])
|
38
|
+
end
|
39
|
+
end
|