Compare commits

..

6 commits

Author SHA1 Message Date
860b8eef72 Remove not-very-useful caching for homepage modeling
Huh, I hadn't realized that like, we'd already set up the controller to
always *run* basically all of the modeling logic, and the caching in
the view layer wasn't saving us any queries anymore. Kinda silly!

Remove the caching call, just to simplify the codebase (I like to avoid
caching things that don't specifically need it!).

And hey, love that the modeling code in the controller is now *way*
faster to run! You love to see it!
2024-10-02 18:26:49 -07:00
61e22e3943 Oops, remove no-longer-true comment about a code block I just deleted! 2024-10-02 18:20:22 -07:00
03e4233f67 Use cached compatible body IDs on homepage modeling code
This should make it load way faster! Maybe don't even need to mess with
caching the resulting HTML anymore, like we currently do?
2024-10-02 17:55:20 -07:00
b6bddb14be Oops, fix new bug in homepage modeling code
Missed a spot on `Item#basic_body_ids`!
2024-10-02 17:54:14 -07:00
e52838ba70 Use Rails serialize method to save/load cached fields in Item
Just packing some serialization complexity away into its own thing, so
the model code doesn't need to sweat it!
2024-10-02 17:50:42 -07:00
7ba68c52d4 Simplify homepage modeling code a bit
I have some other changes planned too, but these are some easy ones. I
also turn back on this stuff in development, in hopes that my changes
can make these queries fast enough to not be a big deal anymore!
2024-10-02 17:26:32 -07:00
7 changed files with 73 additions and 71 deletions

View file

@ -50,10 +50,9 @@ class OutfitsController < ApplicationController
@colors = Color.alphabetical @colors = Color.alphabetical
@species = Species.alphabetical @species = Species.alphabetical
# HACK: Skip this in development, because it's slow!
unless Rails.env.development?
newest_items = Item.newest. newest_items = Item.newest.
select(:id, :name, :updated_at, :thumbnail_url, :rarity_index, :is_manually_nc) select(:id, :name, :updated_at, :thumbnail_url, :rarity_index,
:is_manually_nc, :cached_compatible_body_ids)
.limit(18) .limit(18)
@newest_modeled_items, @newest_unmodeled_items = @newest_modeled_items, @newest_unmodeled_items =
newest_items.partition(&:predicted_fully_modeled?) newest_items.partition(&:predicted_fully_modeled?)
@ -70,7 +69,6 @@ class OutfitsController < ApplicationController
@newest_unmodeled_items_predicted_missing_species_by_color[item] = h @newest_unmodeled_items_predicted_missing_species_by_color[item] = h
@newest_unmodeled_items_predicted_modeled_ratio[item] = item.predicted_modeled_ratio @newest_unmodeled_items_predicted_modeled_ratio[item] = item.predicted_modeled_ratio
end end
end
@species_count = Species.count @species_count = Species.count

View file

@ -10,6 +10,9 @@ class Item < ApplicationRecord
SwfAssetType = 'object' SwfAssetType = 'object'
serialize :cached_compatible_body_ids, coder: Serializers::IntegerSet
serialize :cached_occupied_zone_ids, coder: Serializers::IntegerSet
has_many :closet_hangers has_many :closet_hangers
has_one :contribution, as: :contributed, inverse_of: :contributed has_one :contribution, as: :contributed, inverse_of: :contributed
has_one :nc_mall_record has_one :nc_mall_record
@ -258,8 +261,8 @@ class Item < ApplicationRecord
end end
def update_cached_fields def update_cached_fields
self.cached_occupied_zone_ids = occupied_zone_ids.sort.join(",") self.cached_occupied_zone_ids = occupied_zone_ids
self.cached_compatible_body_ids = compatible_body_ids.sort.join(",") self.cached_compatible_body_ids = compatible_body_ids(use_cached: false)
self.save! self.save!
end end
@ -273,58 +276,35 @@ class Item < ApplicationRecord
write_attribute('species_support_ids', replacement) write_attribute('species_support_ids', replacement)
end end
def support_species?(species)
species_support_ids.blank? || species_support_ids.include?(species.id)
end
def modeled_body_ids
@modeled_body_ids ||= swf_assets.select('DISTINCT body_id').map(&:body_id)
end
def modeled_color_ids
# Might be empty if modeled_body_ids is 0. But it's currently not called
# in that scenario, so, whatever.
@modeled_color_ids ||= PetType.select('DISTINCT color_id').
where(body_id: modeled_body_ids).
map(&:color_id)
end
def basic_body_ids
@basic_body_ids ||= begin
basic_color_ids ||= Color.select([:id]).basic.map(&:id)
PetType.select('DISTINCT body_id').
where(color_id: basic_color_ids).map(&:body_id)
end
end
def predicted_body_ids def predicted_body_ids
@predicted_body_ids ||= if modeled_body_ids.include?(0) @predicted_body_ids ||= if compatible_body_ids.include?(0)
# Oh, look, it's already known to fit everybody! Sweet. We're done. (This # Oh, look, it's already known to fit everybody! Sweet. We're done. (This
# isn't folded into the case below, in case this item somehow got a # isn't folded into the case below, in case this item somehow got a
# body-specific and non-body-specific asset. In all the cases I've seen # body-specific and non-body-specific asset. In all the cases I've seen
# it, that indicates a glitched item, but this method chooses to reflect # it, that indicates a glitched item, but this method chooses to reflect
# behavior elsewhere in the app by saying that we can put this item on # behavior elsewhere in the app by saying that we can put this item on
# anybody. (Heh. Any body.)) # anybody. (Heh. Any body.))
modeled_body_ids compatible_body_ids
elsif modeled_body_ids.size == 1 elsif compatible_body_ids.size == 1
# This might just be a species-specific item. Let's be conservative in # This might just be a species-specific item. Let's be conservative in
# our prediction, though we'll revise it if we see another body ID. # our prediction, though we'll revise it if we see another body ID.
modeled_body_ids compatible_body_ids
else else
# If an item is worn by more than one body, then it must be wearable by # If an item is worn by more than one body, then it must be wearable by
# all bodies of the same color. (To my knowledge, anyway. I'm not aware # all bodies of the same color. (To my knowledge, anyway. I'm not aware
# of any exceptions.) So, let's find those bodies by first finding those # of any exceptions.) So, let's find those bodies by first finding those
# colors. # colors.
basic_modeled_body_ids, nonbasic_modeled_body_ids = modeled_body_ids. basic_body_ids = PetType.basic_body_ids
partition { |bi| basic_body_ids.include?(bi) } basic_compatible_body_ids, nonbasic_compatible_body_ids =
compatible_body_ids.partition { |bi| basic_body_ids.include?(bi) }
output = [] output = []
if basic_modeled_body_ids.present? if basic_compatible_body_ids.present?
output += basic_body_ids output += basic_body_ids
end end
if nonbasic_modeled_body_ids.present? if nonbasic_compatible_body_ids.present?
nonbasic_modeled_color_ids = PetType.select('DISTINCT color_id'). nonbasic_modeled_color_ids = PetType.select('DISTINCT color_id').
where(body_id: nonbasic_modeled_body_ids). where(body_id: nonbasic_compatible_body_ids).
map(&:color_id) map(&:color_id)
output += PetType.select('DISTINCT body_id'). output += PetType.select('DISTINCT body_id').
where(color_id: nonbasic_modeled_color_ids). where(color_id: nonbasic_modeled_color_ids).
@ -335,7 +315,7 @@ class Item < ApplicationRecord
end end
def predicted_missing_body_ids def predicted_missing_body_ids
@predicted_missing_body_ids ||= predicted_body_ids - modeled_body_ids @predicted_missing_body_ids ||= predicted_body_ids - compatible_body_ids
end end
def predicted_missing_standard_body_ids_by_species_id def predicted_missing_standard_body_ids_by_species_id
@ -355,9 +335,8 @@ class Item < ApplicationRecord
end end
def predicted_missing_nonstandard_body_pet_types def predicted_missing_nonstandard_body_pet_types
PetType.joins(:color). body_ids = predicted_missing_body_ids - PetType.basic_body_ids
where(body_id: predicted_missing_body_ids - basic_body_ids, PetType.joins(:color).where(body_id: body_ids, colors: {standard: false})
colors: {standard: false})
end end
def predicted_missing_nonstandard_body_ids_by_species_by_color def predicted_missing_nonstandard_body_ids_by_species_by_color
@ -387,7 +366,7 @@ class Item < ApplicationRecord
end end
def predicted_modeled_ratio def predicted_modeled_ratio
modeled_body_ids.size.to_f / predicted_body_ids.size compatible_body_ids.size.to_f / predicted_body_ids.size
end end
def as_json(options={}) def as_json(options={})
@ -397,7 +376,9 @@ class Item < ApplicationRecord
}.merge(options)) }.merge(options))
end end
def compatible_body_ids def compatible_body_ids(use_cached: true)
return cached_compatible_body_ids if use_cached
swf_assets.map(&:body_id).uniq swf_assets.map(&:body_id).uniq
end end

