From e849d8f1c24d8cc5c35536dee3d9e4f8b3cd4afa Mon Sep 17 00:00:00 2001 From: evazion Date: Wed, 2 Nov 2022 02:03:14 -0500 Subject: [PATCH] posts: optimize filetype: searches. When searching posts by width, height, file size, or file extension, use the values from the media_assets table rather than the posts table. This makes filetype: searches faster because the file_ext is indexed on the media assets table, but not on the posts table. This paves the way for getting rid of the width, height, file_size, and file_ext indexes on the posts table in the future. It's wasteful to index these columns on both the posts table and the media assets table. --- app/logical/post_query_builder.rb | 31 ++++++++++++---------------- app/models/post.rb | 2 +- test/unit/post_query_builder_test.rb | 23 +++++++++++---------- 3 files changed, 26 insertions(+), 30 deletions(-) diff --git a/app/logical/post_query_builder.rb b/app/logical/post_query_builder.rb index 9baaacd13..c66d40cd6 100644 --- a/app/logical/post_query_builder.rb +++ b/app/logical/post_query_builder.rb @@ -90,13 +90,13 @@ class PostQueryBuilder when "md5" relation.attribute_matches(value, :md5, :md5) when "width" - relation.attribute_matches(value, :image_width) + relation.attribute_matches(value, "media_assets.image_width").joins(:media_asset) when "height" - relation.attribute_matches(value, :image_height) + relation.attribute_matches(value, "media_assets.image_height").joins(:media_asset) when "mpixels" - relation.attribute_matches(value, "posts.image_width * posts.image_height / 1000000.0", :float) + relation.attribute_matches(value, "(media_assets.image_width * media_assets.image_height) / 1000000.0", :float).joins(:media_asset) when "ratio" - relation.attribute_matches(value, "ROUND(1.0 * posts.image_width / GREATEST(1, posts.image_height), 2)", :ratio) + relation.attribute_matches(value, "ROUND(media_assets.image_width::numeric / media_assets.image_height::numeric, 2)", :ratio).joins(:media_asset) when "score" relation.attribute_matches(value, :score) when "upvotes" @@ -106,9 +106,9 @@ class PostQueryBuilder when "favcount" relation.attribute_matches(value, :fav_count) when "filesize" - relation.attribute_matches(value, :file_size, :filesize) + relation.attribute_matches(value, "media_assets.file_size", :filesize).joins(:media_asset) when "filetype" - relation.attribute_matches(value, :file_ext, :enum) + relation.attribute_matches(value, "media_assets.file_ext", :enum).joins(:media_asset) when "date" relation.attribute_matches(value, :created_at, :date) when "age" @@ -414,28 +414,23 @@ class PostQueryBuilder relation = relation.reorder("artist_commentaries.updated_at ASC") when "mpixels", "mpixels_desc" - relation = relation.where(Arel.sql("posts.image_width is not null and posts.image_height is not null")) - # Use "w*h/1000000", even though "w*h" would give the same result, so this can use - # the posts_mpixels index. - relation = relation.reorder(Arel.sql("posts.image_width * posts.image_height / 1000000.0 DESC")) + # Use "w*h/1000000", even though "w*h" would give the same result, so this can use the posts_mpixels index. + relation = relation.joins(:media_asset).reorder(Arel.sql("media_assets.image_width * media_assets.image_height / 1000000.0 DESC")) when "mpixels_asc" - relation = relation.where("posts.image_width is not null and posts.image_height is not null") - relation = relation.reorder(Arel.sql("posts.image_width * posts.image_height / 1000000.0 ASC")) + relation = relation.joins(:media_asset).reorder(Arel.sql("media_assets.image_width * media_assets.image_height / 1000000.0 ASC")) when "portrait" - relation = relation.where("posts.image_width IS NOT NULL and posts.image_height IS NOT NULL") - relation = relation.reorder(Arel.sql("1.0 * posts.image_width / GREATEST(1, posts.image_height) ASC")) + relation = relation.joins(:media_asset).reorder(Arel.sql("media_assets.image_width::numeric / media_assets.image_height::numeric ASC")) when "landscape" - relation = relation.where("posts.image_width IS NOT NULL and posts.image_height IS NOT NULL") - relation = relation.reorder(Arel.sql("1.0 * posts.image_width / GREATEST(1, posts.image_height) DESC")) + relation = relation.joins(:media_asset).reorder(Arel.sql("media_assets.image_width::numeric / media_assets.image_height::numeric DESC")) when "filesize", "filesize_desc" - relation = relation.reorder("posts.file_size DESC") + relation = relation.joins(:media_asset).reorder("media_assets.file_size DESC") when "filesize_asc" - relation = relation.reorder("posts.file_size ASC") + relation = relation.joins(:media_asset).reorder("media_assets.file_size ASC") when /\A(?#{COUNT_METATAGS.join("|")})(_(?asc|desc))?\z/i column = $~[:column] diff --git a/app/models/post.rb b/app/models/post.rb index 76b25a9a0..65d218ffc 100644 --- a/app/models/post.rb +++ b/app/models/post.rb @@ -1075,7 +1075,7 @@ class Post < ApplicationRecord when *AutocompleteService::POST_STATUSES status_matches(value, current_user) when *MediaAsset::FILE_TYPES - attribute_matches(value, :file_ext, :enum) + attribute_matches(value, "media_assets.file_ext", :enum).joins(:media_asset) when *Post::RATINGS.values.map(&:downcase) rating_matches(value) when *Post::RATING_ALIASES.keys diff --git a/test/unit/post_query_builder_test.rb b/test/unit/post_query_builder_test.rb index fb8b4072f..08c6be0cd 100644 --- a/test/unit/post_query_builder_test.rb +++ b/test/unit/post_query_builder_test.rb @@ -823,13 +823,13 @@ class PostQueryBuilderTest < ActiveSupport::TestCase end should "return posts for the is: metatag" do - jpg = create(:post, file_ext: "jpg") - png = create(:post, file_ext: "png") - gif = create(:post, file_ext: "gif") - mp4 = create(:post, file_ext: "mp4") - webm = create(:post, file_ext: "webm") - swf = create(:post, file_ext: "swf") - zip = create(:post, file_ext: "zip") + jpg = create(:post, file_ext: "jpg", media_asset: build(:media_asset, file_ext: "jpg")) + png = create(:post, file_ext: "png", media_asset: build(:media_asset, file_ext: "png")) + gif = create(:post, file_ext: "gif", media_asset: build(:media_asset, file_ext: "gif")) + mp4 = create(:post, file_ext: "mp4", media_asset: build(:media_asset, file_ext: "mp4")) + webm = create(:post, file_ext: "webm", media_asset: build(:media_asset, file_ext: "webm")) + swf = create(:post, file_ext: "swf", media_asset: build(:media_asset, file_ext: "swf")) + zip = create(:post, file_ext: "zip", media_asset: build(:media_asset, file_ext: "zip")) assert_tag_match([jpg], "is:jpg") assert_tag_match([png], "is:png") @@ -937,8 +937,8 @@ class PostQueryBuilderTest < ActiveSupport::TestCase end should "return posts for the filetype: metatag" do - png = create(:post, file_ext: "png") - jpg = create(:post, file_ext: "jpg") + png = create(:post, file_ext: "png", media_asset: build(:media_asset, file_ext: "png")) + jpg = create(:post, file_ext: "jpg", media_asset: build(:media_asset, file_ext: "jpg")) assert_tag_match([png], "filetype:png") assert_tag_match([jpg], "-filetype:png") @@ -1275,7 +1275,8 @@ class PostQueryBuilderTest < ActiveSupport::TestCase # posts[0] is portrait, posts[1] is landscape. posts[1].mpixels > posts[0].mpixels. image_height: 100 * n * n, image_width: 100 * (3 - n) * n, - tag_string: tags[n - 1] + tag_string: tags[n - 1], + media_asset: build(:media_asset, image_height: 100 * n * n, image_width: 100 * (3 - n) * n, file_size: 1.megabyte * n) ) u = create(:user, created_at: 2.weeks.ago) @@ -1378,7 +1379,7 @@ class PostQueryBuilderTest < ActiveSupport::TestCase end should "return posts for a filesize search" do - post = create(:post, file_size: 1.megabyte) + post = create(:post, file_size: 1.megabyte, media_asset: build(:media_asset, file_size: 1.megabyte)) assert_tag_match([post], "filesize:1mb") assert_tag_match([post], "filesize:1000kb")