elasticsearch-model 5.1.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 +18 -11
- data/Rakefile +27 -36
- data/elasticsearch-model.gemspec +5 -5
- data/examples/activerecord_custom_analyzer.rb +135 -0
- data/examples/activerecord_mapping_completion.rb +3 -18
- data/examples/datamapper_article.rb +11 -1
- data/gemfiles/3.0.gemfile +6 -1
- data/gemfiles/4.0.gemfile +7 -1
- data/gemfiles/5.0.gemfile +7 -1
- data/lib/elasticsearch/model/adapters/active_record.rb +7 -23
- data/lib/elasticsearch/model/adapters/mongoid.rb +10 -3
- data/lib/elasticsearch/model/importing.rb +1 -1
- data/lib/elasticsearch/model/indexing.rb +6 -4
- data/lib/elasticsearch/model/naming.rb +9 -2
- data/lib/elasticsearch/model/response/aggregations.rb +1 -1
- data/lib/elasticsearch/model/response/base.rb +3 -2
- 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/pagination.rb +2 -192
- data/lib/elasticsearch/model/response/result.rb +1 -1
- data/lib/elasticsearch/model/response/suggestions.rb +1 -1
- data/lib/elasticsearch/model/response.rb +11 -10
- data/lib/elasticsearch/model/version.rb +1 -1
- data/lib/elasticsearch/model.rb +15 -8
- 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/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/app.rb +21 -0
- data/spec/support/model.json +1 -0
- metadata +134 -90
- data/test/integration/active_record_associations_parent_child_test.rb +0 -147
- data/test/integration/active_record_associations_test.rb +0 -339
- data/test/integration/active_record_basic_test.rb +0 -251
- data/test/integration/active_record_custom_serialization_test.rb +0 -67
- data/test/integration/active_record_import_test.rb +0 -115
- data/test/integration/active_record_namespaced_model_test.rb +0 -54
- 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 -176
- 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 -104
- 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 -203
- data/test/unit/indexing_test.rb +0 -687
- 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 -31
- 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
- /data/{test → spec}/support/model.yml +0 -0
|
@@ -0,0 +1,334 @@
|
|
|
1
|
+
require 'spec_helper'
|
|
2
|
+
|
|
3
|
+
describe 'Elasticsearch::Model::Adapter::ActiveRecord Associations' do
|
|
4
|
+
|
|
5
|
+
before(:all) do
|
|
6
|
+
ActiveRecord::Schema.define(version: 1) do
|
|
7
|
+
create_table :categories do |t|
|
|
8
|
+
t.string :title
|
|
9
|
+
t.timestamps null: false
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
create_table :categories_posts do |t|
|
|
13
|
+
t.references :post, :category
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
create_table :authors do |t|
|
|
17
|
+
t.string :first_name, :last_name
|
|
18
|
+
t.timestamps null: false
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
create_table :authorships do |t|
|
|
22
|
+
t.string :first_name, :last_name
|
|
23
|
+
t.references :post
|
|
24
|
+
t.references :author
|
|
25
|
+
t.timestamps null: false
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
create_table :comments do |t|
|
|
29
|
+
t.string :text
|
|
30
|
+
t.string :author
|
|
31
|
+
t.references :post
|
|
32
|
+
t.timestamps null: false
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
add_index(:comments, :post_id) unless index_exists?(:comments, :post_id)
|
|
36
|
+
|
|
37
|
+
create_table :posts do |t|
|
|
38
|
+
t.string :title
|
|
39
|
+
t.text :text
|
|
40
|
+
t.boolean :published
|
|
41
|
+
t.timestamps null: false
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
Comment.__send__ :include, Elasticsearch::Model
|
|
46
|
+
Comment.__send__ :include, Elasticsearch::Model::Callbacks
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
before do
|
|
50
|
+
clear_tables(:categories, :categories_posts, :authors, :authorships, :comments, :posts)
|
|
51
|
+
clear_indices(Post)
|
|
52
|
+
Post.__elasticsearch__.create_index!(force: true)
|
|
53
|
+
Comment.__elasticsearch__.create_index!(force: true)
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
after do
|
|
57
|
+
clear_tables(Post, Category)
|
|
58
|
+
clear_indices(Post)
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
context 'when a document is created' do
|
|
62
|
+
|
|
63
|
+
before do
|
|
64
|
+
Post.create!(title: 'Test')
|
|
65
|
+
Post.create!(title: 'Testing Coding')
|
|
66
|
+
Post.create!(title: 'Coding')
|
|
67
|
+
Post.__elasticsearch__.refresh_index!
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
let(:search_result) do
|
|
71
|
+
Post.search('title:test')
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
it 'indexes the document' do
|
|
75
|
+
expect(search_result.results.size).to eq(2)
|
|
76
|
+
expect(search_result.results.first.title).to eq('Test')
|
|
77
|
+
expect(search_result.records.size).to eq(2)
|
|
78
|
+
expect(search_result.records.first.title).to eq('Test')
|
|
79
|
+
end
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
describe 'has_many_and_belongs_to association' do
|
|
83
|
+
|
|
84
|
+
context 'when an association is updated' do
|
|
85
|
+
|
|
86
|
+
before do
|
|
87
|
+
post.categories = [category_a, category_b]
|
|
88
|
+
Post.__elasticsearch__.refresh_index!
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
let(:category_a) do
|
|
92
|
+
Category.where(title: "One").first_or_create!
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
let(:category_b) do
|
|
96
|
+
Category.where(title: "Two").first_or_create!
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
let(:post) do
|
|
100
|
+
Post.create! title: "First Post", text: "This is the first post..."
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
let(:search_result) do
|
|
104
|
+
Post.search(query: {
|
|
105
|
+
bool: {
|
|
106
|
+
must: {
|
|
107
|
+
multi_match: {
|
|
108
|
+
fields: ['title'],
|
|
109
|
+
query: 'first'
|
|
110
|
+
}
|
|
111
|
+
},
|
|
112
|
+
filter: {
|
|
113
|
+
terms: {
|
|
114
|
+
categories: ['One']
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
} )
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
it 'applies the update with' do
|
|
122
|
+
expect(search_result.results.size).to eq(1)
|
|
123
|
+
expect(search_result.results.first.title).to eq('First Post')
|
|
124
|
+
expect(search_result.records.size).to eq(1)
|
|
125
|
+
expect(search_result.records.first.title).to eq('First Post')
|
|
126
|
+
end
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
context 'when an association is deleted' do
|
|
130
|
+
|
|
131
|
+
before do
|
|
132
|
+
post.categories = [category_a, category_b]
|
|
133
|
+
post.categories = [category_b]
|
|
134
|
+
Post.__elasticsearch__.refresh_index!
|
|
135
|
+
end
|
|
136
|
+
|
|
137
|
+
let(:category_a) do
|
|
138
|
+
Category.where(title: "One").first_or_create!
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
let(:category_b) do
|
|
142
|
+
Category.where(title: "Two").first_or_create!
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
let(:post) do
|
|
146
|
+
Post.create! title: "First Post", text: "This is the first post..."
|
|
147
|
+
end
|
|
148
|
+
|
|
149
|
+
let(:search_result) do
|
|
150
|
+
Post.search(query: {
|
|
151
|
+
bool: {
|
|
152
|
+
must: {
|
|
153
|
+
multi_match: {
|
|
154
|
+
fields: ['title'],
|
|
155
|
+
query: 'first'
|
|
156
|
+
}
|
|
157
|
+
},
|
|
158
|
+
filter: {
|
|
159
|
+
terms: {
|
|
160
|
+
categories: ['One']
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
} )
|
|
165
|
+
end
|
|
166
|
+
|
|
167
|
+
it 'applies the update with a reindex' do
|
|
168
|
+
expect(search_result.results.size).to eq(0)
|
|
169
|
+
expect(search_result.records.size).to eq(0)
|
|
170
|
+
end
|
|
171
|
+
end
|
|
172
|
+
end
|
|
173
|
+
|
|
174
|
+
describe 'has_many through association' do
|
|
175
|
+
|
|
176
|
+
context 'when the association is updated' do
|
|
177
|
+
|
|
178
|
+
before do
|
|
179
|
+
author_a = Author.where(first_name: "John", last_name: "Smith").first_or_create!
|
|
180
|
+
author_b = Author.where(first_name: "Mary", last_name: "Smith").first_or_create!
|
|
181
|
+
author_c = Author.where(first_name: "Kobe", last_name: "Griss").first_or_create!
|
|
182
|
+
|
|
183
|
+
# Create posts
|
|
184
|
+
post_1 = Post.create!(title: "First Post", text: "This is the first post...")
|
|
185
|
+
post_2 = Post.create!(title: "Second Post", text: "This is the second post...")
|
|
186
|
+
post_3 = Post.create!(title: "Third Post", text: "This is the third post...")
|
|
187
|
+
|
|
188
|
+
# Assign authors
|
|
189
|
+
post_1.authors = [author_a, author_b]
|
|
190
|
+
post_2.authors = [author_a]
|
|
191
|
+
post_3.authors = [author_c]
|
|
192
|
+
|
|
193
|
+
Post.__elasticsearch__.refresh_index!
|
|
194
|
+
end
|
|
195
|
+
|
|
196
|
+
context 'if active record is at least 4' do
|
|
197
|
+
|
|
198
|
+
let(:search_result) do
|
|
199
|
+
Post.search('authors.full_name:john')
|
|
200
|
+
end
|
|
201
|
+
|
|
202
|
+
it 'applies the update', if: active_record_at_least_4? do
|
|
203
|
+
expect(search_result.results.size).to eq(2)
|
|
204
|
+
expect(search_result.records.size).to eq(2)
|
|
205
|
+
end
|
|
206
|
+
end
|
|
207
|
+
|
|
208
|
+
context 'if active record is less than 4' do
|
|
209
|
+
|
|
210
|
+
let(:search_result) do
|
|
211
|
+
Post.search('authors.author.full_name:john')
|
|
212
|
+
end
|
|
213
|
+
|
|
214
|
+
it 'applies the update', if: !active_record_at_least_4? do
|
|
215
|
+
expect(search_result.results.size).to eq(2)
|
|
216
|
+
expect(search_result.records.size).to eq(2)
|
|
217
|
+
end
|
|
218
|
+
end
|
|
219
|
+
end
|
|
220
|
+
|
|
221
|
+
context 'when an association is added', if: active_record_at_least_4? do
|
|
222
|
+
|
|
223
|
+
before do
|
|
224
|
+
author_a = Author.where(first_name: "John", last_name: "Smith").first_or_create!
|
|
225
|
+
author_b = Author.where(first_name: "Mary", last_name: "Smith").first_or_create!
|
|
226
|
+
|
|
227
|
+
# Create posts
|
|
228
|
+
post_1 = Post.create!(title: "First Post", text: "This is the first post...")
|
|
229
|
+
|
|
230
|
+
# Assign authors
|
|
231
|
+
post_1.authors = [author_a]
|
|
232
|
+
post_1.authors << author_b
|
|
233
|
+
Post.__elasticsearch__.refresh_index!
|
|
234
|
+
end
|
|
235
|
+
|
|
236
|
+
let(:search_result) do
|
|
237
|
+
Post.search('authors.full_name:john')
|
|
238
|
+
end
|
|
239
|
+
|
|
240
|
+
it 'adds the association' do
|
|
241
|
+
expect(search_result.results.size).to eq(1)
|
|
242
|
+
expect(search_result.records.size).to eq(1)
|
|
243
|
+
end
|
|
244
|
+
end
|
|
245
|
+
end
|
|
246
|
+
|
|
247
|
+
describe 'has_many association' do
|
|
248
|
+
|
|
249
|
+
context 'when an association is added', if: active_record_at_least_4? do
|
|
250
|
+
|
|
251
|
+
before do
|
|
252
|
+
# Create posts
|
|
253
|
+
post_1 = Post.create!(title: "First Post", text: "This is the first post...")
|
|
254
|
+
post_2 = Post.create!(title: "Second Post", text: "This is the second post...")
|
|
255
|
+
|
|
256
|
+
# Add comments
|
|
257
|
+
post_1.comments.create!(author: 'John', text: 'Excellent')
|
|
258
|
+
post_1.comments.create!(author: 'Abby', text: 'Good')
|
|
259
|
+
|
|
260
|
+
post_2.comments.create!(author: 'John', text: 'Terrible')
|
|
261
|
+
|
|
262
|
+
post_1.comments.create!(author: 'John', text: 'Or rather just good...')
|
|
263
|
+
Post.__elasticsearch__.refresh_index!
|
|
264
|
+
end
|
|
265
|
+
|
|
266
|
+
let(:search_result) do
|
|
267
|
+
Post.search(query: {
|
|
268
|
+
nested: {
|
|
269
|
+
path: 'comments',
|
|
270
|
+
query: {
|
|
271
|
+
bool: {
|
|
272
|
+
must: [
|
|
273
|
+
{ match: { 'comments.author' => 'john' } },
|
|
274
|
+
{ match: { 'comments.text' => 'good' } }
|
|
275
|
+
]
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
})
|
|
280
|
+
end
|
|
281
|
+
|
|
282
|
+
it 'adds the association' do
|
|
283
|
+
expect(search_result.results.size).to eq(1)
|
|
284
|
+
end
|
|
285
|
+
end
|
|
286
|
+
end
|
|
287
|
+
|
|
288
|
+
describe '#touch' do
|
|
289
|
+
|
|
290
|
+
context 'when a touch callback is defined on the model' do
|
|
291
|
+
|
|
292
|
+
before do
|
|
293
|
+
# Create categories
|
|
294
|
+
category_a = Category.where(title: "One").first_or_create!
|
|
295
|
+
|
|
296
|
+
# Create post
|
|
297
|
+
post = Post.create!(title: "First Post", text: "This is the first post...")
|
|
298
|
+
|
|
299
|
+
# Assign category
|
|
300
|
+
post.categories << category_a
|
|
301
|
+
category_a.update_attribute(:title, "Updated")
|
|
302
|
+
category_a.posts.each { |p| p.touch }
|
|
303
|
+
|
|
304
|
+
Post.__elasticsearch__.refresh_index!
|
|
305
|
+
end
|
|
306
|
+
|
|
307
|
+
it 'executes the callback after #touch' do
|
|
308
|
+
expect(Post.search('categories:One').size).to eq(0)
|
|
309
|
+
expect(Post.search('categories:Updated').size).to eq(1)
|
|
310
|
+
end
|
|
311
|
+
end
|
|
312
|
+
end
|
|
313
|
+
|
|
314
|
+
describe '#includes' do
|
|
315
|
+
|
|
316
|
+
before do
|
|
317
|
+
post_1 = Post.create(title: 'One')
|
|
318
|
+
post_2 = Post.create(title: 'Two')
|
|
319
|
+
post_1.comments.create(text: 'First comment')
|
|
320
|
+
post_2.comments.create(text: 'Second comment')
|
|
321
|
+
|
|
322
|
+
Comment.__elasticsearch__.refresh_index!
|
|
323
|
+
end
|
|
324
|
+
|
|
325
|
+
let(:search_result) do
|
|
326
|
+
Comment.search('first').records(includes: :post)
|
|
327
|
+
end
|
|
328
|
+
|
|
329
|
+
it 'eager loads associations' do
|
|
330
|
+
expect(search_result.first.association(:post)).to be_loaded
|
|
331
|
+
expect(search_result.first.post.title).to eq('One')
|
|
332
|
+
end
|
|
333
|
+
end
|
|
334
|
+
end
|
|
@@ -0,0 +1,340 @@
|
|
|
1
|
+
require 'spec_helper'
|
|
2
|
+
|
|
3
|
+
describe Elasticsearch::Model::Adapter::ActiveRecord do
|
|
4
|
+
|
|
5
|
+
before(:all) do
|
|
6
|
+
ActiveRecord::Schema.define(:version => 1) do
|
|
7
|
+
create_table :articles do |t|
|
|
8
|
+
t.string :title
|
|
9
|
+
t.string :body
|
|
10
|
+
t.integer :clicks, :default => 0
|
|
11
|
+
t.datetime :created_at, :default => 'NOW()'
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
Article.delete_all
|
|
16
|
+
Article.__elasticsearch__.create_index!(force: true)
|
|
17
|
+
|
|
18
|
+
Article.create!(title: 'Test', body: '', clicks: 1)
|
|
19
|
+
Article.create!(title: 'Testing Coding', body: '', clicks: 2)
|
|
20
|
+
Article.create!(title: 'Coding', body: '', clicks: 3)
|
|
21
|
+
|
|
22
|
+
Article.__elasticsearch__.refresh_index!
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
describe 'indexing a document' do
|
|
26
|
+
|
|
27
|
+
let(:search_result) do
|
|
28
|
+
Article.search('title:test')
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
it 'allows searching for documents' do
|
|
32
|
+
expect(search_result.results.size).to be(2)
|
|
33
|
+
expect(search_result.records.size).to be(2)
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
describe '#results' do
|
|
38
|
+
|
|
39
|
+
let(:search_result) do
|
|
40
|
+
Article.search('title:test')
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
it 'returns an instance of Response::Result' do
|
|
44
|
+
expect(search_result.results.first).to be_a(Elasticsearch::Model::Response::Result)
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
it 'prooperly loads the document' do
|
|
48
|
+
expect(search_result.results.first.title).to eq('Test')
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
context 'when the result contains other data' do
|
|
52
|
+
|
|
53
|
+
let(:search_result) do
|
|
54
|
+
Article.search(query: { match: { title: 'test' } }, highlight: { fields: { title: {} } })
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
it 'allows access to the Elasticsearch result' do
|
|
58
|
+
expect(search_result.results.first.title).to eq('Test')
|
|
59
|
+
expect(search_result.results.first.title?).to be(true)
|
|
60
|
+
expect(search_result.results.first.boo?).to be(false)
|
|
61
|
+
expect(search_result.results.first.highlight?).to be(true)
|
|
62
|
+
expect(search_result.results.first.highlight.title?).to be(true)
|
|
63
|
+
expect(search_result.results.first.highlight.boo?).to be(false)
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
describe '#records' do
|
|
69
|
+
|
|
70
|
+
let(:search_result) do
|
|
71
|
+
Article.search('title:test')
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
it 'returns an instance of the model' do
|
|
75
|
+
expect(search_result.records.first).to be_a(Article)
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
it 'prooperly loads the document' do
|
|
79
|
+
expect(search_result.records.first.title).to eq('Test')
|
|
80
|
+
end
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
describe 'Enumerable' do
|
|
84
|
+
|
|
85
|
+
let(:search_result) do
|
|
86
|
+
Article.search('title:test')
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
it 'allows iteration over results' do
|
|
90
|
+
expect(search_result.results.map(&:_id)).to eq(['1', '2'])
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
it 'allows iteration over records' do
|
|
94
|
+
expect(search_result.records.map(&:id)).to eq([1, 2])
|
|
95
|
+
end
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
describe '#id' do
|
|
99
|
+
|
|
100
|
+
let(:search_result) do
|
|
101
|
+
Article.search('title:test')
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
it 'returns the id' do
|
|
105
|
+
expect(search_result.results.first.id).to eq('1')
|
|
106
|
+
end
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
describe '#id' do
|
|
110
|
+
|
|
111
|
+
let(:search_result) do
|
|
112
|
+
Article.search('title:test')
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
it 'returns the type' do
|
|
116
|
+
expect(search_result.results.first.type).to eq('article')
|
|
117
|
+
end
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
describe '#each_with_hit' do
|
|
121
|
+
|
|
122
|
+
let(:search_result) do
|
|
123
|
+
Article.search('title:test')
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
it 'returns the record with the Elasticsearch hit' do
|
|
127
|
+
search_result.records.each_with_hit do |r, h|
|
|
128
|
+
expect(h._score).not_to be_nil
|
|
129
|
+
expect(h._source.title).not_to be_nil
|
|
130
|
+
end
|
|
131
|
+
end
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
describe 'search results order' do
|
|
135
|
+
|
|
136
|
+
let(:search_result) do
|
|
137
|
+
Article.search(query: { match: { title: 'code' }}, sort: { clicks: :desc })
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
it 'preserves the search results order when accessing a single record' do
|
|
141
|
+
expect(search_result.records[0].clicks).to be(3)
|
|
142
|
+
expect(search_result.records[1].clicks).to be(2)
|
|
143
|
+
expect(search_result.records.first).to eq(search_result.records[0])
|
|
144
|
+
end
|
|
145
|
+
|
|
146
|
+
it 'preserves the search results order for the list of records' do
|
|
147
|
+
search_result.records.each_with_hit do |r, h|
|
|
148
|
+
expect(r.id.to_s).to eq(h._id)
|
|
149
|
+
end
|
|
150
|
+
|
|
151
|
+
search_result.records.map_with_hit do |r, h|
|
|
152
|
+
expect(r.id.to_s).to eq(h._id)
|
|
153
|
+
end
|
|
154
|
+
end
|
|
155
|
+
end
|
|
156
|
+
|
|
157
|
+
describe 'a paged collection' do
|
|
158
|
+
|
|
159
|
+
let(:search_result) do
|
|
160
|
+
Article.search(query: { match: { title: { query: 'test' } } },
|
|
161
|
+
size: 2,
|
|
162
|
+
from: 1)
|
|
163
|
+
end
|
|
164
|
+
|
|
165
|
+
it 'applies the paged options to the search' do
|
|
166
|
+
expect(search_result.results.size).to eq(1)
|
|
167
|
+
expect(search_result.results.first.title).to eq('Testing Coding')
|
|
168
|
+
expect(search_result.records.size).to eq(1)
|
|
169
|
+
expect(search_result.records.first.title).to eq('Testing Coding')
|
|
170
|
+
end
|
|
171
|
+
end
|
|
172
|
+
|
|
173
|
+
describe '#destroy' do
|
|
174
|
+
|
|
175
|
+
before do
|
|
176
|
+
Article.create!(title: 'destroy', body: '', clicks: 1)
|
|
177
|
+
Article.__elasticsearch__.refresh_index!
|
|
178
|
+
Article.where(title: 'destroy').first.destroy
|
|
179
|
+
|
|
180
|
+
Article.__elasticsearch__.refresh_index!
|
|
181
|
+
end
|
|
182
|
+
|
|
183
|
+
let(:search_result) do
|
|
184
|
+
Article.search('title:test')
|
|
185
|
+
end
|
|
186
|
+
|
|
187
|
+
it 'removes the document from the index' do
|
|
188
|
+
expect(Article.count).to eq(3)
|
|
189
|
+
expect(search_result.results.size).to eq(2)
|
|
190
|
+
expect(search_result.records.size).to eq(2)
|
|
191
|
+
end
|
|
192
|
+
end
|
|
193
|
+
|
|
194
|
+
describe 'full document updates' do
|
|
195
|
+
|
|
196
|
+
before do
|
|
197
|
+
article = Article.create!(title: 'update', body: '', clicks: 1)
|
|
198
|
+
Article.__elasticsearch__.refresh_index!
|
|
199
|
+
article.title = 'Writing'
|
|
200
|
+
article.save
|
|
201
|
+
|
|
202
|
+
Article.__elasticsearch__.refresh_index!
|
|
203
|
+
end
|
|
204
|
+
|
|
205
|
+
let(:search_result) do
|
|
206
|
+
Article.search('title:write')
|
|
207
|
+
end
|
|
208
|
+
|
|
209
|
+
it 'applies the update' do
|
|
210
|
+
expect(search_result.results.size).to eq(1)
|
|
211
|
+
expect(search_result.records.size).to eq(1)
|
|
212
|
+
end
|
|
213
|
+
end
|
|
214
|
+
|
|
215
|
+
describe 'attribute updates' do
|
|
216
|
+
|
|
217
|
+
before do
|
|
218
|
+
article = Article.create!(title: 'update', body: '', clicks: 1)
|
|
219
|
+
Article.__elasticsearch__.refresh_index!
|
|
220
|
+
article.title = 'special'
|
|
221
|
+
article.save
|
|
222
|
+
|
|
223
|
+
Article.__elasticsearch__.refresh_index!
|
|
224
|
+
end
|
|
225
|
+
|
|
226
|
+
let(:search_result) do
|
|
227
|
+
Article.search('title:special')
|
|
228
|
+
end
|
|
229
|
+
|
|
230
|
+
it 'applies the update' do
|
|
231
|
+
expect(search_result.results.size).to eq(1)
|
|
232
|
+
expect(search_result.records.size).to eq(1)
|
|
233
|
+
end
|
|
234
|
+
end
|
|
235
|
+
|
|
236
|
+
describe '#save' do
|
|
237
|
+
|
|
238
|
+
before do
|
|
239
|
+
article = Article.create!(title: 'save', body: '', clicks: 1)
|
|
240
|
+
|
|
241
|
+
ActiveRecord::Base.transaction do
|
|
242
|
+
article.body = 'dummy'
|
|
243
|
+
article.save
|
|
244
|
+
|
|
245
|
+
article.title = 'special'
|
|
246
|
+
article.save
|
|
247
|
+
end
|
|
248
|
+
|
|
249
|
+
article.__elasticsearch__.update_document
|
|
250
|
+
Article.__elasticsearch__.refresh_index!
|
|
251
|
+
end
|
|
252
|
+
|
|
253
|
+
let(:search_result) do
|
|
254
|
+
Article.search('body:dummy')
|
|
255
|
+
end
|
|
256
|
+
|
|
257
|
+
it 'applies the save' do
|
|
258
|
+
expect(search_result.results.size).to eq(1)
|
|
259
|
+
expect(search_result.records.size).to eq(1)
|
|
260
|
+
end
|
|
261
|
+
end
|
|
262
|
+
|
|
263
|
+
describe 'a DSL search' do
|
|
264
|
+
|
|
265
|
+
let(:search_result) do
|
|
266
|
+
Article.search(query: { match: { title: { query: 'test' } } })
|
|
267
|
+
end
|
|
268
|
+
|
|
269
|
+
it 'returns the results' do
|
|
270
|
+
expect(search_result.results.size).to eq(2)
|
|
271
|
+
expect(search_result.records.size).to eq(2)
|
|
272
|
+
end
|
|
273
|
+
end
|
|
274
|
+
|
|
275
|
+
describe 'chaining SQL queries on response.records' do
|
|
276
|
+
|
|
277
|
+
let(:search_result) do
|
|
278
|
+
Article.search(query: { match: { title: { query: 'test' } } })
|
|
279
|
+
end
|
|
280
|
+
|
|
281
|
+
it 'executes the SQL request with the chained query criteria' do
|
|
282
|
+
expect(search_result.records.size).to eq(2)
|
|
283
|
+
expect(search_result.records.where(title: 'Test').size).to eq(1)
|
|
284
|
+
expect(search_result.records.where(title: 'Test').first.title).to eq('Test')
|
|
285
|
+
end
|
|
286
|
+
end
|
|
287
|
+
|
|
288
|
+
describe 'ordering of SQL queries' do
|
|
289
|
+
|
|
290
|
+
context 'when order is called on the ActiveRecord query' do
|
|
291
|
+
|
|
292
|
+
let(:search_result) do
|
|
293
|
+
Article.search query: { match: { title: { query: 'test' } } }
|
|
294
|
+
end
|
|
295
|
+
|
|
296
|
+
it 'allows the SQL query to be ordered independent of the Elasticsearch results order', unless: active_record_at_least_4? do
|
|
297
|
+
expect(search_result.records.order('title DESC').first.title).to eq('Testing Coding')
|
|
298
|
+
expect(search_result.records.order('title DESC')[0].title).to eq('Testing Coding')
|
|
299
|
+
end
|
|
300
|
+
|
|
301
|
+
it 'allows the SQL query to be ordered independent of the Elasticsearch results order', if: active_record_at_least_4? do
|
|
302
|
+
expect(search_result.records.order(title: :desc).first.title).to eq('Testing Coding')
|
|
303
|
+
expect(search_result.records.order(title: :desc)[0].title).to eq('Testing Coding')
|
|
304
|
+
end
|
|
305
|
+
end
|
|
306
|
+
|
|
307
|
+
context 'when more methods are chained on the ActiveRecord query' do
|
|
308
|
+
|
|
309
|
+
let(:search_result) do
|
|
310
|
+
Article.search query: {match: {title: {query: 'test'}}}
|
|
311
|
+
end
|
|
312
|
+
|
|
313
|
+
it 'allows the SQL query to be ordered independent of the Elasticsearch results order', if: active_record_at_least_4? do
|
|
314
|
+
expect(search_result.records.distinct.order(title: :desc).first.title).to eq('Testing Coding')
|
|
315
|
+
expect(search_result.records.distinct.order(title: :desc)[0].title).to eq('Testing Coding')
|
|
316
|
+
end
|
|
317
|
+
end
|
|
318
|
+
end
|
|
319
|
+
|
|
320
|
+
describe 'access to the response via methods' do
|
|
321
|
+
|
|
322
|
+
let(:search_result) do
|
|
323
|
+
Article.search(query: { match: { title: { query: 'test' } } },
|
|
324
|
+
aggregations: {
|
|
325
|
+
dates: { date_histogram: { field: 'created_at', interval: 'hour' } },
|
|
326
|
+
clicks: { global: {}, aggregations: { min: { min: { field: 'clicks' } } } }
|
|
327
|
+
},
|
|
328
|
+
suggest: { text: 'tezt', title: { term: { field: 'title', suggest_mode: 'always' } } })
|
|
329
|
+
end
|
|
330
|
+
|
|
331
|
+
it 'allows document keys to be access via methods' do
|
|
332
|
+
expect(search_result.aggregations.dates.buckets.first.doc_count).to eq(2)
|
|
333
|
+
expect(search_result.aggregations.clicks.doc_count).to eq(6)
|
|
334
|
+
expect(search_result.aggregations.clicks.min.value).to eq(1.0)
|
|
335
|
+
expect(search_result.aggregations.clicks.max).to be_nil
|
|
336
|
+
expect(search_result.suggestions.title.first.options.size).to eq(1)
|
|
337
|
+
expect(search_result.suggestions.terms).to eq(['test'])
|
|
338
|
+
end
|
|
339
|
+
end
|
|
340
|
+
end
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
require 'spec_helper'
|
|
2
|
+
|
|
3
|
+
describe 'Elasticsearch::Model::Adapter::ActiveRecord Dynamic Index naming' do
|
|
4
|
+
|
|
5
|
+
before do
|
|
6
|
+
ArticleWithDynamicIndexName.counter = 0
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
it 'exavlues the index_name value' do
|
|
10
|
+
expect(ArticleWithDynamicIndexName.index_name).to eq('articles-1')
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
it 'revaluates the index name with each call' do
|
|
14
|
+
expect(ArticleWithDynamicIndexName.index_name).to eq('articles-1')
|
|
15
|
+
expect(ArticleWithDynamicIndexName.index_name).to eq('articles-2')
|
|
16
|
+
expect(ArticleWithDynamicIndexName.index_name).to eq('articles-3')
|
|
17
|
+
end
|
|
18
|
+
end
|