{"version":3,"file":"inverted-index.cjs","sources":["../../../src/core/inverted-index.ts"],"sourcesContent":["/**\n * Inverted Index Implementation\n * Optimized for large datasets (1M+ words)\n *\n * Architecture:\n * - Token → [docId1, docId2, ...] (posting lists)\n * - Fast intersection/union operations\n * - BM25-like scoring for relevance\n * - Parallel to existing hash-based index (backwards compatible)\n */\n\nimport type { InvertedIndex, DocumentMetadata, PostingList, FuzzyConfig, LanguageProcessor, SearchMatch } from \"./types.js\";\nimport { generateNgrams, calculateLevenshteinDistance, calculateDamerauLevenshteinDistance } from \"../algorithms/levenshtein.js\";\nimport { Trie } from \"./trie.js\";\nimport { calculateBM25Score, normalizeBM25Score, DEFAULT_BM25_CONFIG, type DocumentStats, type CorpusStats } from \"../algorithms/bm25.js\";\nimport { BloomFilter } from \"../algorithms/bloom-filter.js\";\nimport { tokenize } from \"../utils/tokenizer.js\";\n\n/**\n * Build inverted index from documents\n * This runs ALONGSIDE the existing index building\n */\nexport function buildInvertedIndex(\n  //\n  words: string[],\n  languageProcessors: LanguageProcessor[],\n  config: FuzzyConfig,\n  featureSet: Set<string>\n): { invertedIndex: InvertedIndex; documents: DocumentMetadata[] } {\n  const documents: DocumentMetadata[] = [];\n  const invertedIndex: InvertedIndex = {\n    termToPostings: new Map(),\n    termTrie: new Trie(), // Initialize Trie for fast prefix matching\n    phoneticToPostings: new Map(),\n    ngramToPostings: new Map(),\n    synonymToPostings: new Map(),\n    fieldIndices: new Map(),\n    totalDocs: 0,\n    avgDocLength: 0,\n  };\n\n  let totalLength = 0;\n  let docId = 0;\n\n  // Build documents and posting lists\n  for (const word of words) {\n    if (!word || word.trim().length < config.minQueryLength) continue;\n\n    const trimmedWord = word.trim();\n\n    // Process with each language processor\n    for (const processor of languageProcessors) {\n      const normalized = processor.normalize(trimmedWord);\n      const phoneticCode = featureSet.has(\"phonetic\") && processor.supportedFeatures.includes(\"phonetic\") ? processor.getPhoneticCode(trimmedWord) : undefined;\n\n      const compoundParts = featureSet.has(\"compound\") && processor.supportedFeatures.includes(\"compound\") ? processor.splitCompoundWords(trimmedWord) : undefined;\n\n      // Create document metadata\n      // MEMORY OPTIMIZATION: Don't store 'word' - it's already in index.base array!\n      // This saves 30-50% memory on large datasets. Word can be retrieved via index.base[docId]\n      const doc: DocumentMetadata = {\n        id: docId,\n        word: trimmedWord, // TODO: Remove in v2.0 - use index.base[docId] instead\n        normalized,\n        phoneticCode,\n        language: processor.language,\n        compoundParts: compoundParts && compoundParts.length > 1 ? compoundParts : undefined,\n      };\n\n      documents.push(doc);\n      totalLength += normalized.length;\n\n      // Index the normalized term\n      addToPostingList(invertedIndex.termToPostings, normalized, docId);\n      invertedIndex.termTrie!.insert(normalized, [docId]);\n\n      // Index original word (for exact matching)\n      const lowerWord = trimmedWord.toLowerCase();\n      addToPostingList(invertedIndex.termToPostings, lowerWord, docId);\n      invertedIndex.termTrie!.insert(lowerWord, [docId]);\n\n      // TOKENIZATION: Index individual tokens from words with special characters\n      // This allows \"api_\" to match \"api_manager_3254\" by indexing [\"api\", \"manager\", \"3254\"]\n      const tokens = tokenize(trimmedWord, { lowercase: true, minLength: 1 });\n      if (tokens.length > 1) {\n        // Only tokenize if there are multiple tokens (word contains special chars)\n        tokens.forEach((token) => {\n          if (token.length >= config.minQueryLength) {\n            addToPostingList(invertedIndex.termToPostings, token, docId);\n            invertedIndex.termTrie!.insert(token, [docId]);\n          }\n        });\n      }\n\n      // Index word variants (prefixes)\n      if (featureSet.has(\"partial-words\")) {\n        const variants = processor.getWordVariants(trimmedWord, config.performance);\n        variants.forEach((variant) => {\n          addToPostingList(invertedIndex.termToPostings, variant, docId);\n          invertedIndex.termTrie!.insert(variant, [docId]);\n        });\n      }\n\n      // Index phonetic code\n      if (phoneticCode) {\n        addToPostingList(invertedIndex.phoneticToPostings, phoneticCode, docId);\n      }\n\n      // Index n-grams\n      const ngrams = generateNgrams(normalized, config.ngramSize);\n      ngrams.forEach((ngram) => {\n        addToPostingList(invertedIndex.ngramToPostings, ngram, docId);\n      });\n\n      // Index compound parts\n      if (compoundParts && compoundParts.length > 1) {\n        compoundParts.forEach((part) => {\n          const normalizedPart = processor.normalize(part);\n          addToPostingList(invertedIndex.termToPostings, normalizedPart, docId);\n          invertedIndex.termTrie!.insert(normalizedPart, [docId]);\n        });\n      }\n\n      // Index synonyms\n      if (featureSet.has(\"synonyms\")) {\n        const synonyms = processor.getSynonyms(normalized);\n        synonyms.forEach((synonym) => {\n          addToPostingList(invertedIndex.synonymToPostings, synonym, docId);\n        });\n\n        // Custom synonyms\n        if (config.customSynonyms) {\n          const customSynonyms = config.customSynonyms[normalized];\n          if (customSynonyms) {\n            customSynonyms.forEach((synonym) => {\n              addToPostingList(invertedIndex.synonymToPostings, synonym, docId);\n            });\n          }\n        }\n      }\n\n      docId++;\n    }\n  }\n\n  invertedIndex.totalDocs = docId;\n  invertedIndex.avgDocLength = totalLength / Math.max(1, docId);\n\n  // Build BM25 statistics if enabled\n  if (config.useBM25) {\n    const documentFrequencies = new Map<string, number>();\n    const documentLengths = new Map<number, number>();\n\n    // Calculate document frequencies (how many docs contain each term)\n    for (const [term, posting] of invertedIndex.termToPostings.entries()) {\n      documentFrequencies.set(term, posting.docIds.length);\n    }\n\n    // Store document lengths\n    documents.forEach((doc) => {\n      documentLengths.set(doc.id, doc.normalized.length);\n    });\n\n    invertedIndex.bm25Stats = {\n      documentFrequencies,\n      documentLengths,\n    };\n  }\n\n  // Build Bloom Filter if enabled or auto-enable for large datasets\n  const shouldUseBloomFilter = config.useBloomFilter || words.length >= 10000;\n\n  if (shouldUseBloomFilter) {\n    const falsePositiveRate = config.bloomFilterFalsePositiveRate || 0.01;\n    const bloomFilter = new BloomFilter({\n      expectedElements: invertedIndex.termToPostings.size,\n      falsePositiveRate,\n    });\n\n    // Add all terms to bloom filter\n    for (const term of invertedIndex.termToPostings.keys()) {\n      bloomFilter.add(term);\n    }\n\n    invertedIndex.bloomFilter = bloomFilter;\n  }\n\n  return { invertedIndex, documents };\n}\n\n/**\n * Search using inverted index\n * Much faster than hash-based approach for large datasets\n */\nexport function searchInvertedIndex(\n  //\n  invertedIndex: InvertedIndex,\n  documents: DocumentMetadata[],\n  query: string,\n  processors: LanguageProcessor[],\n  config: FuzzyConfig\n): SearchMatch[] {\n  const matches = new Map<number, SearchMatch>();\n  const featureSet = new Set(config.features);\n\n  // Process query with each language processor\n  for (const processor of processors) {\n    const normalizedQuery = processor.normalize(query.trim());\n\n    // 1. Exact term lookup (fastest)\n    findExactMatchesInverted(normalizedQuery, invertedIndex, documents, matches, processor.language);\n\n    // 2. Prefix matches\n    findPrefixMatchesInverted(normalizedQuery, invertedIndex, documents, matches, processor.language);\n\n    // 3. Phonetic matches\n    if (featureSet.has(\"phonetic\") && processor.supportedFeatures.includes(\"phonetic\")) {\n      findPhoneticMatchesInverted(normalizedQuery, processor, invertedIndex, documents, matches);\n    }\n\n    // 4. Synonym matches\n    if (featureSet.has(\"synonyms\")) {\n      findSynonymMatchesInverted(normalizedQuery, invertedIndex, documents, matches);\n    }\n\n    // 5. N-gram matches\n    findNgramMatchesInverted(normalizedQuery, invertedIndex, documents, matches, processor.language, config.ngramSize);\n\n    // 6. Fuzzy matches (most expensive, do last)\n    // OPTIMIZATION: Skip fuzzy matching for very large datasets in fast mode if we have enough good matches\n    const shouldSkipFuzzy = config.performance === \"fast\" && invertedIndex.termToPostings.size > 100000 && matches.size >= config.maxResults * 2;\n\n    if (!shouldSkipFuzzy && (featureSet.has(\"missing-letters\") || featureSet.has(\"extra-letters\") || featureSet.has(\"transpositions\"))) {\n      findFuzzyMatchesInverted(normalizedQuery, invertedIndex, documents, matches, processor, config.maxEditDistance, config);\n    }\n  }\n\n  // Convert to array and return\n  return Array.from(matches.values());\n}\n\n/**\n * Helper: Add document to posting list\n */\nfunction addToPostingList(\n  //\n  postings: Map<string, PostingList>,\n  term: string,\n  docId: number\n): void {\n  let posting = postings.get(term);\n  if (!posting) {\n    posting = { term, docIds: [] };\n    postings.set(term, posting);\n  }\n\n  // MEMORY OPTIMIZATION: Use array.includes() instead of Set\n  // For typical posting list sizes (<1000), this is fast enough and saves memory\n  if (!posting.docIds.includes(docId)) {\n    posting.docIds.push(docId);\n  }\n}\n\n/**\n * Find exact matches in inverted index\n */\nfunction findExactMatchesInverted(\n  //\n  query: string,\n  invertedIndex: InvertedIndex,\n  documents: DocumentMetadata[],\n  matches: Map<number, SearchMatch>,\n  language: string\n): void {\n  // BLOOM FILTER: Fast negative lookup\n  if (invertedIndex.bloomFilter && !invertedIndex.bloomFilter.mightContain(query)) {\n    return; // Definitely not in index, skip expensive lookup\n  }\n\n  const posting = invertedIndex.termToPostings.get(query);\n  if (!posting) return;\n\n  posting.docIds.forEach((docId) => {\n    const doc = documents[docId];\n    if (!doc) return;\n\n    if (!matches.has(docId)) {\n      matches.set(docId, {\n        word: doc.word,\n        normalized: query,\n        matchType: \"exact\",\n        editDistance: 0,\n        language,\n        docId,\n      });\n    }\n  });\n}\n\n/**\n * Find prefix matches in inverted index\n * Now uses Trie for O(k) lookup instead of O(n) iteration!\n */\nfunction findPrefixMatchesInverted(\n  //\n  query: string,\n  invertedIndex: InvertedIndex,\n  documents: DocumentMetadata[],\n  matches: Map<number, SearchMatch>,\n  language: string\n): void {\n  // Use Trie for fast prefix matching (100-1000x faster!)\n  if (invertedIndex.termTrie) {\n    const prefixMatches = invertedIndex.termTrie.findWithPrefix(query);\n\n    for (const [term, docIds] of prefixMatches) {\n      if (term !== query) {\n        // Exclude exact matches (handled separately)\n        docIds.forEach((docId: number) => {\n          const doc = documents[docId];\n          if (!doc) return;\n\n          if (!matches.has(docId)) {\n            matches.set(docId, {\n              word: doc.word,\n              normalized: term,\n              matchType: \"prefix\",\n              language,\n              docId,\n            });\n          }\n        });\n      }\n    }\n  } else {\n    // Fallback to old O(n) method if Trie not available\n    for (const [term, posting] of invertedIndex.termToPostings.entries()) {\n      if (term.startsWith(query) && term !== query) {\n        posting.docIds.forEach((docId) => {\n          const doc = documents[docId];\n          if (!doc) return;\n\n          if (!matches.has(docId)) {\n            matches.set(docId, {\n              word: doc.word,\n              normalized: term,\n              matchType: \"prefix\",\n              language,\n              docId,\n            });\n          }\n        });\n      }\n    }\n  }\n}\n\n/**\n * Find phonetic matches in inverted index\n */\nfunction findPhoneticMatchesInverted(\n  //\n  query: string,\n  processor: LanguageProcessor,\n  invertedIndex: InvertedIndex,\n  documents: DocumentMetadata[],\n  matches: Map<number, SearchMatch>\n): void {\n  const phoneticCode = processor.getPhoneticCode(query);\n  if (!phoneticCode) return;\n\n  const posting = invertedIndex.phoneticToPostings.get(phoneticCode);\n  if (!posting) return;\n\n  posting.docIds.forEach((docId) => {\n    const doc = documents[docId];\n    if (!doc) return;\n\n    if (!matches.has(docId)) {\n      matches.set(docId, {\n        word: doc.word,\n        normalized: query,\n        matchType: \"phonetic\",\n        phoneticCode,\n        language: processor.language,\n        docId,\n      });\n    }\n  });\n}\n\n/**\n * Find synonym matches in inverted index\n */\nfunction findSynonymMatchesInverted(\n  //\n  query: string,\n  invertedIndex: InvertedIndex,\n  documents: DocumentMetadata[],\n  matches: Map<number, SearchMatch>\n): void {\n  const posting = invertedIndex.synonymToPostings.get(query);\n  if (!posting) return;\n\n  posting.docIds.forEach((docId) => {\n    const doc = documents[docId];\n    if (!doc) return;\n\n    if (!matches.has(docId)) {\n      matches.set(docId, {\n        word: doc.word,\n        normalized: query,\n        matchType: \"synonym\",\n        language: \"synonym\",\n        docId,\n      });\n    }\n  });\n}\n\n/**\n * Find n-gram matches in inverted index\n */\nfunction findNgramMatchesInverted(\n  //\n  query: string,\n  invertedIndex: InvertedIndex,\n  documents: DocumentMetadata[],\n  matches: Map<number, SearchMatch>,\n  language: string,\n  ngramSize: number\n): void {\n  if (query.length < ngramSize) return;\n\n  const queryNgrams = generateNgrams(query, ngramSize);\n  const candidateDocs = new Set<number>();\n\n  // Collect all documents that contain at least one n-gram\n  queryNgrams.forEach((ngram) => {\n    const posting = invertedIndex.ngramToPostings.get(ngram);\n    if (posting) {\n      posting.docIds.forEach((docId) => candidateDocs.add(docId));\n    }\n  });\n\n  // Add to matches\n  candidateDocs.forEach((docId) => {\n    const doc = documents[docId];\n    if (!doc) return;\n\n    if (!matches.has(docId)) {\n      matches.set(docId, {\n        word: doc.word,\n        normalized: query,\n        matchType: \"ngram\",\n        language,\n        docId,\n      });\n    }\n  });\n}\n\n/**\n * Find fuzzy matches in inverted index\n * Optimized with length-based pre-filtering (5-10x faster)\n */\nfunction findFuzzyMatchesInverted(\n  //\n  query: string,\n  invertedIndex: InvertedIndex,\n  documents: DocumentMetadata[],\n  matches: Map<number, SearchMatch>,\n  processor: LanguageProcessor,\n  maxDistance: number,\n  config: FuzzyConfig\n): void {\n  const queryLen = query.length;\n  const minLen = queryLen - maxDistance;\n  const maxLen = queryLen + maxDistance;\n\n  // Pre-compute for performance\n  const useTranspositions = config.features?.includes(\"transpositions\");\n\n  // OPTIMIZATION: Dynamic candidate limit based on dataset size\n  // Smaller limit for larger datasets to maintain sub-10ms performance\n  const datasetSize = invertedIndex.termToPostings.size;\n  const MAX_FUZZY_CANDIDATES = datasetSize > 100000 ? 1000 : datasetSize > 50000 ? 1500 : datasetSize > 20000 ? 3000 : datasetSize > 10000 ? 5000 : 8000;\n  let candidatesChecked = 0;\n\n  // OPTIMIZATION: For very large datasets (50K+), use Trie prefix filtering first\n  let termsArray: [string, PostingList][];\n\n  if (datasetSize > 50000 && query.length >= 2 && invertedIndex.termTrie) {\n    // Get prefix matches from Trie (much faster than iterating all terms)\n    // Use longer prefix for 100K+ datasets for better filtering\n    const prefixLength = datasetSize > 100000 ? Math.min(3, query.length) : Math.min(2, query.length);\n    const prefix = query.substring(0, prefixLength);\n    const prefixMatches = invertedIndex.termTrie.findWithPrefix(prefix);\n\n    // Only check terms that share a prefix with the query\n    termsArray = prefixMatches.map(([term, _docIds]: [string, number[]]) => [term, invertedIndex.termToPostings.get(term)] as [string, PostingList | undefined]).filter((entry: [string, PostingList | undefined]): entry is [string, PostingList] => entry[1] !== undefined);\n\n    // If prefix filtering gives us too few candidates, fall back to full search\n    if (termsArray.length < 100) {\n      termsArray = Array.from(invertedIndex.termToPostings.entries());\n    }\n  } else {\n    termsArray = Array.from(invertedIndex.termToPostings.entries());\n  }\n\n  // Sort by length similarity for better early termination\n  termsArray.sort((a, b) => {\n    const aDiff = Math.abs(a[0].length - queryLen);\n    const bDiff = Math.abs(b[0].length - queryLen);\n    return aDiff - bDiff;\n  });\n\n  // Iterate through sorted terms with optimized filtering\n  for (const [term, posting] of termsArray) {\n    // OPTIMIZATION 1: Length-based pre-filter (O(1) check)\n    // This eliminates 80-90% of candidates before expensive Levenshtein\n    const termLen = term.length;\n    if (termLen < minLen || termLen > maxLen) {\n      continue;\n    }\n\n    // OPTIMIZATION 2: Limit candidates in large datasets\n    if (candidatesChecked >= MAX_FUZZY_CANDIDATES) {\n      break;\n    }\n    candidatesChecked++;\n\n    // OPTIMIZATION 3: Enhanced early termination based on match quality and quantity\n    // Less aggressive to ensure better search quality\n    const currentMatches = matches.size;\n    const earlyTerminationThreshold = datasetSize > 50000 ? config.maxResults * 3 : config.maxResults * 5;\n\n    // Only consider early termination if we have significantly more matches than needed\n    if (currentMatches >= earlyTerminationThreshold) {\n      // Calculate minimum edit distance in current matches\n      let minEditDistance = maxDistance;\n      let hasPerfectMatches = false;\n\n      for (const match of matches.values()) {\n        if (match.editDistance !== undefined) {\n          if (match.editDistance === 0) {\n            // Perfect match found - we can terminate early but still need more results\n            hasPerfectMatches = true;\n            minEditDistance = 0;\n            break;\n          } else if (match.editDistance <= 1) {\n            minEditDistance = Math.min(minEditDistance, match.editDistance);\n          }\n        }\n      }\n\n      // Only terminate early if we have many perfect matches\n      // Much more conservative than before\n      if (hasPerfectMatches && currentMatches >= config.maxResults * 10) {\n        break;\n      }\n    }\n\n    // OPTIMIZATION 4: Skip if first character is too different (cheap check)\n    if (query.length > 0 && term.length > 0) {\n      const firstCharDiff = Math.abs(query.charCodeAt(0) - term.charCodeAt(0));\n      if (firstCharDiff > 10 && maxDistance < 2) {\n        // Allow more variance for higher edit distance\n        continue;\n      }\n    }\n\n    // Now do expensive edit distance calculation\n    const distance = useTranspositions ? calculateDamerauLevenshteinDistance(query, term, maxDistance) : calculateLevenshteinDistance(query, term, maxDistance);\n\n    if (distance <= maxDistance) {\n      posting.docIds.forEach((docId) => {\n        const doc = documents[docId];\n        if (!doc) return;\n\n        const existingMatch = matches.get(docId);\n        // Don't replace exact or prefix matches with fuzzy matches\n        // Only update if this is a better fuzzy match (lower edit distance)\n        if (!existingMatch || (existingMatch.matchType !== \"exact\" && existingMatch.matchType !== \"prefix\" && (existingMatch.editDistance || Infinity) > distance)) {\n          matches.set(docId, {\n            word: doc.word,\n            normalized: term,\n            matchType: \"fuzzy\",\n            editDistance: distance,\n            language: processor.language,\n            docId,\n          });\n        }\n      });\n    }\n  }\n}\n\n/**\n * Calculate BM25 scores for search matches\n * Enhances relevance ranking with statistical scoring\n */\nexport function calculateBM25Scores(matches: SearchMatch[], queryTerms: string[], invertedIndex: InvertedIndex, documents: DocumentMetadata[], config: FuzzyConfig): SearchMatch[] {\n  if (!config.useBM25 || !invertedIndex.bm25Stats) {\n    return matches;\n  }\n\n  const bm25Config = {\n    ...DEFAULT_BM25_CONFIG,\n    ...config.bm25Config,\n  };\n\n  // Build corpus stats from inverted index\n  const corpusStats: CorpusStats = {\n    totalDocs: invertedIndex.totalDocs,\n    avgDocLength: invertedIndex.avgDocLength,\n    documentFrequencies: invertedIndex.bm25Stats.documentFrequencies,\n  };\n\n  // Calculate BM25 score for each match\n  return matches.map((match) => {\n    if (match.docId === undefined) {\n      return match;\n    }\n\n    const doc = documents[match.docId];\n    if (!doc) {\n      return match;\n    }\n\n    // Build document stats\n    const termFrequencies = new Map<string, number>();\n    const normalizedTerms = doc.normalized.toLowerCase().split(/\\s+/);\n\n    for (const term of normalizedTerms) {\n      termFrequencies.set(term, (termFrequencies.get(term) || 0) + 1);\n    }\n\n    const docStats: DocumentStats = {\n      docId: doc.id,\n      length: normalizedTerms.length,\n      termFrequencies,\n    };\n\n    // Calculate BM25 score\n    const bm25Score = calculateBM25Score(queryTerms, docStats, corpusStats, bm25Config);\n    const normalizedBM25 = normalizeBM25Score(bm25Score);\n\n    return {\n      ...match,\n      bm25Score: normalizedBM25,\n    };\n  });\n}\n"],"names":["Trie","tokenize","generateNgrams","bloomFilter","BloomFilter","calculateDamerauLevenshteinDistance","calculateLevenshteinDistance","DEFAULT_BM25_CONFIG","calculateBM25Score","normalizeBM25Score"],"mappings":"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"}