/** * Migration — add `search_text` (text) to `price_list`. * * PB can't substring-match a JSON field, so for the Catalogue tab's * "search everything" feature, we denormalize the searchable bits of * each row into a flat text field. The WFDASH Catalogue helper * (`getPriceLists`) then ORs against `search_text` and the other * string columns. * * What's in search_text (per row, joined with spaces): * - sku * - description * - brand * - supplier * - category * - subcategory * - specifications.synonyms (the buyer-friendly aliases) * * Idempotent: re-runs are safe. * * Run with: * npm run pb:migrate:price-list-search-text */ import PocketBase from "pocketbase"; const PB_URL = process.env.NEXT_PUBLIC_POCKETBASE_URL || "http://127.0.0.1:8090"; const ADMIN_EMAIL = process.env.PB_ADMIN_EMAIL; const ADMIN_PASSWORD = process.env.PB_ADMIN_PASSWORD; if (!ADMIN_EMAIL || !ADMIN_PASSWORD) { throw new Error("PB_ADMIN_EMAIL and PB_ADMIN_PASSWORD are required."); } async function main() { const pb = new PocketBase(PB_URL); await pb.collection("_superusers").authWithPassword(ADMIN_EMAIL, ADMIN_PASSWORD); const cols = await pb.collections.getFullList(); const priceList = cols.find((c) => c.name === "price_list"); if (!priceList) { throw new Error("price_list collection not found."); } // Always re-check the index — running the migration on a // collection that already has `search_text` should still ensure // the index exists (handles the case where the field was added // before the index step was introduced). const INDEX_SQL = "CREATE INDEX `idx_pl_search_text` ON `price_list` (`search_text`)"; const hasIndex = (priceList.indexes || []).some( (i) => typeof i === "string" && i.includes("idx_pl_search_text"), ); if (priceList.fields.some((f) => f.name === "search_text")) { if (hasIndex) { console.log("price_list.search_text + index already present, skipping."); return; } await pb.collections.update(priceList.id, { indexes: [...(priceList.indexes || []), INDEX_SQL] }); console.log("Added price_list index on search_text (field already existed)."); console.log("search_text index migration complete."); return; } await pb.collections.update(priceList.id, { fields: [ ...priceList.fields, { name: "search_text", type: "text", required: false, options: { max: 0 } }, ], // Add a non-unique index so the search box doesn't full-scan // the table once we have thousands of rows. indexes: [ ...(priceList.indexes || []), "CREATE INDEX `idx_pl_search_text` ON `price_list` (`search_text`)", ], }); console.log("Added price_list.search_text (+ index)"); console.log("search_text migration complete."); } main().catch((err) => { console.error(err); process.exit(1); });