# frozen_string_literal: true require 'active_record/connection_adapters/abstract/schema_definitions' require 'active_record/connection_adapters/elasticsearch/schema_definitions/table_definition' module ActiveRecord module ConnectionAdapters module Elasticsearch class UpdateTableDefinition < TableDefinition attr_reader :definitions # defines which definitions can be executed composite COMPOSITE_DEFINITIONS = [ AddMappingDefinition, ChangeMappingDefinition, ChangeMetaDefinition, AddSettingDefinition, DeleteAliasDefinition ].freeze def add_mapping(name, type, if_not_exists: false, **options, &block) return if if_not_exists && mapping_exists?(self.name, name, type) define! AddMappingDefinition, new_mapping_definition(name, type, **options, &block) end alias :add_column :add_mapping alias :mapping :add_mapping alias :column :add_mapping def change_meta(name, value, **options) load_meta_definition! define! ChangeMetaDefinition, new_meta_definition(name, value, **options) end def delete_meta(name, **options) load_meta_definition! define! ChangeMetaDefinition, new_meta_definition(name, nil, **options) end def change_mapping(name, type, if_exists: false, **options, &block) return if if_exists && !mapping_exists?(self.name, name, type) mapping = new_mapping_definition(name, type, **options, &block) define! ChangeMappingDefinition, mapping # check if the mapping is assigned as primary_key if mapping.primary_key? change_meta :primary_key, mapping.name change_meta(:auto_increment, mapping.auto_increment) if mapping.auto_increment? end end alias :change_column :change_mapping alias :change :change_mapping def change_mapping_meta(name, **options) mapping = table_mappings(self.name).dig('properties', name.to_s) raise ArgumentError, "you cannot change the 'meta' parameter for an unknown mapping '#{name}'" if mapping.blank? # resolve existing meta & merge with new meta = (mapping['meta'] || {}).merge(options) define! ChangeMappingDefinition, new_mapping_definition(name, mapping['type'], meta: meta) end def change_mapping_attributes(name, **options, &block) mapping = table_mappings(self.name).dig('properties', name.to_s) raise ArgumentError, "you cannot change parameters for an unknown mapping '#{name}'" if mapping.blank? options = mapping.with_indifferent_access.except(:type).merge(options) if options.present? define! ChangeMappingDefinition, new_mapping_definition(name, mapping['type'], **options, &block) end alias :change_mapping_attribute :change_mapping_attributes def add_setting(name, value, if_not_exists: false, **options, &block) return if if_not_exists && setting_exists?(self.name, name) define! AddSettingDefinition, new_setting_definition(name, value, **options, &block) end def change_setting(name, value, if_exists: false, **options, &block) return if if_exists && !setting_exists?(self.name, name) define! ChangeSettingDefinition, new_setting_definition(name, value, **options, &block) end def delete_setting(name, if_exists: false, **options, &block) return if if_exists && !setting_exists?(self.name, name) define! DeleteSettingDefinition, new_setting_definition(name, nil, **options, &block) end def add_alias(name, if_not_exists: false, **options, &block) return if if_not_exists && alias_exists?(self.name, name) define! AddAliasDefinition, new_alias_definition(name, **options, &block) end def change_alias(name, if_exists: false, **options, &block) return if if_exists && !alias_exists?(self.name, name) define! ChangeAliasDefinition, new_alias_definition(name, **options, &block) end def delete_alias(name, if_exists: false, **, &block) return if if_exists && !alias_exists?(self.name, name) define! DeleteAliasDefinition, new_alias_definition(name, &block) end # Appends :datetime columns :created_at and # :updated_at to the table. See {connection.add_timestamps}[rdoc-ref:SchemaStatements#add_timestamps] # # t.timestamps def timestamps(**options) add_mapping(:created_at, :datetime, if_not_exists: true, **options) add_mapping(:updated_at, :datetime, if_not_exists: true, **options) end private # loads existing meta definitions from table_mappings def load_meta_definition! return if @load_meta_definition @load_meta_definition = true table_metas(self.name).each do |name, value| define! ChangeMetaDefinition, new_meta_definition(name, value) end end def define!(klass, item) @definitions ||= {} @definitions[klass] ||= [] @definitions[klass] << item end def _before_assign # check, if the table should be closed before executing the queries close_table(self.name) if _toggle_table_status? # reset table state clear_state! end def _after_exec # reopen the table again open_table(self.name) if _toggle_table_status? # reset table state clear_state! end alias :_rescue_assign :_after_exec alias :_rescue_exec :_after_exec def _toggle_table_status? @toggle_table_status = (force? && state[:status] == 'open') if @toggle_table_status.nil? @toggle_table_status end def _exec return unless definitions.any? definitions.each do |klass, items| # check if the provided definition klass is a composite definition executable_definitions = if COMPOSITE_DEFINITIONS.include?(klass) [InterlacedUpdateTableDefinition.new(name, klass.new(items))] else items.map { |d| InterlacedUpdateTableDefinition.new(name, klass.new(d)) } end executable_definitions.each do |ed| execute(schema_creation.accept(ed), _to_composite_log(klass)) end end # cleanup executed definitions @definitions = {} true end def _to_composite_log(klass) klass.name.demodulize.underscore.gsub(/_definition/, '').gsub(/_/, ' ').upcase end end end end end