Module: ActiveRecord::ConnectionAdapters::Elasticsearch::TableStatements

Extended by:
ActiveSupport::Concern
Included in:
ActiveRecord::ConnectionAdapters::ElasticsearchAdapter
Defined in:
lib/active_record/connection_adapters/elasticsearch/table_statements.rb

Overview

extend adapter with table-related statements

Instance Method Summary collapse

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.



386
387
388
389
390
391
392
393
394
395
396
# File 'lib/active_record/connection_adapters/elasticsearch/table_statements.rb', line 386

def _env_table_name(table_name)
  table_name = table_name.to_s

  # HINT: +"" creates a new +unfrozen+ string!
  name = +""
  name << table_name_prefix unless table_name.start_with?(table_name_prefix)
  name << table_name
  name << table_name_suffix unless table_name.end_with?(table_name_suffix)

  name
end

#add_alias(table_name, name, **options, &block) ⇒ Object

-- alias ---------------------------------------------------------------------------------------------------



369
370
371
# File 'lib/active_record/connection_adapters/elasticsearch/table_statements.rb', line 369

def add_alias(table_name, name, **options, &block)
  _exec_change_table_with(:add_alias, table_name, name, **options, &block)
end

#add_mapping(table_name, name, type, **options, &block) ⇒ Object Also known as: add_column

-- mapping -------------------------------------------------------------------------------------------------



318
319
320
# File 'lib/active_record/connection_adapters/elasticsearch/table_statements.rb', line 318

def add_mapping(table_name, name, type, **options, &block)
  _exec_change_table_with(:add_mapping, table_name, name, type, **options, &block)
end

#add_setting(table_name, name, value, **options, &block) ⇒ Object

-- setting -------------------------------------------------------------------------------------------------



355
356
357
# File 'lib/active_record/connection_adapters/elasticsearch/table_statements.rb', line 355

def add_setting(table_name, name, value, **options, &block)
  _exec_change_table_with(:add_setting, table_name, name, value, **options, &block)
end

#backup_table(table_name, to: nil, close: true) ⇒ 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.

Examples:

backup_table('screenshots', to: 'screenshots-backup-v1')

Raises:

  • (ArgumentError)


183
184
185
186
187
188
189
190
191
# File 'lib/active_record/connection_adapters/elasticsearch/table_statements.rb', line 183

def backup_table(table_name, to: nil, close: true)
  to ||= "#{table_name}-snapshot-#{Time.now.strftime('%s%3N')}"
  raise ArgumentError, "unable to backup '#{table_name}' to already existing target '#{to}'!" if table_exists?(to)

  clone_table(table_name, to)
  close_table(to) if close

  to
end

#block_table(table_name, block_name = :write) ⇒ Boolean

blocks access to the provided table (index) and +block+ name.



129
130
131
# File 'lib/active_record/connection_adapters/elasticsearch/table_statements.rb', line 129

def block_table(table_name, block_name = :write)
  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



373
374
375
# File 'lib/active_record/connection_adapters/elasticsearch/table_statements.rb', line 373

def change_alias(table_name, name, **options, &block)
  _exec_change_table_with(:change_alias, table_name, name, **options, &block)
end

#change_mapping(table_name, name, type, **options, &block) ⇒ Object Also known as: change_column

will fail unless +recreate:true+ option was provided



325
326
327
# File 'lib/active_record/connection_adapters/elasticsearch/table_statements.rb', line 325

def change_mapping(table_name, name, type, **options, &block)
  _exec_change_table_with(:change_mapping, table_name, name, type, **options, &block)
end

#change_mapping_attributes(table_name, name, **options, &block) ⇒ Object



341
342
343
# File 'lib/active_record/connection_adapters/elasticsearch/table_statements.rb', line 341

def change_mapping_attributes(table_name, name, **options, &block)
  _exec_change_table_with(:change_mapping_attributes, table_name, name, **options, &block)
end

#change_mapping_meta(table_name, name, **options) ⇒ Object



337
338
339
# File 'lib/active_record/connection_adapters/elasticsearch/table_statements.rb', line 337

def change_mapping_meta(table_name, name, **options)
  _exec_change_table_with(:change_mapping_meta, table_name, name, **options)
end

#change_meta(table_name, name, value, **options) ⇒ Object



345
346
347
# File 'lib/active_record/connection_adapters/elasticsearch/table_statements.rb', line 345

