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.

Parameters:

  • table_name (String)

Returns:

  • (String)


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')

Parameters:

  • table_name (String)
  • to (String) (defaults to: nil)
    • target_name
  • close (Boolean) (defaults to: true)
    • closes backup after creation (default: true)

Returns:

  • (String)

    backup_name

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.

Parameters:

  • table_name (String)
  • block_name (Symbol) (defaults to: :write)

    The block to add (one of :read, :write, :read_only or :metadata)

Returns:

  • (Boolean)

    acknowledged status



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.

Parameters:

  • table_name (String)
  • target_name (String)
  • options (Hash)

Returns:

  • (Boolean)

    acknowledged status



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.

Parameters:

  • table_name (String)

Returns:

  • (Boolean)

    acknowledged status



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.

Parameters:

  • table_names (Array)

Returns:

  • (Array)

    acknowledged status for each provided table



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, InternalMetadata.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.

Parameters:

  • table_name (String)
  • force (Boolean) (defaults to: false)
    • force a drop on the existing table (default: false)
  • copy_from (nil, String) (defaults to: nil)
    • copy schema from existing table
  • options (Hash)

Returns:

  • (Boolean)

    acknowledged status



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.

Parameters:

  • table_name (String)
  • if_exists (Boolean) (defaults to: false)

Returns:

  • (Boolean)

    acknowledged status



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.

Parameters:

  • table_name (String)

Returns:

  • (Boolean)

    acknowledged status



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.

Parameters:

  • table_names (Array)

Returns:

  • (Array)

    acknowledged status for each provided table



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, InternalMetadata.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.

Parameters:

  • table_name (String)

Returns:

  • (Boolean)

    result state (returns false if refreshing failed)



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.

Parameters:

  • table_names (Array)

Returns:

  • (Array)

    result state (returns false if refreshing failed)



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, InternalMetadata.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.

Parameters:

  • table_name (String)
  • target_name (String)
  • options (Hash)

Returns:

  • (Hash)

    reindex stats



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.

Parameters:

  • table_name (String)
  • target_name (String)
  • timeout (String (frozen)) (defaults to: nil)

    (default: '30s')

  • options (Hash)
    • additional 'clone' options (like settings, alias, ...)


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')

Parameters:

  • table_name (String)
  • from (String)
  • timeout (String (frozen)) (defaults to: nil)
    • renaming timout (default: '30s')
  • open (Boolean) (defaults to: true)
    • opens restored backup after creation (default: true)

Returns:

  • (Boolean)

    acknowledged status

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

Parameters:

  • table_name (String)

Returns:

  • (Boolean)

    acknowledged status



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.

Parameters:

  • table_names (Array)

Returns:

  • (Array)

    acknowledged status for each provided table



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, InternalMetadata.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.

Parameters:

  • table_name (String)
  • block_name (Symbol) (defaults to: nil)

    The block to add (one of :read, :write, :read_only or :metadata)

Returns:

  • (Boolean)

    acknowledged status



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