elastic_record 1.1.4 → 1.1.5
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/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
|