elasticsearch_record 1.2.4 → 1.3.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 41575ee0aefe81ed3069dc81c1c0d9062ea841998a7bb4affaab885dad0a4291
4
- data.tar.gz: 73e98d4b6aad0fda63df0fff562537245f2d4c95fb5f53d67ad891550d8e6b1b
3
+ metadata.gz: 0e7302cf1310d0d583125bf9a9e526203feb42d000569b6daca266d88ae7bb50
4
+ data.tar.gz: e67b0946f566b44232827244c323aa2e221796f4d0ac91adf4696179f4ba56e1
5
5
  SHA512:
6
- metadata.gz: ed60e5fe591f92169d51f333c600338cd4fcee46ca6ca988625399ee522068e3b5c67738fe5b9f3fcc1cede6491cfb1b969c525d5b1af2f2ed03e93b7303237f
7
- data.tar.gz: eb51b9dc16d0457905fb15589ff2542dfe0a8f59678459be910726305a3b0527e3ddcee6d7e5271e2db0d658dabd5f476eea71b4dca27c1f99375d987de50f0b
6
+ metadata.gz: 3fbf9f25937e5c77520af89de811c72bc9e2a51a49cbb1c6f7a77d0b2af8d2e533b3cc2fc11acf3f8b9a78ed5616c97c068cc88a1171e85bcc5611252960b345
7
+ data.tar.gz: f8d77b26a20ddd8db5495638fdd98da72bcd1bd6978f17d6a1a794ab436fc184e3dafa153c67b364f6c89e59c5bca1eb0a59e09b240c4ea8e6055c017eb4738c
data/README.md CHANGED
@@ -198,6 +198,7 @@ total = scope.total
198
198
  - kind
199
199
  - configure
200
200
  - aggregate
201
+ - refresh
201
202
  - query
202
203
  - filter
203
204
  - must_not
@@ -274,6 +275,8 @@ Access these methods through the model's connection.
274
275
  - meta_exists?
275
276
  - max_result_window
276
277
  - cluster_info
278
+ - cluster_settings
279
+ - cluster_health
277
280
 
278
281
  ## Active Record Schema migration methods
279
282
  Access these methods through the model's connection or within any `Migration`.
@@ -285,26 +288,30 @@ Access these methods through the model's connection or within any `Migration`.
285
288
  - close_tables
286
289
  - truncate_table
287
290
  - truncate_tables
291
+ - refresh_table
292
+ - refresh_tables
288
293
  - drop_table
289
294
  - block_table
290
295
  - unblock_table
291
296
  - clone_table
292
297
  - create_table
293
298
  - change_table
299
+ - rename_table
294
300
 
295
301
  **table actions:**
296
302
  - change_meta
297
- - delete_meta
303
+ - remove_meta
298
304
  - add_mapping
299
305
  - change_mapping
300
306
  - change_mapping_meta
301
307
  - change_mapping_attributes
308
+ - remove_mapping
302
309
  - add_setting
303
310
  - change_setting
304
- - delete_setting
311
+ - remove_setting
305
312
  - add_alias
306
313
  - change_alias
307
- - delete_alias
314
+ - remove_alias
308
315
 
309
316
  ```ruby
310
317
  # Example migration
@@ -322,6 +329,11 @@ class AddTests < ActiveRecord::Migration[7.0]
322
329
  # changes the auto-increment value
323
330
  change_meta "assignments", :auto_increment, 3625
324
331
 
332
+ # removes the mapping 'updated_at' from the 'assignments' index.
333
+ # the flag 'recreate' is required, since 'remove' is not supported for elasticsearch.
334
+ # this will recreate the whole index (data will be LOST!!!)
335
+ remove_mapping :assignments, :updated_at, recreate: true
336
+
325
337
  create_table "settings", force: true do |t|
326
338
  t.mapping :created_at, :date
327
339
  t.mapping :key, :integer do |m|
@@ -347,8 +359,8 @@ class AddTests < ActiveRecord::Migration[7.0]
347
359
  t.add_alias('supersettings')
348
360
  end
349
361
 
350
- delete_alias('settings', :supersettings)
351
- delete_setting('settings', 'index.search.idle.after')
362
+ remove_alias('settings', :supersettings)
363
+ remove_setting('settings', 'index.search.idle.after')
352
364
 
353
365
  change_table 'settings', force: true do |t|
354
366
  t.integer :amount_of_newbies
@@ -357,10 +369,14 @@ class AddTests < ActiveRecord::Migration[7.0]
357
369
  create_table "vintage", force: true do |t|
358
370
  t.primary_key :number
359
371
  t.string :name
372
+ t.string :comments
360
373
  t.timestamps
361
374
  end
362
375
 
363
- change_mapping_attributes "vintage", :number, fields: {raw: {type: :keyword}}
376
+ change_table 'vintage', if_exists: true, recreate: true do |t|
377
+ t.change_mapping :number, fields: {raw: {type: :keyword}}
378
+ t.remove_mapping :number
379
+ end
364
380
  end
365
381
 
366
382
  def down
data/docs/CHANGELOG.md CHANGED
@@ -1,5 +1,26 @@
1
1
  # ElasticsearchRecord - CHANGELOG
2
2
 
3
+ ## [1.3.0] - 2023-01-17
4
+ * [add] 'metas: {}' param for `CreateTableDefinition` to provide individual meta information without providing them through 'mappings'
5
+ * [add] 'change_'- & 'remove_'-methods for _mapping, setting & alias_ for `CreateTableDefinition`
6
+ * [add] `ActiveRecord::ConnectionAdapters::Elasticsearch::TableMappingDefinition#meta` for easier access
7
+ * [add] `UpdateTableDefinition#remove_mapping` to always raise an ArgumentError (now created @ `CreateTableDefinition` through change_table(x, recreate: true) )
8
+ * [add] `_env_table_name`-syntax to `ActiveRecord::ConnectionAdapters::Elasticsearch::SchemaDumper#table` for prefixed & suffixed tables in connections
9
+ * [add] `#cluster_health`, `#refresh_table`, `#refresh_tables` & `#rename_table` methods to 'connection'
10
+ * [add] `#change_table` 'recreate: false' parameter to switch between a 'change' or 'recreate' of an index
11
+ * [add] `#refresh` method to relations to explicit set the refresh value of the generated query
12
+ * [ref] `CloneTableDefinition` to adapt settings (number_of_shards & number_of_replicas) from source table by default
13
+ * [ref] `CreateTableDefinition#transform_mappings!` also support simple 'key->attributes' assignment of custom provided mappings
14
+ * [ref] 'delete_'-methods into more common 'remove_' for `UpdateTableDefinition`
15
+ * [ref] `UpdateTableDefinition#change_mapping` to always raise an ArgumentError (now moved to `CreateTableDefinition` through change_table(x, recreate: true) )
16
+ * [ref] `#change_table` missing '&block'
17
+ * [ref] 'delete_'-methods into more common 'remove_' to 'connection'
18
+ * [ref] ':\_\_claim\_\_'-operator to ':\_\_query\_\_' within `Arel::Collectors::ElasticsearchQuery`
19
+ * [ref] update & delete queries to preset a 'refresh: true' as default _(can be overwritten through 'relation.refresh(false)' )_
20
+ * [rem] `#clone_table` unusable '&block'
21
+ * [rem] `#compute_table_name`-method - must be explicit provided with `#_env_table_name(name)`
22
+ * [fix] `#where!` & `#build_where_clause` methods to also build a valid 'where_clause' in nested 'where' & 'or'
23
+
3
24
  ## [1.2.4] - 2022-12-15
