This commit is contained in:
@@ -30,16 +30,31 @@ class NicoTagsController < ApplicationController
|
|||||||
id = params[:id].to_i
|
id = params[:id].to_i
|
||||||
|
|
||||||
tag = Tag.find(id)
|
tag = Tag.find(id)
|
||||||
return head :bad_request if tag.category != 'nico'
|
return head :bad_request unless tag.nico?
|
||||||
|
|
||||||
linked_tag_names = params[:tags].to_s.split(' ')
|
linked_tag_names = params[:tags].to_s.split
|
||||||
linked_tags = Tag.normalise_tags(linked_tag_names, with_tagme: false,
|
linked_tags = Tag.normalise_tags(linked_tag_names, with_tagme: false,
|
||||||
with_no_deerjikist: false)
|
with_no_deerjikist: false)
|
||||||
return head :bad_request if linked_tags.any? { |t| t.category == 'nico' }
|
return head :bad_request if linked_tags.any? { |t| t.nico? }
|
||||||
|
|
||||||
|
ApplicationRecord.transaction do
|
||||||
|
record_tag_snapshots!(linked_tags, created_by_user: current_user)
|
||||||
|
|
||||||
tag.linked_tags = linked_tags
|
tag.linked_tags = linked_tags
|
||||||
tag.save!
|
tag.save!
|
||||||
|
|
||||||
|
NicoTagVersionRecorder.record!(tag:, event_type: :update, created_by_user: current_user)
|
||||||
|
end
|
||||||
|
|
||||||
render json: tag.linked_tags.map { |t| TagRepr.base(t) }, status: :ok
|
render json: tag.linked_tags.map { |t| TagRepr.base(t) }, status: :ok
|
||||||
end
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def record_tag_snapshots! tags, created_by_user:
|
||||||
|
tags.each do |tag|
|
||||||
|
event_type = tag.tag_versions.exists? ? :update : :create
|
||||||
|
TagVersionRecorder.record!(tag:, event_type:, created_by_user:)
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -128,9 +128,11 @@ class PostsController < ApplicationController
|
|||||||
original_created_from:, original_created_before:)
|
original_created_from:, original_created_before:)
|
||||||
post.thumbnail.attach(thumbnail)
|
post.thumbnail.attach(thumbnail)
|
||||||
|
|
||||||
ActiveRecord::Base.transaction do
|
ApplicationRecord.transaction do
|
||||||
post.save!
|
post.save!
|
||||||
tags = Tag.normalise_tags(tag_names)
|
tags = Tag.normalise_tags(tag_names)
|
||||||
|
record_tag_snapshots!(tags, created_by_user: current_user)
|
||||||
|
|
||||||
tags = Tag.expand_parent_tags(tags)
|
tags = Tag.expand_parent_tags(tags)
|
||||||
sync_post_tags!(post, tags)
|
sync_post_tags!(post, tags)
|
||||||
post.resized_thumbnail!
|
post.resized_thumbnail!
|
||||||
@@ -170,10 +172,13 @@ class PostsController < ApplicationController
|
|||||||
|
|
||||||
post = Post.find(params[:id].to_i)
|
post = Post.find(params[:id].to_i)
|
||||||
|
|
||||||
ActiveRecord::Base.transaction do
|
ApplicationRecord.transaction do
|
||||||
post.update!(title:, original_created_from:, original_created_before:)
|
post.update!(title:, original_created_from:, original_created_before:)
|
||||||
tags = post.tags.where(category: 'nico').to_a +
|
|
||||||
Tag.normalise_tags(tag_names, with_tagme: false)
|
normalised_tag = Tag.normalise_tags(tag_names, with_tagme: false)
|
||||||
|
record_tag_snapshots(normalised_tags, create_by_user: current_user)
|
||||||
|
|
||||||
|
tags = post.tags.where(category: 'nico').to_a + normalised_tags
|
||||||
tags = Tag.expand_parent_tags(tags)
|
tags = Tag.expand_parent_tags(tags)
|
||||||
sync_post_tags!(post, tags)
|
sync_post_tags!(post, tags)
|
||||||
PostVersionRecorder.record!(post:, event_type: :update, created_by_user: current_user)
|
PostVersionRecorder.record!(post:, event_type: :update, created_by_user: current_user)
|
||||||
|
|||||||
@@ -7,7 +7,14 @@ class TagChildrenController < ApplicationController
|
|||||||
child_id = params[:child_id]
|
child_id = params[:child_id]
|
||||||
return head :bad_request if parent_id.blank? || child_id.blank?
|
return head :bad_request if parent_id.blank? || child_id.blank?
|
||||||
|
|
||||||
Tag.find(parent_id).children << Tag.find(child_id) rescue nil
|
parent = Tag.find(parent_id)
|
||||||
|
child = Tag.find(child_id)
|
||||||
|
return head :bad_request if parent.nico? || child.nico?
|
||||||
|
|
||||||
|
ApplicationRecord.transaction do
|
||||||
|
TagImplication.find_or_create_by!(parent_tag: parent, tag: child)
|
||||||
|
TagVersionRecorder.record!(tag: child, event_type: :update, created_by_user: current_user)
|
||||||
|
end
|
||||||
|
|
||||||
head :no_content
|
head :no_content
|
||||||
end
|
end
|
||||||
@@ -20,7 +27,14 @@ class TagChildrenController < ApplicationController
|
|||||||
child_id = params[:child_id]
|
child_id = params[:child_id]
|
||||||
return head :bad_request if parent_id.blank? || child_id.blank?
|
return head :bad_request if parent_id.blank? || child_id.blank?
|
||||||
|
|
||||||
Tag.find(parent_id).children.delete(Tag.find(child_id)) rescue nil
|
parent = Tag.find(parent_id)
|
||||||
|
child = Tag.find(child_id)
|
||||||
|
return head :bad_request if parent.nico? || child.nico?
|
||||||
|
|
||||||
|
ApplicationRecord.transaction do
|
||||||
|
TagImplication.find_by(parent_tag: parent, tag: child)&.destroy!
|
||||||
|
TagVersionRecorder.record!(tag: child, event_type: :update, created_by_user: current_user)
|
||||||
|
end
|
||||||
|
|
||||||
head :no_content
|
head :no_content
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -218,15 +218,25 @@ class TagsController < ApplicationController
|
|||||||
|
|
||||||
tag = Tag.find(params[:id])
|
tag = Tag.find(params[:id])
|
||||||
|
|
||||||
if name.present?
|
ApplicationRecord.transaction do
|
||||||
tag.tag_name.update!(name:)
|
old_nico = tag.nico?
|
||||||
end
|
|
||||||
|
|
||||||
if category.present?
|
if category.present?
|
||||||
tag.update!(category:)
|
new_nico = category == 'nico'
|
||||||
|
|
||||||
|
if old_nico != new_nico
|
||||||
|
return render json: { error: 'ニコタグのカテゴリ変更はできません.' },
|
||||||
|
status: :unprocessable_entity
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
render json: TagRepr.base(tag)
|
tag.tag_name.update!(name:) if name.present?
|
||||||
|
tag.update!(category:) if category.present?
|
||||||
|
|
||||||
|
record_tag_version!(tag, event_type: :update, created_by_user: current_user)
|
||||||
|
end
|
||||||
|
|
||||||
|
render json: TagRepr.base(tag.reload)
|
||||||
end
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
@@ -244,4 +254,12 @@ class TagsController < ApplicationController
|
|||||||
children: tag.children.sort_by { _1.name }.map { build_tag_children(_1) },
|
children: tag.children.sort_by { _1.name }.map { build_tag_children(_1) },
|
||||||
material: material.as_json&.merge(file:, content_type:))
|
material: material.as_json&.merge(file:, content_type:))
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def record_tag_version!(tag, event_type:, created_by_user:)
|
||||||
|
if tag.nico?
|
||||||
|
NicoTagVersionRecorder.record!(tag:, event_type:, created_by_user:)
|
||||||
|
else
|
||||||
|
TagVersionRecorder.record!(tag:, event_type:, created_by_user:)
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -1,7 +1,11 @@
|
|||||||
module MyDiscard
|
module MyDiscard
|
||||||
extend ActiveSupport::Concern
|
extend ActiveSupport::Concern
|
||||||
|
|
||||||
included { include Discard::Model }
|
included do
|
||||||
|
include Discard::Model
|
||||||
|
|
||||||
|
default_scope -> { kept }
|
||||||
|
end
|
||||||
|
|
||||||
class_methods do
|
class_methods do
|
||||||
def find_undiscard_or_create_by! attrs, &block
|
def find_undiscard_or_create_by! attrs, &block
|
||||||
|
|||||||
@@ -1,29 +1,13 @@
|
|||||||
class PostVersion < ApplicationRecord
|
class PostVersion < ApplicationRecord
|
||||||
before_update do
|
include VersionRecord
|
||||||
raise ActiveRecord::ReadOnlyRecord, '版は更新できません.'
|
|
||||||
end
|
|
||||||
|
|
||||||
before_destroy do
|
|
||||||
raise ActiveRecord::ReadOnlyRecord, '版は削除できません.'
|
|
||||||
end
|
|
||||||
|
|
||||||
belongs_to :post
|
belongs_to :post
|
||||||
belongs_to :parent, class_name: 'Post', optional: true
|
belongs_to :parent, class_name: 'Post', optional: true
|
||||||
belongs_to :created_by_user, class_name: 'User', optional: true
|
|
||||||
|
|
||||||
enum :event_type, { create: 'create',
|
|
||||||
update: 'update',
|
|
||||||
discard: 'discard',
|
|
||||||
restore: 'restore' }, prefix: true, validate: true
|
|
||||||
|
|
||||||
validates :version_no, presence: true, numericality: { only_integer: true, greater_than: 0 }
|
|
||||||
validates :event_type, presence: true, inclusion: { in: event_types.keys }
|
|
||||||
validates :url, presence: true
|
validates :url, presence: true
|
||||||
|
|
||||||
validate :validate_original_created_range
|
validate :validate_original_created_range
|
||||||
|
|
||||||
scope :chronological, -> { order(:version_no, :id) }
|
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
def validate_original_created_range
|
def validate_original_created_range
|
||||||
|
|||||||
@@ -8,8 +8,6 @@ class Tag < ApplicationRecord
|
|||||||
;
|
;
|
||||||
end
|
end
|
||||||
|
|
||||||
default_scope -> { kept }
|
|
||||||
|
|
||||||
has_many :post_tags, inverse_of: :tag
|
has_many :post_tags, inverse_of: :tag
|
||||||
has_many :active_post_tags, -> { kept }, class_name: 'PostTag', inverse_of: :tag
|
has_many :active_post_tags, -> { kept }, class_name: 'PostTag', inverse_of: :tag
|
||||||
has_many :post_tags_with_discarded, -> { with_discarded }, class_name: 'PostTag'
|
has_many :post_tags_with_discarded, -> { with_discarded }, class_name: 'PostTag'
|
||||||
@@ -36,6 +34,9 @@ class Tag < ApplicationRecord
|
|||||||
has_many :deerjikists, dependent: :delete_all
|
has_many :deerjikists, dependent: :delete_all
|
||||||
has_many :materials
|
has_many :materials
|
||||||
|
|
||||||
|
has_many :tag_versions
|
||||||
|
has_many :nico_tag_versions
|
||||||
|
|
||||||
belongs_to :tag_name
|
belongs_to :tag_name
|
||||||
delegate :wiki_page, to: :tag_name
|
delegate :wiki_page, to: :tag_name
|
||||||
|
|
||||||
@@ -152,10 +153,11 @@ class Tag < ApplicationRecord
|
|||||||
retry
|
retry
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.merge_tags! target_tag, source_tags
|
def self.merge_tags! target_tag, source_tags, created_by_user: nil
|
||||||
target_tag => Tag
|
target_tag => Tag
|
||||||
|
|
||||||
affected_post_ids = Set.new
|
affected_post_ids = Set.new
|
||||||
|
affected_tag_ids = Set.new
|
||||||
|
|
||||||
Tag.transaction do
|
Tag.transaction do
|
||||||
Array(source_tags).compact.uniq.each do |source_tag|
|
Array(source_tags).compact.uniq.each do |source_tag|
|
||||||
@@ -166,7 +168,7 @@ class Tag < ApplicationRecord
|
|||||||
source_tag.post_tags.kept.find_each do |source_pt|
|
source_tag.post_tags.kept.find_each do |source_pt|
|
||||||
post_id = source_pt.post_id
|
post_id = source_pt.post_id
|
||||||
affected_post_ids << post_id
|
affected_post_ids << post_id
|
||||||
source_pt.discard_by!(nil)
|
source_pt.discard_by!(created_by_user)
|
||||||
unless PostTag.kept.exists?(post_id:, tag: target_tag)
|
unless PostTag.kept.exists?(post_id:, tag: target_tag)
|
||||||
PostTag.create!(post_id:, tag: target_tag)
|
PostTag.create!(post_id:, tag: target_tag)
|
||||||
end
|
end
|
||||||
@@ -179,6 +181,7 @@ class Tag < ApplicationRecord
|
|||||||
end
|
end
|
||||||
|
|
||||||
source_tag.discard!
|
source_tag.discard!
|
||||||
|
record_tag_discard!(source_tag, current_by_user: nil)
|
||||||
|
|
||||||
if source_tag.nico?
|
if source_tag.nico?
|
||||||
source_tag_name.discard!
|
source_tag_name.discard!
|
||||||
@@ -186,10 +189,12 @@ class Tag < ApplicationRecord
|
|||||||
source_tag_name.update_columns(canonical_id: target_tag.tag_name_id,
|
source_tag_name.update_columns(canonical_id: target_tag.tag_name_id,
|
||||||
updated_at: Time.current)
|
updated_at: Time.current)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
record_tag_version!(target_tag, event_type: :update, created_by_user: nil)
|
||||||
end
|
end
|
||||||
|
|
||||||
Post.where(id: affected_post_ids.to_a).find_each do |post|
|
Post.where(id: affected_post_ids.to_a).find_each do |post|
|
||||||
PostVersionRecorder.record!(post:, event_type: :update, created_by_user: nil)
|
PostVersionRecorder.record!(post:, event_type: :update, created_by_user:)
|
||||||
end
|
end
|
||||||
|
|
||||||
# 投稿件数を再集計
|
# 投稿件数を再集計
|
||||||
@@ -199,6 +204,14 @@ class Tag < ApplicationRecord
|
|||||||
target_tag.reload
|
target_tag.reload
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def snapshot_aliases = tag_name.aliases.kept.order(:name).pluck(:name)
|
||||||
|
|
||||||
|
def snapshot_parent_tag_ids = parents.order('id').pluck('id')
|
||||||
|
|
||||||
|
def snapshot_linked_tags
|
||||||
|
linked_tags.joins(:tag_name).order('tag_names.name').pluck('tag_names.name')
|
||||||
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
def nico_tag_name_must_start_with_nico
|
def nico_tag_name_must_start_with_nico
|
||||||
|
|||||||
@@ -0,0 +1,16 @@
|
|||||||
|
class TagVersion < ApplicationRecord
|
||||||
|
include VersionRecord
|
||||||
|
|
||||||
|
belongs_to :tag
|
||||||
|
|
||||||
|
enum :category, { deerjikist: 'deerjikist',
|
||||||
|
meme: 'meme',
|
||||||
|
character: 'character',
|
||||||
|
general: 'general',
|
||||||
|
material: 'material',
|
||||||
|
meta: 'meta',
|
||||||
|
nico: 'nico' }, validate: true
|
||||||
|
|
||||||
|
validates :name, presence: true
|
||||||
|
validates :category, presence: true
|
||||||
|
end
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
module VersionRecord
|
||||||
|
extend ActiveSupport::Concern
|
||||||
|
|
||||||
|
def readonly? = persisted?
|
||||||
|
|
||||||
|
included do
|
||||||
|
belongs_to :created_by_user, class_name: 'User', optional: true
|
||||||
|
|
||||||
|
enum :event_type, { create: 'create',
|
||||||
|
update: 'update',
|
||||||
|
discard: 'discard',
|
||||||
|
restore: 'restore' }, prefix: true, validate: true
|
||||||
|
|
||||||
|
validates :version_no, presence: true, numericality: { only_integer: true, greater_than: 0 }
|
||||||
|
validates :event_type, presence: true
|
||||||
|
|
||||||
|
scope :chronological, -> { order(:version_no, :id) }
|
||||||
|
end
|
||||||
|
end
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
class NicoTagVersionRecorder < VersionRecorder
|
||||||
|
def self.record! tag:, event_type:, created_by_user:
|
||||||
|
new(tag:, event_type:, created_by_user:).record!
|
||||||
|
end
|
||||||
|
|
||||||
|
def initialize tag:, event_type:, created_by_user:
|
||||||
|
super(record: tag, event_type:, created_by_user:)
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def version_class = NicoTagVersion
|
||||||
|
def version_association = :nico_tag_versions
|
||||||
|
def record_key = :tag
|
||||||
|
def snapshot_attributes = { name: @tag.name, linked_tags: @tag.snapshot_linked_tags.join(' ') }
|
||||||
|
end
|
||||||
@@ -4,36 +4,15 @@ class PostVersionRecorder
|
|||||||
end
|
end
|
||||||
|
|
||||||
def initialize post:, event_type:, created_by_user:
|
def initialize post:, event_type:, created_by_user:
|
||||||
@post = post
|
super(record: post, event_type:, created_by_user:)
|
||||||
@event_type = event_type
|
|
||||||
@created_by_user = created_by_user
|
|
||||||
end
|
|
||||||
|
|
||||||
def record!
|
|
||||||
@post.with_lock do
|
|
||||||
latest = @post.post_versions.order(version_no: :desc).first
|
|
||||||
attrs = snapshot_attributes
|
|
||||||
|
|
||||||
return latest if @event_type == :update && latest && same_snapshot?(latest, attrs)
|
|
||||||
|
|
||||||
PostVersion.create!(
|
|
||||||
post: @post,
|
|
||||||
version_no: (latest&.version_no || 0) + 1,
|
|
||||||
event_type: @event_type,
|
|
||||||
title: attrs[:title],
|
|
||||||
url: attrs[:url],
|
|
||||||
thumbnail_base: attrs[:thumbnail_base],
|
|
||||||
tags: attrs[:tags],
|
|
||||||
parent: attrs[:parent],
|
|
||||||
original_created_from: attrs[:original_created_from],
|
|
||||||
original_created_before: attrs[:original_created_before],
|
|
||||||
created_at: Time.current,
|
|
||||||
created_by_user: @created_by_user)
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
|
def version_class = PostVersion
|
||||||
|
def version_association = :post_versions
|
||||||
|
def record_key = :post
|
||||||
|
|
||||||
def snapshot_attributes
|
def snapshot_attributes
|
||||||
{ title: @post.title,
|
{ title: @post.title,
|
||||||
url: @post.url,
|
url: @post.url,
|
||||||
@@ -43,15 +22,4 @@ class PostVersionRecorder
|
|||||||
original_created_from: @post.original_created_from,
|
original_created_from: @post.original_created_from,
|
||||||
original_created_before: @post.original_created_before }
|
original_created_before: @post.original_created_before }
|
||||||
end
|
end
|
||||||
|
|
||||||
def same_snapshot? version, attrs
|
|
||||||
true &&
|
|
||||||
version.title == attrs[:title] &&
|
|
||||||
version.url == attrs[:url] &&
|
|
||||||
version.thumbnail_base == attrs[:thumbnail_base] &&
|
|
||||||
version.tags == attrs[:tags] &&
|
|
||||||
version.parent_id == attrs[:parent]&.id &&
|
|
||||||
version.original_created_from == attrs[:original_created_from] &&
|
|
||||||
version.original_created_before == attrs[:original_created_before]
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -0,0 +1,22 @@
|
|||||||
|
class TagVersionRecorder < VersionRecorder
|
||||||
|
def self.record! tag:, event_type:, created_by_user:
|
||||||
|
new(tag:, event_type:, created_by_user:).record!
|
||||||
|
end
|
||||||
|
|
||||||
|
def initialize tag:, event_type:, created_by_user:
|
||||||
|
super(record: tag, event_type:, created_by_user:)
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def version_class = TagVersion
|
||||||
|
def version_association = :tag_versions
|
||||||
|
def record_key = :tag
|
||||||
|
|
||||||
|
def snapshot_attributes
|
||||||
|
{ name: @tag.name,
|
||||||
|
category: @tag.category,
|
||||||
|
aliases: @tag.snapshot_aliases.join(' '),
|
||||||
|
parent_tag_ids: @tag.snapshot_parent_tag_ids.join(' ') }
|
||||||
|
end
|
||||||
|
end
|
||||||
@@ -0,0 +1,57 @@
|
|||||||
|
class VersionRecorder
|
||||||
|
EVENT_TYPES = ['create', 'update', 'discard', 'restore'].freeze
|
||||||
|
|
||||||
|
def initialize record:, event_type:, created_by_user:
|
||||||
|
@record = record
|
||||||
|
@event_type = event_type.to_s
|
||||||
|
@created_by_user = created_by_user
|
||||||
|
|
||||||
|
validate_event_type!
|
||||||
|
end
|
||||||
|
|
||||||
|
def record! record, event_type:, created_by_user:
|
||||||
|
@record.with_lock do
|
||||||
|
latest = latest_version
|
||||||
|
|
||||||
|
if !(latest) && @event_type != 'create'
|
||||||
|
raise "#{ version_class.name } first event must be create"
|
||||||
|
end
|
||||||
|
|
||||||
|
if @event_type == 'create' && latest
|
||||||
|
raise "#{ version_class.name } create event already exists"
|
||||||
|
end
|
||||||
|
|
||||||
|
attrs = snapshot_attributes
|
||||||
|
|
||||||
|
return latest if @event_type == 'update' && latest && same_snapshot?(latest, attrs)
|
||||||
|
|
||||||
|
version_class.create!(base_attributes(latest).merge(record_key => @record).merge(attrs))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def latest_version = versions.order(version_no: :desc).first
|
||||||
|
|
||||||
|
def versions = @record.public_send(version_association)
|
||||||
|
|
||||||
|
def base_attributes latest
|
||||||
|
{ version_no: (latest&.version_no || 0) + 1,
|
||||||
|
event_type: @event_type,
|
||||||
|
created_at: Time.current,
|
||||||
|
created_by_user: @created_by_user }
|
||||||
|
end
|
||||||
|
|
||||||
|
def same_snapshot?(version, attrs) = attrs.all? { |k, v| version.public_send(k) == v }
|
||||||
|
|
||||||
|
def validate_event_type!
|
||||||
|
return if EVENT_TYPES.include?(@event_type)
|
||||||
|
|
||||||
|
raise ArgumentError, "Invalid event_type: #{ @event_type }"
|
||||||
|
end
|
||||||
|
|
||||||
|
def version_class = raise NotImplementedError
|
||||||
|
def version_association = raise NotImplementedError
|
||||||
|
def record_key = raise NotImplementedError
|
||||||
|
def snapshot_attributes = raise NotImplementedError
|
||||||
|
end
|
||||||
@@ -15,6 +15,14 @@ class CreateTagVersions < ActiveRecord::Migration[8.0]
|
|||||||
self.table_name = 'tag_versions'
|
self.table_name = 'tag_versions'
|
||||||
end
|
end
|
||||||
|
|
||||||
|
class NicoTagVersion < ActiveRecord::Base
|
||||||
|
self.table_name = 'nico_tag_versions'
|
||||||
|
end
|
||||||
|
|
||||||
|
class NicoTagRelation < ActiveRecord::Base
|
||||||
|
self.table_name = 'nico_tag_relations'
|
||||||
|
end
|
||||||
|
|
||||||
def up
|
def up
|
||||||
create_table :tag_versions do |t|
|
create_table :tag_versions do |t|
|
||||||
t.references :tag, null: false, foreign_key: true, index: false
|
t.references :tag, null: false, foreign_key: true, index: false
|
||||||
@@ -35,10 +43,28 @@ class CreateTagVersions < ActiveRecord::Migration[8.0]
|
|||||||
name: 'tag_versions_version_no_positive'
|
name: 'tag_versions_version_no_positive'
|
||||||
end
|
end
|
||||||
|
|
||||||
TagVersion.reset_column_information
|
create_table :nico_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.text :linked_tags, 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: 'nico_tag_versions_version_no_positive'
|
||||||
|
end
|
||||||
|
|
||||||
|
TagVersion.reset_column_information
|
||||||
say_with_time 'Backfilling tag_versions' do
|
say_with_time 'Backfilling tag_versions' do
|
||||||
Tag.where(discarded_at: nil).find_in_batches(batch_size: 500) do |tags|
|
Tag.where(discarded_at: nil)
|
||||||
|
.where.not(category: 'nico')
|
||||||
|
.find_in_batches(batch_size: 500) do |tags|
|
||||||
tag_ids = tags.map(&:id)
|
tag_ids = tags.map(&:id)
|
||||||
|
|
||||||
tag_implication_rows_by_tag_id =
|
tag_implication_rows_by_tag_id =
|
||||||
@@ -81,9 +107,50 @@ class CreateTagVersions < ActiveRecord::Migration[8.0]
|
|||||||
})
|
})
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
NicoTagVersion.reset_column_information
|
||||||
|
say_with_time 'Backfilling nico_tag_versions' do
|
||||||
|
Tag.where(discarded_at: nil, category: 'nico')
|
||||||
|
.find_in_batches(batch_size: 500) do |tags|
|
||||||
|
tag_ids = tags.map(&:id)
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
nico_tag_relation_rows_by_tag_id =
|
||||||
|
NicoTagRelation
|
||||||
|
.joins('INNER JOIN tags nico_tags ON nico_tags.id = nico_tag_relations.nico_tag_id')
|
||||||
|
.joins('INNER JOIN tags linked_tags ON linked_tags.id = nico_tag_relations.tag_id')
|
||||||
|
.joins('INNER JOIN tag_names ON tag_names.id = linked_tags.tag_name_id')
|
||||||
|
.where(nico_tags: { id: tag_ids })
|
||||||
|
.where(linked_tags: { discarded_at: nil })
|
||||||
|
.where(tag_names: { discarded_at: nil })
|
||||||
|
.pluck('nico_tags.id', 'tag_names.name')
|
||||||
|
.each_with_object(Hash.new { |h, k| h[k] = [] }) do |row, h|
|
||||||
|
h[row[0]] << row[1]
|
||||||
|
end
|
||||||
|
|
||||||
|
NicoTagVersion.insert_all(tags.map { |tag|
|
||||||
|
{ tag_id: tag.id,
|
||||||
|
version_no: 1,
|
||||||
|
event_type: 'create',
|
||||||
|
name: tag_name_rows_by_tag_id[tag.id],
|
||||||
|
linked_tags: nico_tag_relation_rows_by_tag_id[tag.id].sort.join(' '),
|
||||||
|
created_at: tag.created_at,
|
||||||
|
created_by_user_id: nil }
|
||||||
|
})
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def down
|
def down
|
||||||
|
drop_table :nico_tag_versions
|
||||||
drop_table :tag_versions
|
drop_table :tag_versions
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
Generated
+17
@@ -104,6 +104,21 @@ ActiveRecord::Schema[8.0].define(version: 2026_04_19_035400) do
|
|||||||
t.index ["tag_id"], name: "index_nico_tag_relations_on_tag_id"
|
t.index ["tag_id"], name: "index_nico_tag_relations_on_tag_id"
|
||||||
end
|
end
|
||||||
|
|
||||||
|
create_table "nico_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.text "linked_tags", null: false
|
||||||
|
t.datetime "created_at", null: false
|
||||||
|
t.bigint "created_by_user_id"
|
||||||
|
t.index ["created_at"], name: "index_nico_tag_versions_on_created_at"
|
||||||
|
t.index ["created_by_user_id", "created_at"], name: "index_nico_tag_versions_on_created_by_user_id_and_created_at", order: { created_at: :desc }
|
||||||
|
t.index ["tag_id", "created_at"], name: "index_nico_tag_versions_on_tag_id_and_created_at", order: { created_at: :desc }
|
||||||
|
t.index ["tag_id", "version_no"], name: "index_nico_tag_versions_on_tag_id_and_version_no", unique: true
|
||||||
|
t.check_constraint "`version_no` > 0", name: "nico_tag_versions_version_no_positive"
|
||||||
|
end
|
||||||
|
|
||||||
create_table "post_similarities", primary_key: ["post_id", "target_post_id"], charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t|
|
create_table "post_similarities", primary_key: ["post_id", "target_post_id"], charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t|
|
||||||
t.bigint "post_id", null: false
|
t.bigint "post_id", null: false
|
||||||
t.bigint "target_post_id", null: false
|
t.bigint "target_post_id", null: false
|
||||||
@@ -394,6 +409,8 @@ ActiveRecord::Schema[8.0].define(version: 2026_04_19_035400) do
|
|||||||
add_foreign_key "materials", "users", column: "updated_by_user_id"
|
add_foreign_key "materials", "users", column: "updated_by_user_id"
|
||||||
add_foreign_key "nico_tag_relations", "tags"
|
add_foreign_key "nico_tag_relations", "tags"
|
||||||
add_foreign_key "nico_tag_relations", "tags", column: "nico_tag_id"
|
add_foreign_key "nico_tag_relations", "tags", column: "nico_tag_id"
|
||||||
|
add_foreign_key "nico_tag_versions", "tags"
|
||||||
|
add_foreign_key "nico_tag_versions", "users", column: "created_by_user_id"
|
||||||
add_foreign_key "post_similarities", "posts"
|
add_foreign_key "post_similarities", "posts"
|
||||||
add_foreign_key "post_similarities", "posts", column: "target_post_id"
|
add_foreign_key "post_similarities", "posts", column: "target_post_id"
|
||||||
add_foreign_key "post_tags", "posts"
|
add_foreign_key "post_tags", "posts"
|
||||||
|
|||||||
@@ -115,6 +115,10 @@ namespace :nico do
|
|||||||
datum['tags'].each do |raw|
|
datum['tags'].each do |raw|
|
||||||
name = TagNameSanitisationRule.sanitise("nico:#{ raw }")
|
name = TagNameSanitisationRule.sanitise("nico:#{ raw }")
|
||||||
tag = Tag.find_or_create_by_tag_name!(name, category: :nico)
|
tag = Tag.find_or_create_by_tag_name!(name, category: :nico)
|
||||||
|
|
||||||
|
event_type = tag.nico_tag_versions.exists? ? :update : :create
|
||||||
|
NicoTagVersionRecorder.record!(tag:, event_type:, created_by_user: nil)
|
||||||
|
|
||||||
desired_nico_tag_based_ids << tag.id
|
desired_nico_tag_based_ids << tag.id
|
||||||
|
|
||||||
# 新たに記載される外部タグと連携される内部タグを記載
|
# 新たに記載される外部タグと連携される内部タグを記載
|
||||||
|
|||||||
Reference in New Issue
Block a user