import fs from 'fs/promises'; import path from 'path'; export async function findMatches(query, cacheDir) { if (!query || !query.trim()) return []; const terms = query.toLowerCase().split(/\s+/).filter(t => t); // Load category tree const treePath = path.join(cacheDir, 'category_tree.json'); let tree = []; try { const treeData = await fs.readFile(treePath, 'utf-8'); tree = JSON.parse(treeData); } catch (e) { console.error("Failed to load category tree for search", e); return []; } const matchingCategoryIds = new Set(); // Helper to check text match const isMatch = (text) => { if (!text) return false; const lower = text.toLowerCase(); return terms.every(t => lower.includes(t)); }; // Flatten tree to linear list for easier iteration const queue = [...tree]; const nodes = []; while (queue.length > 0) { const node = queue.shift(); nodes.push(node); if (node.children) { queue.push(...node.children); } } // Process in chunks to avoid too many open files const CHUNK_SIZE = 50; for (let i = 0; i < nodes.length; i += CHUNK_SIZE) { const chunk = nodes.slice(i, i + CHUNK_SIZE); await Promise.all(chunk.map(async (node) => { let nodeMatches = false; // Check category name if (isMatch(node.cName)) { nodeMatches = true; } else { // Check products try { const prodPath = path.join(cacheDir, 'products', `category_${node.kKategorie}.json`); // Check if file exists before reading to avoid throwing too many errors // Actually readFile throws ENOENT which is fine const prodData = await fs.readFile(prodPath, 'utf-8'); const products = JSON.parse(prodData); if (products.some(p => isMatch(p.cName))) { nodeMatches = true; } } catch (e) { // Ignore missing files } } if (nodeMatches) { matchingCategoryIds.add(node.kKategorie); } })); if (matchingCategoryIds.size >= 20) { break; } } return Array.from(matchingCategoryIds); }