4
25
  * [fix] missing `#visit_Arel_Nodes_In` method in `Arel::Visitors::ElasticsearchQuery` to build array conditions
5
26
  * [fix] resolving buckets from relation `ElasticsearchRecord::Result#buckets` not recognizing sub-buckets
@@ -16,6 +16,9 @@ module ActiveRecord
16
16
  @settings = HashWithIndifferentAccess.new
17
17
  @aliases = HashWithIndifferentAccess.new
18
18
 
19
+ # before assigning new settings, we need to resolve some defaults
20
+ assign_default_clone_settings!
21
+
19
22
  transform_settings!(settings) if settings.present?
20
23
  transform_aliases!(aliases) if aliases.present?
21
24
  end
@@ -64,6 +67,12 @@ module ActiveRecord
64
67
 
65
68
  private
66
69
 
70
+ def assign_default_clone_settings!
71
+ settings = table_settings(name)
72
+ setting('index.number_of_shards', (settings.dig('index.number_of_shards') || 1))
73
+ setting('index.number_of_replicas', (settings.dig('index.number_of_replicas') || 0))
74
+ end
75
+
67
76
  def _before_exec
68
77
  block_table(self.name, :write)
69
78
  end
@@ -6,7 +6,7 @@ module ActiveRecord
6
6
  module ConnectionAdapters
7
7
  module Elasticsearch
8
8
  class CreateTableDefinition < TableDefinition
9
- def initialize(conn, name, settings: nil, mappings: nil, aliases: nil, **opts)
9
+ def initialize(conn, name, settings: nil, mappings: nil, aliases: nil, metas: nil, **opts)
10
10
  super(conn, name, **opts)
11
11
 
12
12
  @settings = HashWithIndifferentAccess.new
@@ -17,6 +17,9 @@ module ActiveRecord
17
17
  transform_settings!(settings) if settings.present?
18
18
  transform_mappings!(mappings) if mappings.present?
19
19
  transform_aliases!(aliases) if aliases.present?
20
+ # PLEASE NOTE: metas are already provided through the mappings (['_meta']),
21
+ # but this will support individually provided key<->values...
22
+ transform_metas!(metas) if metas.present?
20
23
  end
21
24
 
22
25
  # returns an array with all +TableSettingDefinition+.
@@ -64,18 +67,23 @@ module ActiveRecord
64
67
  self
65
68
  end
66
69
 
70
+ def change_meta(name, value, **options)
71
+ # simply force full overwrite
72
+ meta(name, value, force: true, **options)
73
+ end
74
+
67
75
  def remove_meta(name)
68
76
  @metas.delete(name)
69
77
  end
70
78
 
71
79
  # adds a new mapping
72
- def mapping(name, type, force: false, **options, &block)
80
+ def mapping(name, type, if_not_exists: false, force: false, **options, &block)
81
+ return if if_not_exists && @mappings.key?(name)
73
82
  raise ArgumentError, "you cannot define an already defined mapping '#{name}'!" if @mappings.key?(name) && !force?(force)
74
83
 
75
84
  mapping = new_mapping_definition(name, type, **options, &block)
76
85
  @mappings[name] = mapping
77
86
 
78
-
79
87
  # check if the mapping is assigned as primary_key
80
88
  if mapping.primary_key?
81
89
  meta :primary_key, mapping.name
@@ -86,16 +94,51 @@ module ActiveRecord
86
94
  end
87
95
 
88
96
  # provide backwards compatibility to columns
89
- alias column mapping
97
+ alias :column :mapping
98
+ alias :add_mapping :mapping
99
+ alias :add_column :mapping
100
+
101
+ def change_mapping(name, type, if_exists: false, **options, &block)
102
+ return if if_exists && !@mappings.key?(name)
103
+
104
+ raise ArgumentError, "you cannot change an unknown mapping '#{name}'" unless @mappings.key?(name)
105
+
106
+ mapping(name, type, force: true, **options, &block)
107
+ end
108
+
109
+ alias :change_column :change_mapping
110
+ alias :change :change_mapping
111
+
112
+ def change_mapping_meta(name, **options)
113
+ raise ArgumentError, "you cannot change the 'meta' parameter for an unknown mapping '#{name}'" unless @mappings.key?(name)
114
+
115
+ # merge mapping options
116
+ opts = @mappings[name].attributes
117
+ opts['meta'] = @mappings[name].meta.merge(options)
118
+
119
+ mapping(name, @mappings[name].type, force: true, **opts)
120
+ end
121
+
122
+ def change_mapping_attributes(name, if_exists: false, **options, &block)
123
+ return if if_exists && !@mappings.key?(name)
124
+
125
+ raise ArgumentError, "you cannot change an unknown mapping '#{name}'" unless @mappings.key?(name)
126
+
127
+ # merge mapping attributes
128
+ opts = @mappings[name].attributes.merge(options)
129
+
130
+ mapping(name, @mappings[name].type, force: true, **opts, &block)
131
+ end
90
132
 
91
133
  def remove_mapping(name)
92
134
  @mappings.delete(name)
93
135
  end
94
136
 
95
137
  # provide backwards compatibility to columns
96
- alias remove_column remove_mapping
138
+ alias :remove_column :remove_mapping
97
139
 
98
- def setting(name, value, force: false, **options, &block)
140
+ def setting(name, value, if_not_exists: false, force: false, **options, &block)
141
+ return if if_not_exists && @settings.key?(name)
99
142
  raise ArgumentError, "you cannot define an already defined setting '#{name}'!" if @settings.key?(name) && !force?(force)
100
143
 
101
144
  @settings[name] = new_setting_definition(name, value, **options, &block)
@@ -103,13 +146,19 @@ module ActiveRecord
103
146
  self
104
147
  end
105
148
 
149
+ def change_setting(name, value, if_exists: false, **options, &block)
150
+ return if if_exists && !@settings.key?(name)
151
+
152
+ # simply force full overwrite
153
+ setting(name, value, force: true, **options, &block)
154
+ end
155
+
106
156
  def remove_setting(name)
107
157
  @settings.delete name
108
158
  end
109
159
 
110
- # we can use +alias+ here, since the instance method is not a reserved keyword!
111
-
112
- def alias(name, force: false, **options, &block)
160
+ def add_alias(name, if_not_exists: false, force: false, **options, &block)
161
+ return if if_not_exists && @aliases.key?(name)
113
162
  raise ArgumentError, "you cannot define an already defined alias '#{name}'." if @aliases.key?(name) && !force?(force)
