Browse Source

#20

pull/243/head
みてるぞ 2 weeks ago
parent
commit
4832f9d99a
6 changed files with 69 additions and 15 deletions
  1. +6
    -0
      backend/app/controllers/posts_controller.rb
  2. +35
    -7
      backend/app/controllers/tags_controller.rb
  3. +2
    -2
      backend/app/models/post.rb
  4. +6
    -2
      backend/app/models/tag.rb
  5. +16
    -0
      backend/app/models/tag_name.rb
  6. +4
    -4
      backend/spec/requests/posts_spec.rb

+ 6
- 0
backend/app/controllers/posts_controller.rb View File

@@ -97,6 +97,8 @@ class PostsController < ApplicationController
tags = Tag.normalise_tags(tag_names) tags = Tag.normalise_tags(tag_names)
tags = Tag.expand_parent_tags(tags) tags = Tag.expand_parent_tags(tags)
sync_post_tags!(post, tags) sync_post_tags!(post, tags)

post.reload
render json: post.as_json(include: { tags: { only: [:id, :category, :post_count], render json: post.as_json(include: { tags: { only: [:id, :category, :post_count],
methods: [:name, :has_wiki] } }), methods: [:name, :has_wiki] } }),
status: :created status: :created
@@ -136,6 +138,8 @@ class PostsController < ApplicationController
Tag.normalise_tags(tag_names, with_tagme: false) Tag.normalise_tags(tag_names, with_tagme: false)
tags = Tag.expand_parent_tags(tags) tags = Tag.expand_parent_tags(tags)
sync_post_tags!(post, tags) sync_post_tags!(post, tags)

post.reload
json = post.as_json json = post.as_json
json['tags'] = build_tag_tree_for(post.tags) json['tags'] = build_tag_tree_for(post.tags)
render json:, status: :ok render json:, status: :ok
@@ -197,6 +201,8 @@ class PostsController < ApplicationController
end end


def filter_posts_by_tags tag_names, match_type def filter_posts_by_tags tag_names, match_type
tag_names = TagName.canonicalise(tag_names)

posts = Post.joins(tags: :tag_name) posts = Post.joins(tags: :tag_name)


if match_type == 'any' if match_type == 'any'


+ 35
- 7
backend/app/controllers/tags_controller.rb View File

@@ -18,13 +18,41 @@ class TagsController < ApplicationController


with_nico = !(params[:nico].to_s.strip.downcase.in?(['0', 'false', 'off', 'no'])) with_nico = !(params[:nico].to_s.strip.downcase.in?(['0', 'false', 'off', 'no']))


tags = (Tag.joins(:tag_name).includes(:tag_name)
.where(((with_nico ? '(tags.category = ? AND tag_names.name LIKE ?) OR ' : '') +
'tag_names.name LIKE ?'),
*(with_nico ? ['nico', "nico:#{ q }%"] : []), "#{ q }%")
.order(Arel.sql('post_count DESC, tag_names.name ASC'))
.limit(20))
render json: tags.as_json(only: [:id, :category, :post_count], methods: [:name, :has_wiki])
alias_rows =
TagName
.where('name LIKE ?', "#{ q }%")
.where.not(canonical_id: nil)
.pluck(:canonical_id, :name)

matched_alias_by_tag_name_id = { }
canonical_ids = []

alias_rows.each do |canonical_id, alias_name|
canonical_ids << canonical_id
matched_alias_by_tag_name_id[canonical_id] ||= alias_name
end

base = Tag.joins(:tag_name).includes(:tag_name)

canonical_hit =
base
.where(((with_nico ? '(tags.category = ? AND tag_names.name LIKE ?) OR ' : '') +
'tag_names.name LIKE ?'),
*(with_nico ? ['nico', "nico:#{ q }%"] : []), "#{ q }%")

tags =
if canonical_ids.present?
canonical_hit.or(base.where(tag_name_id: canonical_ids.uniq))
else
canonical_hit
end

tags = tags.order(Arel.sql('post_count DESC, tag_names.name')).limit(20).to_a

render json: tags.map { |tag|
tag.as_json(only: [:id, :category, :post_count], methods: [:name, :has_wiki])
.merge(matched_alias: matched_alias_by_tag_name_id[tag.tag_name_id])
}
end end


def show def show


+ 2
- 2
backend/app/models/post.rb View File

@@ -9,8 +9,8 @@ class Post < ApplicationRecord
has_many :post_tags_with_discarded, -> { with_discarded }, class_name: 'PostTag' has_many :post_tags_with_discarded, -> { with_discarded }, class_name: 'PostTag'
has_many :tags, through: :active_post_tags has_many :tags, through: :active_post_tags


has_many :user_post_views, dependent: :destroy
has_many :post_similarities, dependent: :destroy
has_many :user_post_views, dependent: :delete_all
has_many :post_similarities, dependent: :delete_all


