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.
Files changed (118) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile +5 -0
  3. data/README.md +14 -7
  4. data/Rakefile +27 -36
  5. data/elasticsearch-model.gemspec +1 -1
  6. data/examples/activerecord_mapping_completion.rb +2 -15
  7. data/gemfiles/3.0.gemfile +6 -1
  8. data/gemfiles/4.0.gemfile +7 -1
  9. data/gemfiles/5.0.gemfile +6 -0
  10. data/lib/elasticsearch/model.rb +15 -8
  11. data/lib/elasticsearch/model/adapters/active_record.rb +7 -26
  12. data/lib/elasticsearch/model/indexing.rb +5 -3
  13. data/lib/elasticsearch/model/naming.rb +6 -1
  14. data/lib/elasticsearch/model/response.rb +2 -2
  15. data/lib/elasticsearch/model/response/pagination.rb +2 -192
  16. data/lib/elasticsearch/model/response/pagination/kaminari.rb +109 -0
  17. data/lib/elasticsearch/model/response/pagination/will_paginate.rb +95 -0
  18. data/lib/elasticsearch/model/response/result.rb +1 -1
  19. data/lib/elasticsearch/model/version.rb +1 -1
  20. data/spec/elasticsearch/model/adapter_spec.rb +119 -0
  21. data/spec/elasticsearch/model/adapters/active_record/associations_spec.rb +334 -0
  22. data/spec/elasticsearch/model/adapters/active_record/basic_spec.rb +340 -0
  23. data/spec/elasticsearch/model/adapters/active_record/dynamic_index_name_spec.rb +18 -0
  24. data/spec/elasticsearch/model/adapters/active_record/import_spec.rb +187 -0
  25. data/spec/elasticsearch/model/adapters/active_record/multi_model_spec.rb +110 -0
  26. data/spec/elasticsearch/model/adapters/active_record/namespaced_model_spec.rb +38 -0
  27. data/spec/elasticsearch/model/adapters/active_record/pagination_spec.rb +315 -0
  28. data/spec/elasticsearch/model/adapters/active_record/parent_child_spec.rb +75 -0
  29. data/spec/elasticsearch/model/adapters/active_record/serialization_spec.rb +61 -0
  30. data/spec/elasticsearch/model/adapters/active_record_spec.rb +207 -0
  31. data/spec/elasticsearch/model/adapters/default_spec.rb +41 -0
  32. data/spec/elasticsearch/model/adapters/mongoid/basic_spec.rb +267 -0
  33. data/spec/elasticsearch/model/adapters/mongoid/multi_model_spec.rb +66 -0
  34. data/spec/elasticsearch/model/adapters/mongoid_spec.rb +235 -0
  35. data/spec/elasticsearch/model/adapters/multiple_spec.rb +125 -0
  36. data/spec/elasticsearch/model/callbacks_spec.rb +33 -0
  37. data/spec/elasticsearch/model/client_spec.rb +66 -0
  38. data/spec/elasticsearch/model/hash_wrapper_spec.rb +12 -0
  39. data/spec/elasticsearch/model/importing_spec.rb +214 -0
  40. data/spec/elasticsearch/model/indexing_spec.rb +918 -0
  41. data/spec/elasticsearch/model/module_spec.rb +101 -0
  42. data/spec/elasticsearch/model/multimodel_spec.rb +55 -0
  43. data/spec/elasticsearch/model/naming_inheritance_spec.rb +184 -0
  44. data/spec/elasticsearch/model/naming_spec.rb +186 -0
  45. data/spec/elasticsearch/model/proxy_spec.rb +107 -0
  46. data/spec/elasticsearch/model/response/aggregations_spec.rb +66 -0
  47. data/spec/elasticsearch/model/response/base_spec.rb +90 -0
  48. data/spec/elasticsearch/model/response/pagination/kaminari_spec.rb +410 -0
  49. data/spec/elasticsearch/model/response/pagination/will_paginate_spec.rb +262 -0
  50. data/spec/elasticsearch/model/response/records_spec.rb +118 -0
  51. data/spec/elasticsearch/model/response/response_spec.rb +131 -0
  52. data/spec/elasticsearch/model/response/result_spec.rb +122 -0
  53. data/spec/elasticsearch/model/response/results_spec.rb +56 -0
  54. data/spec/elasticsearch/model/searching_search_request_spec.rb +112 -0
  55. data/spec/elasticsearch/model/searching_spec.rb +49 -0
  56. data/spec/elasticsearch/model/serializing_spec.rb +22 -0
  57. data/spec/spec_helper.rb +161 -0
  58. data/spec/support/app.rb +21 -0
  59. data/spec/support/app/answer.rb +33 -0
  60. data/spec/support/app/article.rb +22 -0
  61. data/spec/support/app/article_for_pagination.rb +12 -0
  62. data/spec/support/app/article_with_custom_serialization.rb +13 -0
  63. data/spec/support/app/article_with_dynamic_index_name.rb +15 -0
  64. data/spec/support/app/author.rb +9 -0
  65. data/spec/support/app/authorship.rb +4 -0
  66. data/spec/support/app/category.rb +3 -0
  67. data/spec/support/app/comment.rb +3 -0
  68. data/spec/support/app/episode.rb +11 -0
  69. data/spec/support/app/image.rb +19 -0
  70. data/spec/support/app/import_article.rb +12 -0
  71. data/spec/support/app/mongoid_article.rb +21 -0
  72. data/spec/support/app/namespaced_book.rb +10 -0
  73. data/spec/support/app/parent_and_child_searchable.rb +24 -0
  74. data/spec/support/app/post.rb +14 -0
  75. data/spec/support/app/question.rb +27 -0
  76. data/spec/support/app/searchable.rb +48 -0
  77. data/spec/support/app/series.rb +11 -0
  78. data/spec/support/model.json +1 -0
  79. data/{test → spec}/support/model.yml +0 -0
  80. metadata +129 -86
  81. data/test/integration/active_record_associations_parent_child_test.rb +0 -188
  82. data/test/integration/active_record_associations_test.rb +0 -339
  83. data/test/integration/active_record_basic_test.rb +0 -255
  84. data/test/integration/active_record_custom_serialization_test.rb +0 -67
  85. data/test/integration/active_record_import_test.rb +0 -168
  86. data/test/integration/active_record_namespaced_model_test.rb +0 -56
  87. data/test/integration/active_record_pagination_test.rb +0 -149
  88. data/test/integration/dynamic_index_name_test.rb +0 -52
  89. data/test/integration/mongoid_basic_test.rb +0 -240
  90. data/test/integration/multiple_models_test.rb +0 -176
  91. data/test/support/model.json +0 -1
  92. data/test/test_helper.rb +0 -92
  93. data/test/unit/adapter_active_record_test.rb +0 -157
  94. data/test/unit/adapter_default_test.rb +0 -41
  95. data/test/unit/adapter_mongoid_test.rb +0 -161
  96. data/test/unit/adapter_multiple_test.rb +0 -106
  97. data/test/unit/adapter_test.rb +0 -69
  98. data/test/unit/callbacks_test.rb +0 -31
  99. data/test/unit/client_test.rb +0 -27
  100. data/test/unit/hash_wrapper_test.rb +0 -13
  101. data/test/unit/importing_test.rb +0 -224
  102. data/test/unit/indexing_test.rb +0 -720
  103. data/test/unit/module_test.rb +0 -68
  104. data/test/unit/multimodel_test.rb +0 -38
  105. data/test/unit/naming_inheritance_test.rb +0 -94
  106. data/test/unit/naming_test.rb +0 -103
  107. data/test/unit/proxy_test.rb +0 -98
  108. data/test/unit/response_aggregations_test.rb +0 -46
  109. data/test/unit/response_base_test.rb +0 -40
  110. data/test/unit/response_pagination_kaminari_test.rb +0 -433
  111. data/test/unit/response_pagination_will_paginate_test.rb +0 -398
  112. data/test/unit/response_records_test.rb +0 -91
  113. data/test/unit/response_result_test.rb +0 -90
  114. data/test/unit/response_results_test.rb +0 -34
  115. data/test/unit/response_test.rb +0 -104
  116. data/test/unit/searching_search_request_test.rb +0 -78
  117. data/test/unit/searching_test.rb +0 -41
  118. data/test/unit/serializing_test.rb +0 -17
