fix ax modifier column naming and migration approach

This commit is contained in:
Justin Edmund 2025-12-31 00:55:59 -08:00
parent 65f30cb05d
commit 5fd2f5758d
5 changed files with 152 additions and 86 deletions

View file

@ -1,8 +1,8 @@
# frozen_string_literal: true
class MigrateAxModifiersToFk < ActiveRecord::Migration[8.0]
# Old AX_MAPPING from WeaponProcessor: game_skill_id (string) => internal_value (integer)
# We need the reverse: internal_value => game_skill_id
# Old AX_MAPPING from WeaponProcessor stored internal integer values (0, 1, 2...)
# We need to map: old internal_value => game_skill_id => weapon_stat_modifier.id
OLD_INTERNAL_TO_GAME_SKILL_ID = {
2 => 1588, # HP
0 => 1589, # ATK
@ -24,73 +24,92 @@ class MigrateAxModifiersToFk < ActiveRecord::Migration[8.0]
}.freeze
def up
# Build lookup cache: game_skill_id -> weapon_stat_modifier.id
# Build lookup: old_internal_value -> new FK id
modifier_by_game_skill_id = WeaponStatModifier.pluck(:game_skill_id, :id).to_h
# Migrate CollectionWeapon ax_modifier1
CollectionWeapon.where.not(ax_modifier1: nil).find_each do |cw|
game_skill_id = OLD_INTERNAL_TO_GAME_SKILL_ID[cw.ax_modifier1]
modifier_id = game_skill_id ? modifier_by_game_skill_id[game_skill_id] : nil
if modifier_id
cw.update_columns(ax_modifier1_ref_id: modifier_id)
else
Rails.logger.warn "[MigrateAxModifiers] Unknown ax_modifier1=#{cw.ax_modifier1} on CollectionWeapon##{cw.id}"
end
old_to_new_id = OLD_INTERNAL_TO_GAME_SKILL_ID.transform_values do |game_skill_id|
modifier_by_game_skill_id[game_skill_id]
end
# Migrate CollectionWeapon ax_modifier2
CollectionWeapon.where.not(ax_modifier2: nil).find_each do |cw|
game_skill_id = OLD_INTERNAL_TO_GAME_SKILL_ID[cw.ax_modifier2]
modifier_id = game_skill_id ? modifier_by_game_skill_id[game_skill_id] : nil
if modifier_id
cw.update_columns(ax_modifier2_ref_id: modifier_id)
else
Rails.logger.warn "[MigrateAxModifiers] Unknown ax_modifier2=#{cw.ax_modifier2} on CollectionWeapon##{cw.id}"
end
end
# Use raw SQL to query old integer columns (ax_modifier1, ax_modifier2)
# and update the new FK columns (ax_modifier1_id, ax_modifier2_id)
# Migrate GridWeapon ax_modifier1
GridWeapon.where.not(ax_modifier1: nil).find_each do |gw|
game_skill_id = OLD_INTERNAL_TO_GAME_SKILL_ID[gw.ax_modifier1]
modifier_id = game_skill_id ? modifier_by_game_skill_id[game_skill_id] : nil
if modifier_id
gw.update_columns(ax_modifier1_ref_id: modifier_id)
else
Rails.logger.warn "[MigrateAxModifiers] Unknown ax_modifier1=#{gw.ax_modifier1} on GridWeapon##{gw.id}"
end
end
# Migrate CollectionWeapon
execute <<-SQL.squish
UPDATE collection_weapons
SET ax_modifier1_id = CASE ax_modifier1
#{old_to_new_id.map { |old_val, new_id| "WHEN #{old_val} THEN #{new_id}" }.join(' ')}
END
WHERE ax_modifier1 IS NOT NULL
SQL
# Migrate GridWeapon ax_modifier2
GridWeapon.where.not(ax_modifier2: nil).find_each do |gw|
game_skill_id = OLD_INTERNAL_TO_GAME_SKILL_ID[gw.ax_modifier2]
modifier_id = game_skill_id ? modifier_by_game_skill_id[game_skill_id] : nil
if modifier_id
gw.update_columns(ax_modifier2_ref_id: modifier_id)
else
Rails.logger.warn "[MigrateAxModifiers] Unknown ax_modifier2=#{gw.ax_modifier2} on GridWeapon##{gw.id}"
end
end
execute <<-SQL.squish
UPDATE collection_weapons
SET ax_modifier2_id = CASE ax_modifier2
#{old_to_new_id.map { |old_val, new_id| "WHEN #{old_val} THEN #{new_id}" }.join(' ')}
END
WHERE ax_modifier2 IS NOT NULL
SQL
# Migrate GridWeapon
execute <<-SQL.squish
UPDATE grid_weapons
SET ax_modifier1_id = CASE ax_modifier1
#{old_to_new_id.map { |old_val, new_id| "WHEN #{old_val} THEN #{new_id}" }.join(' ')}
END
WHERE ax_modifier1 IS NOT NULL
SQL
execute <<-SQL.squish
UPDATE grid_weapons
SET ax_modifier2_id = CASE ax_modifier2
#{old_to_new_id.map { |old_val, new_id| "WHEN #{old_val} THEN #{new_id}" }.join(' ')}
END
WHERE ax_modifier2 IS NOT NULL
SQL
end
def down
# Build reverse lookup: game_skill_id -> old internal value
# Build reverse lookup: new FK id -> old internal value
modifier_by_game_skill_id = WeaponStatModifier.pluck(:game_skill_id, :id).to_h
game_skill_id_to_internal = OLD_INTERNAL_TO_GAME_SKILL_ID.invert
# Reverse: copy FK back to integer columns using old internal values
WeaponStatModifier.find_each do |modifier|
next unless modifier.game_skill_id
internal_value = game_skill_id_to_internal[modifier.game_skill_id]
next unless internal_value
CollectionWeapon.where(ax_modifier1_ref_id: modifier.id)
.update_all(ax_modifier1: internal_value)
CollectionWeapon.where(ax_modifier2_ref_id: modifier.id)
.update_all(ax_modifier2: internal_value)
GridWeapon.where(ax_modifier1_ref_id: modifier.id)
.update_all(ax_modifier1: internal_value)
GridWeapon.where(ax_modifier2_ref_id: modifier.id)
.update_all(ax_modifier2: internal_value)
new_id_to_old = modifier_by_game_skill_id.each_with_object({}) do |(game_skill_id, new_id), hash|
old_val = game_skill_id_to_internal[game_skill_id]
hash[new_id] = old_val if old_val
end
return if new_id_to_old.empty?
# Reverse: copy FK back to old integer columns
execute <<-SQL.squish
UPDATE collection_weapons
SET ax_modifier1 = CASE ax_modifier1_id
#{new_id_to_old.map { |new_id, old_val| "WHEN #{new_id} THEN #{old_val}" }.join(' ')}
END
WHERE ax_modifier1_id IS NOT NULL
SQL
execute <<-SQL.squish
UPDATE collection_weapons
SET ax_modifier2 = CASE ax_modifier2_id
#{new_id_to_old.map { |new_id, old_val| "WHEN #{new_id} THEN #{old_val}" }.join(' ')}
END
WHERE ax_modifier2_id IS NOT NULL
SQL
execute <<-SQL.squish
UPDATE grid_weapons
SET ax_modifier1 = CASE ax_modifier1_id
#{new_id_to_old.map { |new_id, old_val| "WHEN #{new_id} THEN #{old_val}" }.join(' ')}
END
WHERE ax_modifier1_id IS NOT NULL
SQL
execute <<-SQL.squish
UPDATE grid_weapons
SET ax_modifier2 = CASE ax_modifier2_id
#{new_id_to_old.map { |new_id, old_val| "WHEN #{new_id} THEN #{old_val}" }.join(' ')}
END
WHERE ax_modifier2_id IS NOT NULL
SQL
end
end

