Add Item.is{_not}_modeled
scopes, for use in search later
We're now caching `predicted_fully_modeled?` on the database record, so we can query by it in the database! I'm moving on from the model I did in Impress 2020, of writing really big fancy single-source-of-truth queries based on the assets themselves. I see the merit of that in terms of theoretical reliability, but in practice I think it will be *more* reliable to have one *in-code* definition of modeling status (which we need anyway for generating the homepage modeling requests), and just save that in a queryable way.
This commit is contained in:
parent
39bed6b157
commit
f6f618c9d5
5 changed files with 45 additions and 13 deletions
|
@ -52,7 +52,8 @@ class OutfitsController < ApplicationController
|
||||||
|
|
||||||
newest_items = Item.newest.
|
newest_items = Item.newest.
|
||||||
select(:id, :name, :updated_at, :thumbnail_url, :rarity_index,
|
select(:id, :name, :updated_at, :thumbnail_url, :rarity_index,
|
||||||
:is_manually_nc, :cached_compatible_body_ids)
|
:is_manually_nc, :cached_compatible_body_ids,
|
||||||
|
:cached_predicted_fully_modeled)
|
||||||
.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?)
|
||||||
|
|
|
@ -65,6 +65,12 @@ class Item < ApplicationRecord
|
||||||
where('description NOT LIKE ?',
|
where('description NOT LIKE ?',
|
||||||
'%' + sanitize_sql_like(PAINTBRUSH_SET_DESCRIPTION) + '%')
|
'%' + sanitize_sql_like(PAINTBRUSH_SET_DESCRIPTION) + '%')
|
||||||
}
|
}
|
||||||
|
scope :is_modeled, -> {
|
||||||
|
where(cached_predicted_fully_modeled: true)
|
||||||
|
}
|
||||||
|
scope :is_not_modeled, -> {
|
||||||
|
where(cached_predicted_fully_modeled: false)
|
||||||
|
}
|
||||||
scope :occupies, ->(zone_label) {
|
scope :occupies, ->(zone_label) {
|
||||||
Zone.matching_label(zone_label).
|
Zone.matching_label(zone_label).
|
||||||
map { |z| occupies_zone_id(z.id) }.reduce(none, &:or)
|
map { |z| occupies_zone_id(z.id) }.reduce(none, &:or)
|
||||||
|
@ -263,12 +269,19 @@ class Item < ApplicationRecord
|
||||||
end
|
end
|
||||||
|
|
||||||
def update_cached_fields
|
def update_cached_fields
|
||||||
# Reload our associations, so they include any new records.
|
# First, clear out some cached instance variables we use for performance,
|
||||||
|
# to ensure we recompute the latest values.
|
||||||
|
@predicted_body_ids = nil
|
||||||
|
@predicted_missing_body_ids = nil
|
||||||
|
|
||||||
|
# We also need to reload our associations, so they include any new records.
|
||||||
swf_assets.reload
|
swf_assets.reload
|
||||||
|
|
||||||
# Then, compute and save our cached fields.
|
# Finally, compute and save our cached fields.
|
||||||
self.cached_occupied_zone_ids = occupied_zone_ids
|
self.cached_occupied_zone_ids = occupied_zone_ids
|
||||||
self.cached_compatible_body_ids = compatible_body_ids(use_cached: false)
|
self.cached_compatible_body_ids = compatible_body_ids(use_cached: false)
|
||||||
|
self.cached_predicted_fully_modeled =
|
||||||
|
predicted_fully_modeled?(use_cached: false)
|
||||||
self.save!
|
self.save!
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -387,7 +400,8 @@ class Item < ApplicationRecord
|
||||||
body_ids_by_species_by_color
|
body_ids_by_species_by_color
|
||||||
end
|
end
|
||||||
|
|
||||||
def predicted_fully_modeled?
|
def predicted_fully_modeled?(use_cached: true)
|
||||||
|
return cached_predicted_fully_modeled? if use_cached
|
||||||
predicted_missing_body_ids.empty?
|
predicted_missing_body_ids.empty?
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,16 @@
|
||||||
|
class AddCachedPredictedFullyModeledToItems < ActiveRecord::Migration[7.2]
|
||||||
|
def change
|
||||||
|
add_column :items, :cached_predicted_fully_modeled, :boolean,
|
||||||
|
default: false, null: false
|
||||||
|
|
||||||
|
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
|
|
@ -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_11_16_041926) do
|
ActiveRecord::Schema[7.2].define(version: 2024_11_19_214543) 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
|
||||||
|
@ -139,6 +139,7 @@ ActiveRecord::Schema[7.2].define(version: 2024_11_16_041926) do
|
||||||
t.integer "dyeworks_base_item_id"
|
t.integer "dyeworks_base_item_id"
|
||||||
t.string "cached_occupied_zone_ids", default: ""
|
t.string "cached_occupied_zone_ids", default: ""
|
||||||
t.text "cached_compatible_body_ids", default: ""
|
t.text "cached_compatible_body_ids", default: ""
|
||||||
|
t.boolean "cached_predicted_fully_modeled", default: false, null: false
|
||||||
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"
|
||||||
|
|
|
@ -41,21 +41,21 @@ RSpec.describe Item do
|
||||||
it("predicts no more compatible bodies") do
|
it("predicts no more compatible bodies") do
|
||||||
expect(item.predicted_missing_body_ids).to be_empty
|
expect(item.predicted_missing_body_ids).to be_empty
|
||||||
end
|
end
|
||||||
pending("appears in Item.is_modeled") do
|
it("appears in Item.is_modeled") do
|
||||||
expect(Item.is_modeled.find(item.id)).to be_present
|
expect(Item.is_modeled.find_by_id(item.id)).to be_present
|
||||||
end
|
end
|
||||||
pending("does not appear in Item.is_not_modeled") do
|
it("does not appear in Item.is_not_modeled") do
|
||||||
expect(Item.is_not_modeled.find(item.id)).to be_nil
|
expect(Item.is_not_modeled.find_by_id(item.id)).to be_nil
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
shared_examples "a not-fully-modeled item" do
|
shared_examples "a not-fully-modeled item" do
|
||||||
it("is not fully modeled") { should_not be_predicted_fully_modeled }
|
it("is not fully modeled") { should_not be_predicted_fully_modeled }
|
||||||
pending("does not appear in Item.is_modeled") do
|
it("does not appear in Item.is_modeled") do
|
||||||
expect(Item.is_modeled.find(item.id)).to be_nil
|
expect(Item.is_modeled.find_by_id(item.id)).to be_nil
|
||||||
end
|
end
|
||||||
pending("appears in in Item.is_not_modeled") do
|
it("appears in Item.is_not_modeled") do
|
||||||
expect(Item.is_not_modeled.find(item.id)).to be_present
|
expect(Item.is_not_modeled.find_by_id(item.id)).to be_present
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue