diff --git a/app/controllers/posts_controller.rb b/app/controllers/posts_controller.rb index 7ed34a00e..4c6c1ba39 100644 --- a/app/controllers/posts_controller.rb +++ b/app/controllers/posts_controller.rb @@ -8,9 +8,13 @@ class PostsController < ApplicationController respond_with(@post) do |format| format.html { redirect_to(@post) } end + elsif params[:random].to_s.truthy? + post_set = PostSets::Post.new(params[:tags], params[:page], params[:limit], format: request.format.symbol, view: params[:view]) + query = "#{post_set.normalized_query.to_s} random:#{post_set.per_page}".strip + redirect_to posts_path(tags: query, page: params[:page], limit: params[:limit], format: request.format.symbol, view: params[:view]) else tag_query = params[:tags] || params.dig(:post, :tags) - @post_set = PostSets::Post.new(tag_query, params[:page], params[:limit], random: params[:random], format: params[:format], view: params[:view]) + @post_set = PostSets::Post.new(tag_query, params[:page], params[:limit], format: request.format.symbol, view: params[:view]) @posts = authorize @post_set.posts, policy_class: PostPolicy @post_set.log! respond_with(@posts) do |format| diff --git a/app/logical/post_query_builder.rb b/app/logical/post_query_builder.rb index e9ebf7c44..2e00515ce 100644 --- a/app/logical/post_query_builder.rb +++ b/app/logical/post_query_builder.rb @@ -38,7 +38,7 @@ class PostQueryBuilder ordpool note comment commentary id rating source status filetype disapproved parent child search embedded md5 width height mpixels ratio score upvotes downvotes favcount filesize date age order limit tagcount pixiv_id pixiv - unaliased exif duration + unaliased exif duration random ] + COUNT_METATAGS + COUNT_METATAG_SYNONYMS + CATEGORY_COUNT_METATAGS ORDER_METATAGS = %w[ @@ -218,6 +218,8 @@ class PostQueryBuilder user_subquery_matches(PostVote.active.positive.visible(current_user), value, field: :user) when "downvoter", "downvote" user_subquery_matches(PostVote.active.negative.visible(current_user), value, field: :user) + when "random" + Post.all # handled in the `build` method when *CATEGORY_COUNT_METATAGS short_category = name.delete_suffix("tags") category = TagCategory.short_name_mapping[short_category] @@ -513,6 +515,11 @@ class PostQueryBuilder relation = search_order(relation, find_metatag(:order)) end + if count = find_metatag(:random) + count = Integer(count).clamp(0, PostSets::Post::MAX_PER_PAGE) + relation = relation.random(count) + end + relation end @@ -688,6 +695,9 @@ class PostQueryBuilder when /(#{TagCategory.short_name_list.join("|")})tags_asc/ relation = relation.order("posts.tag_count_#{TagCategory.short_name_mapping[$1]} ASC") + when "random" + relation = relation.order("random()") + when "rank" relation = relation.where("posts.score > 0 and posts.created_at >= ?", 2.days.ago) relation = relation.order(Arel.sql("log(3, posts.score) + (extract(epoch from posts.created_at) - extract(epoch from timestamp '2005-05-24')) / 35000 DESC")) diff --git a/app/logical/post_sets/post.rb b/app/logical/post_sets/post.rb index 34f576e93..a57b8c78b 100644 --- a/app/logical/post_sets/post.rb +++ b/app/logical/post_sets/post.rb @@ -7,16 +7,15 @@ module PostSets MAX_PER_PAGE = 200 MAX_SIDEBAR_TAGS = 25 - attr_reader :page, :random, :format, :tag_string, :query, :normalized_query, :view + attr_reader :page, :format, :tag_string, :query, :normalized_query, :view delegate :post_count, to: :normalized_query - def initialize(tags, page = 1, per_page = nil, user: CurrentUser.user, random: false, format: "html", view: "simple") + def initialize(tags, page = 1, per_page = nil, user: CurrentUser.user, format: "html", view: "simple") @query = PostQueryBuilder.new(tags, user, tag_limit: user.tag_query_limit, safe_mode: CurrentUser.safe_mode?, hide_deleted_posts: user.hide_deleted_posts?) @normalized_query = query.normalized_query @tag_string = tags @page = page @per_page = per_page - @random = random.to_s.truthy? @format = format.to_s @view = view.presence || "simple" end @@ -92,25 +91,11 @@ module PostSets end def max_per_page - (format == "sitemap") ? 10_000 : MAX_PER_PAGE - end - - def is_random? - random || query.find_metatag(:order) == "random" - end - - def get_random_posts - ::Post.user_tag_match(tag_string).random(per_page) + (format.to_sym == :sitemap) ? 10_000 : MAX_PER_PAGE end def posts - @posts ||= begin - if is_random? - get_random_posts.paginate(page, search_count: false, limit: per_page, max_limit: max_per_page).load - else - normalized_query.paginated_posts(page, includes: includes, count: post_count, search_count: !post_count.nil?, limit: per_page, max_limit: max_per_page).load - end - end + @posts ||= normalized_query.paginated_posts(page, includes: includes, count: post_count, search_count: !post_count.nil?, limit: per_page, max_limit: max_per_page).load end def hide_from_crawler? diff --git a/app/views/posts/partials/index/_posts.html.erb b/app/views/posts/partials/index/_posts.html.erb index 5bbf4ecbc..e8956443a 100644 --- a/app/views/posts/partials/index/_posts.html.erb +++ b/app/views/posts/partials/index/_posts.html.erb @@ -36,7 +36,5 @@ <% end %> <% end %> - <% unless post_set.is_random? %> - <%= numbered_paginator(post_set.posts) %> - <% end %> + <%= numbered_paginator(post_set.posts) %> diff --git a/test/functional/posts_controller_test.rb b/test/functional/posts_controller_test.rb index 491274015..107866550 100644 --- a/test/functional/posts_controller_test.rb +++ b/test/functional/posts_controller_test.rb @@ -309,18 +309,21 @@ class PostsControllerTest < ActionDispatch::IntegrationTest get posts_path, params: { tags: "order:random" } assert_response :success - get posts_path, params: { random: "1" } - assert_response :success + get posts_path(random: "1") + assert_redirected_to posts_path(tags: "random:20", format: :html) - get posts_path(format: :json), params: { random: "1" } - assert_response :success + get posts_path(random: "1"), as: :json + assert_redirected_to posts_path(tags: "random:20", format: :json) + + get posts_path(tags: "touhou", random: "true") + assert_redirected_to posts_path(tags: "touhou random:20", format: :html) end should "render with multiple posts" do @posts = create_list(:post, 2) - get posts_path, params: { random: "1" } - assert_response :success + get posts_path(random: "1") + assert_redirected_to posts_path(tags: "random:20", format: :html) end should "return all posts for a .json response" do diff --git a/test/unit/post_query_builder_test.rb b/test/unit/post_query_builder_test.rb index a0706f543..c48821468 100644 --- a/test/unit/post_query_builder_test.rb +++ b/test/unit/post_query_builder_test.rb @@ -1099,6 +1099,14 @@ class PostQueryBuilderTest < ActiveSupport::TestCase assert_tag_match([], "exif:DNE") end + should "return posts for the random: metatag" do + post = create(:post) + + assert_tag_match([], "random:0") + assert_tag_match([post], "random:1") + assert_tag_match([post], "random:1000") + end + should "return posts ordered by a particular attribute" do posts = (1..2).map do |n| tags = ["tagme", "gentag1 gentag2 artist:arttag char:chartag copy:copytag"] @@ -1210,6 +1218,12 @@ class PostQueryBuilderTest < ActiveSupport::TestCase end end + should "return posts for order:random" do + post = create(:post) + + assert_tag_match([post], "order:random") + end + should "return posts for a filesize search" do post = create(:post, file_size: 1.megabyte)