View file

@ -1 +1 @@
DataMigrate::Data.define(version: 20251214193836)
DataMigrate::Data.define(version: 20251230000002)

View file

@ -3,23 +3,34 @@
class AddWeaponStatModifierFks < ActiveRecord::Migration[8.0]
def change
# collection_weapons - add FK columns
add_reference :collection_weapons, :ax_modifier1_ref,
foreign_key: { to_table: :weapon_stat_modifiers }
add_reference :collection_weapons, :ax_modifier2_ref,
foreign_key: { to_table: :weapon_stat_modifiers }
add_reference :collection_weapons, :befoulment_modifier,
foreign_key: { to_table: :weapon_stat_modifiers }
# Note: old ax_modifier1/ax_modifier2 integer columns still exist for data migration
add_column :collection_weapons, :ax_modifier1_id, :bigint
add_column :collection_weapons, :ax_modifier2_id, :bigint
add_column :collection_weapons, :befoulment_modifier_id, :bigint
add_column :collection_weapons, :befoulment_strength, :float
add_column :collection_weapons, :exorcism_level, :integer, default: 0
add_index :collection_weapons, :ax_modifier1_id
add_index :collection_weapons, :ax_modifier2_id
add_index :collection_weapons, :befoulment_modifier_id
add_foreign_key :collection_weapons, :weapon_stat_modifiers, column: :ax_modifier1_id
add_foreign_key :collection_weapons, :weapon_stat_modifiers, column: :ax_modifier2_id
add_foreign_key :collection_weapons, :weapon_stat_modifiers, column: :befoulment_modifier_id
# grid_weapons - same pattern
add_reference :grid_weapons, :ax_modifier1_ref,
foreign_key: { to_table: :weapon_stat_modifiers }
add_reference :grid_weapons, :ax_modifier2_ref,
foreign_key: { to_table: :weapon_stat_modifiers }
add_reference :grid_weapons, :befoulment_modifier,
foreign_key: { to_table: :weapon_stat_modifiers }
add_column :grid_weapons, :ax_modifier1_id, :bigint
add_column :grid_weapons, :ax_modifier2_id, :bigint
add_column :grid_weapons, :befoulment_modifier_id, :bigint
add_column :grid_weapons, :befoulment_strength, :float
add_column :grid_weapons, :exorcism_level, :integer, default: 0
add_index :grid_weapons, :ax_modifier1_id
add_index :grid_weapons, :ax_modifier2_id
add_index :grid_weapons, :befoulment_modifier_id
add_foreign_key :grid_weapons, :weapon_stat_modifiers, column: :ax_modifier1_id
add_foreign_key :grid_weapons, :weapon_stat_modifiers, column: :ax_modifier2_id
add_foreign_key :grid_weapons, :weapon_stat_modifiers, column: :befoulment_modifier_id
end
end