View file

@ -116,6 +116,10 @@ class PetType < ApplicationRecord
"#{color.human_name}-#{species.human_name}" "#{color.human_name}-#{species.human_name}"
end end
def self.basic_body_ids
PetType.basic.distinct.pluck(:body_id)
end
def self.all_by_ids_or_children(ids, pet_states) def self.all_by_ids_or_children(ids, pet_states)
pet_states_by_pet_type_id = {} pet_states_by_pet_type_id = {}
pet_states.each do |pet_state| pet_states.each do |pet_state|

View file

@ -90,7 +90,6 @@
%h3= t '.newest_items.unmodeled.header' %h3= t '.newest_items.unmodeled.header'
%ul#newest-unmodeled-items %ul#newest-unmodeled-items
- @newest_unmodeled_items.each do |item| - @newest_unmodeled_items.each do |item|
- cache "items/#{item.id} modeling_progress locale=#{I18n.locale} updated_at=#{item.updated_at.to_i}" do
%li{'data-item-id' => item.id} %li{'data-item-id' => item.id}
= link_to image_tag(item.thumbnail_url), item, :class => 'image-link' = link_to image_tag(item.thumbnail_url), item, :class => 'image-link'
= link_to item, :class => 'header' do = link_to item, :class => 'header' do

View file