114
163
 
115
164
  @aliases[name] = new_alias_definition(name, **options, &block)
@@ -117,6 +166,13 @@ module ActiveRecord
117
166
  self
118
167
  end
119
168
 
169
+ def change_alias(name, if_exists: false, **options, &block)
170
+ return if if_exists && !@aliases.key?(name)
171
+
172
+ # simply force full overwrite
173
+ add_alias(name, force: true, **options, &block)
174
+ end
175
+
120
176
  def remove_alias(name)
121
177
  @aliases.delete name
122
178
  end
@@ -154,6 +210,51 @@ module ActiveRecord
154
210
  def state
155
211
  nil
156
212
  end
213
+
214
+ def transform_mappings!(mappings)
215
+ # transform +_meta+ mappings
216
+ if mappings['_meta'].present?
217
+ mappings['_meta'].each do |name, value|
218
+ self.meta(name, value, force: true)
219
+ end
220
+ end
221
+
222
+ # transform properties (=columns)
223
+ if mappings['properties'].present?
224
+ mappings['properties'].each do |name, attributes|
225
+ self.mapping(name, attributes.delete('type'), force: true, **attributes)
226
+ end
227
+ elsif mappings.present? && mappings.values[0].is_a?(Hash)
228
+ # raw settings where provided with just (key => attributes)
229
+ mappings.each do |name, attributes|
230
+ self.mapping(name, attributes.delete('type'), force: true, **attributes)
231
+ end
232
+ end
233
+ end
234
+
235
+ def transform_settings!(settings)
236
+ # exclude settings, that are provided through the API but are not part of the index-settings
237
+ settings
238
+ .with_indifferent_access
239
+ .each { |name, value|
240
+ # don't transform ignored names
241
+ next if ActiveRecord::ConnectionAdapters::Elasticsearch::TableSettingDefinition.match_ignore_names?(name)
242
+
243
+ self.setting(name, value, force: true)
244
+ }
245
+ end
246
+
247
+ def transform_aliases!(aliases)
248
+ aliases.each do |name, attributes|
249
+ self.alias(name, force: true, **attributes)
250
+ end
251
+ end
252
+
253
+ def transform_metas!(metas)
254
+ metas.each do |name, value|
255
+ self.meta(name, value, force: true)
256
+ end
257
+ end
157
258
  end
158
259
  end
159
260
  end
@@ -147,40 +147,6 @@ module ActiveRecord
147
147
  def strict?(strict = nil)
148
148
  opts.fetch(:strict, false) || strict
149
149
  end
150
-
151
- def transform_mappings!(mappings)
152
- # transform +_meta+ mappings
153
- if mappings['_meta'].present?
154
- mappings['_meta'].each do |name, value|
155
- self.meta(name, value)
156
- end
157
- end
158
-
159
- # transform properties (=columns)
160
- if mappings['properties'].present?
161
- mappings['properties'].each do |name, attributes|
162
- self.mapping(name, attributes.delete('type'), **attributes)
163
- end
164
- end
165
- end
166
-
167
- def transform_settings!(settings)
168
- # exclude settings, that are provided through the API but are not part of the index-settings
169
- settings
170
- .with_indifferent_access
171
- .each { |name, value|
172
- # don't transform ignored names
173
- next if ActiveRecord::ConnectionAdapters::Elasticsearch::TableSettingDefinition.match_ignore_names?(name)
174
-
175
- self.setting(name, value)
176
- }
177
- end
178
-
179
- def transform_aliases!(aliases)
180
- aliases.each do |name, attributes|
181
- self.alias(name, **attributes)
182
- end
183
- end
184
150
  end
185
151
  end
186
152
  end
@@ -99,6 +99,12 @@ module ActiveRecord
99
99
  _lazy_attributes[:auto_increment] = value
100
100
  end
101
101
 
102
+ # returns the meta hash
103
+ # @param [Hash]
104
+ def meta
105
+ __get_attribute(:meta) || {}
106
+ end
107
+
102
108
  def meta=(value)
103
109
  if value.nil?
104
110
  __remove_attribute(:meta)
@@ -8,15 +8,13 @@ module ActiveRecord
8
8
  module Elasticsearch
9
9
  class UpdateTableDefinition < TableDefinition
10
10
 
11
- attr_reader :definitions
12
-
13
11
  # defines which definitions can be executed composite
14
12
  COMPOSITE_DEFINITIONS = [
15
13
  AddMappingDefinition,
16
14
  ChangeMappingDefinition,
17
15
  ChangeMetaDefinition,
18
16
  AddSettingDefinition,
19
- DeleteAliasDefinition
17
+ RemoveAliasDefinition
20
18
  ].freeze
21
19
 
22
20
  def add_mapping(name, type, if_not_exists: false, **options, &block)
@@ -35,23 +33,14 @@ module ActiveRecord
35
33
  define! ChangeMetaDefinition, new_meta_definition(name, value, **options)
36
34
  end
37
35
 
38
- def delete_meta(name, **options)
36
+ def remove_meta(name, **options)
39
37
  load_meta_definition!
40
38
 
41
39
  define! ChangeMetaDefinition, new_meta_definition(name, nil, **options)
42
40
  end
43
41
 
44
- def change_mapping(name, type, if_exists: false, **options, &block)
45
- return if if_exists && !mapping_exists?(self.name, name, type)
46
-
47
- mapping = new_mapping_definition(name, type, **options, &block)
48
- define! ChangeMappingDefinition, mapping
49
-
50
- # check if the mapping is assigned as primary_key
51
- if mapping.primary_key?
52
- change_meta :primary_key, mapping.name
53
- change_meta(:auto_increment, mapping.auto_increment) if mapping.auto_increment?
54
- end
42
+ def change_mapping(name, type, **options, &block)
43
+ raise ArgumentError, "you cannot change the mapping '#{name}' without the 'recreate: true' flag in the '#change_table' call"
55
44
  end
56
45
 
57
46
  alias :change_column :change_mapping
@@ -67,16 +56,28 @@ module ActiveRecord
67
56
  define! ChangeMappingDefinition, new_mapping_definition(name, mapping['type'], meta: meta)
68
57
  end
69
58
 
70
- def change_mapping_attributes(name, **options, &block)
71
- mapping = table_mappings(self.name).dig('properties', name.to_s)
72
- raise ArgumentError, "you cannot change parameters for an unknown mapping '#{name}'" if mapping.blank?
59
+ def change_mapping_attributes(name, if_exists: false, **options, &block)
60
+ return if if_exists && !mapping_exists?(self.name, name)
61
+
62
+ # receive current mapping
63
+ current_mapping = table_mappings(self.name).dig('properties', name.to_s)
64
+ raise ArgumentError, "you cannot change an unknown mapping '#{name}'" if current_mapping.blank?
73
65
 
