Compare commits

...

2 commits

Author SHA1 Message Date
193b1fa5e3 Improve performance for occupies:X searches
I noticed in the app that these queries were slowwww! I was able to
track it down to a bad query plan, as we explain in the comment.

I searched online for "mysql query performance filter on one join table
sort by another", and was surprised to find this answer suggest a
subquery, which I've often been told to expect to be slower compared to
joins? But it certainly worked in this case!

https://stackoverflow.com/questions/35679693/mysql-optimize-join-with-filter-and-order-on-different-tables
2024-06-03 11:45:51 -07:00
aa3dc9549f Add index to items.name database field
I am. Kinda surprised we didn't have this already, huh?? I guess in
most searches, the difference isn't very noticeable, because we don't
have a lot of item names to sort anyway? But we do this *so often* that
I think an index will certainly help! Let's add it!
2024-06-03 11:44:06 -07:00
3 changed files with 23 additions and 2 deletions

View file

@ -60,8 +60,23 @@ class Item < ApplicationRecord
} }
scope :occupies, ->(zone_label) { scope :occupies, ->(zone_label) {
zone_ids = Zone.matching_label(zone_label).map(&:id) zone_ids = Zone.matching_label(zone_label).map(&:id)
# NOTE: In searches, this query performs much better using a subquery
# instead of joins! This is because, in the joins case, filtering by an
# `swf_assets` field but sorting by an `items` field causes the query
# planner to only be able to use an index for *one* of them. In this case,
# MySQL can use the `swf_assets`.`zone_id` index to get the item IDs for
# the subquery, then use the `items`.`name` index to sort them.
i = arel_table
psa = ParentSwfAssetRelationship.arel_table
sa = SwfAsset.arel_table sa = SwfAsset.arel_table
joins(:swf_assets).where(sa[:zone_id].in(zone_ids)).distinct where(
ParentSwfAssetRelationship.joins(:swf_asset).
where(sa[:zone_id].in(zone_ids)).
where(psa[:parent_type].eq("Item")).
where(psa[:parent_id].eq(i[:id])).
arel.exists
)
} }
scope :not_occupies, ->(zone_label) { scope :not_occupies, ->(zone_label) {
zone_ids = Zone.matching_label(zone_label).map(&:id) zone_ids = Zone.matching_label(zone_label).map(&:id)

View file

@ -0,0 +1,5 @@
class AddIndexOnNameToItems < ActiveRecord::Migration[7.1]
def change
add_index :items, :name
end
end

View file

@ -10,7 +10,7 @@
# #
# It's strongly recommended that you check this file into your version control system. # It's strongly recommended that you check this file into your version control system.
ActiveRecord::Schema[7.1].define(version: 2024_05_22_233638) do ActiveRecord::Schema[7.1].define(version: 2024_06_03_181855) do
create_table "alt_styles", charset: "utf8mb4", collation: "utf8mb4_unicode_520_ci", force: :cascade do |t| create_table "alt_styles", charset: "utf8mb4", collation: "utf8mb4_unicode_520_ci", force: :cascade do |t|
t.integer "species_id", null: false t.integer "species_id", null: false
t.integer "color_id", null: false t.integer "color_id", null: false
@ -140,6 +140,7 @@ ActiveRecord::Schema[7.1].define(version: 2024_05_22_233638) do
t.index ["modeling_status_hint", "created_at"], name: "items_modeling_status_hint_and_created_at" t.index ["modeling_status_hint", "created_at"], name: "items_modeling_status_hint_and_created_at"
t.index ["modeling_status_hint", "id"], name: "items_modeling_status_hint_and_id" t.index ["modeling_status_hint", "id"], name: "items_modeling_status_hint_and_id"
t.index ["modeling_status_hint"], name: "items_modeling_status_hint" t.index ["modeling_status_hint"], name: "items_modeling_status_hint"
t.index ["name"], name: "index_items_on_name"
end end
create_table "login_cookies", id: :integer, charset: "utf8mb4", collation: "utf8mb4_unicode_520_ci", force: :cascade do |t| create_table "login_cookies", id: :integer, charset: "utf8mb4", collation: "utf8mb4_unicode_520_ci", force: :cascade do |t|