From f3d9e88ea221e5999beaa9ed0045f388e48259b2 Mon Sep 17 00:00:00 2001 From: miteruzo Date: Sun, 19 Apr 2026 05:29:59 +0900 Subject: [PATCH] #309 --- .../20260409123700_create_post_versions.rb | 6 +- .../20260419035400_create_tag_versions.rb | 89 +++++++++++++++++++ backend/db/schema.rb | 21 ++++- 3 files changed, 112 insertions(+), 4 deletions(-) create mode 100644 backend/db/migrate/20260419035400_create_tag_versions.rb diff --git a/backend/db/migrate/20260409123700_create_post_versions.rb b/backend/db/migrate/20260409123700_create_post_versions.rb index a2c6da7..58df885 100644 --- a/backend/db/migrate/20260409123700_create_post_versions.rb +++ b/backend/db/migrate/20260409123700_create_post_versions.rb @@ -2,15 +2,15 @@ require 'set' class CreatePostVersions < ActiveRecord::Migration[8.0] - class Post < ApplicationRecord + class Post < ActiveRecord::Base self.table_name = 'posts' end - class PostTag < ApplicationRecord + class PostTag < ActiveRecord::Base self.table_name = 'post_tags' end - class PostVersion < ApplicationRecord + class PostVersion < ActiveRecord::Base self.table_name = 'post_versions' end diff --git a/backend/db/migrate/20260419035400_create_tag_versions.rb b/backend/db/migrate/20260419035400_create_tag_versions.rb new file mode 100644 index 0000000..a195841 --- /dev/null +++ b/backend/db/migrate/20260419035400_create_tag_versions.rb @@ -0,0 +1,89 @@ +class CreateTagVersions < ActiveRecord::Migration[8.0] + class Tag < ActiveRecord::Base + self.table_name = 'tags' + end + + class TagName < ActiveRecord::Base + self.table_name = 'tag_names' + end + + class TagImplication < ActiveRecord::Base + self.table_name = 'tag_implications' + end + + class TagVersion < ActiveRecord::Base + self.table_name = 'tag_versions' + end + + def up + create_table :tag_versions do |t| + t.references :tag, null: false, foreign_key: true, index: false + t.integer :version_no, null: false + t.string :event_type, null: false + t.string :name, null: false + t.string :category, null: false + t.text :aliases, null: false + t.text :parent_tag_ids, null: false + t.datetime :created_at, null: false + t.references :created_by_user, foreign_key: { to_table: :users }, index: false + + t.index [:tag_id, :version_no], unique: true + t.index :created_at + t.index [:tag_id, :created_at], order: { created_at: :desc } + t.index [:created_by_user_id, :created_at], order: { created_at: :desc } + t.check_constraint 'version_no > 0', + name: 'tag_versions_version_no_positive' + end + + TagVersion.reset_column_information + + say_with_time 'Backfilling tag_versions' do + Tag.where(discarded_at: nil).find_in_batches(batch_size: 500) do |tags| + tag_ids = tags.map(&:id) + + tag_implication_rows_by_tag_id = + TagImplication + .where(tag_id: tag_ids) + .pluck(:tag_id, :parent_tag_id) + .each_with_object(Hash.new { |h, k| h[k] = [] }) do |row, h| + h[row[0]] << row[1] + end + + tag_name_rows_by_tag_id = + TagName + .joins('INNER JOIN tags ON tags.tag_name_id = tag_names.id') + .where(tags: { id: tag_ids }) + .pluck('tags.id', 'tag_names.name') + .each_with_object({ }) do |row, h| + h[row[0]] = row[1] + end + + tag_alias_rows_by_tag_id = + TagName + .joins('INNER JOIN tags ON tags.tag_name_id = tag_names.canonical_id') + .where(tags: { id: tag_ids }) + .where(tag_names: { discarded_at: nil }) + .pluck('tags.id', 'tag_names.name') + .each_with_object(Hash.new { |h, k| h[k] = [] }) do |row, h| + h[row[0]] << row[1] + end + + TagVersion.insert_all(tags.map { |tag| + { tag_id: tag.id, + version_no: 1, + event_type: 'create', + name: tag_name_rows_by_tag_id[tag.id], + category: tag.category, + aliases: tag_alias_rows_by_tag_id[tag.id].sort.join(' '), + parent_tag_ids: tag_implication_rows_by_tag_id[tag.id].sort.join(' '), + created_at: tag.created_at, + created_by_user_id: nil } + }) + end + end + end + + def down + drop_table :tag_versions + end +end diff --git a/backend/db/schema.rb b/backend/db/schema.rb index 42c7cd4..8ebf572 100644 --- a/backend/db/schema.rb +++ b/backend/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: 2026_04_09_123700) do +ActiveRecord::Schema[8.0].define(version: 2026_04_19_035400) do create_table "active_storage_attachments", charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t| t.string "name", null: false t.string "record_type", null: false @@ -216,6 +216,23 @@ ActiveRecord::Schema[8.0].define(version: 2026_04_09_123700) do t.index ["target_tag_id"], name: "index_tag_similarities_on_target_tag_id" end + create_table "tag_versions", charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t| + t.bigint "tag_id", null: false + t.integer "version_no", null: false + t.string "event_type", null: false + t.string "name", null: false + t.string "category", null: false + t.text "aliases", null: false + t.text "parent_tag_ids", null: false + t.datetime "created_at", null: false + t.bigint "created_by_user_id" + t.index ["created_at"], name: "index_tag_versions_on_created_at" + t.index ["created_by_user_id", "created_at"], name: "index_tag_versions_on_created_by_user_id_and_created_at", order: { created_at: :desc } + t.index ["tag_id", "created_at"], name: "index_tag_versions_on_tag_id_and_created_at", order: { created_at: :desc } + t.index ["tag_id", "version_no"], name: "index_tag_versions_on_tag_id_and_version_no", unique: true + t.check_constraint "`version_no` > 0", name: "tag_versions_version_no_positive" + end + create_table "tags", charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t| t.bigint "tag_name_id", null: false t.string "category", default: "general", null: false @@ -394,6 +411,8 @@ ActiveRecord::Schema[8.0].define(version: 2026_04_09_123700) do add_foreign_key "tag_names", "tag_names", column: "canonical_id" add_foreign_key "tag_similarities", "tags" add_foreign_key "tag_similarities", "tags", column: "target_tag_id" + add_foreign_key "tag_versions", "tags" + add_foreign_key "tag_versions", "users", column: "created_by_user_id" add_foreign_key "tags", "tag_names" add_foreign_key "theatre_comments", "theatres" add_foreign_key "theatre_comments", "users"