3:I[4707,[],""]
6:I[6423,[],""]
8:I[2972,["972","static/chunks/972-5c59758923e28d42.js","202","static/chunks/app/%5Blocale%5D/guides/%5Bslug%5D/page-218cc45de949c3d3.js"],""]
4:["locale","en","d"]
5:["slug","rag-without-vector-database","d"]
0:["WrGklf9BQ3ZZUJK-OObp-",[[["",{"children":[["locale","en","d"],{"children":["guides",{"children":[["slug","rag-without-vector-database","d"],{"children":["__PAGE__?{\"locale\":\"en\",\"slug\":\"rag-without-vector-database\"}",{}]}]}]}]},"$undefined","$undefined",true],["",{"children":[["locale","en","d"],{"children":["guides",{"children":[["slug","rag-without-vector-database","d"],{"children":["__PAGE__",{},[["$L1","$L2",null],null],null]},[null,["$","$L3",null,{"parallelRouterKey":"children","segmentPath":["children","$4","children","guides","children","$5","children"],"error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L6",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":"$undefined","notFoundStyles":"$undefined"}]],null]},[null,["$","$L3",null,{"parallelRouterKey":"children","segmentPath":["children","$4","children","guides","children"],"error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L6",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":"$undefined","notFoundStyles":"$undefined"}]],null]},[[[["$","link","0",{"rel":"stylesheet","href":"/_next/static/css/e1c2344b7b83da59.css","precedence":"next","crossOrigin":"$undefined"}]],"$L7"],null],null]},[[null,["$","$L3",null,{"parallelRouterKey":"children","segmentPath":["children"],"error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L6",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":["$","html",null,{"lang":"en","children":["$","body",null,{"className":"min-h-screen bg-background text-foreground flex items-center justify-center","children":["$","div",null,{"className":"text-center space-y-6 px-4","children":[["$","h1",null,{"className":"text-6xl font-bold text-primary","children":"404"}],["$","p",null,{"className":"text-xl text-muted-foreground","children":"Page not found"}],["$","$L8",null,{"href":"/en","className":"inline-block px-6 py-3 bg-primary text-primary-foreground rounded-lg font-semibold hover:bg-primary/90 transition-colors","children":"Go to Homepage"}]]}]}]}],"notFoundStyles":[]}]],null],null],["$L9",null]]]]
c:I[346,["972","static/chunks/972-5c59758923e28d42.js","203","static/chunks/app/%5Blocale%5D/layout-eaa8629840a87bdd.js"],"ThemeProvider"]
d:I[6932,["972","static/chunks/972-5c59758923e28d42.js","203","static/chunks/app/%5Blocale%5D/layout-eaa8629840a87bdd.js"],"LocaleSwitcher"]
e:I[9783,["972","static/chunks/972-5c59758923e28d42.js","203","static/chunks/app/%5Blocale%5D/layout-eaa8629840a87bdd.js"],"MobileMenu"]
f:I[8003,["972","static/chunks/972-5c59758923e28d42.js","203","static/chunks/app/%5Blocale%5D/layout-eaa8629840a87bdd.js"],""]
a:T451,{"@context":"https://schema.org","@type":"FAQPage","mainEntity":[{"@type":"Question","name":"When should I skip a vector database?","acceptedAnswer":{"@type":"Answer","text":"When your corpus is under ~10,000 documents, updates are infrequent, or you want to avoid infrastructure complexity. BM25 or TF-IDF retrieval with a simple index file can outperform vector search on keyword-heavy technical content."}},{"@type":"Question","name":"Is vector-free RAG less accurate?","acceptedAnswer":{"@type":"Answer","text":"Not necessarily. For keyword-rich domains (legal, technical documentation, code), lexical retrieval often matches or beats semantic search. The gap widens in favor of vectors only for ambiguous natural-language queries over diverse content."}},{"@type":"Question","name":"Can I combine BM25 with EU-hosted inference?","acceptedAnswer":{"@type":"Answer","text":"Yes. BM25 retrieval runs locally — no data leaves your environment. Only the retrieved context is sent to the LLM for generation. With JuiceFactory's zero-retention API, the entire pipeline keeps data within your control."}}]}b:Tae0,"""
Minimal RAG pipeline using JuiceFactory EU-hosted inference.
No vector database — embeddings stored in a single JSON file.
"""

import json
import os
from pathlib import Path
from typing import List, Dict, Optional
from openai import OpenAI

API_KEY = os.environ["JUICEFACTORY_API_KEY"]
BASE_URL = "https://api.juicefactory.ai/v1"
EMBEDDING_MODEL = "qwen3-embed"
CHAT_MODEL = "qwen3-vl"
INDEX_PATH = Path("rag_index.json")

client = OpenAI(api_key=API_KEY, base_url=BASE_URL)


def chunk_text(text: str, size: int = 512, overlap: int = 64) -> List[str]:
    """Split text into overlapping word chunks."""
    words = text.split()
    chunks = []
    for i in range(0, len(words), size - overlap):
        chunk = " ".join(words[i:i + size])
        if chunk.strip():
            chunks.append(chunk)
    return chunks


def embed(texts: List[str]) -> List[List[float]]:
    """Generate embeddings via qwen3-embed."""
    response = client.embeddings.create(model=EMBEDDING_MODEL, input=texts)
    return [item.embedding for item in response.data]


def cosine_similarity(a: List[float], b: List[float]) -> float:
    dot = sum(x * y for x, y in zip(a, b))
    norm_a = sum(x * x for x in a) ** 0.5
    norm_b = sum(x * x for x in b) ** 0.5
    return dot / (norm_a * norm_b) if norm_a and norm_b else 0.0


def build_index(documents: Dict[str, str]) -> List[Dict]:
    """Embed all chunks from all documents into a flat index."""
    index = []
    for doc_id, text in documents.items():
        for chunk in chunk_text(text):
            index.append({"doc_id": doc_id, "text": chunk})
    embeddings = embed([item["text"] for item in index])
    for item, vec in zip(index, embeddings):
        item["embedding"] = vec
    INDEX_PATH.write_text(json.dumps(index))
    return index


def load_index() -> Optional[List[Dict]]:
    if not INDEX_PATH.exists():
        return None
    return json.loads(INDEX_PATH.read_text())


def retrieve(query: str, index: List[Dict], k: int = 3) -> List[Dict]:
    query_vec = embed([query])[0]
    scored = [(item, cosine_similarity(query_vec, item["embedding"])) for item in index]
    scored.sort(key=lambda x: x[1], reverse=True)
    return [item for item, _ in scored[:k]]


def answer(query: str, index: List[Dict]) -> str:
    chunks = retrieve(query, index)
    context = "\n\n".join(f"[{c['doc_id']}] {c['text']}" for c in chunks)
    response = client.chat.completions.create(
        model=CHAT_MODEL,
        messages=[
            {"role": "system", "content": "Answer based only on the provided context. If the answer is not in the context, say so."},
            {"role": "user", "content": f"Context:\n{context}\n\nQuestion: {query}"},
        ],
        temperature=0.2,
    )
    return response.choices[0].message.content
2:[["$","script",null,{"type":"application/ld+json","dangerouslySetInnerHTML":{"__html":"{\"@context\":\"https://schema.org\",\"@type\":\"BreadcrumbList\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https://juicefactory.ai/en/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Guides\",\"item\":\"https://juicefactory.ai/en/guides\"},{\"@type\":\"ListItem\",\"position\":3,\"name\":\"RAG Without a Vector Database\",\"item\":\"https://juicefactory.ai/en/guides/rag-without-vector-database\"}]}"}}],["$","script",null,{"type":"application/ld+json","dangerouslySetInnerHTML":{"__html":"$a"}}],["$","script",null,{"type":"application/ld+json","dangerouslySetInnerHTML":{"__html":"{\"@context\":\"https://schema.org\",\"@type\":\"TechArticle\",\"headline\":\"RAG Without a Vector Database: Minimal Python Pipeline in 100 Lines (2026)\",\"description\":\"Build complete RAG in pure Python — no Qdrant, no Pinecone, no Docker. EU-hosted embeddings via qwen3-embed, persistence to JSON, full code under 100 lines.\",\"author\":{\"@type\":\"Organization\",\"name\":\"Juice Factory\",\"url\":\"https://juicefactory.ai\"},\"publisher\":{\"@type\":\"Organization\",\"name\":\"Juice Factory\",\"logo\":{\"@type\":\"ImageObject\",\"url\":\"https://juicefactory.ai/logo-opengraph.png\"}},\"url\":\"https://juicefactory.ai/en/guides/rag-without-vector-database\",\"datePublished\":\"2025-01-15\",\"dateModified\":\"2026-05-15\",\"inLanguage\":\"en\",\"mainEntityOfPage\":{\"@type\":\"WebPage\",\"@id\":\"https://juicefactory.ai/en/guides/rag-without-vector-database\"}}"}}],["$","section",null,{"className":"pt-32 pb-20 px-4","children":["$","div",null,{"className":"container mx-auto max-w-4xl","children":["$","article",null,{"className":"prose prose-neutral dark:prose-invert max-w-none","children":[["$","h1","h1-0",{"children":"RAG Without a Vector Database: A Minimal Python Pipeline (Under 100 Lines)"}],"\n",["$","p","p-0",{"children":"Most RAG tutorials reach for Qdrant, Pinecone, or pgvector before they show you a single embedding. For a small or medium document corpus that's overengineering — you don't need a separate database, a Docker container, or a query DSL to do retrieval well. You need a list, a dot product, and an embedding model."}],"\n",["$","p","p-1",{"children":"This guide builds a complete retrieval-augmented generation pipeline in pure Python with no external infrastructure. It runs against EU-hosted inference (qwen3-embed for embeddings, qwen3-vl for generation), persists embeddings to a single JSON file, and answers questions in under 100 lines."}],"\n",["$","p","p-2",{"children":"It's the fastest way to get RAG running for prototyping, internal tools, or any corpus under ~10,000 chunks. We'll also cover the signals that tell you when it's time to graduate to a real vector database."}],"\n",["$","h2","h2-0",{"children":"When you don't need a vector database"}],"\n",["$","p","p-3",{"children":"A vector database earns its place when one of these is true:"}],"\n",["$","ul","ul-0",{"children":["\n",["$","li","li-0",{"children":["Your corpus has ",["$","strong","strong-0",{"children":">100,000 chunks"}]," and full scans become slow (>500ms)"]}],"\n",["$","li","li-1",{"children":["You need ",["$","strong","strong-0",{"children":"filtered retrieval"}]," (search inside a tenant, a date range, a category)"]}],"\n",["$","li","li-2",{"children":["You need ",["$","strong","strong-0",{"children":"hybrid search"}]," (BM25 + vector) or ",["$","strong","strong-1",{"children":"rerankers"}]," at scale"]}],"\n",["$","li","li-3",{"children":["You have ",["$","strong","strong-0",{"children":"multiple writers"}]," and need transactional consistency"]}],"\n"]}],"\n",["$","p","p-4",{"children":"For everything below that — internal documentation search, customer support knowledge bases up to a few thousand articles, personal RAG over your own writing, single-tenant SaaS with bounded corpora — a list of vectors in memory does the job. Cosine similarity over 10,000 768-dim vectors takes ~5ms in pure Python on a laptop. Adding a vector DB adds 100ms of network latency and an operational dependency."}],"\n",["$","h2","h2-1",{"children":"Prerequisites"}],"\n",["$","ul","ul-1",{"children":["\n",["$","li","li-0",{"children":"Python 3.9+"}],"\n",["$","li","li-1",{"children":["A JuiceFactory API key — ",["$","a","a-0",{"href":"https://portal.juicefactory.ai/auth/signup","children":"free signup"}]]}],"\n",["$","li","li-2",{"children":[["$","code","code-0",{"children":"openai"}]," Python package: ",["$","code","code-1",{"children":"pip install openai"}]]}],"\n"]}],"\n",["$","p","p-5",{"children":"That's it. No Docker, no extra services."}],"\n",["$","h2","h2-2",{"children":"The full pipeline"}],"\n",["$","p","p-6",{"children":["Here is the entire RAG system. Save it as ",["$","code","code-0",{"children":"rag.py"}],":"]}],"\n",["$","pre","pre-0",{"children":["$","code","code-0",{"className":"language-python","children":"$b"}]}],"\n",["$","p","p-7",{"children":"Ninety-three lines, including blank lines and docstrings. Use it like this:"}],"\n",["$","pre","pre-1",{"children":["$","code","code-0",{"className":"language-python","children":"docs = {\n    \"policy.txt\": Path(\"policy.txt\").read_text(),\n    \"handbook.txt\": Path(\"handbook.txt\").read_text(),\n}\nindex = load_index() or build_index(docs)\nprint(answer(\"What is the parental leave policy?\", index))\n"}]}],"\n",["$","p","p-8",{"children":["The first run embeds the corpus and writes ",["$","code","code-0",{"children":"rag_index.json"}],". Subsequent runs skip the embedding step and load the index from disk. For 200 medium-length documents (~2000 chunks at 512 words), the index file is around 60 MB and loads in under a second."]}],"\n",["$","h2","h2-3",{"children":"Why this works"}],"\n",["$","p","p-9",{"children":"The pipeline has four moving parts:"}],"\n",["$","ol","ol-0",{"children":["\n",["$","li","li-0",{"children":[["$","strong","strong-0",{"children":"Chunking"}]," — fixed-size word windows with overlap. Crude but effective for prose."]}],"\n",["$","li","li-1",{"children":[["$","strong","strong-0",{"children":"Embedding"}]," — qwen3-embed produces 2560-dimensional vectors via the JuiceFactory API. Every embedding call stays in EU jurisdiction."]}],"\n",["$","li","li-2",{"children":[["$","strong","strong-0",{"children":"Retrieval"}]," — cosine similarity over the in-memory index. O(n) per query, but n is small."]}],"\n",["$","li","li-3",{"children":[["$","strong","strong-0",{"children":"Generation"}]," — qwen3-vl answers using the top-k chunks as context, with a strict instruction to ground its answer."]}],"\n"]}],"\n",["$","p","p-10",{"children":["Both the embedding model and the chat model run on JuiceFactory's stateless EU infrastructure. No prompt or embedding is retained server-side. Your ",["$","code","code-0",{"children":"rag_index.json"}]," is the only place embeddings exist, and it lives on your machine."]}],"\n",["$","h2","h2-4",{"children":"Adding PII filtering before embedding"}],"\n",["$","p","p-11",{"children":"GDPR-sensitive corpora benefit from stripping personal data before chunks are embedded. Here is a minimal regex-based filter — for production you'd want Microsoft Presidio or a similar NER-based tool, but this catches the common cases:"}],"\n",["$","pre","pre-2",{"children":["$","code","code-0",{"className":"language-python","children":"import re\n\nPII_PATTERNS = [\n    (re.compile(r\"[\\w.+-]+@[\\w-]+\\.[\\w.-]+\"), \"[EMAIL]\"),\n    (re.compile(r\"\\+?\\d[\\d\\s().-]{8,}\\d\"), \"[PHONE]\"),\n    (re.compile(r\"\\b\\d{6}[-+]\\d{4}\\b\"), \"[PERSONNUMMER]\"),  # Swedish personnr\n]\n\ndef redact_pii(text: str) -> str:\n    for pattern, label in PII_PATTERNS:\n        text = pattern.sub(label, text)\n    return text\n"}]}],"\n",["$","p","p-12",{"children":["Apply ",["$","code","code-0",{"children":"redact_pii()"}]," to each chunk inside ",["$","code","code-1",{"children":"build_index()"}]," before embedding. The redacted form is what gets stored — original PII never reaches the embedding model and never lands in your index."]}],"\n",["$","h2","h2-5",{"children":"Persisting the index efficiently"}],"\n",["$","p","p-13",{"children":"A single JSON file is fine up to a few thousand chunks. Beyond that it gets slow to load. Two simple upgrades:"}],"\n",["$","pre","pre-3",{"children":["$","code","code-0",{"className":"language-python","children":"import pickle\nimport gzip\n\n# Replace JSON write/read with pickle + gzip\ndef save_index(index, path=\"rag_index.pkl.gz\"):\n    with gzip.open(path, \"wb\") as f:\n        pickle.dump(index, f)\n\ndef load_index_pickle(path=\"rag_index.pkl.gz\"):\n    with gzip.open(path, \"rb\") as f:\n        return pickle.load(f)\n"}]}],"\n",["$","p","p-14",{"children":"Pickled and gzipped, the same 60 MB JSON shrinks to about 25 MB and loads in roughly half the time."}],"\n",["$","p","p-15",{"children":"For corpora over 50,000 chunks, switch the cosine-similarity loop to NumPy:"}],"\n",["$","pre","pre-4",{"children":["$","code","code-0",{"className":"language-python","children":"import numpy as np\n\ndef build_matrix(index):\n    return np.array([item[\"embedding\"] for item in index], dtype=np.float32)\n\ndef retrieve_np(query: str, index, matrix, k: int = 3):\n    q = np.array(embed([query])[0], dtype=np.float32)\n    q /= np.linalg.norm(q)\n    matrix_norm = matrix / np.linalg.norm(matrix, axis=1, keepdims=True)\n    scores = matrix_norm @ q\n    top = np.argsort(-scores)[:k]\n    return [index[i] for i in top]\n"}]}],"\n",["$","p","p-16",{"children":"This vectorises the search. For 50,000 chunks of 2560-dim float32 it scans in ~30ms — still under the latency budget for an interactive tool, still no vector database."}],"\n",["$","h2","h2-6",{"children":"When to graduate to a real vector DB"}],"\n",["$","p","p-17",{"children":"Switch when one of these is true:"}],"\n",["$","ul","ul-2",{"children":["\n",["$","li","li-0",{"children":[["$","strong","strong-0",{"children":"Index size exceeds 100k chunks"}]," and NumPy scans cross 200ms"]}],"\n",["$","li","li-1",{"children":[["$","strong","strong-0",{"children":"You need filtered search"}]," — for example, \"search only chunks belonging to tenant X\" or \"documents from the last 30 days\""]}],"\n",["$","li","li-2",{"children":[["$","strong","strong-0",{"children":"You have concurrent writers"}]," modifying the index from multiple processes"]}],"\n",["$","li","li-3",{"children":[["$","strong","strong-0",{"children":"Hybrid search matters"}]," — you want BM25 lexical scores blended with vector scores"]}],"\n",["$","li","li-4",{"children":[["$","strong","strong-0",{"children":"Operational requirements demand it"}]," — a real vector DB gives you snapshots, replicas, monitoring, and access controls"]}],"\n"]}],"\n",["$","p","p-18",{"children":["For that path, our ",["$","a","a-0",{"href":"/en/guides/rag-python-gdpr-document-search","children":"production RAG guide"}]," walks through the FastAPI + PyMuPDF + Qdrant stack with the same EU-only inference path. Same JuiceFactory API, just bigger plumbing."]}],"\n",["$","h2","h2-7",{"children":"Cost estimate"}],"\n",["$","p","p-19",{"children":"For a typical 200-document corpus with ~2,000 chunks of 512 words each:"}],"\n",["$","table","table-0",{"children":[["$","thead","thead-0",{"children":["$","tr","tr-0",{"children":[["$","th","th-0",{"children":"Step"}],["$","th","th-1",{"children":"Tokens"}],["$","th","th-2",{"children":"Cost (qwen3-embed)"}]]}]}],["$","tbody","tbody-0",{"children":[["$","tr","tr-0",{"children":[["$","td","td-0",{"children":"One-time index build"}],["$","td","td-1",{"children":"~1M tokens"}],["$","td","td-2",{"children":"~€0.05"}]]}],["$","tr","tr-1",{"children":[["$","td","td-0",{"children":"Per query (1 embedding + retrieval + generation)"}],["$","td","td-1",{"children":"~2K tokens"}],["$","td","td-2",{"children":"~€0.001"}]]}]]}]]}],"\n",["$","p","p-20",{"children":"Re-running the index is rare — only when documents change. The marginal cost of a query is dominated by the generation step, which is the same whether you use a vector DB or not."}],"\n",["$","h2","h2-8",{"children":"Summary"}],"\n",["$","p","p-21",{"children":"You don't need a vector database to do RAG. For small and medium corpora, an in-memory list with cosine similarity is faster, simpler, and easier to debug than any external service. Add NumPy when you cross 50k chunks. Switch to a real vector DB when you cross 100k or need filtering, hybrid search, or operational features."}],"\n",["$","p","p-22",{"children":"Either way, keep inference in the EU. JuiceFactory's stateless qwen3-embed and qwen3-vl run on Swedish infrastructure with zero retention by default — your prompts, chunks, and answers never leave the boundary."}],"\n",["$","p","p-23",{"children":["$","a","a-0",{"href":"https://portal.juicefactory.ai/auth/signup","children":"Get an API key →"}]}]]}]}]}],["$","section",null,{"className":"py-12 px-4","children":["$","div",null,{"className":"container mx-auto max-w-4xl","children":[["$","h2",null,{"className":"text-2xl font-bold mb-6 text-foreground","children":"Related Guides"}],["$","div",null,{"className":"grid gap-4 sm:grid-cols-2 lg:grid-cols-3","children":[["$","$L8","gdpr-safe-ai-inference",{"href":"/en/guides/gdpr-safe-ai-inference","className":"block rounded-lg border border-border bg-card p-5 transition-colors hover:bg-accent hover:text-accent-foreground","children":["$","h3",null,{"className":"text-sm font-semibold leading-snug text-foreground","children":"GDPR-Safe AI Inference"}]}],["$","$L8","rag-python-gdpr-document-search",{"href":"/en/guides/rag-python-gdpr-document-search","className":"block rounded-lg border border-border bg-card p-5 transition-colors hover:bg-accent hover:text-accent-foreground","children":["$","h3",null,{"className":"text-sm font-semibold leading-snug text-foreground","children":"RAG in Python: GDPR-Safe Document Search"}]}],["$","$L8","rag-with-qwen",{"href":"/en/guides/rag-with-qwen","className":"block rounded-lg border border-border bg-card p-5 transition-colors hover:bg-accent hover:text-accent-foreground","children":["$","h3",null,{"className":"text-sm font-semibold leading-snug text-foreground","children":"RAG with Qwen: Private Document Search"}]}]]}]]}]}],["$","section",null,{"className":"py-16 px-4","children":["$","div",null,{"className":"container mx-auto max-w-4xl","children":["$","div",null,{"className":"bg-gradient-to-br from-primary/5 to-secondary/5 backdrop-blur-sm border border-primary/30 rounded-lg p-12 space-y-6 text-center","children":[["$","h2",null,{"className":"text-3xl md:text-4xl font-bold","children":"Try JuiceFactory — Free API Key in 30 Seconds"}],["$","p",null,{"className":"text-xl text-muted-foreground max-w-2xl mx-auto","children":"EU-hosted, zero-retention, OpenAI-compatible. 10,000 free tokens, no credit card."}],["$","div",null,{"className":"flex flex-col sm:flex-row gap-4 justify-center pt-4","children":[["$","a",null,{"href":"https://portal.juicefactory.ai/auth/signup","className":"px-8 py-4 bg-primary text-primary-foreground rounded-lg text-lg font-semibold hover:bg-primary/90 transition-colors inline-block","children":"Get a free API key"}],["$","$L8",null,{"href":"/en/guides/migrate-openai-to-eu-api","className":"px-8 py-4 border border-border rounded-lg text-lg font-semibold hover:bg-accent transition-colors inline-block","children":"Read the migration guide"}]]}]]}]}]}]]
7:["$","html",null,{"lang":"en","suppressHydrationWarning":true,"children":[["$","head",null,{}],["$","body",null,{"className":"min-h-screen bg-background text-foreground antialiased","children":[["$","$Lc",null,{"attribute":"class","defaultTheme":"dark","enableSystem":true,"disableTransitionOnChange":true,"children":[["$","nav",null,{"className":"fixed top-0 left-0 right-0 z-50 bg-background/80 backdrop-blur-md border-b border-border","children":["$","div",null,{"className":"container mx-auto px-4 h-16 flex items-center justify-between","children":[["$","$L8",null,{"href":"/en","className":"flex items-center gap-2","children":[["$","svg",null,{"className":"w-8 h-8 text-primary animate-pulse-glow","fill":"none","viewBox":"0 0 24 24","stroke":"currentColor","children":["$","path",null,{"strokeLinecap":"round","strokeLinejoin":"round","strokeWidth":2,"d":"M13 10V3L4 14h7v7l9-11h-7z"}]}],["$","span",null,{"className":"text-2xl font-bold bg-gradient-primary bg-clip-text text-transparent","children":"Juice Factory"}]]}],["$","div",null,{"className":"hidden md:flex items-center gap-6","children":[["$","$L8",null,{"href":"/en/defense","className":"text-sm text-muted-foreground hover:text-foreground transition-colors","children":"Defense"}],["$","$L8",null,{"href":"/en/tech","className":"text-sm text-muted-foreground hover:text-foreground transition-colors","children":"Technology"}],["$","$L8",null,{"href":"/en/byok","className":"text-sm text-muted-foreground hover:text-foreground transition-colors","children":"BYOK"}],["$","$L8",null,{"href":"/en/private-ai-for-business","className":"text-sm text-muted-foreground hover:text-foreground transition-colors","children":"Private AI"}],["$","$L8",null,{"href":"/en/guides","className":"text-sm text-muted-foreground hover:text-foreground transition-colors","children":"Guides"}],["$","$L8",null,{"href":"/en/trust","className":"text-sm text-muted-foreground hover:text-foreground transition-colors","children":"Trust Center"}],["$","$Ld",null,{"currentLocale":"en"}],["$","a",null,{"href":"https://portal.juicefactory.ai/auth/login","className":"px-4 py-2 bg-primary text-primary-foreground rounded-md text-sm font-medium hover:bg-primary/90 transition-colors","children":"Login"}]]}],["$","$Le",null,{"locale":"en","nav":{"home":"Home","defense":"Defense","tech":"Technology","byok":"BYOK","privateAi":"Private AI","guides":"Guides","trust":"Trust Center","login":"Login"}}]]}]}],["$","$L3",null,{"parallelRouterKey":"children","segmentPath":["children","$4","children"],"error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L6",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":"$undefined","notFoundStyles":"$undefined"}],["$","footer",null,{"className":"py-12 px-4 border-t border-border","children":["$","div",null,{"className":"container mx-auto flex flex-col md:flex-row justify-between items-center gap-4","children":[["$","p",null,{"className":"text-sm text-muted-foreground","children":["© ",2026," ","Juice Factory. All rights reserved."]}],["$","div",null,{"className":"flex gap-6","children":[["$","$L8",null,{"href":"/en/trust","className":"text-sm text-muted-foreground hover:text-foreground transition-colors","children":"Trust Center"}],["$","$L8",null,{"href":"/en/security","className":"text-sm text-muted-foreground hover:text-foreground transition-colors","children":"Security"}],["$","$L8",null,{"href":"/en/data-processing","className":"text-sm text-muted-foreground hover:text-foreground transition-colors","children":"Data Processing"}],["$","$L8",null,{"href":"/en/privacy","className":"text-sm text-muted-foreground hover:text-foreground transition-colors","children":"Privacy Policy"}],["$","$L8",null,{"href":"/en/terms","className":"text-sm text-muted-foreground hover:text-foreground transition-colors","children":"Terms of Service"}]]}]]}]}]]}],["$","$Lf",null,{"src":"https://www.googletagmanager.com/gtag/js?id=G-HGZMPNZK5F","strategy":"afterInteractive"}],["$","$Lf",null,{"id":"ga4-init","strategy":"afterInteractive","children":"\n            window.dataLayer = window.dataLayer || [];\n            function gtag(){dataLayer.push(arguments);}\n            gtag('js', new Date());\n            gtag('config', 'G-HGZMPNZK5F');\n          "}],["$","$Lf",null,{"id":"matomo-init","strategy":"afterInteractive","children":"\n            var _paq = window._paq = window._paq || [];\n            _paq.push(['trackPageView']);\n            _paq.push(['enableLinkTracking']);\n            (function() {\n              var u=\"https://matomo.manprogroup.com/\";\n              _paq.push(['setTrackerUrl', u+'matomo.php']);\n              _paq.push(['setSiteId', '14']);\n              var d=document, g=d.createElement('script'), s=d.getElementsByTagName('script')[0];\n              g.async=true; g.src=u+'matomo.js'; s.parentNode.insertBefore(g,s);\n            })();\n          "}]]}]]}]
9:[["$","meta","0",{"name":"viewport","content":"width=device-width, initial-scale=1"}],["$","meta","1",{"charSet":"utf-8"}],["$","title","2",{"children":"RAG Without a Vector Database: Minimal Python Pipeline in 100 Lines (2026)"}],["$","meta","3",{"name":"description","content":"Build complete RAG in pure Python — no Qdrant, no Pinecone, no Docker. EU-hosted embeddings via qwen3-embed, persistence to JSON, full code under 100 lines."}],["$","link","4",{"rel":"canonical","href":"https://juicefactory.ai/en/guides/rag-without-vector-database"}],["$","link","5",{"rel":"alternate","hrefLang":"en","href":"https://juicefactory.ai/en/guides/rag-without-vector-database"}],["$","link","6",{"rel":"alternate","hrefLang":"sv","href":"https://juicefactory.ai/sv/guides/rag-without-vector-database"}],["$","link","7",{"rel":"alternate","hrefLang":"de","href":"https://juicefactory.ai/de/guides/rag-without-vector-database"}],["$","link","8",{"rel":"alternate","hrefLang":"fr","href":"https://juicefactory.ai/fr/guides/rag-without-vector-database"}],["$","link","9",{"rel":"alternate","hrefLang":"x-default","href":"https://juicefactory.ai/en/guides/rag-without-vector-database"}],["$","meta","10",{"property":"og:title","content":"RAG Without a Vector Database: Minimal Python Pipeline in 100 Lines (2026)"}],["$","meta","11",{"property":"og:description","content":"Build complete RAG in pure Python — no Qdrant, no Pinecone, no Docker. EU-hosted embeddings via qwen3-embed, persistence to JSON, full code under 100 lines."}],["$","meta","12",{"property":"og:url","content":"https://juicefactory.ai/en/guides/rag-without-vector-database"}],["$","meta","13",{"property":"og:site_name","content":"Juice Factory"}],["$","meta","14",{"property":"og:locale","content":"en"}],["$","meta","15",{"property":"og:image:alt","content":"Juice Factory AI Guide"}],["$","meta","16",{"property":"og:image:type","content":"image/png"}],["$","meta","17",{"property":"og:image","content":"http://localhost:3000/en/guides/rag-without-vector-database/opengraph-image?b184df05bd4ad81a"}],["$","meta","18",{"property":"og:image:width","content":"1200"}],["$","meta","19",{"property":"og:image:height","content":"630"}],["$","meta","20",{"property":"og:type","content":"article"}],["$","meta","21",{"name":"twitter:card","content":"summary_large_image"}],["$","meta","22",{"name":"twitter:title","content":"RAG Without a Vector Database: Minimal Python Pipeline in 100 Lines (2026)"}],["$","meta","23",{"name":"twitter:description","content":"Build complete RAG in pure Python — no Qdrant, no Pinecone, no Docker. EU-hosted embeddings via qwen3-embed, persistence to JSON, full code under 100 lines."}],["$","meta","24",{"name":"twitter:image","content":"https://juicefactory.ai/logo-opengraph.png"}],["$","link","25",{"rel":"icon","href":"/favicon.png"}]]
1:null