View file

@ -2,16 +2,10 @@
class FinalizeAxModifierColumns < ActiveRecord::Migration[8.0]
def change
# Remove old integer columns
# Remove old integer columns (data has been migrated to ax_modifier1_id/ax_modifier2_id FKs)
remove_column :collection_weapons, :ax_modifier1, :integer
remove_column :collection_weapons, :ax_modifier2, :integer
remove_column :grid_weapons, :ax_modifier1, :integer
remove_column :grid_weapons, :ax_modifier2, :integer
# Rename new FK columns to the original names
rename_column :collection_weapons, :ax_modifier1_ref_id, :ax_modifier1_id
rename_column :collection_weapons, :ax_modifier2_ref_id, :ax_modifier2_id
rename_column :grid_weapons, :ax_modifier1_ref_id, :ax_modifier1_id
rename_column :grid_weapons, :ax_modifier2_ref_id, :ax_modifier2_id
end
end

View file

@ -10,7 +10,7 @@
#
# It's strongly recommended that you check this file into your version control system.
ActiveRecord::Schema[8.0].define(version: 2025_12_21_210000) do
ActiveRecord::Schema[8.0].define(version: 2025_12_30_000004) do
# These are extensions that must be enabled in order to support this database
enable_extension "btree_gin"
enable_extension "pg_catalog.plpgsql"
@ -235,15 +235,21 @@ ActiveRecord::Schema[8.0].define(version: 2025_12_21_210000) do
t.uuid "weapon_key4_id"
t.uuid "awakening_id"
t.integer "awakening_level", default: 1, null: false
t.integer "ax_modifier1"
t.float "ax_strength1"
t.integer "ax_modifier2"
t.float "ax_strength2"
t.integer "element"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.string "game_id"
t.bigint "ax_modifier1_id"
t.bigint "ax_modifier2_id"
t.bigint "befoulment_modifier_id"
t.float "befoulment_strength"
t.integer "exorcism_level", default: 0
t.index ["awakening_id"], name: "index_collection_weapons_on_awakening_id"
t.index ["ax_modifier1_id"], name: "index_collection_weapons_on_ax_modifier1_id"
t.index ["ax_modifier2_id"], name: "index_collection_weapons_on_ax_modifier2_id"
t.index ["befoulment_modifier_id"], name: "index_collection_weapons_on_befoulment_modifier_id"
t.index ["user_id", "game_id"], name: "index_collection_weapons_on_user_id_and_game_id", unique: true, where: "(game_id IS NOT NULL)"
t.index ["user_id", "weapon_id"], name: "index_collection_weapons_on_user_id_and_weapon_id"
t.index ["user_id"], name: "index_collection_weapons_on_user_id"
@ -381,9 +387,11 @@ ActiveRecord::Schema[8.0].define(version: 2025_12_21_210000) do
t.datetime "updated_at", null: false
t.integer "reroll_slot"
t.uuid "collection_artifact_id"
t.boolean "orphaned", default: false, null: false
t.index ["artifact_id"], name: "index_grid_artifacts_on_artifact_id"
t.index ["collection_artifact_id"], name: "index_grid_artifacts_on_collection_artifact_id"
t.index ["grid_character_id"], name: "index_grid_artifacts_on_grid_character_id", unique: true
t.index ["orphaned"], name: "index_grid_artifacts_on_orphaned"
end
create_table "grid_characters", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t|
@ -422,7 +430,9 @@ ActiveRecord::Schema[8.0].define(version: 2025_12_21_210000) do
t.integer "transcendence_step", default: 0, null: false
t.boolean "quick_summon", default: false
t.uuid "collection_summon_id"
t.boolean "orphaned", default: false, null: false
t.index ["collection_summon_id"], name: "index_grid_summons_on_collection_summon_id"
t.index ["orphaned"], name: "index_grid_summons_on_orphaned"
t.index ["party_id", "position"], name: "index_grid_summons_on_party_id_and_position"
t.index ["party_id"], name: "index_grid_summons_on_party_id"
t.index ["summon_id"], name: "index_grid_summons_on_summon_id"
@ -439,9 +449,7 @@ ActiveRecord::Schema[8.0].define(version: 2025_12_21_210000) do
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.uuid "weapon_key3_id"
t.integer "ax_modifier1"
t.float "ax_strength1"
t.integer "ax_modifier2"
t.float "ax_strength2"
t.integer "element"
t.integer "awakening_level", default: 1, null: false
@ -449,8 +457,18 @@ ActiveRecord::Schema[8.0].define(version: 2025_12_21_210000) do
t.integer "transcendence_step", default: 0
t.string "weapon_key4_id"
t.uuid "collection_weapon_id"
t.boolean "orphaned", default: false, null: false
t.bigint "ax_modifier1_id"
t.bigint "ax_modifier2_id"
t.bigint "befoulment_modifier_id"
t.float "befoulment_strength"
t.integer "exorcism_level", default: 0
t.index ["awakening_id"], name: "index_grid_weapons_on_awakening_id"
t.index ["ax_modifier1_id"], name: "index_grid_weapons_on_ax_modifier1_id"
t.index ["ax_modifier2_id"], name: "index_grid_weapons_on_ax_modifier2_id"
t.index ["befoulment_modifier_id"], name: "index_grid_weapons_on_befoulment_modifier_id"
t.index ["collection_weapon_id"], name: "index_grid_weapons_on_collection_weapon_id"
t.index ["orphaned"], name: "index_grid_weapons_on_orphaned"
t.index ["party_id", "position"], name: "index_grid_weapons_on_party_id_and_position"
t.index ["party_id"], name: "index_grid_weapons_on_party_id"
t.index ["weapon_id"], name: "index_grid_weapons_on_weapon_id"
@ -924,7 +942,7 @@ ActiveRecord::Schema[8.0].define(version: 2025_12_21_210000) do
t.boolean "element_changeable", default: false, null: false
t.boolean "has_weapon_keys", default: false, null: false
t.boolean "has_awakening", default: false, null: false
t.boolean "has_ax_skills", default: false, null: false
t.integer "augment_type", default: 0, null: false
t.index ["order"], name: "index_weapon_series_on_order"
t.index ["slug"], name: "index_weapon_series_on_slug", unique: true
end
@ -945,6 +963,24 @@ ActiveRecord::Schema[8.0].define(version: 2025_12_21_210000) do
t.index ["weapon_granblue_id"], name: "index_weapon_skills_on_weapon_granblue_id"
end
create_table "weapon_stat_modifiers", force: :cascade do |t|
t.string "slug", null: false
t.string "name_en", null: false
t.string "name_jp"
t.string "category", null: false
t.string "stat"
t.integer "polarity", default: 1, null: false
t.string "suffix"
t.float "base_min"
t.float "base_max"
t.integer "game_skill_id"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.index ["category"], name: "index_weapon_stat_modifiers_on_category"
t.index ["game_skill_id"], name: "index_weapon_stat_modifiers_on_game_skill_id", unique: true
t.index ["slug"], name: "index_weapon_stat_modifiers_on_slug", unique: true
end
create_table "weapons", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t|
t.string "name_en"
t.string "name_jp"
@ -1024,6 +1060,9 @@ ActiveRecord::Schema[8.0].define(version: 2025_12_21_210000) do
add_foreign_key "collection_weapons", "weapon_keys", column: "weapon_key2_id"
add_foreign_key "collection_weapons", "weapon_keys", column: "weapon_key3_id"
add_foreign_key "collection_weapons", "weapon_keys", column: "weapon_key4_id"
add_foreign_key "collection_weapons", "weapon_stat_modifiers", column: "ax_modifier1_id"
add_foreign_key "collection_weapons", "weapon_stat_modifiers", column: "ax_modifier2_id"
add_foreign_key "collection_weapons", "weapon_stat_modifiers", column: "befoulment_modifier_id"
add_foreign_key "collection_weapons", "weapons"
add_foreign_key "crew_gw_participations", "crews"
add_foreign_key "crew_gw_participations", "gw_events"
@ -1049,6 +1088,9 @@ ActiveRecord::Schema[8.0].define(version: 2025_12_21_210000) do
add_foreign_key "grid_weapons", "collection_weapons"
add_foreign_key "grid_weapons", "parties"
add_foreign_key "grid_weapons", "weapon_keys", column: "weapon_key3_id"
add_foreign_key "grid_weapons", "weapon_stat_modifiers", column: "ax_modifier1_id"
add_foreign_key "grid_weapons", "weapon_stat_modifiers", column: "ax_modifier2_id"
add_foreign_key "grid_weapons", "weapon_stat_modifiers", column: "befoulment_modifier_id"
add_foreign_key "grid_weapons", "weapons"
add_foreign_key "gw_crew_scores", "crew_gw_participations"
add_foreign_key "gw_individual_scores", "crew_gw_participations"