From 4a1f0523a4a2142cd7a86fb4789248011dc45508 Mon Sep 17 00:00:00 2001 From: evazion Date: Tue, 11 Dec 2018 18:10:20 -0600 Subject: [PATCH] search: add comment_count, note_count metatags (#4004). Add these metatags: * comment_count * deleted_comment_count * active_comment_count * note_count * deleted_note_count * active_note_count * order:comment_count * order:deleted_comment_count * order:active_comment_count * order:note_count * order:deleted_note_count * order:active_note_count --- app/logical/post_query_builder.rb | 32 +++++++++++++++++++++++++++++++ app/models/post.rb | 27 ++++++++++++++++++++++++++ app/models/tag.rb | 9 ++++++++- test/unit/post_test.rb | 15 +++++++++++++++ 4 files changed, 82 insertions(+), 1 deletion(-) diff --git a/app/logical/post_query_builder.rb b/app/logical/post_query_builder.rb index 8d7bd0b8b..f386432b9 100644 --- a/app/logical/post_query_builder.rb +++ b/app/logical/post_query_builder.rb @@ -85,6 +85,28 @@ class PostQueryBuilder relation end + def table_for_metatag(metatag) + if metatag.in?(Tag::COUNT_METATAGS) + metatag[/(?[a-z]+)_count\z/i, :table] + else + nil + end + end + + def tables_for_query(q) + metatags = q.keys + metatags << q[:order].remove(/_(asc|desc)\z/i) if q[:order].present? + + tables = metatags.map { |metatag| table_for_metatag(metatag.to_s) } + tables.compact.uniq + end + + def add_joins(q, relation) + tables = tables_for_query(q) + relation = relation.with_stats(tables) + relation + end + def hide_deleted_posts?(q) return false if CurrentUser.admin_mode? return false if q[:status].in?(%w[deleted active any all]) @@ -107,6 +129,7 @@ class PostQueryBuilder relation = relation.where("posts.rating = 's'") end + relation = add_joins(q, relation) relation = add_range_relation(q[:post_id], "posts.id", relation) relation = add_range_relation(q[:mpixels], "posts.image_width * posts.image_height / 1000000.0", relation) relation = add_range_relation(q[:ratio], "ROUND(1.0 * posts.image_width / GREATEST(1, posts.image_height), 2)", relation) @@ -122,6 +145,10 @@ class PostQueryBuilder end relation = add_range_relation(q[:post_tag_count], "posts.tag_count", relation) + Tag::COUNT_METATAGS.each do |column| + relation = add_range_relation(q[column.to_sym], "posts.#{column}", relation) + end + if q[:md5] relation = relation.where("posts.md5": q[:md5]) end @@ -544,6 +571,11 @@ class PostQueryBuilder when "filesize_asc" relation = relation.order("posts.file_size ASC") + when /\A(?#{Tag::COUNT_METATAGS.join("|")})(_(?asc|desc))?\z/i + column = $~[:column] + direction = $~[:direction] || "desc" + relation = relation.order(column => direction, :id => direction) + when "tagcount", "tagcount_desc" relation = relation.order("posts.tag_count DESC") diff --git a/app/models/post.rb b/app/models/post.rb index cd206825a..c928ac07f 100644 --- a/app/models/post.rb +++ b/app/models/post.rb @@ -1609,6 +1609,33 @@ class Post < ApplicationRecord joins("CROSS JOIN unnest(string_to_array(tag_string, ' ')) AS tag") end + def with_comment_stats + relation = left_outer_joins(:comments).group(:id).select("posts.*") + relation = relation.select("COUNT(comments.id) AS comment_count") + relation = relation.select("COUNT(comments.id) FILTER (WHERE comments.is_deleted = TRUE) AS deleted_comment_count") + relation = relation.select("COUNT(comments.id) FILTER (WHERE comments.is_deleted = FALSE) AS active_comment_count") + relation + end + + def with_note_stats + relation = left_outer_joins(:notes).group(:id).select("posts.*") + relation = relation.select("COUNT(notes.id) AS note_count") + relation = relation.select("COUNT(notes.id) FILTER (WHERE notes.is_active = TRUE) AS active_note_count") + relation = relation.select("COUNT(notes.id) FILTER (WHERE notes.is_active = FALSE) AS deleted_note_count") + relation + end + + def with_stats(tables) + return all if tables.empty? + + relation = all + tables.each do |table| + relation = relation.send("with_#{table}_stats") + end + + from(relation.arel.as("posts")) + end + def pending where(is_pending: true) end diff --git a/app/models/tag.rb b/app/models/tag.rb index 26519fff8..1a2176d33 100644 --- a/app/models/tag.rb +++ b/app/models/tag.rb @@ -1,5 +1,9 @@ class Tag < ApplicationRecord COSINE_SIMILARITY_RELATED_TAG_THRESHOLD = 300 + COUNT_METATAGS = %w[ + comment_count deleted_comment_count active_comment_count + note_count deleted_note_count active_note_count + ] METATAGS = %w[ -user user -approver approver commenter comm noter noteupdater artcomm -pool pool ordpool -favgroup favgroup -fav fav ordfav md5 -rating rating @@ -7,7 +11,7 @@ class Tag < ApplicationRecord -source id -id date age order limit -status status tagcount parent -parent child pixiv_id pixiv search upvote downvote filetype -filetype flagger -flagger appealer -appealer disapproval -disapproval - ] + TagCategory.short_name_list.map {|x| "#{x}tags"} + ] + TagCategory.short_name_list.map {|x| "#{x}tags"} + COUNT_METATAGS SUBQUERY_METATAGS = %w[commenter comm noter noteupdater artcomm flagger -flagger appealer -appealer] @@ -790,6 +794,9 @@ class Tag < ApplicationRecord q[:downvote] = User.name_to_id(g2) end + when *COUNT_METATAGS + q[g1.to_sym] = parse_helper(g2) + end else diff --git a/test/unit/post_test.rb b/test/unit/post_test.rb index 2af27e446..1e67ecf80 100644 --- a/test/unit/post_test.rb +++ b/test/unit/post_test.rb @@ -2103,6 +2103,16 @@ class PostTest < ActiveSupport::TestCase assert_tag_match([posts[1]], "noter:none") end + should "return posts for the note_count: metatag" do + posts = FactoryBot.create_list(:post, 3) + FactoryBot.create(:note, post: posts[0], is_active: true) + FactoryBot.create(:note, post: posts[1], is_active: false) + + assert_tag_match([posts[1], posts[0]], "note_count:1") + assert_tag_match([posts[0]], "active_note_count:1") + assert_tag_match([posts[1]], "deleted_note_count:1") + end + should "return posts for the artcomm: metatag" do users = FactoryBot.create_list(:user, 2) posts = FactoryBot.create_list(:post, 2) @@ -2387,6 +2397,8 @@ class PostTest < ActiveSupport::TestCase p end + FactoryBot.create(:note, post: posts.second) + assert_tag_match(posts.reverse, "order:id_desc") assert_tag_match(posts.reverse, "order:score") assert_tag_match(posts.reverse, "order:favcount") @@ -2404,6 +2416,8 @@ class PostTest < ActiveSupport::TestCase assert_tag_match(posts.reverse, "order:chartags") assert_tag_match(posts.reverse, "order:copytags") assert_tag_match(posts.reverse, "order:rank") + assert_tag_match(posts.reverse, "order:note_count") + assert_tag_match(posts.reverse, "order:note_count_desc") assert_tag_match(posts, "order:id_asc") assert_tag_match(posts, "order:score_asc") @@ -2421,6 +2435,7 @@ class PostTest < ActiveSupport::TestCase assert_tag_match(posts, "order:arttags_asc") assert_tag_match(posts, "order:chartags_asc") assert_tag_match(posts, "order:copytags_asc") + assert_tag_match(posts, "order:note_count_asc") end should "return posts for order:comment_bumped" do