@ -0,0 +1,9 @@
class AllowNullInItemsCachedFields < ActiveRecord::Migration[7.2]
def change
# This is a bit more compatible with ActiveRecord's `serialize` utility,
# which seems pretty insistent that empty arrays should be saved as `NULL`,
# rather than the empty string our serializer would return if called :(
change_column_null :items, :cached_compatible_body_ids, true
change_column_null :items, :cached_occupied_zone_ids, true
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.2].define(version: 2024_10_01_052510) do ActiveRecord::Schema[7.2].define(version: 2024_10_03_004726) 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
@ -137,8 +137,8 @@ ActiveRecord::Schema[7.2].define(version: 2024_10_01_052510) do
t.text "description", size: :medium, null: false t.text "description", size: :medium, null: false
t.string "rarity", default: "", null: false t.string "rarity", default: "", null: false
t.integer "dyeworks_base_item_id" t.integer "dyeworks_base_item_id"
t.string "cached_occupied_zone_ids", default: "", null: false t.string "cached_occupied_zone_ids", default: ""
t.text "cached_compatible_body_ids", default: "", null: false t.text "cached_compatible_body_ids", default: ""
t.index ["dyeworks_base_item_id"], name: "index_items_on_dyeworks_base_item_id" 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", "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" t.index ["modeling_status_hint", "created_at"], name: "items_modeling_status_hint_and_created_at"

View file

@ -0,0 +1,11 @@
module Serializers
module IntegerSet
def self.dump(array)
array.sort.join(",")
end
def self.load(string)
(string || "").split(",").map(&:to_i)
end
end
end