74
- options = mapping.with_indifferent_access.except(:type).merge(options) if options.present?
66
+ # build new mapping
67
+ mapping = new_mapping_definition(name, current_mapping[:type], **options, &block)
68
+ define! ChangeMappingDefinition, mapping
75
69
 
76
- define! ChangeMappingDefinition, new_mapping_definition(name, mapping['type'], **options, &block)
70
+ # check if the mapping is assigned as new primary_key
71
+ if mapping.primary_key?
72
+ change_meta :primary_key, mapping.name
73
+ change_meta(:auto_increment, mapping.auto_increment) if mapping.auto_increment?
74
+ end
77
75
  end
78
76
 
79
- alias :change_mapping_attribute :change_mapping_attributes
77
+ def remove_mapping(name)
78
+ raise ArgumentError, "you cannot remove the mapping '#{name}' without the 'recreate: true' flag in the '#change_table' call"
79
+ end
80
+ alias :remove_column :remove_mapping
80
81
 
81
82
  def add_setting(name, value, if_not_exists: false, **options, &block)
82
83
  return if if_not_exists && setting_exists?(self.name, name)
@@ -90,10 +91,10 @@ module ActiveRecord
90
91
  define! ChangeSettingDefinition, new_setting_definition(name, value, **options, &block)
91
92
  end
92
93
 
93
- def delete_setting(name, if_exists: false, **options, &block)
94
+ def remove_setting(name, if_exists: false, **options, &block)
94
95
  return if if_exists && !setting_exists?(self.name, name)
95
96
 
96
- define! DeleteSettingDefinition, new_setting_definition(name, nil, **options, &block)
97
+ define! RemoveSettingDefinition, new_setting_definition(name, nil, **options, &block)
97
98
  end
98
99
 
99
100
  def add_alias(name, if_not_exists: false, **options, &block)
@@ -108,10 +109,10 @@ module ActiveRecord
108
109
  define! ChangeAliasDefinition, new_alias_definition(name, **options, &block)
109
110
  end
110
111
 
111
- def delete_alias(name, if_exists: false, **, &block)
112
+ def remove_alias(name, if_exists: false, **, &block)
112
113
  return if if_exists && !alias_exists?(self.name, name)
113
114
 
114
- define! DeleteAliasDefinition, new_alias_definition(name, &block)
115
+ define! RemoveAliasDefinition, new_alias_definition(name, &block)
115
116
  end
116
117
 
117
118
  # Appends <tt>:datetime</tt> columns <tt>:created_at</tt> and
@@ -123,6 +124,11 @@ module ActiveRecord
123
124
  add_mapping(:updated_at, :datetime, if_not_exists: true, **options)
124
125
  end
125
126
 
127
+ # returns the definitions hash
128
+ def definitions
129
+ @definitions ||= {}
130
+ end
131
+
126
132
  private
127
133
 
128
134
  # loads existing meta definitions from table_mappings
@@ -130,16 +136,14 @@ module ActiveRecord
130
136
  return if @load_meta_definition
131
137
  @load_meta_definition = true
132
138
 
133
-
134
139
  table_metas(self.name).each do |name, value|
135
140
  define! ChangeMetaDefinition, new_meta_definition(name, value)
136
141
  end
137
142
  end
138
143
 
139
144
  def define!(klass, item)
140
- @definitions ||= {}
141
- @definitions[klass] ||= []
142
- @definitions[klass] << item
145
+ self.definitions[klass] ||= []
146
+ self.definitions[klass] << item
143
147
  end
144
148
 
145
149
  def _before_assign
@@ -157,6 +161,7 @@ module ActiveRecord
157
161
  # reset table state
158
162
  clear_state!
159
163
  end
164
+
160
165
  alias :_rescue_assign :_after_exec
161
166
  alias :_rescue_exec :_after_exec
162
167
 
@@ -14,12 +14,12 @@ module ActiveRecord
14
14
  # setting definitions
15
15
  AddSettingDefinition = Struct.new(:items) # composite
16
16
  ChangeSettingDefinition = Struct.new(:items) # composite
17
- DeleteSettingDefinition = Struct.new(:items) # composite
17
+ RemoveSettingDefinition = Struct.new(:items) # composite
18
18
 
19
19
  # alias definitions
20
20
  AddAliasDefinition = Struct.new(:item) # single
21
21
  ChangeAliasDefinition = Struct.new(:item) # single
22
- DeleteAliasDefinition = Struct.new(:items) # composite
22
+ RemoveAliasDefinition = Struct.new(:items) # composite
23
23
  end
24
24
  end
25
25
  end
@@ -20,6 +20,11 @@ module ActiveRecord
20
20
  @options[:table_name_suffix] = @connection.table_name_suffix if @options[:table_name_suffix].blank?
21
21
  end
22
22
 
23
+ # returns true if the connection has table-names, that are environment related
24
+ def _has_env_table_names?
25
+ @options[:table_name_prefix].present? || @options[:table_name_suffix].present?
26
+ end
27
+
23
28
  # overwrite the method to 'fix' a possible ActiveRecord bug:
24
29
  # If a +table_name_prefix+ or +table_name_suffix+ was provided we also only want to dump those tables,
25
30
  # which matches the prefix/suffix. So possible, environment-related tables will be ignored
@@ -35,7 +40,7 @@ module ActiveRecord
35
40
  return true if @options[:table_name_prefix].present? && !table_name.start_with?(@options[:table_name_prefix].to_s)
36
41
 
37
42
  # if the table ends NOT with +suffix+ it must be ignored
38
- return true if @options[:table_name_suffix].present? && !table_name.end_with?(@options[:table_name_suffix].to_s)
43
+ return true if @options[:table_name_suffix].present? && !table_name.end_with?(@options[:table_name_suffix].to_s)
39
44
 
40
45
  false
41
46
  end
@@ -50,7 +55,14 @@ module ActiveRecord
50
55
  # resolve string printer
51
56
  tbl = StringIO.new
52
57
 
53
- tbl.print " create_table #{remove_prefix_and_suffix(table).inspect}"
58
+ tbl.print " create_table"
59
+
60
+ if _has_env_table_names?
61
+ tbl.print " _env_table_name(#{remove_prefix_and_suffix(table).inspect})"
62
+ else
63
+ tbl.print " #{remove_prefix_and_suffix(table).inspect}"
64
+ end
65
+
54
66
  tbl.print ", force: true do |t|"
55
67
 
56
68
  # META
@@ -399,6 +399,12 @@ module ActiveRecord
399
399
  settings['persistent'].merge(settings['transient'])
400
400
  end
401
401
 
402
+ # returns the cluster health
403
+ # @return [Hash]
404
+ def cluster_health(**options)
405
+ api(:cluster, :health, options, 'CLUSTER HEALTH').to_h
406
+ end
407
+
402
408
  # transforms provided schema-type to a sql-type
403
409
  # overwrite original methods to provide a elasticsearch version
404
410
  # @param [String, Symbol] type
@@ -467,7 +473,7 @@ module ActiveRecord
467
473
 
