elasticsearch-model 6.0.0 → 6.1.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/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
|