import chalk from 'chalk'; import { client } from '../src/db/typesense.ts'; import type { DBInstance } from '../src/db/index.ts'; import fs from "node:fs/promises"; import { sql } from 'drizzle-orm' const DollarToInt = (dollar: any) => { if (dollar === null) return null; return Math.round(dollar * 100); } export const Sleep = (ms: number) => { return new Promise(resolve => setTimeout(resolve, ms)); } export const FileExists = async (path: string): Promise => { try { await fs.access(path); return true; } catch { return false; } } export const GetNumberOrNull = (value: any): number | null => { const number = Number(value); // Attempt to convert the value to a number if (Number.isNaN(number)) { return null; // Return null if the result is NaN } return number; // Otherwise, return the number } // Delete and recreate the 'cards' index export const createCardCollection = async () => { try { await client.collections('cards').delete(); } catch (error) { // Ignore error, just means collection doesn't exist } await client.collections().create({ name: 'cards', fields: [ { name: 'id', type: 'string' }, { name: 'cardId', type: 'int32' }, { name: 'productId', type: 'int32' }, { name: 'variant', type: 'string', facet: true }, { name: 'productName', type: 'string' }, { name: 'productLineName', type: 'string', facet: true }, { name: 'rarityName', type: 'string', facet: true }, { name: 'setName', type: 'string', facet: true }, { name: 'setCode', type: 'string' }, { name: 'cardType', type: 'string', facet: true }, { name: 'energyType', type: 'string', facet: true }, { name: 'number', type: 'string', sort: true }, { name: 'Artist', type: 'string' }, { name: 'sealed', type: 'bool' }, { name: 'releaseDate', type: 'int32' }, { name: 'marketPrice', type: 'int32', optional: true, sort: true }, { name: 'content', type: 'string', token_separators: ['/'] }, { name: 'sku_id', type: 'string[]', optional: true, reference: 'skus.id', async_reference: true } ], }); console.log(chalk.green('Collection "cards" created successfully.')); } // Delete and recreate the 'skus' index export const createSkuCollection = async () => { try { await client.collections('skus').delete(); } catch (error) { // Ignore error, just means collection doesn't exist } await client.collections().create({ name: 'skus', fields: [ { name: 'id', type: 'string' }, { name: 'condition', type: 'string' }, { name: 'highestPrice', type: 'int32', optional: true }, { name: 'lowestPrice', type: 'int32', optional: true }, { name: 'marketPrice', type: 'int32', optional: true }, ] }); console.log(chalk.green('Collection "skus" created successfully.')); } export const upsertCardCollection = async (db:DBInstance) => { const pokemon = await db.query.cards.findMany({ with: { set: true, tcgdata: true, prices: true }, }); await client.collections('cards').documents().import(pokemon.map(card => { // Use the NM SKU price matching the card's variant (kept fresh by syncPrices) // Fall back to any NM sku, then to tcgdata price const nmSku = card.prices.find(p => p.condition === 'Near Mint' && p.variant === card.variant) ?? card.prices.find(p => p.condition === 'Near Mint'); const marketPrice = nmSku?.marketPrice ? DollarToInt(nmSku.marketPrice) : card.tcgdata?.marketPrice ? DollarToInt(card.tcgdata.marketPrice) : null; return { id: card.cardId.toString(), cardId: card.cardId, productId: card.productId, variant: card.variant, productName: card.productName, productLineName: card.productLineName, rarityName: card.rarityName, setName: card.set?.setName || "", setCode: card.set?.setCode || "", cardType: card.cardType || "", energyType: card.energyType || "", number: card.number, Artist: card.artist || "", sealed: card.sealed, content: [card.productName, card.productLineName, card.set?.setName || "", card.set?.setCode || "", card.number, card.rarityName, card.artist || ""].join(' '), releaseDate: card.tcgdata?.releaseDate ? Math.floor(new Date(card.tcgdata.releaseDate).getTime() / 1000) : 0, ...(marketPrice !== null && { marketPrice }), sku_id: card.prices.map(price => price.skuId.toString()) }; }), { action: 'upsert' }); console.log(chalk.green('Collection "cards" indexed successfully.')); } export const upsertSkuCollection = async (db:DBInstance) => { const skus = await db.query.skus.findMany(); await client.collections('skus').documents().import(skus.map(sku => ({ id: sku.skuId.toString(), condition: sku.condition, highestPrice: DollarToInt(sku.highestPrice), lowestPrice: DollarToInt(sku.lowestPrice), marketPrice: DollarToInt(sku.marketPrice), })), { action: 'upsert' }); console.log(chalk.green('Collection "skus" indexed successfully.')); } export const UpdateVariants = async (db:DBInstance) => { const updates = await db.execute(sql`update cards as c set product_name = a.product_name, product_line_name = a.product_line_name, product_url_name = a.product_url_name, rarity_name = a.rarity_name, sealed = a.sealed, set_id = a.set_id, card_type = a.card_type, energy_type = a.energy_type, number = a.number, artist = a.artist from ( select t.product_id, b.variant, coalesce(o.product_name, regexp_replace(regexp_replace(coalesce(nullif(t.product_name, ''), t.product_url_name),' \\\\(.*\\\\)',''),' - .*$','')) as product_name, coalesce(o.product_line_name, t.product_line_name) as product_line_name, coalesce(o.product_url_name, t.product_url_name) as product_url_name, coalesce(o.rarity_name, t.rarity_name) as rarity_name, coalesce(o.sealed, t.sealed) as sealed, coalesce(o.set_id, t.set_id) as set_id, coalesce(o.card_type, t.card_type) as card_type, coalesce(o.energy_type, t.energy_type) as energy_type, coalesce(o.number, t.number) as number, coalesce(o.artist, t.artist) as artist from tcg_cards t join (select distinct product_id, variant from skus) b on t.product_id = b.product_id left join tcg_overrides o on t.product_id = o.product_id ) a where c.product_id = a.product_id and c.variant = a.variant and ( c.product_name is distinct from a.product_name or c.product_line_name is distinct from a.product_line_name or c.product_url_name is distinct from a.product_url_name or c.rarity_name is distinct from a.rarity_name or c.sealed is distinct from a.sealed or c.set_id is distinct from a.set_id or c.card_type is distinct from a.card_type or c.energy_type is distinct from a.energy_type or c."number" is distinct from a."number" or c.artist is distinct from a.artist ) `); console.log(`Updated ${updates.rowCount} rows in cards table`); const inserts = await db.execute(sql`insert into cards (product_id, variant, product_name, product_line_name, product_url_name, rarity_name, sealed, set_id, card_type, energy_type, "number", artist) select t.product_id, b.variant, coalesce(o.product_name, regexp_replace(regexp_replace(coalesce(nullif(t.product_name, ''), t.product_url_name),' \\\\(.*\\\\)',''),' - .*$','')) as product_name, coalesce(o.product_line_name, t.product_line_name) as product_line_name, coalesce(o.product_url_name, t.product_url_name) as product_url_name, coalesce(o.rarity_name, t.rarity_name) as rarity_name, coalesce(o.sealed, t.sealed) as sealed, coalesce(o.set_id, t.set_id) as set_id, coalesce(o.card_type, t.card_type) as card_type, coalesce(o.energy_type, t.energy_type) as energy_type, coalesce(o.number, t.number) as number, coalesce(o.artist, t.artist) as artist from tcg_cards t join (select distinct product_id, variant from skus) b on t.product_id = b.product_id left join tcg_overrides o on t.product_id = o.product_id where not exists (select 1 from cards where product_id=t.product_id and variant=b.variant) `); console.log(`Inserted ${inserts.rowCount} rows into cards table`); }