Module: ActiveRecord::ConnectionAdapters::Elasticsearch::TableStatements
- Extended by:
- ActiveSupport::Concern
- Defined in:
- lib/active_record/connection_adapters/elasticsearch/table_statements.rb
Overview
extend adapter with table-related statements
== Table name decoration
Every statement below resolves its provided table name(s) through +#_env_table_name+, which recaps them with the +table_name_prefix+ & +table_name_suffix+ of the connection config. This happens by default - so a migration only ever has to name the base table (index):
create_table 'settings' # => creates 'settings-dev' on a '-dev' suffixed connection
Provide +decorate: false+ to address an index by its literal name instead. This is required for names that are already resolved and for base names that happen to start with the prefix (or end with the suffix), which +#_env_table_name+ cannot tell apart:
drop_table 'settings-pro', decorate: false
The default of a NOT explicitly provided +decorate:+ argument is resolved from +ElasticsearchRecord.decorate_table_names+ - setting it to false restores the former, opt-in behaviour, where the decoration had to be applied by hand through +#_env_table_name+. A single statement can still opt in or out on its own.
PLEASE NOTE: the decoration only applies to table (index) names - +alias+, +mapping+, +setting+ & +meta+ names are never touched.
== Internal tables
+schema_migrations+ & +ar_internal_metadata+ carry the migration state of the connection. Only +#truncate_table+ guards them - it raises instead of wiping the state of a whole environment, which in Elasticsearch means a +drop+ & +create+ of the index.
Every other statement passes them through on purpose. +#drop_table+ especially MUST stay open: ActiveRecord resets both tables through it (+ActiveRecord::SchemaMigration#drop_table+ & +ActiveRecord::InternalMetadata#drop_table+ both call +connection.drop_table(table_name, if_exists: true)+), so a guard there would break that API without an escape hatch.
Instance Method Summary collapse
-
#_env_table_name(table_name) ⇒ String
recaps a provided +table_name+ with optionally configured +table_name_prefix+ & +table_name_suffix+.
-
#add_alias(table_name, name, **options, &block) ⇒ Object
-- alias ---------------------------------------------------------------------------------------------------.
-
#add_mapping(table_name, name, type, **options, &block) ⇒ Object
(also: #add_column)
-- mapping -------------------------------------------------------------------------------------------------.
-
#add_setting(table_name, name, value, **options, &block) ⇒ Object
-- setting -------------------------------------------------------------------------------------------------.
-
#backup_table(table_name, to: nil, close: true, decorate: nil) ⇒ String
creates a backup (snapshot) of the entire table (index) from provided +table_name+.
-
#block_table(table_name, block_name = :write, decorate: nil) ⇒ Boolean
blocks access to the provided table (index) and +block+ name.
- #change_alias(table_name, name, **options, &block) ⇒ Object
-
#change_mapping(table_name, name, type, **options, &block) ⇒ Object
(also: #change_column)
will fail unless +recreate:true+ option was provided.
- #change_mapping_attributes(table_name, name, **options, &block) ⇒ Object
- #change_mapping_meta(table_name, name, **options) ⇒ Object
- #change_meta(table_name, name, value, **options) ⇒ Object
- #change_setting(table_name, name, value, **options, &block) ⇒ Object
-
#change_table(table_name, if_exists: false, recreate: false, decorate: nil, **options, &block) ⇒ Object
A block for changing mappings, settings & aliases in +table+.
-
#clone_table(table_name, target_name, decorate: nil, **options) ⇒ Boolean
clones an entire table (index) with its docs to the provided +target_name+.
-
#close_table(table_name, decorate: nil) ⇒ Boolean
Closes an index.
-
#close_tables(*table_names, decorate: nil) ⇒ Array
Closes indices by provided names.
-
#create_table(table_name, force: false, copy_from: nil, if_not_exists: false, decorate: nil, **options) ⇒ Boolean
creates a new table (index).
-
#drop_table(table_name, if_exists: false, decorate: nil) ⇒ Boolean
drops an index [:if_exists] Set to +true+ to only drop the table if it exists.
-
#open_table(table_name, decorate: nil) ⇒ Boolean
Opens a closed index.
-
#open_tables(*table_names, decorate: nil) ⇒ Array
Opens closed indices.
-
#refresh_table(table_name, decorate: nil) ⇒ Boolean
refresh an index.
-
#refresh_tables(*table_names, decorate: nil) ⇒ Array
refresh indices by provided names.
-
#reindex_table(table_name, target_name, decorate: nil, **options) ⇒ Hash
Copies documents from a source to a destination.
- #remove_alias(table_name, name, **options, &block) ⇒ Object
- #remove_mapping(table_name, name, **options) ⇒ Object (also: #remove_column)
- #remove_meta(table_name, name, **options) ⇒ Object
- #remove_setting(table_name, name, **options, &block) ⇒ Object
-
#rename_table(table_name, target_name, timeout: '1m', decorate: nil, **options) ⇒ Object
renames a table (index) by executing multiple steps: - clone table - wait for 'green' state - drop old table The +timeout+ option will define how long to wait for the 'green' state.
-
#restore_table(table_name, from:, timeout: '1m', unblock: true, drop_backup: false, decorate: nil) ⇒ nil
restores a entire table (index) from provided +target_name+.
-
#truncate_table(table_name, decorate: nil) ⇒ Boolean
(also: #truncate)
truncates index by provided name.
-
#truncate_tables(*table_names, decorate: nil) ⇒ Array
truncate indices by provided names.
-
#unblock_table(table_name, block_name = nil, decorate: nil) ⇒ Boolean
unblocks access to the provided table (index) and +block+ name.
Instance Method Details
#_env_table_name(table_name) ⇒ String
recaps a provided +table_name+ with optionally configured +table_name_prefix+ & +table_name_suffix+. This depends on the connection config of the current environment.
PLEASE NOTE: the method is idempotent through a +start_with?+ / +end_with?+ check, so it can safely be called on an already resolved name. That check cannot tell a resolved name apart from a base name that legitimately starts with the prefix (or ends with the suffix) - use +decorate: false+ on the statement to address such an index literally.
521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 |
# File 'lib/active_record/connection_adapters/elasticsearch/table_statements.rb', line 521 def _env_table_name(table_name) # ensure *table_name* is a string table_name = table_name.to_s # ensure *prefix* and *suffix* are strings prefix = table_name_prefix.to_s suffix = table_name_suffix.to_s # HINT: +"" creates a new +unfrozen+ string! name = +"" name << prefix unless table_name.start_with?(prefix) name << table_name name << suffix unless table_name.end_with?(suffix) name end |
#add_alias(table_name, name, **options, &block) ⇒ Object
-- alias ---------------------------------------------------------------------------------------------------
499 500 501 |
# File 'lib/active_record/connection_adapters/elasticsearch/table_statements.rb', line 499 def add_alias(table_name, name, **, &block) _exec_change_table_with(:add_alias, table_name, name, **, &block) end |
#add_mapping(table_name, name, type, **options, &block) ⇒ Object Also known as: add_column
-- mapping -------------------------------------------------------------------------------------------------
PLEASE NOTE: every statement below reaches +#change_table+ through +#_exec_change_table_with+, which forwards a provided +decorate:+ flag - only the TABLE name is ever decorated, never the mapping / meta / setting / alias name.
448 449 450 |
# File 'lib/active_record/connection_adapters/elasticsearch/table_statements.rb', line 448 def add_mapping(table_name, name, type, **, &block) _exec_change_table_with(:add_mapping, table_name, name, type, **, &block) end |
#add_setting(table_name, name, value, **options, &block) ⇒ Object
-- setting -------------------------------------------------------------------------------------------------
485 486 487 |
# File 'lib/active_record/connection_adapters/elasticsearch/table_statements.rb', line 485 def add_setting(table_name, name, value, **, &block) _exec_change_table_with(:add_setting, table_name, name, value, **, &block) end |
#backup_table(table_name, to: nil, close: true, decorate: nil) ⇒ String
creates a backup (snapshot) of the entire table (index) from provided +table_name+. The backup will be closed, to prevent read/write access. The +target_name+ will be auto-generated, if not provided.
262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 |
# File 'lib/active_record/connection_adapters/elasticsearch/table_statements.rb', line 262 def backup_table(table_name, to: nil, close: true, decorate: nil) table_name = _decorate_table_name(table_name, decorate: decorate) # IMPORTANT: the auto-generated name is built from the ALREADY resolved +table_name+, so # it stays within the current environment without being decorated a second time (which # would append the suffix BEHIND the '-snapshot-' part). to = to.nil? ? "#{table_name}-snapshot-#{Time.now.strftime('%s%3N')}" : _decorate_table_name(to, decorate: decorate) raise ArgumentError, "unable to backup '#{table_name}' to already existing target '#{to}'!" if table_exists?(to) clone_table(table_name, to, decorate: false) close_table(to, decorate: false) if close to end |
#block_table(table_name, block_name = :write, decorate: nil) ⇒ Boolean
blocks access to the provided table (index) and +block+ name.
200 201 202 203 204 |
# File 'lib/active_record/connection_adapters/elasticsearch/table_statements.rb', line 200 def block_table(table_name, block_name = :write, decorate: nil) table_name = _decorate_table_name(table_name, decorate: decorate) api('indices.add_block', { index: table_name, block: block_name }, "BLOCK #{block_name.to_s.upcase} TABLE").dig('acknowledged') end |
#change_alias(table_name, name, **options, &block) ⇒ Object
503 504 505 |
# File 'lib/active_record/connection_adapters/elasticsearch/table_statements.rb', line 503 def change_alias(table_name, name, **, &block) _exec_change_table_with(:change_alias, table_name, name, **, &block) end |
#change_mapping(table_name, name, type, **options, &block) ⇒ Object Also known as: change_column
will fail unless +recreate:true+ option was provided
455 456 457 |
# File 'lib/active_record/connection_adapters/elasticsearch/table_statements.rb', line 455 def change_mapping(table_name, name, type, **, &block) _exec_change_table_with(:change_mapping, table_name, name, type, **, &block) end |
#change_mapping_attributes(table_name, name, **options, &block) ⇒ Object
471 472 473 |
# File 'lib/active_record/connection_adapters/elasticsearch/table_statements.rb', line 471 def change_mapping_attributes(table_name, name, **, &block) _exec_change_table_with(:change_mapping_attributes, table_name, name, **, &block) end |
#change_mapping_meta(table_name, name, **options) ⇒ Object
467 468 469 |
# File 'lib/active_record/connection_adapters/elasticsearch/table_statements.rb', line 467 def (table_name, name, **) _exec_change_table_with(:change_mapping_meta, table_name, name, **) end |
#change_meta(table_name, name, value, **options) ⇒ Object
475 476 477 |
# File 'lib/active_record/connection_adapters/elasticsearch/table_statements.rb', line 475 def (table_name, name, value, **) _exec_change_table_with(:change_meta, table_name, name, value, **) end |
#change_setting(table_name, name, value, **options, &block) ⇒ Object
489 490 491 |
# File 'lib/active_record/connection_adapters/elasticsearch/table_statements.rb', line 489 def change_setting(table_name, name, value, **, &block) _exec_change_table_with(:change_setting, table_name, name, value, **, &block) end |
#change_table(table_name, if_exists: false, recreate: false, decorate: nil, **options, &block) ⇒ Object
A block for changing mappings, settings & aliases in +table+.
# change_table() yields a ChangeTableDefinition instance change_table(:suppliers) do |t| t.mapping :name, :string # Other column alterations here end
406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 |
# File 'lib/active_record/connection_adapters/elasticsearch/table_statements.rb', line 406 def change_table(table_name, if_exists: false, recreate: false, decorate: nil, **, &block) table_name = _decorate_table_name(table_name, decorate: decorate) return if if_exists && !table_exists?(table_name) # check 'recreate' flag. # If true, a 'create_table' with copy of the current will be executed return create_table(table_name, force: true, copy_from: table_name, decorate: false, **, &block) if recreate # build new update definition definition = update_table_definition(table_name, self, **) # yield optional block if block_given? definition.assign do |d| yield d end end # execute definition query(ies) definition.exec! end |
#clone_table(table_name, target_name, decorate: nil, **options) ⇒ Boolean
clones an entire table (index) with its docs to the provided +target_name+. During cloning, the table will be automatically 'write'-blocked.
232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 |
# File 'lib/active_record/connection_adapters/elasticsearch/table_statements.rb', line 232 def clone_table(table_name, target_name, decorate: nil, **) table_name = _decorate_table_name(table_name, decorate: decorate) target_name = _decorate_table_name(target_name, decorate: decorate) # create new definition definition = clone_table_definition(table_name, target_name, **()) # yield optional block if block_given? definition.assign do |d| yield d end end # execute definition query(ies) definition.exec! end |
#close_table(table_name, decorate: nil) ⇒ Boolean
Closes an index.
93 94 95 96 97 98 99 100 101 |
# File 'lib/active_record/connection_adapters/elasticsearch/table_statements.rb', line 93 def close_table(table_name, decorate: nil) table_name = _decorate_table_name(table_name, decorate: decorate) # IMPORTANT: Clears out internal caches for the *table_name* schema_cache.clear_data_source_cache!(table_name) # call the API api('indices.close', { index: table_name }, 'CLOSE TABLE').dig('acknowledged') end |
#close_tables(*table_names, decorate: nil) ⇒ Array
Closes indices by provided names.
107 108 109 |
# File 'lib/active_record/connection_adapters/elasticsearch/table_statements.rb', line 107 def close_tables(*table_names, decorate: nil) table_names.map { |table_name| close_table(table_name, decorate: decorate) } end |
#create_table(table_name, force: false, copy_from: nil, if_not_exists: false, decorate: nil, **options) ⇒ Boolean
creates a new table (index). [:force] Set to +true+ to drop an existing index Defaults to false. [:copy_from] Set to an existing index, to copy it's schema. [:if_not_exists] Set to +true+ to skip creation if index already exists. Defaults to false.
363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 |
# File 'lib/active_record/connection_adapters/elasticsearch/table_statements.rb', line 363 def create_table(table_name, force: false, copy_from: nil, if_not_exists: false, decorate: nil, **) table_name = _decorate_table_name(table_name, decorate: decorate) return if if_not_exists && table_exists?(table_name) # copy schema from existing table .merge!(table_schema(_decorate_table_name(copy_from, decorate: decorate))) if copy_from # create new definition definition = create_table_definition(table_name, **()) # yield optional block if block_given? definition.assign do |d| yield d end end # force drop existing table if force drop_table(table_name, if_exists: true, decorate: false) else # IMPORTANT: Clears out internal caches schema_cache.clear_data_source_cache!(table_name) end # execute definition query(ies) definition.exec! end |
#drop_table(table_name, if_exists: false, decorate: nil) ⇒ Boolean
drops an index [:if_exists] Set to +true+ to only drop the table if it exists. Defaults to false.
PLEASE NOTE: unlike +#truncate_table+ this does NOT guard the AR-internal indices - ActiveRecord resets them through exactly this statement (+ActiveRecord::SchemaMigration#drop_table+ & +ActiveRecord::InternalMetadata#drop_table+ both call +connection.drop_table(table_name, if_exists: true)+).
185 186 187 188 189 190 191 192 193 |
# File 'lib/active_record/connection_adapters/elasticsearch/table_statements.rb', line 185 def drop_table(table_name, if_exists: false, decorate: nil, **) table_name = _decorate_table_name(table_name, decorate: decorate) # IMPORTANT: Clears out internal caches for the *table_name* schema_cache.clear_data_source_cache!(table_name) # call the API api('indices.delete', { index: table_name, ignore: (if_exists ? 404 : nil) }, 'DROP TABLE').dig('acknowledged') end |
#open_table(table_name, decorate: nil) ⇒ Boolean
Opens a closed index.
71 72 73 74 75 76 77 78 79 |
# File 'lib/active_record/connection_adapters/elasticsearch/table_statements.rb', line 71 def open_table(table_name, decorate: nil) table_name = _decorate_table_name(table_name, decorate: decorate) # IMPORTANT: Clears out internal caches for the *table_name* schema_cache.clear_data_source_cache!(table_name) # call the API api('indices.open', { index: table_name }, 'OPEN TABLE').dig('acknowledged') end |
#open_tables(*table_names, decorate: nil) ⇒ Array
Opens closed indices.
85 86 87 |
# File 'lib/active_record/connection_adapters/elasticsearch/table_statements.rb', line 85 def open_tables(*table_names, decorate: nil) table_names.map { |table_name| open_table(table_name, decorate: decorate) } end |
#refresh_table(table_name, decorate: nil) ⇒ Boolean
refresh an index. A refresh makes recent operations performed on one or more indices available for search. raises an exception if the index could not be found.
118 119 120 121 122 123 |
# File 'lib/active_record/connection_adapters/elasticsearch/table_statements.rb', line 118 def refresh_table(table_name, decorate: nil) table_name = _decorate_table_name(table_name, decorate: decorate) # call the API api('indices.refresh', { index: table_name }, 'REFRESH TABLE').dig('_shards', 'failed') == 0 end |
#refresh_tables(*table_names, decorate: nil) ⇒ Array
refresh indices by provided names.
129 130 131 |
# File 'lib/active_record/connection_adapters/elasticsearch/table_statements.rb', line 129 def refresh_tables(*table_names, decorate: nil) table_names.map { |table_name| refresh_table(table_name, decorate: decorate) } end |
#reindex_table(table_name, target_name, decorate: nil, **options) ⇒ Hash
Copies documents from a source to a destination.
435 436 437 438 439 440 |
# File 'lib/active_record/connection_adapters/elasticsearch/table_statements.rb', line 435 def reindex_table(table_name, target_name, decorate: nil, **) table_name = _decorate_table_name(table_name, decorate: decorate) target_name = _decorate_table_name(target_name, decorate: decorate) api(:reindex, { body: { source: { index: table_name }, dest: { index: target_name } } }.merge(), 'REINDEX TABLE') end |
#remove_alias(table_name, name, **options, &block) ⇒ Object
507 508 509 |
# File 'lib/active_record/connection_adapters/elasticsearch/table_statements.rb', line 507 def remove_alias(table_name, name, **, &block) _exec_change_table_with(:remove_alias, table_name, name, **, &block) end |
#remove_mapping(table_name, name, **options) ⇒ Object Also known as: remove_column
461 462 463 |
# File 'lib/active_record/connection_adapters/elasticsearch/table_statements.rb', line 461 def remove_mapping(table_name, name, **) _exec_change_table_with(:remove_mapping, table_name, name, **) end |
#remove_meta(table_name, name, **options) ⇒ Object
479 480 481 |
# File 'lib/active_record/connection_adapters/elasticsearch/table_statements.rb', line 479 def (table_name, name, **) _exec_change_table_with(:remove_meta, table_name, name, **) end |
#remove_setting(table_name, name, **options, &block) ⇒ Object
493 494 495 |
# File 'lib/active_record/connection_adapters/elasticsearch/table_statements.rb', line 493 def remove_setting(table_name, name, **, &block) _exec_change_table_with(:remove_setting, table_name, name, **, &block) end |
#rename_table(table_name, target_name, timeout: '1m', decorate: nil, **options) ⇒ Object
renames a table (index) by executing multiple steps:
- clone table
- wait for 'green' state
- drop old table The +timeout+ option will define how long to wait for the 'green' state.
332 333 334 335 336 337 338 339 340 341 342 343 344 345 |
# File 'lib/active_record/connection_adapters/elasticsearch/table_statements.rb', line 332 def rename_table(table_name, target_name, timeout: '1m', decorate: nil, **) # IMPORTANT: both names must be resolved HERE and not only forwarded to the statements # below - the +schema_cache+ and the +cluster_health+ call in between address the index # directly and would otherwise miss the decorated one table_name = _decorate_table_name(table_name, decorate: decorate) target_name = _decorate_table_name(target_name, decorate: decorate) # IMPORTANT: Clears out internal caches schema_cache.clear_data_source_cache!(table_name) clone_table(table_name, target_name, decorate: false, **) cluster_health(index: target_name, wait_for_status: 'green', timeout: timeout) drop_table(table_name, decorate: false) end |
#restore_table(table_name, from:, timeout: '1m', unblock: true, drop_backup: false, decorate: nil) ⇒ nil
restores a entire table (index) from provided +target_name+. The +table_name+ will be dropped, if exists. The +from+ will persist, if not provided +drop_backup:true+.
IMPORTANT: both strategies restore through a +clone+, which inherits the settings of its source - including the 'write'-block that is required to clone at all. The restored table is therefore open but read-only until that block is released, which is what the +unblock+ flag is for. (there is no +open+ flag: a clone is always created open - even from a closed source) see @ ActiveRecord::ConnectionAdapters::Elasticsearch::CloneTableDefinition#_before_exec
303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 |
# File 'lib/active_record/connection_adapters/elasticsearch/table_statements.rb', line 303 def restore_table(table_name, from:, timeout: '1m', unblock: true, drop_backup: false, decorate: nil) table_name = _decorate_table_name(table_name, decorate: decorate) from = _decorate_table_name(from, decorate: decorate) raise ArgumentError, "unable to restore from missing target '#{from}'!" unless table_exists?(from) drop_table(table_name, if_exists: true, decorate: false) # choose best strategy if drop_backup rename_table(from, table_name, timeout: timeout, decorate: false) else clone_table(from, table_name, decorate: false) end # release the inherited 'write'-block, if provided unblock_table(table_name, :write, decorate: false) if unblock end |
#truncate_table(table_name, decorate: nil) ⇒ Boolean Also known as: truncate
truncates index by provided name. HINT: Elasticsearch does not have a +truncate+ concept:
- so we have to store the current index' schema
- drop the index
- and create it again
PLEASE NOTE: an AR-internal index (+schema_migrations+ / +ar_internal_metadata+) raises instead - a truncate would drop the migration state of the whole environment. The check runs on the ALREADY resolved name and +#_internal_table_names+ holds both forms, so neither a base nor a resolved name slips through.
148 149 150 151 152 153 154 155 156 |
# File 'lib/active_record/connection_adapters/elasticsearch/table_statements.rb', line 148 def truncate_table(table_name, decorate: nil) table_name = _decorate_table_name(table_name, decorate: decorate) # ensure the provided *table_name* is NOT an internal table_name raise ArgumentError, "Cannot truncate internal table '#{table_name}'!" if _internal_table_names.include?(table_name) # force: automatically drops an existing index create_table(table_name, force: true, decorate: false, **table_schema(table_name)) end |
#truncate_tables(*table_names, decorate: nil) ⇒ Array
truncate indices by provided names. PLEASE NOTE: a single AR-internal index raises through +#truncate_table+ and aborts the whole call - the tables before it are already truncated at that point.
167 168 169 |
# File 'lib/active_record/connection_adapters/elasticsearch/table_statements.rb', line 167 def truncate_tables(*table_names, decorate: nil) table_names.map { |table_name| truncate_table(table_name, decorate: decorate) } end |
#unblock_table(table_name, block_name = nil, decorate: nil) ⇒ Boolean
unblocks access to the provided table (index) and +block+ name. provide a nil-value to unblock all blocks, otherwise provide the blocked name.
212 213 214 215 216 217 218 219 220 221 222 223 |
# File 'lib/active_record/connection_adapters/elasticsearch/table_statements.rb', line 212 def unblock_table(table_name, block_name = nil, decorate: nil) if block_name.nil? change_table(table_name, decorate: decorate) do |t| t.change_setting('index.blocks.read', nil) t.change_setting('index.blocks.write', nil) t.change_setting('index.blocks.read_only', nil) t.change_setting('index.blocks.metadata', nil) end else change_setting(table_name, "index.blocks.#{block_name}", nil, decorate: decorate) end end |