468
474
  # overwrite original methods to provide a elasticsearch version
469
475
  def extract_table_options!(options)
470
- options.extract!(:settings, :mappings, :aliases, :force, :strict)
476
+ options.extract!(:settings, :mappings, :aliases, :metas, :force, :strict)
471
477
  end
472
478
  end
473
479
  end
@@ -11,6 +11,7 @@ module ActiveRecord
11
11
  # ORIGINAL methods untouched:
12
12
  #
13
13
  # SUPPORTED but not used:
14
+ # -
14
15
  #
15
16
  # UNSUPPORTED methods that will be ignored:
16
17
  # - native_database_types
@@ -25,11 +26,6 @@ module ActiveRecord
25
26
  # - change_column_default
26
27
  # - change_column_null
27
28
  # - rename_column
28
- #
29
- # UPCOMING future methods:
30
- # - clone (option -> close, or read-only (#lock / unlock) )
31
- # - refresh
32
- # - rename_table
33
29
 
34
30
  define_unsupported_method :create_join_table, :drop_join_table, :create_alter_table,
35
31
  :change_column_default, :change_column_null, :rename_column, :rename_table
@@ -70,6 +66,26 @@ module ActiveRecord
70
66
  table_names.map { |table_name| close_table(table_name) }
71
67
  end
72
68
 
69
+ # refresh an index.
70
+ # A refresh makes recent operations performed on one or more indices available for search.
71
+ # raises an exception if the index could not be found.
72
+ #
73
+ # @param [String] table_name
74
+ # @return [Boolean] result state (returns false if refreshing failed)
75
+ def refresh_table(table_name)
76
+ api(:indices, :refresh, { index: table_name }, 'REFRESH TABLE').dig('_shards','failed') == 0
77
+ end
78
+
79
+ # refresh indices by provided names.
80
+ # @param [Array] table_names
81
+ # @return [Array] result state (returns false if refreshing failed)
82
+ def refresh_tables(*table_names)
83
+ table_names -= [schema_migration.table_name, InternalMetadata.table_name]
84
+ return if table_names.empty?
85
+
86
+ table_names.map { |table_name| refresh_table(table_name) }
87
+ end
88
+
73
89
  # truncates index by provided name.
74
90
  # HINT: Elasticsearch does not have a +truncate+ concept:
75
91
  # - so we have to store the current index' schema
@@ -132,15 +148,14 @@ module ActiveRecord
132
148
  end
133
149
  end
134
150
 
135
- # clones an entire table to the provided +target_name+.
151
+ # clones an entire table (index) to the provided +target_name+.
136
152
  # During cloning, the table will be automatically 'write'-blocked.
137
153
  # @param [String] table_name
138
154
  # @param [String] target_name
139
155
  # @param [Hash] options
140
- # @param [Proc] block
141
- def clone_table(table_name, target_name, **options, &block)
156
+ def clone_table(table_name, target_name, **options)
142
157
  # create new definition
143
- definition = clone_table_definition(table_name, target_name, **extract_table_options!(options), &block)
158
+ definition = clone_table_definition(table_name, target_name, **extract_table_options!(options))
144
159
 
145
160
  # yield optional block
146
161
  if block_given?
@@ -153,6 +168,24 @@ module ActiveRecord
153
168
  definition.exec!
154
169
  end
155
170
 
171
+ # renames a table (index) by executing multiple steps:
172
+ # - clone table
173
+ # - wait for 'green' state
174
+ # - drop old table
175
+ # The +timeout+ option will define how long to wait for the 'green' state.
176
+ #
177
+ # @param [String] table_name
178
+ # @param [String] target_name
179
+ # @param [String (frozen)] timeout (default: '30s')
180
+ # @param [Hash] options - additional 'clone' options (like settings, alias, ...)
181
+ def rename_table(table_name, target_name, timeout: '30s', **options)
182
+ schema_cache.clear_data_source_cache!(table_name)
183
+
184
+ clone_table(table_name, target_name, **options)
185
+ cluster_health(index: target_name, wait_for_status: 'green', timeout: timeout)
186
+ drop_table(table_name)
187
+ end
188
+
156
189
  # creates a new table (index).
157
190
  # [<tt>:force</tt>]
158
191
  # Set to +true+ to drop an existing table
@@ -168,9 +201,6 @@ module ActiveRecord
168
201
  # @param [Hash] options
169
202
  # @return [Boolean] acknowledged status
170
203
  def create_table(table_name, force: false, copy_from: nil, if_not_exists: false, **options)
171
- # IMPORTANT: compute will add possible configured prefix & suffix
172
- table_name = compute_table_name(table_name)
173
-
174
204
  return if if_not_exists && table_exists?(table_name)
175
205
 
176
206
  # copy schema from existing table
@@ -204,12 +234,14 @@ module ActiveRecord
204
234
  # t.mapping :name, :string
205
235
  # # Other column alterations here
206
236
  # end
207
- def change_table(table_name, if_exists: false, **options)
208
- # IMPORTANT: compute will add possible configured prefix & suffix
209
- table_name = compute_table_name(table_name)
210
-
237
+ def change_table(table_name, if_exists: false, recreate: false, **options, &block)
211
238
  return if if_exists && !table_exists?(table_name)
212
239
 
240
+ # check 'recreate' flag.
241
+ # If true, a 'create_table' with copy of the current will be executed
242
+ return create_table(table_name, force: true, copy_from: table_name, **options, &block) if recreate
243
+
244
+ # build new update definition
213
245
  definition = update_table_definition(table_name, self, **options)
214
246
 
215
247
  # yield optional block
@@ -231,12 +263,19 @@ module ActiveRecord
231
263
 
232
264
  alias :add_column :add_mapping
233
265
 
266
+ # will fail unless +recreate:true+ option was provided
234
267
  def change_mapping(table_name, name, type, **options, &block)
235
268
  _exec_change_table_with(:change_mapping, table_name, name, type, **options, &block)
236
269
  end
237
270
 
238
271
  alias :change_column :change_mapping
239
272
 
273
+ def remove_mapping(table_name, name, **options)
274
+ _exec_change_table_with(:remove_mapping, table_name, name, **options)
275
+ end
276
+
277
+ alias :remove_column :remove_mapping
278
+
240
279
  def change_mapping_meta(table_name, name, **options)
241
280
  _exec_change_table_with(:change_mapping_meta, table_name, name, **options)
242
281
  end
@@ -245,14 +284,12 @@ module ActiveRecord
245
284
  _exec_change_table_with(:change_mapping_attributes, table_name, name, **options, &block)
246
285
  end
247
286
 
248
- alias :change_mapping_attribute :change_mapping_attributes
249
-
250
287
  def change_meta(table_name, name, value, **options)
251
288
  _exec_change_table_with(:change_meta, table_name, name, value, **options)
252
289
  end
253
290
 
