From 5fd2f5758d4557a2cb1cb5f10c6c9723cc56c10f Mon Sep 17 00:00:00 2001 From: Justin Edmund Date: Wed, 31 Dec 2025 00:55:59 -0800 Subject: [PATCH] fix ax modifier column naming and migration approach --- ...251230000002_migrate_ax_modifiers_to_fk.rb | 139 ++++++++++-------- db/data_schema.rb | 2 +- ...1230000002_add_weapon_stat_modifier_fks.rb | 35 +++-- ...1230000003_finalize_ax_modifier_columns.rb | 8 +- db/schema.rb | 54 ++++++- 5 files changed, 152 insertions(+), 86 deletions(-) diff --git a/db/data/20251230000002_migrate_ax_modifiers_to_fk.rb b/db/data/20251230000002_migrate_ax_modifiers_to_fk.rb index 19f16fb..96782a4 100644 --- a/db/data/20251230000002_migrate_ax_modifiers_to_fk.rb +++ b/db/data/20251230000002_migrate_ax_modifiers_to_fk.rb @@ -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 diff --git a/db/data_schema.rb b/db/data_schema.rb index 07672e6..bb71a21 100644 --- a/db/data_schema.rb +++ b/db/data_schema.rb @@ -1 +1 @@ -DataMigrate::Data.define(version: 20251214193836) +DataMigrate::Data.define(version: 20251230000002) diff --git a/db/migrate/20251230000002_add_weapon_stat_modifier_fks.rb b/db/migrate/20251230000002_add_weapon_stat_modifier_fks.rb index 14fa62f..4f15f92 100644 --- a/db/migrate/20251230000002_add_weapon_stat_modifier_fks.rb +++ b/db/migrate/20251230000002_add_weapon_stat_modifier_fks.rb @@ -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 diff --git a/db/migrate/20251230000003_finalize_ax_modifier_columns.rb b/db/migrate/20251230000003_finalize_ax_modifier_columns.rb index 6629249..281a63d 100644 --- a/db/migrate/20251230000003_finalize_ax_modifier_columns.rb +++ b/db/migrate/20251230000003_finalize_ax_modifier_columns.rb @@ -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 diff --git a/db/schema.rb b/db/schema.rb index b2c3b2d..9442424 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -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"