def change_meta(table_name, name, value, **options)
  _exec_change_table_with(:change_meta, table_name, name, value, **options)
end

#change_setting(table_name, name, value, **options, &block) ⇒ Object



359
360
361
# File 'lib/active_record/connection_adapters/elasticsearch/table_statements.rb', line 359

def change_setting(table_name, name, value, **options, &block)
  _exec_change_table_with(:change_setting, table_name, name, value, **options, &block)
end

#change_table(table_name, if_exists: false, recreate: false, **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



286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
# File 'lib/active_record/connection_adapters/elasticsearch/table_statements.rb', line 286

def change_table(table_name, if_exists: false, recreate: false, **options, &block)
  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, **options, &block) if recreate

  # build new update definition
  definition = update_table_definition(table_name, self, **options)

  # 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, **options) ⇒ Boolean

clones an entire table (index) with its docs to the provided +target_name+. During cloning, the table will be automatically 'write'-blocked.



157
158
159
160
161
162
163
164
165
166
167
168
169
170
# File 'lib/active_record/connection_adapters/elasticsearch/table_statements.rb', line 157

def clone_table(table_name, target_name, **options)
  # create new definition
  definition = clone_table_definition(table_name, target_name, **extract_table_options!(options))

  # 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) ⇒ Boolean

Closes an index.



54
55
56
57
# File 'lib/active_record/connection_adapters/elasticsearch/table_statements.rb', line 54

def close_table(table_name)
  schema_cache.clear_data_source_cache!(table_name)
  api(:indices, :close, { index: table_name }, 'CLOSE TABLE').dig('acknowledged')
end

#close_tables(*table_names) ⇒ Array

Closes indices by provided names.



62
63
64
65
66
67
# File 'lib/active_record/connection_adapters/elasticsearch/table_statements.rb', line 62

def close_tables(*table_names)
  table_names -= [schema_migration.table_name, .table_name]
  return if table_names.empty?

  table_names.map { |table_name| close_table(table_name) }
end

#create_table(table_name, force: false, copy_from: nil, if_not_exists: false, **options) ⇒ Boolean

creates a new table (index). [:force] Set to +true+ to drop an existing table 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 table already exists. Defaults to false.



252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
# File 'lib/active_record/connection_adapters/elasticsearch/table_statements.rb', line 252

def create_table(table_name, force: false, copy_from: nil, if_not_exists: false, **options)
  return if if_not_exists && table_exists?(table_name)

  # copy schema from existing table
  options.merge!(table_schema(copy_from)) if copy_from

  # create new definition
  definition = create_table_definition(table_name, **extract_table_options!(options))

  # 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)
  else
    schema_cache.clear_data_source_cache!(table_name.to_s)
  end

  # execute definition query(ies)
  definition.exec!
end

#drop_table(table_name, if_exists: false) ⇒ Boolean

drops an index [:if_exists] Set to +true+ to only drop the table if it exists. Defaults to false.



120
121
122
123
# File 'lib/active_record/connection_adapters/elasticsearch/table_statements.rb', line 120

def drop_table(table_name, if_exists: false, **)
  schema_cache.clear_data_source_cache!(table_name)
  api(:indices, :delete, { index: table_name, ignore: (if_exists ? 404 : nil) }, 'DROP TABLE').dig('acknowledged')
end

#open_table(table_name) ⇒ Boolean

Opens a closed index.



36
37
38
39
# File 'lib/active_record/connection_adapters/elasticsearch/table_statements.rb', line 36

def open_table(table_name)
  schema_cache.clear_data_source_cache!(table_name)
  api(:indices, :open, { index: table_name }, 'OPEN TABLE').dig('acknowledged')
end

#open_tables(*table_names) ⇒ Array

Opens closed indices.



44
45
46
47
48
49
# File 'lib/active_record/connection_adapters/elasticsearch/table_statements.rb', line 44

def open_tables(*table_names)
  table_names -= [schema_migration.table_name, .table_name]
  return if table_names.empty?

  table_names.map { |table_name| open_table(table_name) }
end

#refresh_table(table_name) ⇒ 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.



75
76
77
# File 'lib/active_record/connection_adapters/elasticsearch/table_statements.rb', line 75

def refresh_table(table_name)
  api(:indices, :refresh, { index: table_name }, 'REFRESH TABLE').dig('_shards', 'failed') == 0