254
- def delete_meta(table_name, name, **options)
255
- _exec_change_table_with(:delete_meta, table_name, name, **options)
291
+ def remove_meta(table_name, name, **options)
292
+ _exec_change_table_with(:remove_meta, table_name, name, **options)
256
293
  end
257
294
 
258
295
  # -- setting -------------------------------------------------------------------------------------------------
@@ -265,8 +302,8 @@ module ActiveRecord
265
302
  _exec_change_table_with(:change_setting, table_name, name, value, **options, &block)
266
303
  end
267
304
 
268
- def delete_setting(table_name, name, **options, &block)
269
- _exec_change_table_with(:delete_setting, table_name, name, **options, &block)
305
+ def remove_setting(table_name, name, **options, &block)
306
+ _exec_change_table_with(:remove_setting, table_name, name, **options, &block)
270
307
  end
271
308
 
272
309
  # -- alias ---------------------------------------------------------------------------------------------------
@@ -279,28 +316,31 @@ module ActiveRecord
279
316
  _exec_change_table_with(:change_alias, table_name, name, **options, &block)
280
317
  end
281
318
 
282
- def delete_alias(table_name, name, **options, &block)
283
- _exec_change_table_with(:delete_alias, table_name, name, **options, &block)
319
+ def remove_alias(table_name, name, **options, &block)
320
+ _exec_change_table_with(:remove_alias, table_name, name, **options, &block)
284
321
  end
285
322
 
286
- # computes a provided +table_name+ with optionally configured +table_name_prefix+ & +table_name_suffix+.
323
+ # recaps a provided +table_name+ with optionally configured +table_name_prefix+ & +table_name_suffix+.
324
+ # This depends on the connection config of the current environment.
325
+ #
287
326
  # @param [String] table_name
288
327
  # @return [String]
289
- def compute_table_name(table_name)
328
+ def _env_table_name(table_name)
290
329
  table_name = table_name.to_s
291
-
330
+
292
331
  # HINT: +"" creates a new +unfrozen+ string!
293
- str = +""
294
- str << table_name_prefix unless table_name.start_with?(table_name_prefix)
295
- str << table_name
296
- str << table_name_suffix unless table_name.end_with?(table_name_suffix)
297
- str
332
+ name = +""
333
+ name << table_name_prefix unless table_name.start_with?(table_name_prefix)
334
+ name << table_name
335
+ name << table_name_suffix unless table_name.end_with?(table_name_suffix)
336
+
337
+ name
298
338
  end
299
339
 
300
340
  private
301
341
 
302
- def _exec_change_table_with(method, table_name, *args, **kwargs, &block)
303
- change_table(table_name) do |t|
342
+ def _exec_change_table_with(method, table_name, *args, recreate: false, **kwargs, &block)
343
+ change_table(table_name, recreate: recreate) do |t|
304
344
  t.send(method, *args, **kwargs, &block)
305
345
  end
306
346
  end
@@ -9,6 +9,9 @@ module Arel # :nodoc: all
9
9
  # required for ActiveRecord
10
10
  attr_accessor :preparable
11
11
 
12
+ # returns the current bind index (default: 1)
13
+ attr_reader :bind_index
14
+
12
15
  def initialize
13
16
  # force initialize a body as hash
14
17
  super(body: {})
@@ -44,7 +47,8 @@ module Arel # :nodoc: all
44
47
  # adds / sets any argument
45
48
  if args.length == 2
46
49
  @arguments[args[0]] = args[1]
47
- else # should be a hash
50
+ else
51
+ # should be a hash
48
52
  @arguments.merge!(args[0])
49
53
  end
50
54
  when :body
@@ -90,10 +94,13 @@ module Arel # :nodoc: all
90
94
 
91
95
  private
92
96
 
93
- # calls a assign on the body
97
+ # calls a assign on the body.
98
+ # forwards value to the +#claim+ method if key is +:__query__+.
99
+ # @param [Symbol] key
100
+ # @param [Object] value
94
101
  def assign(key, value)
95
102
  # check for special provided key, to claim through an assign
96
- if key == :__claim__
103
+ if key == :__query__
97
104
  if value.is_a?(Array)
98
105
  value.each do |arg|
99
106
  vkey = arg.keys.first
@@ -13,7 +13,7 @@ module Arel # :nodoc: all
13
13
 
14
14
  # SELECT // SEARCH
15
15
  def visit_Arel_Nodes_SelectStatement(o)
16
- # prepare query
16
+ # prepare query type
17
17
  claim(:type, ::ElasticsearchRecord::Query::TYPE_SEARCH)
18
18
 
19
19
  resolve(o.cores) # visit_Arel_Nodes_SelectCore
@@ -26,20 +26,23 @@ module Arel # :nodoc: all
26
26
  resolve(o.configure)
27
27
  end
28
28
 
29
- # UPDATE
29
+ # UPDATE (by query - not a single record...)
30
30
  def visit_Arel_Nodes_UpdateStatement(o)
31
31
  # switch between updating a single Record or multiple by query
32
32
  if o.relation.is_a?(::Arel::Table)
33
33
  raise NotImplementedError, "if you've made it this far, something went wrong ..."
34
34
  end
35
35
 
36
- # prepare query
36
+ # prepare query type
37
37
  claim(:type, ::ElasticsearchRecord::Query::TYPE_UPDATE_BY_QUERY)
38
38
 
39
+ # force refresh after update - but it can be unset again through the 'configure' ...
40
+ claim(:refresh, true)
41
+
39
42
  # sets the index
40
43
  resolve(o.relation)
41
44
 
42
- # updating multiple entries need a script
45
+ # updating multiple entries needs a script
43
46
  assign(:script, {}) do
44
47
  assign(:inline, "") do
45
48
  updates = collect(o.values)
@@ -59,16 +62,19 @@ module Arel # :nodoc: all
59
62
  resolve(o.configure)
60
63
  end
61
64
 
62
- # DELETE
65
+ # DELETE (by query - not a single record...)
63
66
  def visit_Arel_Nodes_DeleteStatement(o)
64
67
  # switch between updating a single Record or multiple by query
65
68
  if o.relation.is_a?(::Arel::Table)
66
69
  raise NotImplementedError, "if you've made it this far, something went wrong ..."
67
70
  end
68
71
 
69
- # prepare query
72
+ # prepare query type
70
73
  claim(:type, ::ElasticsearchRecord::Query::TYPE_DELETE_BY_QUERY)
71
74
 
75
+ # force refresh after delete - but it can be unset again through the 'configure' ...
76
+ claim(:refresh, true)
77
+
72
78
  # sets the index
73
79
  resolve(o.relation)
74
80
 
@@ -84,13 +90,15 @@ module Arel # :nodoc: all
84
90
  resolve(o.configure)
85
91
  end
86
92
 
87
- # INSERT
93
+ # INSERT (by query - not a single record...)
94
+ # this is also used by 'meta' or 'schema_migrations' tables ...
88
95
  def visit_Arel_Nodes_InsertStatement(o)
89
-
90
96
  # switch between updating a single Record or multiple by query
