Merge branch 'feat/inventory'

This commit is contained in:
2026-05-26 05:32:26 -04:00
35 changed files with 3305 additions and 1073 deletions

33
scripts/diagnose-join.ts Normal file
View File

@@ -0,0 +1,33 @@
import 'dotenv/config';
import chalk from 'chalk';
import util from 'node:util';
import { client } from '../src/db/typesense.ts';
const variants = [
'$skus(*, $cards(*))',
'$skus(*,$cards(*))',
'$skus(*, card_id, $cards(*))',
'$skus(*, $cards(*, strategy:nest))',
'$skus(*, $cards(*, strategy:merge))',
];
const debug = await client.debug.retrieve();
console.log(chalk.cyan(`Typesense server version: ${debug.version}`));
console.log();
for (const include of variants) {
console.log(chalk.yellow(`include_fields: ${include}`));
try {
const res: any = await client.collections('inventories').documents().search({
q: '*',
query_by: 'content',
per_page: 1,
include_fields: include,
});
const doc = res.hits?.[0]?.document;
console.log(util.inspect(doc, { depth: null, colors: false }));
} catch (err: any) {
console.log(chalk.red(` ERROR: ${err.message ?? err}`));
}
console.log();
}

View File

@@ -4,6 +4,8 @@ import type { DBInstance } from '../src/db/index.ts';
import fs from "node:fs/promises";
import { sql } from 'drizzle-orm'
import * as util from 'util';
const DollarToInt = (dollar: any) => {
if (dollar === null) return null;
@@ -63,7 +65,7 @@ export const createCardCollection = async () => {
{ 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 }
// { name: 'sku_id', type: 'string[]', optional: true, reference: 'skus.id', async_reference: true }
],
});
console.log(chalk.green('Collection "cards" created successfully.'));
@@ -84,11 +86,40 @@ export const createSkuCollection = async () => {
{ name: 'highestPrice', type: 'int32', optional: true },
{ name: 'lowestPrice', type: 'int32', optional: true },
{ name: 'marketPrice', type: 'int32', optional: true },
{ name: 'card_id', type: 'string', reference: 'cards.id', async_reference: true },
]
});
console.log(chalk.green('Collection "skus" created successfully.'));
}
// Delete and recreate the 'inventory' index
export const createInventoryCollection = async () => {
try {
await client.collections('inventories').delete();
} catch (error) {
// Ignore error, just means collection doesn't exist
}
await client.collections().create({
name: 'inventories',
fields: [
{ name: 'id', type: 'string' },
{ name: 'userId', type: 'string' },
{ name: 'catalogName', type: 'string' },
{ name: 'card_id', type: 'string', reference: 'cards.id', async_reference: true },
{ name: 'sku_id', type: 'string', reference: 'skus.id', async_reference: true },
{ name: 'purchasePrice', type: 'int32', optional: true },
// content,setName,productLineName,rarityName,energyType,cardType from cards for searching
{ name: 'content', type: 'string', token_separators: ['/'] },
{ name: 'setName', type: 'string' },
{ name: 'productLineName', type: 'string' },
{ name: 'rarityName', type: 'string' },
{ name: 'energyType', type: 'string' },
{ name: 'cardType', type: 'string' },
]
});
console.log(chalk.green('Collection "inventories" created successfully.'));
}
export const upsertCardCollection = async (db:DBInstance) => {
const pokemon = await db.query.cards.findMany({
@@ -123,7 +154,7 @@ export const upsertCardCollection = async (db:DBInstance) => {
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())
// sku_id: card.prices.map(price => price.skuId.toString())
};
}), { action: 'upsert' });
console.log(chalk.green('Collection "cards" indexed successfully.'));
@@ -137,10 +168,39 @@ export const upsertSkuCollection = async (db:DBInstance) => {
highestPrice: DollarToInt(sku.highestPrice),
lowestPrice: DollarToInt(sku.lowestPrice),
marketPrice: DollarToInt(sku.marketPrice),
})), { action: 'upsert' });
card_id: sku.cardId.toString(),
})), { action: 'upsert' });
console.log(chalk.green('Collection "skus" indexed successfully.'));
}
export const upsertInventoryCollection = async (db:DBInstance) => {
const inv = await db.query.inventory.findMany({
with: { sku: { with: { card: { with: { set: true } } } } }
});
await client.collections('inventories').documents().import(inv.map(i => ({
id: i.inventoryId,
userId: i.userId,
catalogName: i.catalogName,
card_id: i.sku?.cardId.toString(),
sku_id: i.skuId.toString(),
purchasePrice: DollarToInt(i.purchasePrice),
productLineName: i.sku?.card?.productLineName,
rarityName: i.sku?.card?.rarityName,
setName: i.sku?.card?.set?.setName || "",
cardType: i.sku?.card?.cardType || "",
energyType: i.sku?.card?.energyType || "",
content: [
i.sku?.card?.productName,
i.sku?.card?.productLineName,
i.sku?.card?.set?.setName || "",
i.sku?.card?.number,
i.sku?.card?.rarityName,
i.sku?.card?.artist || ""
].join(' '),
})), { action: 'upsert' });
console.log(chalk.green('Collection "inventories" indexed successfully.'));
}
@@ -184,4 +244,7 @@ where not exists (select 1 from cards where product_id=t.product_id and variant=
`);
console.log(`Inserted ${inserts.rowCount} rows into cards table`);
const skuUpdates = await db.execute(sql`update skus s set card_id = c.card_id from cards c where s.product_id = c.product_id and s.variant = c.variant and s.card_id is distinct from c.card_id`);
console.log(`Updated ${skuUpdates.rowCount} rows in skus table`);
}

View File

@@ -204,9 +204,9 @@ async function syncProductLine(productLine: string, field: string, fieldValue: s
},
});
console.log(`item: ${item.setId}\tdetail: ${detailData.setId}`);
console.log(`item: ${item.setCode}\tdetail: ${detailData.setCode}`);
console.log(`item: ${item.setName}\tdetail: ${detailData.setName}`);
// console.log(`item: ${item.setId}\tdetail: ${detailData.setId}`);
// console.log(`item: ${item.setCode}\tdetail: ${detailData.setCode}`);
// console.log(`item: ${item.setName}\tdetail: ${detailData.setName}`);
// set is...
await db.insert(schema.sets).values({
setId: detailData.setId,

View File

@@ -3,9 +3,12 @@ import { db, ClosePool } from '../src/db/index.ts';
import * as Indexing from './pokemon-helper.ts';
//await Indexing.createCardCollection();
//await Indexing.createSkuCollection();
await Indexing.upsertCardCollection(db);
// await Indexing.createCardCollection();
await Indexing.createSkuCollection();
await Indexing.createInventoryCollection();
// await Indexing.upsertCardCollection(db);
await Indexing.upsertSkuCollection(db);
await Indexing.upsertInventoryCollection(db);
await ClosePool();
console.log(chalk.green('Pokémon reindex complete.'));