end

#refresh_tables(*table_names) ⇒ Array

refresh indices by provided names.



82
83
84
85
86
87
# File 'lib/active_record/connection_adapters/elasticsearch/table_statements.rb', line 82

def refresh_tables(*table_names)
  table_names -= [schema_migration.table_name, .table_name]
  return if table_names.empty?

  table_names.map { |table_name| refresh_table(table_name) }
end

#reindex_table(table_name, target_name, **options) ⇒ Hash

Copies documents from a source to a destination.



312
313
314
# File 'lib/active_record/connection_adapters/elasticsearch/table_statements.rb', line 312

def reindex_table(table_name, target_name, **options)
  api(:core, :reindex, { body: { source: { index: table_name }, dest: { index: target_name } } }.merge(options), 'REINDEX TABLE')
end

#remove_alias(table_name, name, **options, &block) ⇒ Object



377
378
379
# File 'lib/active_record/connection_adapters/elasticsearch/table_statements.rb', line 377

def remove_alias(table_name, name, **options, &block)
  _exec_change_table_with(:remove_alias, table_name, name, **options, &block)
end

#remove_mapping(table_name, name, **options) ⇒ Object Also known as: remove_column



331
332
333
# File 'lib/active_record/connection_adapters/elasticsearch/table_statements.rb', line 331

def remove_mapping(table_name, name, **options)
  _exec_change_table_with(:remove_mapping, table_name, name, **options)
end

#remove_meta(table_name, name, **options) ⇒ Object



349
350
351
# File 'lib/active_record/connection_adapters/elasticsearch/table_statements.rb', line 349

def remove_meta(table_name, name, **options)
  _exec_change_table_with(:remove_meta, table_name, name, **options)
end

#remove_setting(table_name, name, **options, &block) ⇒ Object



363
364
365
# File 'lib/active_record/connection_adapters/elasticsearch/table_statements.rb', line 363

def remove_setting(table_name, name, **options, &block)
  _exec_change_table_with(:remove_setting, table_name, name, **options, &block)
end

#rename_table(table_name, target_name, timeout: 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.


230
231
232
233
234
235
236
# File 'lib/active_record/connection_adapters/elasticsearch/table_statements.rb', line 230

def rename_table(table_name, target_name, timeout: nil, **options)
  schema_cache.clear_data_source_cache!(table_name)

  clone_table(table_name, target_name, **options)
  cluster_health(index: target_name, wait_for_status: 'green', timeout: timeout.presence || '30s')
  drop_table(table_name)
end

#restore_table(table_name, from:, timeout: nil, open: true, drop_backup: false) ⇒ Boolean

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+.

Examples:

restore_table('screenshots', from: 'screenshots-backup-v1')

Raises:

  • (ArgumentError)


205
206
207
208
209
210
211
212
213
214
215
216
217
218
# File 'lib/active_record/connection_adapters/elasticsearch/table_statements.rb', line 205

def restore_table(table_name, from:, timeout: nil, open: true, drop_backup: false)
  raise ArgumentError, "unable to restore from missing target '#{from}'!" unless table_exists?(from)
  drop_table(table_name, if_exists: true)

  # choose best strategy
  if drop_backup
    rename_table(from, table_name, timeout: timeout)
  else
    clone_table(from, table_name)
  end

  # open, if provided
  open_table(from) if open
end

#truncate_table(table_name) ⇒ 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


96
97
98
99
# File 'lib/active_record/connection_adapters/elasticsearch/table_statements.rb', line 96

def truncate_table(table_name)
  # force: automatically drops an existing index
  create_table(table_name, force: true, **table_schema(table_name))
end

#truncate_tables(*table_names) ⇒ Array

truncate indices by provided names.



106
107
108
109
110
111
# File 'lib/active_record/connection_adapters/elasticsearch/table_statements.rb', line 106

def truncate_tables(*table_names)
  table_names -= [schema_migration.table_name, .table_name]
  return if table_names.empty?

  table_names.map { |table_name| truncate_table(table_name) }
end

#unblock_table(table_name, block_name = 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.



138
139
140
141
142
143
144
145
146
147
148
149
# File 'lib/active_record/connection_adapters/elasticsearch/table_statements.rb', line 138

def unblock_table(table_name, block_name = nil)
  if block_name.nil?
    change_table(table_name) 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)
  end
end