search: make order:random truly random; add random:N metatag.

Make the `order:random` metatag truly randomize the search. Add a
`random:N` metatag that returns up to N random posts, like what
`order:random` did before.

`order:random` now returns the entire search in random order. Before it
just returned a pageful of pseudorandom posts. This will be more
accurate for small searches, but slower for large searches. If
`order:random` times out, try `random:N` instead.

The `random:N` metatag returns up to N pseudorandom posts. This is
faster than `order:random` for large searches, but for small searches,
it may return less than N posts, and the randomness may be biased. Some
posts may be more likely than others to appear. N must be between 0 and
200.

Also, `/posts?tags=touhou&random=1` now redirects to `/posts?tags=touhou+random:N`.
Before the `random=1` param acted like a free `order:random` tag; now it
redirects to a `random:N` search, so it counts against your tag limit.
This commit is contained in:
evazion
2021-11-25 15:58:35 -06:00
parent 5dc67613e6
commit 0baca68a37
6 changed files with 44 additions and 30 deletions

View File

@@ -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"))