このコミットが含まれているのは:
@@ -31,9 +31,9 @@ class PostVersionsController < ApplicationController
|
|||||||
q = q.where('post_versions.post_id = ?', post_id) if post_id
|
q = q.where('post_versions.post_id = ?', post_id) if post_id
|
||||||
if external_tag_id || (tag_id && !(Tag.exists?(id: tag_id)))
|
if external_tag_id || (tag_id && !(Tag.exists?(id: tag_id)))
|
||||||
q = q.where('JSON_CONTAINS(post_versions.tags_json,' +
|
q = q.where('JSON_CONTAINS(post_versions.tags_json,' +
|
||||||
"JSON_OBJECT('external_tag_id', #{ external_tag_id })) " +
|
"JSON_OBJECT('external_tag_id', #{ external_tag_id || tag_id })) " +
|
||||||
'OR JSON_CONTAINS(prev.tags_json,' +
|
'OR JSON_CONTAINS(prev.tags_json,' +
|
||||||
"JSON_OBJECT('external_tag_id', #{ external_tag_id }))")
|
"JSON_OBJECT('external_tag_id', #{ external_tag_id || tag_id }))")
|
||||||
elsif tag_id
|
elsif tag_id
|
||||||
q = q.where("JSON_CONTAINS(post_versions.tags_json, JSON_OBJECT('tag_id', #{ tag_id })) " +
|
q = q.where("JSON_CONTAINS(post_versions.tags_json, JSON_OBJECT('tag_id', #{ tag_id })) " +
|
||||||
"OR JSON_CONTAINS(prev.tags_json, JSON_OBJECT('tag_id', #{ tag_id }))")
|
"OR JSON_CONTAINS(prev.tags_json, JSON_OBJECT('tag_id', #{ tag_id }))")
|
||||||
|
|||||||
@@ -49,7 +49,7 @@ class PostsController < ApplicationController
|
|||||||
filtered_posts
|
filtered_posts
|
||||||
.joins("LEFT JOIN (#{ pt_max_sql }) pt_max ON pt_max.post_id = posts.id")
|
.joins("LEFT JOIN (#{ pt_max_sql }) pt_max ON pt_max.post_id = posts.id")
|
||||||
.reselect('posts.*', Arel.sql("#{ updated_at_all_sql } AS updated_at_all"))
|
.reselect('posts.*', Arel.sql("#{ updated_at_all_sql } AS updated_at_all"))
|
||||||
.preload(:uploaded_user, :parents, :children,
|
.preload(:external_tags, :uploaded_user, :parents, :children,
|
||||||
post_tags: [:sections, { tag: [:deerjikists, :materials,
|
post_tags: [:sections, { tag: [:deerjikists, :materials,
|
||||||
{ tag_name: :wiki_page }] }])
|
{ tag_name: :wiki_page }] }])
|
||||||
.with_attached_thumbnail
|
.with_attached_thumbnail
|
||||||
@@ -102,12 +102,14 @@ class PostsController < ApplicationController
|
|||||||
end
|
end
|
||||||
|
|
||||||
def random
|
def random
|
||||||
post = filtered_posts.preload(:uploaded_user, :parents, :children,
|
post =
|
||||||
post_tags: [:sections, { tag: [:deerjikists, :materials,
|
filtered_posts
|
||||||
{ tag_name: :wiki_page }] }])
|
.preload(:uploaded_user, :parents, :children,
|
||||||
.with_attached_thumbnail
|
post_tags: [:sections, { tag: [:deerjikists, :materials,
|
||||||
.order('RAND()')
|
{ tag_name: :wiki_page }] }])
|
||||||
.first
|
.with_attached_thumbnail
|
||||||
|
.order('RAND()')
|
||||||
|
.first
|
||||||
return head :not_found unless post
|
return head :not_found unless post
|
||||||
|
|
||||||
render json: PostRepr.base(post, current_user)
|
render json: PostRepr.base(post, current_user)
|
||||||
@@ -426,8 +428,20 @@ class PostsController < ApplicationController
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def tagged_post_ids_for(name) =
|
def tagged_post_ids_for(name)
|
||||||
Post.joins(tags: :tag_name).where(tag_names: { name: }).select(:id)
|
posts_by_internal_tags =
|
||||||
|
Post
|
||||||
|
.joins(tags: :tag_name)
|
||||||
|
.where(tag_names: { name: })
|
||||||
|
.select(:id)
|
||||||
|
|
||||||
|
posts_by_external_tags =
|
||||||
|
Post
|
||||||
|
.joins(:external_tags)
|
||||||
|
.where("CONCAT(external_tags.platform, ':', external_tags.name) = ?", name)
|
||||||
|
|
||||||
|
(posts_by_internal_tags + posts_by_external_tags).uniq(&:id)
|
||||||
|
end
|
||||||
|
|
||||||
def sync_post_tags! post, desired_tags, sections
|
def sync_post_tags! post, desired_tags, sections
|
||||||
desired_tags.each do |t|
|
desired_tags.each do |t|
|
||||||
|
|||||||
@@ -34,49 +34,155 @@ class TagsController < ApplicationController
|
|||||||
|
|
||||||
offset = (page - 1) * limit
|
offset = (page - 1) * limit
|
||||||
|
|
||||||
q =
|
tags =
|
||||||
if post_id.present?
|
if post_id.present?
|
||||||
Tag.joins(:posts, :tag_name)
|
Tag.joins(:posts, :tag_name).where(posts: { id: post_id })
|
||||||
else
|
else
|
||||||
Tag.joins(:tag_name)
|
Tag.joins(:tag_name)
|
||||||
end
|
end
|
||||||
.includes(:tag_name, :materials, tag_name: :wiki_page)
|
|
||||||
q = q.where(posts: { id: post_id }) if post_id.present?
|
|
||||||
|
|
||||||
q = q.where('tag_names.name LIKE ?', "%#{ name }%") if name
|
external_tags =
|
||||||
q = q.where(category:) if category
|
if post_id.present?
|
||||||
q = q.where('tags.post_count >= ?', post_count_between[0]) if post_count_between[0]
|
ExternalTag.joins(:posts).where(posts: { id: post_id })
|
||||||
q = q.where('tags.post_count <= ?', post_count_between[1]) if post_count_between[1]
|
else
|
||||||
q = q.where('tags.created_at >= ?', created_between[0]) if created_between[0]
|
ExternalTag.all
|
||||||
q = q.where('tags.created_at <= ?', created_between[1]) if created_between[1]
|
end
|
||||||
q = q.where('tags.updated_at >= ?', updated_between[0]) if updated_between[0]
|
|
||||||
q = q.where('tags.updated_at <= ?', updated_between[1]) if updated_between[1]
|
if name
|
||||||
if deprecated_given
|
tags = tags.where('tag_names.name LIKE ?', "%#{ name }%")
|
||||||
q = deprecated ? q.where.not(deprecated_at: nil) : q.where(deprecated_at: nil)
|
external_tags =
|
||||||
|
external_tags.where("CONCAT(external_tags.platform, ':', external_tags.name) LIKE ?",
|
||||||
|
"%#{ name }%")
|
||||||
end
|
end
|
||||||
|
|
||||||
|
if category == 'nico'
|
||||||
|
tags = tags.none
|
||||||
|
elsif category
|
||||||
|
tags = tags.where(category:)
|
||||||
|
external_tags = external_tags.none
|
||||||
|
end
|
||||||
|
|
||||||
|
if post_count_between[0]
|
||||||
|
tags = tags.where('tags.post_count >= ?', post_count_between[0])
|
||||||
|
external_tags = external_tags.where('external_tags.post_count >= ?', post_count_between[0])
|
||||||
|
end
|
||||||
|
|
||||||
|
if post_count_between[1]
|
||||||
|
tags = tags.where('tags.post_count <= ?', post_count_between[1])
|
||||||
|
external_tags = external_tags.where('external_tags.post_count <= ?', post_count_between[1])
|
||||||
|
end
|
||||||
|
|
||||||
|
if created_between[0]
|
||||||
|
tags = tags.where('tags.created_at >= ?', created_between[0])
|
||||||
|
external_tags = external_tags.where('external_tags.created_at >= ?', created_between[0])
|
||||||
|
end
|
||||||
|
|
||||||
|
if created_between[1]
|
||||||
|
tags = tags.where('tags.created_at <= ?', created_between[1])
|
||||||
|
external_tags = external_tags.where('external_tags.created_at <= ?', created_between[1])
|
||||||
|
end
|
||||||
|
|
||||||
|
if updated_between[0]
|
||||||
|
tags = tags.where('tags.updated_at >= ?', updated_between[0])
|
||||||
|
external_tags = external_tags.where('external_tags.created_at >= ?', updated_between[0])
|
||||||
|
end
|
||||||
|
|
||||||
|
if updated_between[1]
|
||||||
|
tags = tags.where('tags.updated_at <= ?', updated_between[1])
|
||||||
|
external_tags = external_tags.where('external_tags.created_at <= ?', updated_between[1])
|
||||||
|
end
|
||||||
|
|
||||||
|
if deprecated_given
|
||||||
|
if deprecated
|
||||||
|
tags = tags.where.not(deprecated_at: nil)
|
||||||
|
external_tags = external_tags.none
|
||||||
|
else
|
||||||
|
tags = tags.where(deprecated_at: nil)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
tag_sql = tags.select("'tag' AS source",
|
||||||
|
'tags.id',
|
||||||
|
'tag_names.name',
|
||||||
|
'tags.category',
|
||||||
|
'tags.post_count',
|
||||||
|
'tags.created_at',
|
||||||
|
'tags.updated_at',
|
||||||
|
'tags.deprecated_at').to_sql
|
||||||
|
|
||||||
|
external_tag_sql = external_tags.select(
|
||||||
|
"'external_tag' AS source",
|
||||||
|
'external_tags.id',
|
||||||
|
"CONCAT(external_tags.platform, ':', external_tags.name) AS name",
|
||||||
|
"'nico' AS category",
|
||||||
|
'external_tags.post_count',
|
||||||
|
'external_tags.created_at',
|
||||||
|
'external_tags.created_at AS updated_at',
|
||||||
|
'NULL AS deprecated_at').to_sql
|
||||||
|
|
||||||
|
union_sql = "#{ tag_sql } UNION ALL #{ external_tag_sql }"
|
||||||
|
|
||||||
sort_sql =
|
sort_sql =
|
||||||
case order[0]
|
if order[0] == 'category'
|
||||||
when 'name'
|
'CASE category ' +
|
||||||
'tag_names.name'
|
|
||||||
when 'category'
|
|
||||||
'CASE tags.category ' +
|
|
||||||
"WHEN 'deerjikist' THEN 0 " +
|
"WHEN 'deerjikist' THEN 0 " +
|
||||||
"WHEN 'meme' THEN 1 " +
|
"WHEN 'meme' THEN 1 " +
|
||||||
"WHEN 'character' THEN 2 " +
|
"WHEN 'character' THEN 2 " +
|
||||||
"WHEN 'general' THEN 3 " +
|
"WHEN 'general' THEN 3 " +
|
||||||
"WHEN 'material' THEN 4 " +
|
"WHEN 'material' THEN 4 " +
|
||||||
"WHEN 'meta' THEN 5 " +
|
"WHEN 'meta' THEN 5 " +
|
||||||
"WHEN 'nico' THEN 6 END"
|
"WHEN 'nico' THEN 6 " +
|
||||||
|
'END'
|
||||||
else
|
else
|
||||||
"tags.#{ order[0] }"
|
order[0]
|
||||||
end
|
end
|
||||||
tags = q.order(Arel.sql("#{ sort_sql } #{ order[1] }, tags.id #{ order[1] }"))
|
|
||||||
.limit(limit)
|
|
||||||
.offset(offset)
|
|
||||||
.to_a
|
|
||||||
|
|
||||||
render json: { tags: TagRepr.many(tags), count: q.size }
|
connection = ApplicationRecord.connection
|
||||||
|
|
||||||
|
count = connection.select_value(<<~SQL)
|
||||||
|
SELECT
|
||||||
|
COUNT(0)
|
||||||
|
FROM
|
||||||
|
(#{ union_sql }) legacy_tags
|
||||||
|
SQL
|
||||||
|
|
||||||
|
rows = connection.select_all(<<~SQL).to_a
|
||||||
|
SELECT
|
||||||
|
source
|
||||||
|
, id
|
||||||
|
FROM
|
||||||
|
(#{ union_sql }) legacy_tags
|
||||||
|
ORDER BY
|
||||||
|
#{ sort_sql } #{ order[1] }
|
||||||
|
, id #{ order[1] }
|
||||||
|
, source #{ order[1] }
|
||||||
|
LIMIT
|
||||||
|
#{ limit }
|
||||||
|
OFFSET
|
||||||
|
#{ offset }
|
||||||
|
SQL
|
||||||
|
|
||||||
|
tag_ids = rows.filter { _1['source'] == 'tag' }.map { _1['id'] }
|
||||||
|
external_tag_ids = rows.filter { _1['source'] == 'external_tag' }.map { _1['id'] }
|
||||||
|
|
||||||
|
tags_by_id =
|
||||||
|
Tag
|
||||||
|
.joins(:tag_name)
|
||||||
|
.includes(:tag_name, :materials, tag_name: :wiki_page)
|
||||||
|
.where(id: tag_ids)
|
||||||
|
.index_by(&:id)
|
||||||
|
external_tags_by_id =
|
||||||
|
ExternalTag
|
||||||
|
.where(id: external_tag_ids)
|
||||||
|
.index_by(&:id)
|
||||||
|
|
||||||
|
render json: { tags: rows.map { |row|
|
||||||
|
if row['source'] == 'tag'
|
||||||
|
TagRepr.base(tags_by_id.fetch(row['id']))
|
||||||
|
else
|
||||||
|
external_tag_json(external_tags_by_id.fetch(row['id']))
|
||||||
|
end
|
||||||
|
}, count: }
|
||||||
end
|
end
|
||||||
|
|
||||||
def with_depth
|
def with_depth
|
||||||
@@ -128,12 +234,7 @@ class TagsController < ApplicationController
|
|||||||
|
|
||||||
canonical_hit = base.where('tag_names.name LIKE ?', prefix)
|
canonical_hit = base.where('tag_names.name LIKE ?', prefix)
|
||||||
|
|
||||||
internal_tags =
|
internal_tags = canonical_hit.or(base.where(tag_name_id: canonical_ids.uniq))
|
||||||
if with_nico
|
|
||||||
canonical_hit.or(base.where(tag_name_id: canonical_ids.uniq))
|
|
||||||
else
|
|
||||||
canonical_hit
|
|
||||||
end
|
|
||||||
|
|
||||||
internal_rows =
|
internal_rows =
|
||||||
internal_tags
|
internal_tags
|
||||||
@@ -153,16 +254,7 @@ class TagsController < ApplicationController
|
|||||||
.where("CONCAT(platform, ':', name) LIKE ? OR name LIKE ?", prefix, prefix)
|
.where("CONCAT(platform, ':', name) LIKE ? OR name LIKE ?", prefix, prefix)
|
||||||
.order(post_count: :desc, name: :asc)
|
.order(post_count: :desc, name: :asc)
|
||||||
.limit(20)
|
.limit(20)
|
||||||
.map { |tag|
|
.map { external_tag_json(_1) }
|
||||||
{ 'id' => tag.id,
|
|
||||||
'name' => "#{ tag.platform }:#{ tag.name }",
|
|
||||||
'category' => 'nico',
|
|
||||||
'deprecated_at' => nil,
|
|
||||||
'created_at' => tag.created_at,
|
|
||||||
'updated_at' => tag.created_at,
|
|
||||||
'post_count' => tag.post_count,
|
|
||||||
'matched_alias' => nil }
|
|
||||||
}
|
|
||||||
|
|
||||||
rows =
|
rows =
|
||||||
(internal_rows + external_rows)
|
(internal_rows + external_rows)
|
||||||
@@ -187,14 +279,20 @@ class TagsController < ApplicationController
|
|||||||
name = params[:name].to_s.strip
|
name = params[:name].to_s.strip
|
||||||
return render_bad_request('name は必須です.') if name.blank?
|
return render_bad_request('name は必須です.') if name.blank?
|
||||||
|
|
||||||
tag = Tag.joins(:tag_name)
|
tag =
|
||||||
.includes(:tag_name, :materials, tag_name: :wiki_page)
|
Tag
|
||||||
.find_by(tag_names: { name: })
|
.joins(:tag_name)
|
||||||
if tag
|
.includes(:tag_name, :materials, tag_name: :wiki_page)
|
||||||
render json: TagRepr.base(tag)
|
.find_by(tag_names: { name: })
|
||||||
else
|
return render json: TagRepr.base(tag) if tag
|
||||||
head :not_found
|
|
||||||
end
|
platform, external_name = name.split(':', 2)
|
||||||
|
return head :not_found unless external_name
|
||||||
|
|
||||||
|
external_tag = ExternalTag.find_by(platform:, name: external_name)
|
||||||
|
return head :not_found unless external_tag
|
||||||
|
|
||||||
|
render json: ExternalTagRepr.base(external_tag)
|
||||||
end
|
end
|
||||||
|
|
||||||
def deerjikists
|
def deerjikists
|
||||||
@@ -805,4 +903,20 @@ class TagsController < ApplicationController
|
|||||||
|
|
||||||
render_validation_error fields:
|
render_validation_error fields:
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def external_tag_json tag
|
||||||
|
{ 'id' => tag.id,
|
||||||
|
'name' => "#{ tag.platform }:#{ tag.name }",
|
||||||
|
'category' => 'nico',
|
||||||
|
'deprecated_at' => nil,
|
||||||
|
'created_at' => tag.created_at,
|
||||||
|
'updated_at' => tag.created_at,
|
||||||
|
'post_count' => tag.post_count,
|
||||||
|
'matched_alias' => nil,
|
||||||
|
'aliases' => [],
|
||||||
|
'parents' => [],
|
||||||
|
'has_wiki' => false,
|
||||||
|
'material_id' => nil,
|
||||||
|
'has_deerjikists' => false }
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -200,9 +200,24 @@ RSpec.describe 'Tags API', type: :request do
|
|||||||
end
|
end
|
||||||
|
|
||||||
context 'with mixed legacy pagination' do
|
context 'with mixed legacy pagination' do
|
||||||
let!(:first_tag) { create(:tag, name: 'a_mixed_page', category: :meme) }
|
let!(:first_tag) do
|
||||||
let!(:middle_tag) { create(:tag, name: 'm_mixed_page', category: :meta) }
|
create(:tag,
|
||||||
let!(:last_tag) { create(:tag, name: 'z_mixed_page', category: :general) }
|
tag_name: create(:tag_name, name: 'a_mixed_page'),
|
||||||
|
category: :meme)
|
||||||
|
end
|
||||||
|
|
||||||
|
let!(:middle_tag) do
|
||||||
|
create(:tag,
|
||||||
|
tag_name: create(:tag_name, name: 'm_mixed_page'),
|
||||||
|
category: :meta)
|
||||||
|
end
|
||||||
|
|
||||||
|
let!(:last_tag) do
|
||||||
|
create(:tag,
|
||||||
|
tag_name: create(:tag_name, name: 'z_mixed_page'),
|
||||||
|
category: :general)
|
||||||
|
end
|
||||||
|
|
||||||
let!(:first_external) do
|
let!(:first_external) do
|
||||||
create(:external_tag, id: first_tag.id, name: 'a_mixed_page')
|
create(:external_tag, id: first_tag.id, name: 'a_mixed_page')
|
||||||
end
|
end
|
||||||
|
|||||||
新しいイシューから参照
ユーザーをブロックする