91
97
  if o.relation.is_a?(::Arel::Table)
92
- # prepare query
98
+ # prepare query type
93
99
  claim(:type, ::ElasticsearchRecord::Query::TYPE_CREATE)
100
+
101
+ # force refresh after insert
94
102
  claim(:refresh, true)
95
103
 
96
104
  # sets the index
@@ -89,8 +89,9 @@ module Arel # :nodoc: all
89
89
  # prepare query
90
90
  claim(:type, ::ElasticsearchRecord::Query::TYPE_INDEX_UPDATE_SETTING)
91
91
 
92
- # special overcomplicated blocks to assign a hash of settings directly to the body
93
- assign(:__claim__, {}) do
92
+ # special overcomplicated blocks to assign a hash of settings directly to the body...
93
+ # todo: refactor this in future versions
94
+ assign(:__query__, {}) do
94
95
  assign(:body, {}) do
95
96
  resolve(o.items, :visit_TableSettingDefinition)
96
97
  end
@@ -98,7 +99,7 @@ module Arel # :nodoc: all
98
99
  end
99
100
 
100
101
  alias :visit_AddSettingDefinition :visit_ChangeSettingDefinition
101
- alias :visit_DeleteSettingDefinition :visit_ChangeSettingDefinition
102
+ alias :visit_RemoveSettingDefinition :visit_ChangeSettingDefinition
102
103
 
103
104
  def visit_ChangeAliasDefinition(o)
104
105
  # prepare query
@@ -110,7 +111,7 @@ module Arel # :nodoc: all
110
111
 
111
112
  alias :visit_AddAliasDefinition :visit_ChangeAliasDefinition
112
113
 
113
- def visit_DeleteAliasDefinition(o)
114
+ def visit_RemoveAliasDefinition(o)
114
115
  # prepare query
115
116
  claim(:type, ::ElasticsearchRecord::Query::TYPE_INDEX_DELETE_ALIAS)
116
117
 
@@ -8,8 +8,8 @@ module ElasticsearchRecord
8
8
 
9
9
  module VERSION
10
10
  MAJOR = 1
11
- MINOR = 2
12
- TINY = 4
11
+ MINOR = 3
12
+ TINY = 0
13
13
  PRE = nil
14
14
 
15
15
  STRING = [MAJOR, MINOR, TINY, PRE].compact.join(".")
@@ -33,9 +33,9 @@ module ElasticsearchRecord
33
33
  # since total will be limited to 10000 results, we need to resolve the real values by a custom query.
34
34
  # This query is called through +#select_count+.
35
35
  #
36
- # HINT: :__claim__ directly interacts with the query-object and sets a 'terminate_after' argument
37
- # (see @ Arel::Collectors::ElasticsearchQuery#assign)
38
- arel = spawn.unscope!(:offset, :limit, :order, :configure, :aggs).configure!(:__claim__, argument: { terminate_after: limit_value }).arel
36
+ # HINT: +:__query__+ directly interacts with the query-object and sets the 'terminate_after' argument
37
+ # see @ ElasticsearchRecord::Query#arguments & Arel::Collectors::ElasticsearchQuery#assign
38
+ arel = spawn.unscope!(:offset, :limit, :order, :configure, :aggs).configure!(:__query__, argument: { terminate_after: limit_value }).arel
39
39
  klass.connection.select_count(arel, "#{klass.name} Count")
40
40
  else
41
41
  # since total will be limited to 10000 results, we need to resolve the real values by a custom query.
@@ -27,9 +27,12 @@ module ElasticsearchRecord
27
27
  self
28
28
  end
29
29
 
30
- # sets or overwrites additional arguments for the query (not the :query-node, the whole query).
31
- # You can also force a overwrite of previously defined arguments, like +size+ or +from+.
32
- # This is useful to force remove of keys.
30
+ # sets or overwrites additional arguments for the whole query (not the current 'query-node' - the whole query).
31
+ # Previously defined arguments (like +size+ or +from+) can also be overwritten.
32
+ # Providing a +nil+ value will remove the key - this is useful to force remove of keys.
33
+ #
34
+ # Providing the special key +:__query__+ will directly access the query object, to alter query-related values
35
+ # (like 'refresh, arguments, columns, ...' - see @ Arel::Collectors::ElasticsearchQuery
33
36
  #
34
37
  # @example
35
38
  # # adds {refresh true} to the query
@@ -37,6 +40,10 @@ module ElasticsearchRecord
37
40
  #
38
41
  # # overwrites or sets {from: 50} but removes the :sort key
39
42
  # configure({from: 50, sort: nil})
43
+ #
44
+ # # sets the query's 'refresh' to true
45
+ # configure(:__query__, refresh: true)
46
+ #
40
47
  # @param [Array] args
41
48
  def configure(*args)
42
49
  spawn.configure!(*args)
@@ -48,10 +55,10 @@ module ElasticsearchRecord
48
55
 
49
56
  if args.length == 1 && args.first.is_a?(Hash)
50
57
  self.configure_value = self.configure_value.merge(args[0])
51
- elsif args.length == 2 && args[0] == :__claim__
52
- tmp = self.configure_value[:__claim__] || []
58
+ elsif args.length == 2 && args[0] == :__query__
59
+ tmp = self.configure_value[:__query__] || []
53
60
  tmp << args[1]
54
- self.configure_value = self.configure_value.merge(:__claim__ => tmp)
61
+ self.configure_value = self.configure_value.merge(:__query__ => tmp)
55
62
  elsif args.length == 2
56
63
  self.configure_value = self.configure_value.merge(args[0] => args[1])
57
64
  end
@@ -69,7 +76,8 @@ module ElasticsearchRecord
69
76
 
70
77
  alias_method :aggs, :aggregate
71
78
 
72
- def aggregate!(opts, *rest) # :nodoc:
79
+ def aggregate!(opts, *rest)
80
+ # :nodoc:
73
81
  case opts
74
82
  when Symbol, String
75
83
  self.aggs_clause += build_query_clause(opts, rest)
@@ -84,6 +92,16 @@ module ElasticsearchRecord
84
92
  self
85
93
  end
86
94
 
95
+ # sets the query's +refresh+ value.
96
+ # @param [Boolean] value (default: true)
97
+ def refresh(value = true)
98
+ spawn.refresh!(value)
99
+ end
100
+
101
+ def refresh!(value = true)
102
+ configure!(:__query__, refresh: value)
103
+ end
104
+
87
105
  # add a whole query 'node' to the query.
88
106
  # @example
89
107
  # query(:bool, {filter: ...})
@@ -92,7 +110,8 @@ module ElasticsearchRecord
92
110
  spawn.query!(*args)
93
111
  end
94
112
 
95
- def query!(kind, opts, *rest) # :nodoc:
113
+ def query!(kind, opts, *rest)
114
+ # :nodoc:
96
115
  kind!(kind)
97
116
  self.query_clause += build_query_clause(opts.keys[0], opts.values[0], rest)
98
117
  self