has_one_attached :thumbnail has_one_attached :thumbnail




+ 6
- 2
backend/app/models/tag.rb View File

@@ -22,6 +22,8 @@ class Tag < ApplicationRecord
class_name: 'TagImplication', foreign_key: :tag_id, dependent: :destroy class_name: 'TagImplication', foreign_key: :tag_id, dependent: :destroy
has_many :parents, through: :reversed_tag_implications, source: :parent_tag has_many :parents, through: :reversed_tag_implications, source: :parent_tag


has_many :tag_similarities, dependent: :delete_all

belongs_to :tag_name belongs_to :tag_name
delegate :name, to: :tag_name, allow_nil: true delegate :name, to: :tag_name, allow_nil: true
validates :tag_name, presence: true validates :tag_name, presence: true
@@ -72,7 +74,7 @@ class Tag < ApplicationRecord


tags = tag_names.map do |name| tags = tag_names.map do |name|
pf, cat = CATEGORY_PREFIXES.find { |p, _| name.start_with?(p) } || ['', nil] pf, cat = CATEGORY_PREFIXES.find { |p, _| name.start_with?(p) } || ['', nil]
name = name.delete_prefix(pf)
name = TagName.canonicalise(name.delete_prefix(pf)).first
find_or_create_by_tag_name!(name, category: (cat || :general)).tap do |tag| find_or_create_by_tag_name!(name, category: (cat || :general)).tap do |tag|
if cat && tag.category != cat if cat && tag.category != cat
tag.update!(category: cat) tag.update!(category: cat)
@@ -109,6 +111,8 @@ class Tag < ApplicationRecord


def self.find_or_create_by_tag_name! name, category: def self.find_or_create_by_tag_name! name, category:
tn = TagName.find_or_create_by!(name: name.to_s.strip) tn = TagName.find_or_create_by!(name: name.to_s.strip)
tn = tn.canonical if tn.canonical_id?

Tag.find_or_create_by!(tag_name_id: tn.id) do |t| Tag.find_or_create_by!(tag_name_id: tn.id) do |t|
t.category = category t.category = category
end end
@@ -127,7 +131,7 @@ class Tag < ApplicationRecord
end end


def tag_name_must_be_canonical def tag_name_must_be_canonical
if tag_name&.canonical_id
if tag_name&.canonical_id?
errors.add :tag_name, 'tag_names へは実体を示す必要があります.' errors.add :tag_name, 'tag_names へは実体を示す必要があります.'
end end
end end


+ 16
- 0
backend/app/models/tag_name.rb View File

@@ -9,6 +9,16 @@ class TagName < ApplicationRecord


validate :canonical_must_be_canonical validate :canonical_must_be_canonical
validate :alias_name_must_not_have_prefix validate :alias_name_must_not_have_prefix
validate :canonical_must_not_be_present_with_tag_or_wiki_page

def self.canonicalise names
names = Array(names).map { |n| n.to_s.strip }.reject(&:blank?)
return [] if names.blank?

tns = TagName.includes(:canonical).where(name: names).index_by(&:name)

names.map { |name| tns[name]&.canonical&.name || name }.uniq
end


private private


@@ -23,4 +33,10 @@ class TagName < ApplicationRecord
errors.add :name, 'エーリアス名にプレフィクスを含むことはできません.' errors.add :name, 'エーリアス名にプレフィクスを含むことはできません.'
end end
end end

def canonical_must_not_be_present_with_tag_or_wiki_page
if canonical_id? && (tag || wiki_page)
errors.add :canonical, 'タグもしくは Wiki の参照がある名前はエーリアスになれません.'
end
end
end end

+ 4
- 4
backend/spec/requests/posts_spec.rb View File

@@ -155,6 +155,7 @@ RSpec.describe 'Posts API', type: :request do


describe 'POST /posts' do describe 'POST /posts' do
let(:member) { create(:user, :member) } let(:member) { create(:user, :member) }
let!(:alias_tag_name) { TagName.create!(name: 'manko', canonical: tag_name) }


it '401 when not logged in' do it '401 when not logged in' do
sign_out sign_out
@@ -201,10 +202,9 @@ RSpec.describe 'Posts API', type: :request do
expect(json).to include('id', 'title', 'url') expect(json).to include('id', 'title', 'url')


# tags が name を含むこと(API 側の serialization が正しいこと) # tags が name を含むこと(API 側の serialization が正しいこと)
expect(json).to have_key('tags')
expect(json['tags']).to be_an(Array)
expect(json['tags'][0]).to have_key('name')
expect(json['tags'][0]['name']).to eq('spec_tag')
names = json.fetch('tags').map { |t| t['name'] }
expect(names).to include('spec_tag')
expect(names).not_to include('manko')
end end


context "when nico tag already exists in tags" do context "when nico tag already exists in tags" do


Loading…
Cancel
Save