From efda6d74ab6b9405cd563ccc72bb45b4f10eabe3 Mon Sep 17 00:00:00 2001 From: Emi Matchu Date: Mon, 30 Sep 2024 23:10:37 -0700 Subject: [PATCH] Add cached fields to Item model for searching, but don't use them yet MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This is the first part of a change to improve search performance, by caching occupied zone IDs and supported body IDs onto the Item record itself, instead of always doing joins with `SwfAsset`. It's unfortunate, because part of the power of SQL is joins! But doing joins with big tables, in ways that can't take advantage of indexes in the same ways as we often want to, is… slow. It's possible there's something I'm misunderstanding about SQL optimization, and this _could_ be done with query optimization or indexes instead of duplicating data like this? This complexity carries the risk of data getting out of sync in unforeseen ways. But this is what I know how to do, and it seems to be working, so! Okay! --- app/models/item.rb | 8 +++++++- app/models/parent_swf_asset_relationship.rb | 7 +++++++ .../20241001052510_add_cached_fields_to_items.rb | 16 ++++++++++++++++ db/schema.rb | 6 ++++-- 4 files changed, 34 insertions(+), 3 deletions(-) create mode 100644 db/migrate/20241001052510_add_cached_fields_to_items.rb diff --git a/app/models/item.rb b/app/models/item.rb index 6dc6e85b..9e55a061 100644 --- a/app/models/item.rb +++ b/app/models/item.rb @@ -296,6 +296,12 @@ class Item < ApplicationRecord restricted_zones + occupied_zones end + def update_cached_fields + self.cached_occupied_zone_ids = occupied_zone_ids.sort.join(",") + self.cached_compatible_body_ids = compatible_body_ids.sort.join(",") + self.save! + end + def species_support_ids @species_support_ids_array ||= read_attribute('species_support_ids').split(',').map(&:to_i) rescue nil end @@ -305,7 +311,7 @@ class Item < ApplicationRecord replacement = replacement.join(',') if replacement.is_a?(Array) write_attribute('species_support_ids', replacement) end - + def support_species?(species) species_support_ids.blank? || species_support_ids.include?(species.id) end diff --git a/app/models/parent_swf_asset_relationship.rb b/app/models/parent_swf_asset_relationship.rb index acf93baf..6f24bfad 100644 --- a/app/models/parent_swf_asset_relationship.rb +++ b/app/models/parent_swf_asset_relationship.rb @@ -4,6 +4,9 @@ class ParentSwfAssetRelationship < ApplicationRecord belongs_to :parent, :polymorphic => true belongs_to :swf_asset + + after_save :update_parent_cached_fields + after_destroy :update_parent_cached_fields def item=(replacement) self.parent = replacement @@ -16,4 +19,8 @@ class ParentSwfAssetRelationship < ApplicationRecord def pet_state=(replacement) self.parent = replacement end + + def update_parent_cached_fields + parent.try(:update_cached_fields) + end end diff --git a/db/migrate/20241001052510_add_cached_fields_to_items.rb b/db/migrate/20241001052510_add_cached_fields_to_items.rb new file mode 100644 index 00000000..5a73042a --- /dev/null +++ b/db/migrate/20241001052510_add_cached_fields_to_items.rb @@ -0,0 +1,16 @@ +class AddCachedFieldsToItems < ActiveRecord::Migration[7.2] + def change + add_column :items, :cached_occupied_zone_ids, :string, null: false, default: "" + add_column :items, :cached_compatible_body_ids, :text, null: false, default: "" + + reversible do |direction| + direction.up do + puts "Updating cached item fields for all items…" + Item.includes(:swf_assets).find_in_batches.with_index do |items, batch| + puts "Updating item batch ##{batch+1}…" + items.each(&:update_cached_fields) + end + end + end + end +end diff --git a/db/schema.rb b/db/schema.rb index f71890c4..3f097e7a 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -10,7 +10,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema[7.2].define(version: 2024_09_28_022359) do +ActiveRecord::Schema[7.2].define(version: 2024_10_01_052510) do create_table "alt_styles", charset: "utf8mb4", collation: "utf8mb4_unicode_520_ci", force: :cascade do |t| t.integer "species_id", null: false t.integer "color_id", null: false @@ -137,6 +137,8 @@ ActiveRecord::Schema[7.2].define(version: 2024_09_28_022359) do t.text "description", size: :medium, null: false t.string "rarity", default: "", null: false t.integer "dyeworks_base_item_id" + t.string "cached_occupied_zone_ids", default: "", null: false + t.text "cached_compatible_body_ids", default: "", null: false t.index ["dyeworks_base_item_id"], name: "index_items_on_dyeworks_base_item_id" t.index ["modeling_status_hint", "created_at", "id"], name: "items_modeling_status_hint_and_created_at_and_id" t.index ["modeling_status_hint", "created_at"], name: "items_modeling_status_hint_and_created_at" @@ -154,7 +156,7 @@ ActiveRecord::Schema[7.2].define(version: 2024_09_28_022359) do end create_table "modeling_logs", id: :integer, charset: "utf8mb4", collation: "utf8mb4_unicode_520_ci", force: :cascade do |t| - t.datetime "created_at", precision: nil, default: -> { "CURRENT_TIMESTAMP" }, null: false + t.datetime "created_at", precision: nil, default: -> { "current_timestamp()" }, null: false t.text "log_json", size: :long, null: false t.string "pet_name", limit: 128, null: false end