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.
Files changed (125) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile +5 -0
  3. data/README.md +18 -11
  4. data/Rakefile +27 -36
  5. data/elasticsearch-model.gemspec +5 -5
  6. data/examples/activerecord_custom_analyzer.rb +135 -0
  7. data/examples/activerecord_mapping_completion.rb +3 -18
  8. data/examples/datamapper_article.rb +11 -1
  9. data/gemfiles/3.0.gemfile +6 -1
  10. data/gemfiles/4.0.gemfile +7 -1
  11. data/gemfiles/5.0.gemfile +7 -1
  12. data/lib/elasticsearch/model/adapters/active_record.rb +7 -23
  13. data/lib/elasticsearch/model/adapters/mongoid.rb +10 -3
  14. data/lib/elasticsearch/model/importing.rb +1 -1
  15. data/lib/elasticsearch/model/indexing.rb +6 -4
  16. data/lib/elasticsearch/model/naming.rb +9 -2
  17. data/lib/elasticsearch/model/response/aggregations.rb +1 -1
  18. data/lib/elasticsearch/model/response/base.rb +3 -2
  19. data/lib/elasticsearch/model/response/pagination/kaminari.rb +109 -0
  20. data/lib/elasticsearch/model/response/pagination/will_paginate.rb +95 -0
  21. data/lib/elasticsearch/model/response/pagination.rb +2 -192
  22. data/lib/elasticsearch/model/response/result.rb +1 -1
  23. data/lib/elasticsearch/model/response/suggestions.rb +1 -1
  24. data/lib/elasticsearch/model/response.rb +11 -10
  25. data/lib/elasticsearch/model/version.rb +1 -1
  26. data/lib/elasticsearch/model.rb +15 -8
  27. data/spec/elasticsearch/model/adapter_spec.rb +119 -0
  28. data/spec/elasticsearch/model/adapters/active_record/associations_spec.rb +334 -0
  29. data/spec/elasticsearch/model/adapters/active_record/basic_spec.rb +340 -0
  30. data/spec/elasticsearch/model/adapters/active_record/dynamic_index_name_spec.rb +18 -0
  31. data/spec/elasticsearch/model/adapters/active_record/import_spec.rb +187 -0
  32. data/spec/elasticsearch/model/adapters/active_record/multi_model_spec.rb +110 -0
  33. data/spec/elasticsearch/model/adapters/active_record/namespaced_model_spec.rb +38 -0
  34. data/spec/elasticsearch/model/adapters/active_record/pagination_spec.rb +315 -0
  35. data/spec/elasticsearch/model/adapters/active_record/parent_child_spec.rb +75 -0
  36. data/spec/elasticsearch/model/adapters/active_record/serialization_spec.rb +61 -0
  37. data/spec/elasticsearch/model/adapters/active_record_spec.rb +207 -0
  38. data/spec/elasticsearch/model/adapters/default_spec.rb +41 -0
  39. data/spec/elasticsearch/model/adapters/mongoid/basic_spec.rb +267 -0
  40. data/spec/elasticsearch/model/adapters/mongoid/multi_model_spec.rb +66 -0
  41. data/spec/elasticsearch/model/adapters/mongoid_spec.rb +235 -0
  42. data/spec/elasticsearch/model/adapters/multiple_spec.rb +125 -0
  43. data/spec/elasticsearch/model/callbacks_spec.rb +33 -0
  44. data/spec/elasticsearch/model/client_spec.rb +66 -0
  45. data/spec/elasticsearch/model/hash_wrapper_spec.rb +12 -0
  46. data/spec/elasticsearch/model/importing_spec.rb +214 -0
  47. data/spec/elasticsearch/model/indexing_spec.rb +918 -0
  48. data/spec/elasticsearch/model/module_spec.rb +101 -0
  49. data/spec/elasticsearch/model/multimodel_spec.rb +55 -0
  50. data/spec/elasticsearch/model/naming_inheritance_spec.rb +184 -0
  51. data/spec/elasticsearch/model/naming_spec.rb +186 -0
  52. data/spec/elasticsearch/model/proxy_spec.rb +107 -0
  53. data/spec/elasticsearch/model/response/aggregations_spec.rb +66 -0
  54. data/spec/elasticsearch/model/response/base_spec.rb +90 -0
  55. data/spec/elasticsearch/model/response/pagination/kaminari_spec.rb +410 -0
  56. data/spec/elasticsearch/model/response/pagination/will_paginate_spec.rb +262 -0
  57. data/spec/elasticsearch/model/response/records_spec.rb +118 -0
  58. data/spec/elasticsearch/model/response/response_spec.rb +131 -0
  59. data/spec/elasticsearch/model/response/result_spec.rb +122 -0
  60. data/spec/elasticsearch/model/response/results_spec.rb +56 -0
  61. data/spec/elasticsearch/model/searching_search_request_spec.rb +112 -0
  62. data/spec/elasticsearch/model/searching_spec.rb +49 -0
  63. data/spec/elasticsearch/model/serializing_spec.rb +22 -0
  64. data/spec/spec_helper.rb +161 -0
  65. data/spec/support/app/answer.rb +33 -0
  66. data/spec/support/app/article.rb +22 -0
  67. data/spec/support/app/article_for_pagination.rb +12 -0
  68. data/spec/support/app/article_with_custom_serialization.rb +13 -0
  69. data/spec/support/app/article_with_dynamic_index_name.rb +15 -0
  70. data/spec/support/app/author.rb +9 -0
  71. data/spec/support/app/authorship.rb +4 -0
  72. data/spec/support/app/category.rb +3 -0
  73. data/spec/support/app/comment.rb +3 -0
  74. data/spec/support/app/episode.rb +11 -0
  75. data/spec/support/app/image.rb +19 -0
  76. data/spec/support/app/import_article.rb +12 -0
  77. data/spec/support/app/mongoid_article.rb +21 -0
  78. data/spec/support/app/namespaced_book.rb +10 -0
  79. data/spec/support/app/parent_and_child_searchable.rb +24 -0
  80. data/spec/support/app/post.rb +14 -0
  81. data/spec/support/app/question.rb +27 -0
  82. data/spec/support/app/searchable.rb +48 -0
  83. data/spec/support/app/series.rb +11 -0
  84. data/spec/support/app.rb +21 -0
  85. data/spec/support/model.json +1 -0
  86. metadata +134 -90
  87. data/test/integration/active_record_associations_parent_child_test.rb +0 -147
  88. data/test/integration/active_record_associations_test.rb +0 -339
  89. data/test/integration/active_record_basic_test.rb +0 -251
  90. data/test/integration/active_record_custom_serialization_test.rb +0 -67
  91. data/test/integration/active_record_import_test.rb +0 -115
  92. data/test/integration/active_record_namespaced_model_test.rb +0 -54
  93. data/test/integration/active_record_pagination_test.rb +0 -149
  94. data/test/integration/dynamic_index_name_test.rb +0 -52
  95. data/test/integration/mongoid_basic_test.rb +0 -176
  96. data/test/integration/multiple_models_test.rb +0 -176
  97. data/test/support/model.json +0 -1
  98. data/test/test_helper.rb +0 -92
  99. data/test/unit/adapter_active_record_test.rb +0 -157
  100. data/test/unit/adapter_default_test.rb +0 -41
  101. data/test/unit/adapter_mongoid_test.rb +0 -104
  102. data/test/unit/adapter_multiple_test.rb +0 -106
  103. data/test/unit/adapter_test.rb +0 -69
  104. data/test/unit/callbacks_test.rb +0 -31
  105. data/test/unit/client_test.rb +0 -27
  106. data/test/unit/hash_wrapper_test.rb +0 -13
  107. data/test/unit/importing_test.rb +0 -203
  108. data/test/unit/indexing_test.rb +0 -687
  109. data/test/unit/module_test.rb +0 -68
  110. data/test/unit/multimodel_test.rb +0 -38
  111. data/test/unit/naming_inheritance_test.rb +0 -94
  112. data/test/unit/naming_test.rb +0 -103
  113. data/test/unit/proxy_test.rb +0 -98
  114. data/test/unit/response_aggregations_test.rb +0 -46
  115. data/test/unit/response_base_test.rb +0 -40
  116. data/test/unit/response_pagination_kaminari_test.rb +0 -433
  117. data/test/unit/response_pagination_will_paginate_test.rb +0 -398
  118. data/test/unit/response_records_test.rb +0 -91
  119. data/test/unit/response_result_test.rb +0 -90
  120. data/test/unit/response_results_test.rb +0 -31
  121. data/test/unit/response_test.rb +0 -104
  122. data/test/unit/searching_search_request_test.rb +0 -78
  123. data/test/unit/searching_test.rb +0 -41
  124. data/test/unit/serializing_test.rb +0 -17
  125. /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