Building a RAG System with MongoDB and Node.js
- Authors
- Name
- Hamza Rahman
- Published on
- -4 mins read
If you already run MongoDB, you do not need a separate vector database to add retrieval to your app. MongoDB's built-in text search can find the documents relevant to a question, and you pass those into the model to answer from. This is the keyword-based middle ground between passing whole files into the prompt and a full vector database like Pinecone.
When MongoDB fits for RAG
MongoDB text search is a good choice when you already use MongoDB, you want keyword retrieval without new infrastructure, and your documents are searchable by the words they contain. It is not the right tool when you need semantic search that matches on meaning rather than exact words. For that, see the Pinecone version.
Setup
Install the dependencies:
npm install openai mongodb dotenvPut your keys in a .env file:
OPENAI_API_KEY=your-api-key-hereMONGODB_URI=your-mongodb-connection-stringConnect to OpenAI and MongoDB:
import { OpenAI } from 'openai'import { MongoClient } from 'mongodb'import dotenv from 'dotenv'
dotenv.config()
const openai = new OpenAI({ apiKey: process.env.OPENAI_API_KEY })const client = new MongoClient(process.env.MONGODB_URI)const db = client.db('your-db')Create a text index
Text search needs a text index. Create one on the fields you want to search, and weight the title higher than the body so title matches rank first.
async function setupTextSearch() { await db.collection('documents').createIndex( { content: 'text', title: 'text' }, { weights: { title: 3, content: 1 } } )}You only need to run this once per collection.
Add documents
Store each document with whatever metadata you want to keep alongside it.
async function indexDocument(document) { await db.collection('documents').insertOne({ title: document.title, content: document.content, metadata: { source: document.source, date: new Date(), category: document.category, }, })}The RAG query
The flow is the same as any RAG system: find relevant documents, combine them into context, and ask the model to answer from that context.
async function mongoRAG(question) { // 1. Search MongoDB for relevant documents const results = await db .collection('documents') .find({ $text: { $search: question } }, { score: { $meta: 'textScore' } }) .sort({ score: { $meta: 'textScore' } }) .limit(3) .toArray()
// 2. Combine the matched documents into one context string const context = results.map((doc) => doc.content).join('\n')
// 3. Ask the model, grounded in that context const response = await openai.chat.completions.create({ model: 'gpt-5.4-mini', messages: [ { role: 'system', content: 'Answer using only the provided context. If the answer is not there, say so.', }, { role: 'user', content: `Context:\n${context}\n\nQuestion: ${question}`, }, ], })
return response.choices[0].message.content}$text runs the search, textScore ranks results by relevance, and .limit(3) keeps only the top matches so the context stays small.
Narrowing the search
Two small variations cover most needs.
Search within a category, so a question only pulls from the right section of your data:
async function categorySearch(question, category) { return db .collection('documents') .find({ $and: [{ 'metadata.category': category }, { $text: { $search: question } }], }) .toArray()}Make matching less strict by searching each word separately, which helps when the exact phrase is not present:
async function fuzzySearch(question) { const words = question.split(' ') return db .collection('documents') .find({ $or: words.map((word) => ({ $text: { $search: word } })) }) .toArray()}Limitations
MongoDB text search matches words, not meaning. A question that uses different words from the document ("car" vs "automobile") can miss, because there is no semantic understanding. If your retrieval needs to match on meaning, or you are searching across millions of documents, a vector database is the better fit.
Related
- Build a RAG system with Node.js and OpenAI, no database required: the simplest starting point.
- Building a RAG system with Pinecone and Node.js: semantic search with a vector database.
Related articles
Building a RAG System with Pinecone and Node.js
Build a RAG system in Node.js with Pinecone and OpenAI embeddings. Semantic search that matches on meaning, for large document collections that outgrow keyword search.
Build a RAG System with Node.js and OpenAI - No Database Required
Build a simple RAG system in Node.js with OpenAI and plain text files. No vector database, no embeddings: pass docs into the model and get answers.
Cheerio vs node-html-parser
Cheerio vs node-html-parser for parsing HTML in Node.js: node-html-parser is lighter and faster, Cheerio has a fuller jQuery-style API. How to choose between them.