@@ -106,7 +125,8 @@ module ElasticsearchRecord
106
125
  spawn.filter!(*args)
107
126
  end
108
127
 
109
- def filter!(opts, *rest) # :nodoc:
128
+ def filter!(opts, *rest)
129
+ # :nodoc:
110
130
  set_default_kind!
111
131
  self.query_clause += build_query_clause(:filter, opts, rest)
112
132
  self
@@ -120,7 +140,8 @@ module ElasticsearchRecord
120
140
  spawn.must_not!(*args)
121
141
  end
122
142
 
123
- def must_not!(opts, *rest) # :nodoc:
143
+ def must_not!(opts, *rest)
144
+ # :nodoc:
124
145
  set_default_kind!
125
146
  self.query_clause += build_query_clause(:must_not, opts, rest)
126
147
  self
@@ -134,7 +155,8 @@ module ElasticsearchRecord
134
155
  spawn.must!(*args)
135
156
  end
136
157
 
137
- def must!(opts, *rest) # :nodoc:
158
+ def must!(opts, *rest)
159
+ # :nodoc:
138
160
  set_default_kind!
139
161
  self.query_clause += build_query_clause(:must, opts, rest)
140
162
  self
@@ -148,7 +170,8 @@ module ElasticsearchRecord
148
170
  spawn.should!(*args)
149
171
  end
150
172
 
151
- def should!(opts, *rest) # :nodoc:
173
+ def should!(opts, *rest)
174
+ # :nodoc:
152
175
  set_default_kind!
153
176
  self.query_clause += build_query_clause(:should, opts, rest)
154
177
  self
@@ -176,7 +199,8 @@ module ElasticsearchRecord
176
199
  super
177
200
  end
178
201
 
179
- def where!(opts, *rest) # :nodoc:
202
+ def where!(opts, *rest)
203
+ # :nodoc:
180
204
  case opts
181
205
  # check the first provided parameter +opts+ and validate, if this is an alias for "must, must_not, should or filter"
182
206
  # if true, we expect the rest[0] to be a hash.
@@ -186,7 +210,7 @@ module ElasticsearchRecord
186
210
  when :filter, :must, :must_not, :should
187
211
  send("#{opts}!", *rest)
188
212
  else
189
- raise ArgumentError, "Unsupported argument type for where: #{opts}"
213
+ raise ArgumentError, "Unsupported prefix type '#{opts}'. Allowed types are: :filter, :must, :must_not, :should"
190
214
  end
191
215
  when Array
192
216
  # check if this is a nested array of multiple [<kind>,<data>]
@@ -197,42 +221,8 @@ module ElasticsearchRecord
197
221
  else
198
222
  where!(*opts, *rest)
199
223
  end
200
- when String
201
- # fallback to ActiveRecords +#where_clause+
202
- # currently NOT supported
203
- super(opts, rest)
204
224
  else
205
- # hash -> {name: 'hans'}
206
- # protects against forwarding params directly to where ...
207
- # User.where(params) <- will never work
208
- # User.where(params.permit(:user)) <- ok
209
- opts = sanitize_forbidden_attributes(opts)
210
-
211
- # resolve possible aliases
212
- opts = opts.transform_keys do |key|
213
- key = key.to_s
214
- klass.attribute_aliases[key] || key
215
- end
216
-
217
- # check if we have keys without Elasticsearch fields
218
- if (invalid = (opts.keys - klass.searchable_column_names)).present?
219
- raise(ActiveRecord::UnknownAttributeReference,
220
- "Unable to build query with unknown searchable attributes: #{invalid.map(&:inspect).join(", ")}. " \
221
- "If you want to build a custom query you should use one of those methods: 'filter, must, must_not, should'. " \
222
- "#{klass.name}.filter('#{invalid[0]}' => '...')"
223
- )
224
- end
225
-
226
- # force set default kind, if not previously set
227
- set_default_kind!
228
-
229
- # builds predicates from opts (transforms this in a more unreadable way but is required for nested assignment & binds ...)
230
- parts = opts.map do |key,value|
231
- # builds and returns a new Arel Node from provided key/value pair
232
- predicate_builder[key,value]
233
- end
234
-
235
- self.where_clause += ::ActiveRecord::Relation::WhereClause.new(parts)
225
+ self.where_clause += build_where_clause(opts, rest)
236
226
  end
237
227
 
238
228
  self
@@ -271,6 +261,45 @@ module ElasticsearchRecord
271
261
 
272
262
  private
273
263
 
264
+ def build_where_clause(opts, _rest = [])
265
+ case opts
266
+ when Symbol, Array, String
267
+ raise ArgumentError, "Unsupported or unresolved argument class '#{opts.class}' for build_where_clause: #{opts}"
268
+ else
269
+ # hash -> {name: 'hans'}
270
+ # protects against forwarding params directly to where ...
271
+ # User.where(params) <- will never work
272
+ # User.where(params.permit(:user)) <- ok
273
+ opts = sanitize_forbidden_attributes(opts)
274
+
275
+ # resolve possible aliases
276
+ opts = opts.transform_keys do |key|
277
+ key = key.to_s
278
+ klass.attribute_aliases[key] || key
279
+ end
280
+
281
+ # check if we have keys without Elasticsearch fields
282
+ if (invalid = (opts.keys - klass.searchable_column_names)).present?
283
+ raise(ActiveRecord::UnknownAttributeReference,
284
+ "Unable to build query with unknown searchable attributes: #{invalid.map(&:inspect).join(", ")}. " \
285
+ "If you want to build a custom query you should use one of those methods: 'filter, must, must_not, should'. " \
286
+ "#{klass.name}.filter('#{invalid[0]}' => '...')"
287
+ )
288
+ end
289
+
290
+ # force set default kind, if not previously set
291
+ set_default_kind!
292
+
293
+ # builds predicates from opts (transforms this in a more unreadable way but is required for nested assignment & binds ...)
294
+ parts = opts.map do |key, value|
295
+ # builds and returns a new Arel Node from provided key/value pair
296
+ predicate_builder[key, value]
297
+ end
298
+
299
+ ::ActiveRecord::Relation::WhereClause.new(parts)
300
+ end
301
+ end
302
+
274
303
  def build_query_clause(kind, data, rest = [])
275
304
  ElasticsearchRecord::Relation::QueryClause.new(kind, Array.wrap(data), rest.extract_options!)
276
305
  end
@@ -104,7 +104,7 @@ module ElasticsearchRecord
104
104
  relation.offset!(nil).limit!(nil)
105
105
 
106
106
  # remove the 'index' from the query arguments (pit doesn't like that)
107
- relation.configure!(:__claim__, { index: nil })
107
+ relation.configure!(:__query__, { index: nil })
108
108
 
109
109
  # we store the results in this array
110
110
  results = []
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: elasticsearch_record
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.2.4
4
+ version: 1.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Tobias Gonsior
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2022-12-15 00:00:00.000000000 Z
11
+ date: 2023-01-17 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activerecord