elasticsearch-model 6.0.0 → 6.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Gemfile +5 -0
- data/README.md +14 -7
- data/Rakefile +27 -36
- data/elasticsearch-model.gemspec +1 -1
- data/examples/activerecord_mapping_completion.rb +2 -15
- data/gemfiles/3.0.gemfile +6 -1
- data/gemfiles/4.0.gemfile +7 -1
- data/gemfiles/5.0.gemfile +6 -0
- data/lib/elasticsearch/model.rb +15 -8
- data/lib/elasticsearch/model/adapters/active_record.rb +7 -26
- data/lib/elasticsearch/model/indexing.rb +5 -3
- data/lib/elasticsearch/model/naming.rb +6 -1
- data/lib/elasticsearch/model/response.rb +2 -2
- data/lib/elasticsearch/model/response/pagination.rb +2 -192
- data/lib/elasticsearch/model/response/pagination/kaminari.rb +109 -0
- data/lib/elasticsearch/model/response/pagination/will_paginate.rb +95 -0
- data/lib/elasticsearch/model/response/result.rb +1 -1
- data/lib/elasticsearch/model/version.rb +1 -1
- data/spec/elasticsearch/model/adapter_spec.rb +119 -0
- data/spec/elasticsearch/model/adapters/active_record/associations_spec.rb +334 -0
- data/spec/elasticsearch/model/adapters/active_record/basic_spec.rb +340 -0
- data/spec/elasticsearch/model/adapters/active_record/dynamic_index_name_spec.rb +18 -0
- data/spec/elasticsearch/model/adapters/active_record/import_spec.rb +187 -0
- data/spec/elasticsearch/model/adapters/active_record/multi_model_spec.rb +110 -0
- data/spec/elasticsearch/model/adapters/active_record/namespaced_model_spec.rb +38 -0
- data/spec/elasticsearch/model/adapters/active_record/pagination_spec.rb +315 -0
- data/spec/elasticsearch/model/adapters/active_record/parent_child_spec.rb +75 -0
- data/spec/elasticsearch/model/adapters/active_record/serialization_spec.rb +61 -0
- data/spec/elasticsearch/model/adapters/active_record_spec.rb +207 -0
- data/spec/elasticsearch/model/adapters/default_spec.rb +41 -0
- data/spec/elasticsearch/model/adapters/mongoid/basic_spec.rb +267 -0
- data/spec/elasticsearch/model/adapters/mongoid/multi_model_spec.rb +66 -0
- data/spec/elasticsearch/model/adapters/mongoid_spec.rb +235 -0
- data/spec/elasticsearch/model/adapters/multiple_spec.rb +125 -0
- data/spec/elasticsearch/model/callbacks_spec.rb +33 -0
- data/spec/elasticsearch/model/client_spec.rb +66 -0
- data/spec/elasticsearch/model/hash_wrapper_spec.rb +12 -0
- data/spec/elasticsearch/model/importing_spec.rb +214 -0
- data/spec/elasticsearch/model/indexing_spec.rb +918 -0
- data/spec/elasticsearch/model/module_spec.rb +101 -0
- data/spec/elasticsearch/model/multimodel_spec.rb +55 -0
- data/spec/elasticsearch/model/naming_inheritance_spec.rb +184 -0
- data/spec/elasticsearch/model/naming_spec.rb +186 -0
- data/spec/elasticsearch/model/proxy_spec.rb +107 -0
- data/spec/elasticsearch/model/response/aggregations_spec.rb +66 -0
- data/spec/elasticsearch/model/response/base_spec.rb +90 -0
- data/spec/elasticsearch/model/response/pagination/kaminari_spec.rb +410 -0
- data/spec/elasticsearch/model/response/pagination/will_paginate_spec.rb +262 -0
- data/spec/elasticsearch/model/response/records_spec.rb +118 -0
- data/spec/elasticsearch/model/response/response_spec.rb +131 -0
- data/spec/elasticsearch/model/response/result_spec.rb +122 -0
- data/spec/elasticsearch/model/response/results_spec.rb +56 -0
- data/spec/elasticsearch/model/searching_search_request_spec.rb +112 -0
- data/spec/elasticsearch/model/searching_spec.rb +49 -0
- data/spec/elasticsearch/model/serializing_spec.rb +22 -0
- data/spec/spec_helper.rb +161 -0
- data/spec/support/app.rb +21 -0
- data/spec/support/app/answer.rb +33 -0
- data/spec/support/app/article.rb +22 -0
- data/spec/support/app/article_for_pagination.rb +12 -0
- data/spec/support/app/article_with_custom_serialization.rb +13 -0
- data/spec/support/app/article_with_dynamic_index_name.rb +15 -0
- data/spec/support/app/author.rb +9 -0
- data/spec/support/app/authorship.rb +4 -0
- data/spec/support/app/category.rb +3 -0
- data/spec/support/app/comment.rb +3 -0
- data/spec/support/app/episode.rb +11 -0
- data/spec/support/app/image.rb +19 -0
- data/spec/support/app/import_article.rb +12 -0
- data/spec/support/app/mongoid_article.rb +21 -0
- data/spec/support/app/namespaced_book.rb +10 -0
- data/spec/support/app/parent_and_child_searchable.rb +24 -0
- data/spec/support/app/post.rb +14 -0
- data/spec/support/app/question.rb +27 -0
- data/spec/support/app/searchable.rb +48 -0
- data/spec/support/app/series.rb +11 -0
- data/spec/support/model.json +1 -0
- data/{test → spec}/support/model.yml +0 -0
- metadata +129 -86
- data/test/integration/active_record_associations_parent_child_test.rb +0 -188
- data/test/integration/active_record_associations_test.rb +0 -339
- data/test/integration/active_record_basic_test.rb +0 -255
- data/test/integration/active_record_custom_serialization_test.rb +0 -67
- data/test/integration/active_record_import_test.rb +0 -168
- data/test/integration/active_record_namespaced_model_test.rb +0 -56
- data/test/integration/active_record_pagination_test.rb +0 -149
- data/test/integration/dynamic_index_name_test.rb +0 -52
- data/test/integration/mongoid_basic_test.rb +0 -240
- data/test/integration/multiple_models_test.rb +0 -176
- data/test/support/model.json +0 -1
- data/test/test_helper.rb +0 -92
- data/test/unit/adapter_active_record_test.rb +0 -157
- data/test/unit/adapter_default_test.rb +0 -41
- data/test/unit/adapter_mongoid_test.rb +0 -161
- data/test/unit/adapter_multiple_test.rb +0 -106
- data/test/unit/adapter_test.rb +0 -69
- data/test/unit/callbacks_test.rb +0 -31
- data/test/unit/client_test.rb +0 -27
- data/test/unit/hash_wrapper_test.rb +0 -13
- data/test/unit/importing_test.rb +0 -224
- data/test/unit/indexing_test.rb +0 -720
- data/test/unit/module_test.rb +0 -68
- data/test/unit/multimodel_test.rb +0 -38
- data/test/unit/naming_inheritance_test.rb +0 -94
- data/test/unit/naming_test.rb +0 -103
- data/test/unit/proxy_test.rb +0 -98
- data/test/unit/response_aggregations_test.rb +0 -46
- data/test/unit/response_base_test.rb +0 -40
- data/test/unit/response_pagination_kaminari_test.rb +0 -433
- data/test/unit/response_pagination_will_paginate_test.rb +0 -398
- data/test/unit/response_records_test.rb +0 -91
- data/test/unit/response_result_test.rb +0 -90
- data/test/unit/response_results_test.rb +0 -34
- data/test/unit/response_test.rb +0 -104
- data/test/unit/searching_search_request_test.rb +0 -78
- data/test/unit/searching_test.rb +0 -41
- data/test/unit/serializing_test.rb +0 -17
@@ -0,0 +1,75 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe 'Elasticsearch::Model::Adapter::ActiveRecord Parent-Child' do
|
4
|
+
|
5
|
+
before(:all) do
|
6
|
+
ActiveRecord::Schema.define(version: 1) do
|
7
|
+
create_table :questions do |t|
|
8
|
+
t.string :title
|
9
|
+
t.text :text
|
10
|
+
t.string :author
|
11
|
+
t.timestamps null: false
|
12
|
+
end
|
13
|
+
|
14
|
+
create_table :answers do |t|
|
15
|
+
t.text :text
|
16
|
+
t.string :author
|
17
|
+
t.references :question
|
18
|
+
t.timestamps null: false
|
19
|
+
end
|
20
|
+
|
21
|
+
add_index(:answers, :question_id) unless index_exists?(:answers, :question_id)
|
22
|
+
|
23
|
+
clear_tables(Question)
|
24
|
+
ParentChildSearchable.create_index!(force: true)
|
25
|
+
|
26
|
+
q_1 = Question.create!(title: 'First Question', author: 'John')
|
27
|
+
q_2 = Question.create!(title: 'Second Question', author: 'Jody')
|
28
|
+
|
29
|
+
q_1.answers.create!(text: 'Lorem Ipsum', author: 'Adam')
|
30
|
+
q_1.answers.create!(text: 'Dolor Sit', author: 'Ryan')
|
31
|
+
|
32
|
+
q_2.answers.create!(text: 'Amet Et', author: 'John')
|
33
|
+
|
34
|
+
Question.__elasticsearch__.refresh_index!
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
describe 'has_child search' do
|
39
|
+
|
40
|
+
let(:search_result) do
|
41
|
+
Question.search(query: { has_child: { type: 'answer', query: { match: { author: 'john' } } } })
|
42
|
+
end
|
43
|
+
|
44
|
+
it 'finds parents by matching on child search criteria' do
|
45
|
+
expect(search_result.records.first.title).to eq('Second Question')
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
describe 'hash_parent search' do
|
50
|
+
|
51
|
+
let(:search_result) do
|
52
|
+
Answer.search(query: { has_parent: { parent_type: 'question', query: { match: { author: 'john' } } } })
|
53
|
+
end
|
54
|
+
|
55
|
+
it 'finds children by matching in parent criteria' do
|
56
|
+
expect(search_result.records.map(&:author)).to match(['Adam', 'Ryan'])
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
context 'when a parent is deleted' do
|
61
|
+
|
62
|
+
before do
|
63
|
+
Question.where(title: 'First Question').each(&:destroy)
|
64
|
+
Question.__elasticsearch__.refresh_index!
|
65
|
+
end
|
66
|
+
|
67
|
+
let(:search_result) do
|
68
|
+
Answer.search(query: { has_parent: { parent_type: 'question', query: { match_all: {} } } })
|
69
|
+
end
|
70
|
+
|
71
|
+
it 'deletes the children' do
|
72
|
+
expect(search_result.results.total).to eq(1)
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
@@ -0,0 +1,61 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe 'Elasticsearch::Model::Adapter::ActiveRecord Serialization' do
|
4
|
+
|
5
|
+
before(:all) do
|
6
|
+
ActiveRecord::Schema.define(:version => 1) do
|
7
|
+
create_table ArticleWithCustomSerialization.table_name do |t|
|
8
|
+
t.string :title
|
9
|
+
t.string :status
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
ArticleWithCustomSerialization.delete_all
|
14
|
+
ArticleWithCustomSerialization.__elasticsearch__.create_index!(force: true)
|
15
|
+
end
|
16
|
+
|
17
|
+
context 'when the model has a custom serialization defined' do
|
18
|
+
|
19
|
+
before do
|
20
|
+
ArticleWithCustomSerialization.create!(title: 'Test', status: 'green')
|
21
|
+
ArticleWithCustomSerialization.__elasticsearch__.refresh_index!
|
22
|
+
end
|
23
|
+
|
24
|
+
context 'when a document is indexed' do
|
25
|
+
|
26
|
+
let(:search_result) do
|
27
|
+
ArticleWithCustomSerialization.__elasticsearch__.client.get(index: 'article_with_custom_serializations',
|
28
|
+
type: '_doc',
|
29
|
+
id: '1')
|
30
|
+
end
|
31
|
+
|
32
|
+
it 'applies the serialization when indexing' do
|
33
|
+
expect(search_result['_source']).to eq('title' => 'Test')
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
context 'when a document is updated' do
|
38
|
+
|
39
|
+
before do
|
40
|
+
article.update_attributes(title: 'UPDATED', status: 'yellow')
|
41
|
+
ArticleWithCustomSerialization.__elasticsearch__.refresh_index!
|
42
|
+
end
|
43
|
+
|
44
|
+
let!(:article) do
|
45
|
+
art = ArticleWithCustomSerialization.create!(title: 'Test', status: 'red')
|
46
|
+
ArticleWithCustomSerialization.__elasticsearch__.refresh_index!
|
47
|
+
art
|
48
|
+
end
|
49
|
+
|
50
|
+
let(:search_result) do
|
51
|
+
ArticleWithCustomSerialization.__elasticsearch__.client.get(index: 'article_with_custom_serializations',
|
52
|
+
type: '_doc',
|
53
|
+
id: article.id)
|
54
|
+
end
|
55
|
+
|
56
|
+
it 'applies the serialization when updating' do
|
57
|
+
expect(search_result['_source']).to eq('title' => 'UPDATED')
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
@@ -0,0 +1,207 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Elasticsearch::Model::Adapter::ActiveRecord do
|
4
|
+
|
5
|
+
before(:all) do
|
6
|
+
class DummyClassForActiveRecord; end
|
7
|
+
end
|
8
|
+
|
9
|
+
after(:all) do
|
10
|
+
Elasticsearch::Model::Adapter::Adapter.adapters.delete(DummyClassForActiveRecord)
|
11
|
+
remove_classes(DummyClassForActiveRecord)
|
12
|
+
end
|
13
|
+
|
14
|
+
let(:model) do
|
15
|
+
DummyClassForActiveRecord.new.tap do |m|
|
16
|
+
allow(m).to receive(:response).and_return(double('response', response: response))
|
17
|
+
allow(m).to receive(:ids).and_return(ids)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
let(:response) do
|
22
|
+
{ 'hits' => {'hits' => [ {'_id' => 2 }, {'_id' => 1 } ]} }
|
23
|
+
end
|
24
|
+
|
25
|
+
let(:ids) do
|
26
|
+
[2, 1]
|
27
|
+
end
|
28
|
+
|
29
|
+
let(:record_1) do
|
30
|
+
double('record').tap do |rec|
|
31
|
+
allow(rec).to receive(:id).and_return(1)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
let(:record_2) do
|
36
|
+
double('record').tap do |rec|
|
37
|
+
allow(rec).to receive(:id).and_return(2)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
let(:records) do
|
42
|
+
[record_1, record_2].tap do |r|
|
43
|
+
allow(r).to receive(:load).and_return(true)
|
44
|
+
allow(r).to receive(:exec_queries).and_return(true)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
describe 'adapter registration' do
|
49
|
+
|
50
|
+
before(:all) do
|
51
|
+
DummyClassForActiveRecord.__send__ :include, Elasticsearch::Model::Adapter::ActiveRecord::Records
|
52
|
+
end
|
53
|
+
|
54
|
+
it 'can register an adapater' do
|
55
|
+
expect(Elasticsearch::Model::Adapter.adapters[Elasticsearch::Model::Adapter::ActiveRecord]).not_to be_nil
|
56
|
+
expect(Elasticsearch::Model::Adapter.adapters[Elasticsearch::Model::Adapter::ActiveRecord].call(DummyClassForActiveRecord)).to be(false)
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
describe '#records' do
|
61
|
+
|
62
|
+
before(:all) do
|
63
|
+
DummyClassForActiveRecord.__send__ :include, Elasticsearch::Model::Adapter::ActiveRecord::Records
|
64
|
+
end
|
65
|
+
|
66
|
+
let(:instance) do
|
67
|
+
model.tap do |inst|
|
68
|
+
allow(inst).to receive(:klass).and_return(double('class', primary_key: :some_key, where: records)).at_least(:once)
|
69
|
+
allow(inst).to receive(:order).and_return(double('class', primary_key: :some_key, where: records)).at_least(:once)
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
it 'returns the list of records' do
|
74
|
+
expect(instance.records).to eq(records)
|
75
|
+
end
|
76
|
+
|
77
|
+
it 'loads the records' do
|
78
|
+
expect(instance.load).to eq(true)
|
79
|
+
end
|
80
|
+
|
81
|
+
context 'when :includes is specified' do
|
82
|
+
|
83
|
+
before do
|
84
|
+
expect(records).to receive(:includes).with([:submodel]).once.and_return(records)
|
85
|
+
instance.options[:includes] = [:submodel]
|
86
|
+
end
|
87
|
+
|
88
|
+
it 'incorporates the includes option in the query' do
|
89
|
+
expect(instance.records).to eq(records)
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
describe 'callbacks registration' do
|
95
|
+
|
96
|
+
before do
|
97
|
+
expect(DummyClassForActiveRecord).to receive(:after_commit).exactly(3).times
|
98
|
+
end
|
99
|
+
|
100
|
+
it 'should register the model class for callbacks' do
|
101
|
+
Elasticsearch::Model::Adapter::ActiveRecord::Callbacks.included(DummyClassForActiveRecord)
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
describe 'importing' do
|
106
|
+
|
107
|
+
before do
|
108
|
+
DummyClassForActiveRecord.__send__ :extend, Elasticsearch::Model::Adapter::ActiveRecord::Importing
|
109
|
+
end
|
110
|
+
|
111
|
+
context 'when an invalid scope is specified' do
|
112
|
+
|
113
|
+
it 'raises a NoMethodError' do
|
114
|
+
expect {
|
115
|
+
DummyClassForActiveRecord.__find_in_batches(scope: :not_found_method)
|
116
|
+
}.to raise_exception(NoMethodError)
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
context 'when a valid scope is specified' do
|
121
|
+
|
122
|
+
before do
|
123
|
+
expect(DummyClassForActiveRecord).to receive(:find_in_batches).once.and_return([])
|
124
|
+
expect(DummyClassForActiveRecord).to receive(:published).once.and_return(DummyClassForActiveRecord)
|
125
|
+
end
|
126
|
+
|
127
|
+
it 'uses the scope' do
|
128
|
+
expect(DummyClassForActiveRecord.__find_in_batches(scope: :published)).to eq([])
|
129
|
+
end
|
130
|
+
end
|
131
|
+
|
132
|
+
context 'allow query criteria to be specified' do
|
133
|
+
|
134
|
+
before do
|
135
|
+
expect(DummyClassForActiveRecord).to receive(:find_in_batches).once.and_return([])
|
136
|
+
expect(DummyClassForActiveRecord).to receive(:where).with(color: 'red').once.and_return(DummyClassForActiveRecord)
|
137
|
+
end
|
138
|
+
|
139
|
+
it 'uses the scope' do
|
140
|
+
expect(DummyClassForActiveRecord.__find_in_batches(query: -> { where(color: 'red') })).to eq([])
|
141
|
+
end
|
142
|
+
end
|
143
|
+
|
144
|
+
context 'when preprocessing batches' do
|
145
|
+
|
146
|
+
context 'if the query returns results' do
|
147
|
+
|
148
|
+
before do
|
149
|
+
class << DummyClassForActiveRecord
|
150
|
+
def find_in_batches(options = {}, &block)
|
151
|
+
yield [:a, :b]
|
152
|
+
end
|
153
|
+
|
154
|
+
def update_batch(batch)
|
155
|
+
batch.collect { |b| b.to_s + '!' }
|
156
|
+
end
|
157
|
+
end
|
158
|
+
end
|
159
|
+
|
160
|
+
it 'applies the preprocessing method' do
|
161
|
+
DummyClassForActiveRecord.__find_in_batches(preprocess: :update_batch) do |batch|
|
162
|
+
expect(batch).to match(['a!', 'b!'])
|
163
|
+
end
|
164
|
+
end
|
165
|
+
end
|
166
|
+
|
167
|
+
context 'if the query does not return results' do
|
168
|
+
|
169
|
+
before do
|
170
|
+
class << DummyClassForActiveRecord
|
171
|
+
def find_in_batches(options = {}, &block)
|
172
|
+
yield [:a, :b]
|
173
|
+
end
|
174
|
+
|
175
|
+
def update_batch(batch)
|
176
|
+
[]
|
177
|
+
end
|
178
|
+
end
|
179
|
+
end
|
180
|
+
|
181
|
+
it 'applies the preprocessing method' do
|
182
|
+
DummyClassForActiveRecord.__find_in_batches(preprocess: :update_batch) do |batch|
|
183
|
+
expect(batch).to match([])
|
184
|
+
end
|
185
|
+
end
|
186
|
+
end
|
187
|
+
end
|
188
|
+
|
189
|
+
context 'when transforming models' do
|
190
|
+
|
191
|
+
let(:instance) do
|
192
|
+
model.tap do |inst|
|
193
|
+
allow(inst).to receive(:id).and_return(1)
|
194
|
+
allow(inst).to receive(:__elasticsearch__).and_return(double('object', id: 1, as_indexed_json: {}))
|
195
|
+
end
|
196
|
+
end
|
197
|
+
|
198
|
+
it 'returns an proc' do
|
199
|
+
expect(DummyClassForActiveRecord.__transform.respond_to?(:call)).to be(true)
|
200
|
+
end
|
201
|
+
|
202
|
+
it 'provides a default transformation' do
|
203
|
+
expect(DummyClassForActiveRecord.__transform.call(instance)).to eq(index: { _id: 1, data: {} })
|
204
|
+
end
|
205
|
+
end
|
206
|
+
end
|
207
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Elasticsearch::Model::Adapter::Default do
|
4
|
+
|
5
|
+
before(:all) do
|
6
|
+
class DummyClassForDefaultAdapter; end
|
7
|
+
DummyClassForDefaultAdapter.__send__ :include, Elasticsearch::Model::Adapter::Default::Records
|
8
|
+
DummyClassForDefaultAdapter.__send__ :include, Elasticsearch::Model::Adapter::Default::Importing
|
9
|
+
end
|
10
|
+
|
11
|
+
after(:all) do
|
12
|
+
Elasticsearch::Model::Adapter::Adapter.adapters.delete(DummyClassForDefaultAdapter)
|
13
|
+
remove_classes(DummyClassForDefaultAdapter)
|
14
|
+
end
|
15
|
+
|
16
|
+
let(:instance) do
|
17
|
+
DummyClassForDefaultAdapter.new.tap do |m|
|
18
|
+
allow(m).to receive(:klass).and_return(double('class', primary_key: :some_key, find: [1])).at_least(:once)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
it 'should have the default records implementation' do
|
23
|
+
expect(instance.records).to eq([1])
|
24
|
+
end
|
25
|
+
|
26
|
+
it 'should have the default Callback implementation' do
|
27
|
+
expect(Elasticsearch::Model::Adapter::Default::Callbacks).to be_a(Module)
|
28
|
+
end
|
29
|
+
|
30
|
+
it 'should have the default Importing implementation' do
|
31
|
+
expect {
|
32
|
+
DummyClassForDefaultAdapter.new.__find_in_batches
|
33
|
+
}.to raise_exception(Elasticsearch::Model::NotImplemented)
|
34
|
+
end
|
35
|
+
|
36
|
+
it 'should have the default transform implementation' do
|
37
|
+
expect {
|
38
|
+
DummyClassForDefaultAdapter.new.__transform
|
39
|
+
}.to raise_exception(Elasticsearch::Model::NotImplemented)
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,267 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Elasticsearch::Model::Adapter::Mongoid, if: test_mongoid? do
|
4
|
+
|
5
|
+
before(:all) do
|
6
|
+
connect_mongoid('mongoid_test')
|
7
|
+
Elasticsearch::Model::Adapter.register \
|
8
|
+
Elasticsearch::Model::Adapter::Mongoid,
|
9
|
+
lambda { |klass| !!defined?(::Mongoid::Document) && klass.respond_to?(:ancestors) && klass.ancestors.include?(::Mongoid::Document) }
|
10
|
+
|
11
|
+
MongoidArticle.__elasticsearch__.create_index! force: true
|
12
|
+
|
13
|
+
MongoidArticle.delete_all
|
14
|
+
|
15
|
+
MongoidArticle.__elasticsearch__.refresh_index!
|
16
|
+
MongoidArticle.__elasticsearch__.client.cluster.health wait_for_status: 'yellow'
|
17
|
+
end
|
18
|
+
|
19
|
+
after do
|
20
|
+
clear_indices(MongoidArticle)
|
21
|
+
clear_tables(MongoidArticle)
|
22
|
+
end
|
23
|
+
|
24
|
+
describe 'searching' do
|
25
|
+
|
26
|
+
before do
|
27
|
+
MongoidArticle.create! title: 'Test'
|
28
|
+
MongoidArticle.create! title: 'Testing Coding'
|
29
|
+
MongoidArticle.create! title: 'Coding'
|
30
|
+
MongoidArticle.__elasticsearch__.refresh_index!
|
31
|
+
end
|
32
|
+
|
33
|
+
let(:search_result) do
|
34
|
+
MongoidArticle.search('title:test')
|
35
|
+
end
|
36
|
+
|
37
|
+
it 'find the documents successfully' do
|
38
|
+
expect(search_result.results.size).to eq(2)
|
39
|
+
expect(search_result.records.size).to eq(2)
|
40
|
+
end
|
41
|
+
|
42
|
+
describe '#results' do
|
43
|
+
|
44
|
+
it 'returns a Elasticsearch::Model::Response::Result' do
|
45
|
+
expect(search_result.results.first).to be_a(Elasticsearch::Model::Response::Result)
|
46
|
+
end
|
47
|
+
|
48
|
+
it 'retrieves the document from Elasticsearch' do
|
49
|
+
expect(search_result.results.first.title).to eq('Test')
|
50
|
+
end
|
51
|
+
|
52
|
+
it 'retrieves all results' do
|
53
|
+
expect(search_result.results.collect(&:title)).to match(['Test', 'Testing Coding'])
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
describe '#records' do
|
58
|
+
|
59
|
+
it 'returns an instance of the model' do
|
60
|
+
expect(search_result.records.first).to be_a(MongoidArticle)
|
61
|
+
end
|
62
|
+
|
63
|
+
it 'retrieves the document from Elasticsearch' do
|
64
|
+
expect(search_result.records.first.title).to eq('Test')
|
65
|
+
end
|
66
|
+
|
67
|
+
it 'iterates over the records' do
|
68
|
+
expect(search_result.records.first.title).to eq('Test')
|
69
|
+
end
|
70
|
+
|
71
|
+
it 'retrieves all records' do
|
72
|
+
expect(search_result.records.collect(&:title)).to match(['Test', 'Testing Coding'])
|
73
|
+
end
|
74
|
+
|
75
|
+
describe '#each_with_hit' do
|
76
|
+
|
77
|
+
it 'yields each hit with the model object' do
|
78
|
+
search_result.records.each_with_hit do |r, h|
|
79
|
+
expect(h._source).not_to be_nil
|
80
|
+
expect(h._source.title).not_to be_nil
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
it 'preserves the search order' do
|
85
|
+
search_result.records.each_with_hit do |r, h|
|
86
|
+
expect(r.id.to_s).to eq(h._id)
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
describe '#map_with_hit' do
|
92
|
+
|
93
|
+
it 'yields each hit with the model object' do
|
94
|
+
search_result.records.map_with_hit do |r, h|
|
95
|
+
expect(h._source).not_to be_nil
|
96
|
+
expect(h._source.title).not_to be_nil
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
it 'preserves the search order' do
|
101
|
+
search_result.records.map_with_hit do |r, h|
|
102
|
+
expect(r.id.to_s).to eq(h._id)
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
describe '#destroy' do
|
110
|
+
|
111
|
+
let(:article) do
|
112
|
+
MongoidArticle.create!(title: 'Test')
|
113
|
+
end
|
114
|
+
|
115
|
+
before do
|
116
|
+
article
|
117
|
+
MongoidArticle.create!(title: 'Coding')
|
118
|
+
article.destroy
|
119
|
+
MongoidArticle.__elasticsearch__.refresh_index!
|
120
|
+
end
|
121
|
+
|
122
|
+
it 'removes documents from the index' do
|
123
|
+
expect(MongoidArticle.search('title:test').results.total).to eq(0)
|
124
|
+
expect(MongoidArticle.search('title:code').results.total).to eq(1)
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
describe 'updates to the document' do
|
129
|
+
|
130
|
+
let(:article) do
|
131
|
+
MongoidArticle.create!(title: 'Test')
|
132
|
+
end
|
133
|
+
|
134
|
+
before do
|
135
|
+
article.title = 'Writing'
|
136
|
+
article.save
|
137
|
+
MongoidArticle.__elasticsearch__.refresh_index!
|
138
|
+
end
|
139
|
+
|
140
|
+
it 'indexes updates' do
|
141
|
+
expect(MongoidArticle.search('title:write').results.total).to eq(1)
|
142
|
+
expect(MongoidArticle.search('title:test').results.total).to eq(0)
|
143
|
+
end
|
144
|
+
end
|
145
|
+
|
146
|
+
describe 'DSL search' do
|
147
|
+
|
148
|
+
before do
|
149
|
+
MongoidArticle.create! title: 'Test'
|
150
|
+
MongoidArticle.create! title: 'Testing Coding'
|
151
|
+
MongoidArticle.create! title: 'Coding'
|
152
|
+
MongoidArticle.__elasticsearch__.refresh_index!
|
153
|
+
end
|
154
|
+
|
155
|
+
let(:search_result) do
|
156
|
+
MongoidArticle.search(query: { match: { title: { query: 'test' } } })
|
157
|
+
end
|
158
|
+
|
159
|
+
it 'finds the matching documents' do
|
160
|
+
expect(search_result.results.size).to eq(2)
|
161
|
+
expect(search_result.records.size).to eq(2)
|
162
|
+
end
|
163
|
+
end
|
164
|
+
|
165
|
+
describe 'paging a collection' do
|
166
|
+
|
167
|
+
before do
|
168
|
+
MongoidArticle.create! title: 'Test'
|
169
|
+
MongoidArticle.create! title: 'Testing Coding'
|
170
|
+
MongoidArticle.create! title: 'Coding'
|
171
|
+
MongoidArticle.__elasticsearch__.refresh_index!
|
172
|
+
end
|
173
|
+
|
174
|
+
let(:search_result) do
|
175
|
+
MongoidArticle.search(query: { match: { title: { query: 'test' } } },
|
176
|
+
size: 2,
|
177
|
+
from: 1)
|
178
|
+
end
|
179
|
+
|
180
|
+
it 'applies the size and from parameters' do
|
181
|
+
expect(search_result.results.size).to eq(1)
|
182
|
+
expect(search_result.results.first.title).to eq('Testing Coding')
|
183
|
+
expect(search_result.records.size).to eq(1)
|
184
|
+
expect(search_result.records.first.title).to eq('Testing Coding')
|
185
|
+
end
|
186
|
+
end
|
187
|
+
|
188
|
+
describe 'importing' do
|
189
|
+
|
190
|
+
before do
|
191
|
+
97.times { |i| MongoidArticle.create! title: "Test #{i}" }
|
192
|
+
MongoidArticle.__elasticsearch__.create_index! force: true
|
193
|
+
MongoidArticle.__elasticsearch__.client.cluster.health wait_for_status: 'yellow'
|
194
|
+
end
|
195
|
+
|
196
|
+
context 'when there is no default scope' do
|
197
|
+
|
198
|
+
let!(:batch_count) do
|
199
|
+
batches = 0
|
200
|
+
errors = MongoidArticle.import(batch_size: 10) do |response|
|
201
|
+
batches += 1
|
202
|
+
end
|
203
|
+
MongoidArticle.__elasticsearch__.refresh_index!
|
204
|
+
batches
|
205
|
+
end
|
206
|
+
|
207
|
+
it 'imports all the documents' do
|
208
|
+
expect(MongoidArticle.search('*').results.total).to eq(97)
|
209
|
+
end
|
210
|
+
|
211
|
+
it 'uses the specified batch size' do
|
212
|
+
expect(batch_count).to eq(10)
|
213
|
+
end
|
214
|
+
end
|
215
|
+
|
216
|
+
context 'when there is a default scope' do
|
217
|
+
|
218
|
+
around(:all) do |example|
|
219
|
+
10.times { |i| MongoidArticle.create! title: 'Test', views: "#{i}" }
|
220
|
+
MongoidArticle.default_scope -> { MongoidArticle.gt(views: 3) }
|
221
|
+
example.run
|
222
|
+
MongoidArticle.default_scoping = nil
|
223
|
+
end
|
224
|
+
|
225
|
+
before do
|
226
|
+
MongoidArticle.__elasticsearch__.import
|
227
|
+
MongoidArticle.__elasticsearch__.refresh_index!
|
228
|
+
end
|
229
|
+
|
230
|
+
it 'uses the default scope' do
|
231
|
+
expect(MongoidArticle.search('*').results.total).to eq(6)
|
232
|
+
end
|
233
|
+
end
|
234
|
+
|
235
|
+
context 'when there is a default scope and a query specified' do
|
236
|
+
|
237
|
+
around(:all) do |example|
|
238
|
+
10.times { |i| MongoidArticle.create! title: 'Test', views: "#{i}" }
|
239
|
+
MongoidArticle.default_scope -> { MongoidArticle.gt(views: 3) }
|
240
|
+
example.run
|
241
|
+
MongoidArticle.default_scoping = nil
|
242
|
+
end
|
243
|
+
|
244
|
+
before do
|
245
|
+
MongoidArticle.import(query: -> { lte(views: 4) })
|
246
|
+
MongoidArticle.__elasticsearch__.refresh_index!
|
247
|
+
end
|
248
|
+
|
249
|
+
it 'combines the query and the default scope' do
|
250
|
+
expect(MongoidArticle.search('*').results.total).to eq(1)
|
251
|
+
end
|
252
|
+
end
|
253
|
+
|
254
|
+
context 'when the batch is empty' do
|
255
|
+
|
256
|
+
before do
|
257
|
+
MongoidArticle.delete_all
|
258
|
+
MongoidArticle.import
|
259
|
+
MongoidArticle.__elasticsearch__.refresh_index!
|
260
|
+
end
|
261
|
+
|
262
|
+
it 'does not make any requests to create documents' do
|
263
|
+
expect(MongoidArticle.search('*').results.total).to eq(0)
|
264
|
+
end
|
265
|
+
end
|
266
|
+
end
|
267
|
+
end
|