When building BotForge, our AI no-code chatbot platform, we needed a retrieval system that could handle messy, real-world user queries — typos, partial phrases, semantically similar-but-differently-worded questions.
A naive vector search alone wasn't good enough. It's powerful but brittle to out-of-vocabulary terms and exact keyword lookups.
We ran four retrieval strategies simultaneously using Promise.all\
, then merged results with a weighted scoring function.
\
javascript
const [semanticResults, textResults, regexResults, fuzzyResults] = await Promise.all([ semanticSearch(query, embeddings), // weight 1.8x
mongoFullTextSearch(query), // weight 1.5x
regexKeywordSearch(query), // weight 1.0x
fuzzyPerWordMatch(query), // weight 0.6x
]) ``
Using Gemini gemini-embedding-2\
to produce 3072-dimensional vectors, we compute cosine similarity against stored document embeddings. This catches meaning — "how do I reset my login?" matches "account recovery options" even with no shared words.
A native MongoDB Atlas text index for fast, exact keyword hits. Great for technical terms, product names, and precise phrases.
Each significant word in the query is compiled to a case-insensitive regex. Catches partial matches and hyphenated variants.
Levenshtein distance matching per query word — handles typos and misspellings like "configuraton" → "configuration".
Each result carries a base score from its retrieval strategy. We deduplicate by chunk ID, sum scores across strategies, and sort descending:
\
javascript
function mergeResults(tiers, weights) {
const scoreMap = new Map()
tiers.forEach((results, i) => {
results.forEach(({ id, score, chunk }) => {
const weighted = score * weights[i]
scoreMap.set(id, {
chunk,
total: (scoreMap.get(id)?.total || 0) + weighted,
})
})
})
return [...scoreMap.values()].sort((a, b) => b.total - a.total).slice(0, 5)
}
``
The parallel architecture was the key insight — running all four strategies concurrently keeps latency close to the slowest individual strategy (semantic search) rather than multiplying it.