@@ -0,0 +1,66 @@
1
+ require 'spec_helper'
2
+
3
+ describe 'Elasticsearch::Model::Adapter::ActiveRecord Multimodel', if: test_mongoid? do
4
+
5
+ before(:all) do
6
+ connect_mongoid('mongoid_test')
7
+
8
+ begin
9
+ ActiveRecord::Schema.define(:version => 1) do
10
+ create_table Episode.table_name do |t|
11
+ t.string :name
12
+ t.datetime :created_at, :default => 'NOW()'
13
+ end
14
+ end
15
+ rescue
16
+ end
17
+ end
18
+
19
+ before do
20
+ clear_tables(Episode, Image)
21
+ Episode.__elasticsearch__.create_index! force: true
22
+ Episode.create name: "TheEpisode"
23
+ Episode.create name: "A great Episode"
24
+ Episode.create name: "The greatest Episode"
25
+ Episode.__elasticsearch__.refresh_index!
26
+
27
+ Image.__elasticsearch__.create_index! force: true
28
+ Image.create! name: "The Image"
29
+ Image.create! name: "A great Image"
30
+ Image.create! name: "The greatest Image"
31
+ Image.__elasticsearch__.refresh_index!
32
+ Image.__elasticsearch__.client.cluster.health wait_for_status: 'yellow'
33
+ end
34
+
35
+ after do
36
+ [Episode, Image].each do |model|
37
+ model.__elasticsearch__.client.delete_by_query(index: model.index_name, q: '*')
38
+ model.delete_all
39
+ model.__elasticsearch__.refresh_index!
40
+ end
41
+ end
42
+
43
+ context 'when the search is across multimodels with different adapters' do
44
+
45
+ let(:search_result) do
46
+ Elasticsearch::Model.search(%q<"greatest Episode" OR "greatest Image"^2>, [Episode, Image])
47
+ end
48
+
49
+ it 'executes the search across models' do
50
+ expect(search_result.results.size).to eq(2)
51
+ expect(search_result.records.size).to eq(2)
52
+ end
53
+
54
+ it 'returns the correct type of model instance' do
55
+ expect(search_result.records[0]).to be_a(Image)
56
+ expect(search_result.records[1]).to be_a(Episode)
57
+ end
58
+
59
+ it 'creates the model instances with the correct attributes' do
60
+ expect(search_result.results[0].name).to eq('The greatest Image')
61
+ expect(search_result.records[0].name).to eq('The greatest Image')
62
+ expect(search_result.results[1].name).to eq('The greatest Episode')
63
+ expect(search_result.records[1].name).to eq('The greatest Episode')
64
+ end
65
+ end
66
+ end
@@ -0,0 +1,235 @@
1
+ require 'spec_helper'
2
+
3
+ describe Elasticsearch::Model::Adapter::Mongoid do
4
+
5
+ before(:all) do
6
+ class DummyClassForMongoid; end
7
+ ::Symbol.class_eval { def in; self; end }
8
+ end
9
+
10
+ after(:all) do
11
+ Elasticsearch::Model::Adapter::Adapter.adapters.delete(DummyClassForMongoid)
12
+ remove_classes(DummyClassForMongoid)
13
+ end
14
+
15
+ let(:response) do
16
+ { 'hits' => {'hits' => [ {'_id' => 2}, {'_id' => 1} ]} }
17
+ end
18
+
19
+ let(:ids) do
20
+ [2, 1]
21
+ end
22
+
23
+ let(:record_1) do
24
+ double('record').tap do |rec|
25
+ allow(rec).to receive(:id).and_return(1)
26
+ end
27
+ end
28
+
29
+ let(:record_2) do
30
+ double('record').tap do |rec|
31
+ allow(rec).to receive(:load).and_return(true)
32
+ allow(rec).to receive(:id).and_return(2)
33
+ end
34
+ end
35
+
36
+ let(:records) do
37
+ [record_1, record_2]
38
+ end
39
+
40
+ let(:model) do
41
+ DummyClassForMongoid.new.tap do |m|
42
+ allow(m).to receive(:response).and_return(double('response', response: response))
43
+ allow(m).to receive(:ids).and_return(ids)
44
+ end
45
+ end
46
+
47
+ describe 'adapter registration' do
48
+
49
+ it 'registers an adapater' do
50
+ expect(Elasticsearch::Model::Adapter.adapters[Elasticsearch::Model::Adapter::Mongoid]).not_to be_nil
51
+ expect(Elasticsearch::Model::Adapter.adapters[Elasticsearch::Model::Adapter::Mongoid].call(DummyClassForMongoid)).to be(false)
52
+ end
53
+
54
+ it 'registers the records module' do
55
+ expect(Elasticsearch::Model::Adapter::Mongoid::Records).to be_a(Module)
56
+ end
57
+ end
58
+
59
+ describe '#records' do
60
+
61
+ before(:all) do
62
+ DummyClassForMongoid.__send__ :include, Elasticsearch::Model::Adapter::Mongoid::Records
63
+ end
64
+
65
+ let(:instance) do
66
+ model.tap do |inst|
67
+ allow(inst).to receive(:klass).and_return(double('class', where: records)).at_least(:once)
68
+ end
69
+ end
70
+
71
+ it 'returns the records' do
72
+ expect(instance.records).to eq(records)
73
+ end
74
+
75
+ context 'when an order is not defined for the Mongoid query' do
76
+
77
+ context 'when the records have a different order than the hits' do
78
+
79
+ before do
80
+ records.instance_variable_set(:@records, records)
81
+ end
82
+
83
+ it 'reorders the records based on hits order' do
84
+ expect(records.collect(&:id)).to eq([1, 2])
85
+ expect(instance.records.to_a.collect(&:id)).to eq([2, 1])
86
+ end
87
+ end
88
+
89
+ context 'when an order is defined for the Mongoid query' do
90
+
91
+ context 'when the records have a different order than the hits' do
92
+
93
+ before do
94
+ records.instance_variable_set(:@records, records)
95
+ expect(instance.records).to receive(:asc).and_return(records)
96
+ end
97
+
98
+ it 'reorders the records based on hits order' do
99
+ expect(records.collect(&:id)).to eq([1, 2])
100
+ expect(instance.records.to_a.collect(&:id)).to eq([2, 1])
101
+ expect(instance.asc.to_a.collect(&:id)).to eq([1, 2])
102
+ end
103
+ end
104
+ end
105
+ end
106
+
107
+ describe 'callbacks registration' do
108
+
109
+ before do
110
+ expect(DummyClassForMongoid).to receive(:after_create).once
111
+ expect(DummyClassForMongoid).to receive(:after_update).once
112
+ expect(DummyClassForMongoid).to receive(:after_destroy).once
113
+ end
114
+
115
+ it 'should register the model class for callbacks' do
116
+ Elasticsearch::Model::Adapter::Mongoid::Callbacks.included(DummyClassForMongoid)
117
+ end
118
+ end
119
+ end
120
+
121
+ describe 'importing' do
122
+
123
+ before(:all) do
124
+ DummyClassForMongoid.__send__ :extend, Elasticsearch::Model::Adapter::Mongoid::Importing
125
+ end
126
+
127
+ let(:relation) do
128
+ double('relation', each_slice: []).tap do |rel|
129
+ allow(rel).to receive(:published).and_return(rel)
130
+ allow(rel).to receive(:no_timeout).and_return(rel)
131
+ allow(rel).to receive(:class_exec).and_return(rel)
132
+ end
133
+ end
134
+
135
+ before do
136
+ allow(DummyClassForMongoid).to receive(:all).and_return(relation)
137
+ end
138
+
139
+ context 'when a scope is specified' do
140
+
141
+ it 'applies the scope' do
142
+ expect(DummyClassForMongoid.__find_in_batches(scope: :published) do; end).to eq([])
143
+ end
144
+ end
145
+
146
+ context 'query criteria specified as a proc' do
147
+
148
+ let(:query) do
149
+ Proc.new { where(color: "red") }
150
+ end
151
+
152
+ it 'execites the query' do
153
+ expect(DummyClassForMongoid.__find_in_batches(query: query) do; end).to eq([])
154
+ end
155
+ end
156
+
157
+ context 'query criteria specified as a hash' do
158
+
159
+ before do
160
+ expect(relation).to receive(:where).with(color: 'red').and_return(relation)
161
+ end
162
+
163
+ let(:query) do
164
+ { color: "red" }
165
+ end
166
+
167
+ it 'execites the query' do
168
+ expect(DummyClassForMongoid.__find_in_batches(query: query) do; end).to eq([])
169
+ end
170
+ end
171
+
172
+ context 'when preprocessing batches' do
173
+
174
+ context 'if the query returns results' do
175
+
176
+ before do
177
+ class << DummyClassForMongoid
178
+ def find_in_batches(options = {}, &block)
179
+ yield [:a, :b]
180
+ end
181
+
182
+ def update_batch(batch)
183
+ batch.collect { |b| b.to_s + '!' }
184
+ end
185
+ end
186
+ end
187
+
188
+ it 'applies the preprocessing method' do
189
+ DummyClassForMongoid.__find_in_batches(preprocess: :update_batch) do |batch|
190
+ expect(batch).to match(['a!', 'b!'])
191
+ end
192
+ end
193
+ end
194
+
195
+ context 'if the query does not return results' do
196
+
197
+ before do
198
+ class << DummyClassForMongoid
199
+ def find_in_batches(options = {}, &block)
200
+ yield [:a, :b]
201
+ end
202
+
203
+ def update_batch(batch)
204
+ []
205
+ end
206
+ end
207
+ end
208
+
209
+ it 'applies the preprocessing method' do
210
+ DummyClassForMongoid.__find_in_batches(preprocess: :update_batch) do |batch|
211
+ expect(batch).to match([])
212
+ end
213
+ end
214
+ end
215
+ end
216
+
217
+ context 'when transforming models' do
218
+
219
+ let(:instance) do
220
+ model.tap do |inst|
221
+ allow(inst).to receive(:as_indexed_json).and_return({})
222
+ allow(inst).to receive(:id).and_return(1)
223
+ end
224
+ end
225
+
226
+ it 'returns an proc' do
227
+ expect(DummyClassForMongoid.__transform.respond_to?(:call)).to be(true)
228
+ end
229
+
230
+ it 'provides a default transformation' do
231
+ expect(DummyClassForMongoid.__transform.call(instance)).to eq(index: { _id: '1', data: {} })
232
+ end
233
+ end
234
+ end
235
+ end
@@ -0,0 +1,125 @@
1
+ require 'spec_helper'
2
+
3
+ describe Elasticsearch::Model::Adapter::Multiple do
4
+
5
+ before(:all) do
6
+ class DummyOne
7
+ include Elasticsearch::Model
8
+
9
+ index_name 'dummy'
10
+ document_type 'dummy_one'
11
+
12
+ def self.find(ids)
13
+ ids.map { |id| new(id) }
14
+ end
15
+
16
+ attr_reader :id
17
+
18
+ def initialize(id)
19
+ @id = id.to_i
20
+ end
21
+ end
22
+
23
+ module Namespace
24
+ class DummyTwo
25
+ include Elasticsearch::Model
26
+
27
+ index_name 'dummy'
28
+ document_type 'dummy_two'
29
+
30
+ def self.find(ids)
31
+ ids.map { |id| new(id) }
32
+ end
33
+
34
+ attr_reader :id
35
+
36
+ def initialize(id)
37
+ @id = id.to_i
38
+ end
39
+ end
40
+ end
41
+
42
+ class DummyTwo
43
+ include Elasticsearch::Model
44
+
45
+ index_name 'other_index'
46
+ document_type 'dummy_two'
47
+
48
+ def self.find(ids)
49
+ ids.map { |id| new(id) }
50
+ end
51
+
52
+ attr_reader :id
53
+
54
+ def initialize(id)
55
+ @id = id.to_i
56
+ end
57
+ end
58
+ end
59
+
60
+ after(:all) do
61
+ [DummyOne, Namespace::DummyTwo, DummyTwo].each do |adapter|
62
+ Elasticsearch::Model::Adapter::Adapter.adapters.delete(adapter)
63
+ end
64
+ Namespace.send(:remove_const, :DummyTwo) if defined?(Namespace::DummyTwo)
65
+ remove_classes(DummyOne, DummyTwo, Namespace)
66
+ end
67
+
68
+ let(:hits) do
69
+ [
70
+ {
71
+ _index: 'dummy',
72
+ _type: 'dummy_two',
73
+ _id: '2'
74
+ },
75
+ {
76
+ _index: 'dummy',
77
+ _type: 'dummy_one',
78
+ _id: '2'
79
+ },
80
+ {
81
+ _index: 'other_index',
82
+ _type: 'dummy_two',
83
+ _id: '1'
84
+ },
85
+ {
86
+ _index: 'dummy',
87
+ _type: 'dummy_two',
88
+ _id: '1'
89
+ },
90
+ {
91
+ _index: 'dummy',
92
+ _type: 'dummy_one',
93
+ _id: '3'
94
+ }
95
+ ]
96
+ end
97
+
98
+ let(:response) do
99
+ double('response', response: { 'hits' => { 'hits' => hits } })
100
+ end
101
+
102
+ let(:multimodel) do
103
+ Elasticsearch::Model::Multimodel.new(DummyOne, DummyTwo, Namespace::DummyTwo)
104
+ end
105
+
106
+ describe '#records' do
107
+
108
+ before do
109
+ multimodel.class.send :include, Elasticsearch::Model::Adapter::Multiple::Records
110
+ expect(multimodel).to receive(:response).at_least(:once).and_return(response)
111
+ end
112
+
113
+ it 'instantiates the correct types of instances' do
114
+ expect(multimodel.records[0]).to be_a(Namespace::DummyTwo)
115
+ expect(multimodel.records[1]).to be_a(DummyOne)
116
+ expect(multimodel.records[2]).to be_a(DummyTwo)
117
+ expect(multimodel.records[3]).to be_a(Namespace::DummyTwo)
118
+ expect(multimodel.records[4]).to be_a(DummyOne)
119
+ end
120
+
121
+ it 'returns the results in the correct order' do
122
+ expect(multimodel.records.map(&:id)).to eq([2, 2, 1, 1, 3])
123
+ end
124
+ end
125
+ end
@@ -0,0 +1,33 @@
1
+ require 'spec_helper'
2
+
3
+ describe Elasticsearch::Model::Callbacks do
4
+
5
+ before(:all) do
6
+ class ::DummyCallbacksModel
7
+ end
8
+
9
+ module DummyCallbacksAdapter
10
+ module CallbacksMixin
11
+ end
12
+
13
+ def callbacks_mixin
14
+ CallbacksMixin
15
+ end; module_function :callbacks_mixin
16
+ end
17
+ end
18
+
19
+ after(:all) do
20
+ remove_classes(DummyCallbacksModel, DummyCallbacksAdapter)
21
+ end
22
+
23
+ context 'when a model includes the Callbacks module' do
24
+
25
+ before do
26
+ Elasticsearch::Model::Callbacks.included(DummyCallbacksModel)
27
+ end
28
+
29
+ it 'includes the callbacks mixin from the model adapter' do
30
+ expect(DummyCallbacksModel.ancestors).to include(Elasticsearch::Model::Adapter::Default::Callbacks)
31
+ end
32
+ end
33
+ end