From 0f902f0d271938cd01f65720052aba5db6cfb9c0 Mon Sep 17 00:00:00 2001 From: miteruzo Date: Wed, 30 Jul 2025 00:20:03 +0900 Subject: [PATCH] #59 --- backend/app/models/post_similarity.rb | 4 +++ ...20250729124200_create_post_similarities.rb | 9 ++++++ .../20250729210600_create_tag_similarities.rb | 9 ++++++ backend/db/schema.rb | 22 ++++++++++++++- backend/lib/tasks/calc_post_similarities.rake | 28 +++++++++++++++++++ 5 files changed, 71 insertions(+), 1 deletion(-) create mode 100644 backend/app/models/post_similarity.rb create mode 100644 backend/db/migrate/20250729124200_create_post_similarities.rb create mode 100644 backend/db/migrate/20250729210600_create_tag_similarities.rb create mode 100644 backend/lib/tasks/calc_post_similarities.rake diff --git a/backend/app/models/post_similarity.rb b/backend/app/models/post_similarity.rb new file mode 100644 index 0000000..a753518 --- /dev/null +++ b/backend/app/models/post_similarity.rb @@ -0,0 +1,4 @@ +class PostSimilarity < ApplicationRecord + belongs_to :post, class_name: 'Post', foreign_key: 'post_id' + belongs_to :target_post, class_name: 'Post', foreign_key: 'target_post_id' +end diff --git a/backend/db/migrate/20250729124200_create_post_similarities.rb b/backend/db/migrate/20250729124200_create_post_similarities.rb new file mode 100644 index 0000000..16ad1b7 --- /dev/null +++ b/backend/db/migrate/20250729124200_create_post_similarities.rb @@ -0,0 +1,9 @@ +class CreatePostSimilarities < ActiveRecord::Migration[7.0] + def change + create_table :post_similarities do |t| + t.references :post, null: false, foreign_key: { to_table: :posts } + t.references :target_post, null: false, foreign_key: { to_table: :posts } + t.float :cos, null: false + end + end +end diff --git a/backend/db/migrate/20250729210600_create_tag_similarities.rb b/backend/db/migrate/20250729210600_create_tag_similarities.rb new file mode 100644 index 0000000..daab1f1 --- /dev/null +++ b/backend/db/migrate/20250729210600_create_tag_similarities.rb @@ -0,0 +1,9 @@ +class CreateTagSimilarities < ActiveRecord::Migration[7.0] + def change + create_table :tag_similarities do |t| + t.references :tag, null: false, foreign_key: { to_table: :tags } + t.references :target_tag, null: false, foreign_key: { to_table: :tags } + t.float :cos, null: false + end + end +end diff --git a/backend/db/schema.rb b/backend/db/schema.rb index fb6ae14..f339414 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: 2025_06_29_140234) do +ActiveRecord::Schema[8.0].define(version: 2025_07_29_210600) 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 @@ -55,6 +55,14 @@ ActiveRecord::Schema[8.0].define(version: 2025_06_29_140234) do t.index ["tag_id"], name: "index_nico_tag_relations_on_tag_id" end + create_table "post_similarities", charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t| + t.bigint "post_id", null: false + t.bigint "target_post_id", null: false + t.float "cos", null: false + t.index ["post_id"], name: "index_post_similarities_on_post_id" + t.index ["target_post_id"], name: "index_post_similarities_on_target_post_id" + end + create_table "post_tags", charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t| t.bigint "post_id", null: false t.bigint "tag_id", null: false @@ -97,6 +105,14 @@ ActiveRecord::Schema[8.0].define(version: 2025_06_29_140234) do t.index ["tag_id"], name: "index_tag_aliases_on_tag_id" end + create_table "tag_similarities", charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t| + t.bigint "tag_id", null: false + t.bigint "target_tag_id", null: false + t.float "cos", null: false + t.index ["tag_id"], name: "index_tag_similarities_on_tag_id" + t.index ["target_tag_id"], name: "index_tag_similarities_on_target_tag_id" + end + create_table "tags", charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t| t.string "name", null: false t.string "category", default: "general", null: false @@ -148,6 +164,8 @@ ActiveRecord::Schema[8.0].define(version: 2025_06_29_140234) do add_foreign_key "active_storage_variant_records", "active_storage_blobs", column: "blob_id" add_foreign_key "nico_tag_relations", "tags" add_foreign_key "nico_tag_relations", "tags", column: "nico_tag_id" + add_foreign_key "post_similarities", "posts" + add_foreign_key "post_similarities", "posts", column: "target_post_id" add_foreign_key "post_tags", "posts" add_foreign_key "post_tags", "tags" add_foreign_key "post_tags", "users", column: "created_user_id" @@ -156,6 +174,8 @@ ActiveRecord::Schema[8.0].define(version: 2025_06_29_140234) do add_foreign_key "posts", "users", column: "uploaded_user_id" add_foreign_key "settings", "users" add_foreign_key "tag_aliases", "tags" + add_foreign_key "tag_similarities", "tags" + add_foreign_key "tag_similarities", "tags", column: "target_tag_id" add_foreign_key "user_ips", "ip_addresses" add_foreign_key "user_ips", "users" add_foreign_key "user_post_views", "posts" diff --git a/backend/lib/tasks/calc_post_similarities.rake b/backend/lib/tasks/calc_post_similarities.rake new file mode 100644 index 0000000..97e4fd2 --- /dev/null +++ b/backend/lib/tasks/calc_post_similarities.rake @@ -0,0 +1,28 @@ +namespace :post_similarity do + desc '関聯投稿テーブル作成' + task calc: :environment do + dot = -> a, b { (a.keys & b.keys).sum { |k| a[k] * b[k] } } + norm = -> v { Math.sqrt(v.values.sum { |e| e * e }) } + cos = -> a, b do + na = norm.(a) + nb = norm.(b) + if na.zero? || nb.zero? + 0.0 + else + dot.(a, b) / na / nb + end + end + + posts = Post.includes(:tags).to_a + posts.each_with_index do |post, i| + existence_of_tags = post.tags.index_with(1) + ((i + 1)...posts.size).each do |j| + target_post = posts[j] + existence_of_target_tags = target_post.tags.index_with(1) + PostSimilarity.find_or_initialize_by(post:, target_post:).tap { |ps| + ps.cos = cos.(existence_of_tags, existence_of_target_tags) + }.save! + end + end + end +end