01
Title
  • Let people settle in — don't rush the opener
  • Brief framing: "This is a practical session — we're going to build something, look at real code, and by the end you'll understand how to add semantic search to any CF app"
  • No deep math, no PhD required — just ColdFusion developers building useful things
02
About Me
  • Keep it brief — name, background, why you're qualified to talk about this
  • Mention you helped write the CF certification — establishes credibility without bragging
  • The NAS setup is relevant — you ran this on real hardware with real constraints, not a cloud VM
  • @DFGrumpy is on X if people want to follow up after
03
Roadmap — What we'll cover
  • Set expectations: 4 sections, two live demos, real CF code throughout
  • Emphasize: "We're not building AI — we're using AI tools that CF 2025 already supports"
  • LLM and RAG are terms they'll hear — define them early so nobody feels lost
  • The goal isn't theory — it's a pattern they can apply to their own data today
04
You've built search before
  • This is the "you've been here" moment — make it relatable
  • Ask the room: "How many of you have built a search feature with LIKE or full-text search?" — pause for hands
  • The silent failure and confident wrong answer are the two things that make users lose trust in search
  • The real problem card is the payoff: the code is fine, keyword search just wasn't designed for meaning
  • Don't linger — this isn't a problem slide, it's a setup
05
Zero keyword overlap
  • The example here is the killer demo of the concept — "CF server not responding" vs "how do I restart the service" share zero words
  • A user typed those both into your search box expecting the same answer — keyword search treats them as completely unrelated
  • This is why semantic search exists — not as a trend, but as a solution to a real problem your users have right now
06
There's a better way
  • Pivot from problem to solution — keep energy up here
  • "Semantic search" — matches on meaning, not words
  • Key point: runs on your own infrastructure, no cloud required — this matters for CF shops with sensitive data
  • The "available in CF 2025 today" pod is the hook — this isn't a future thing, it's shipping now
  • Good moment to say: "This is what we're going to build today"
07
What are embeddings?
  • This is the one concept people need to grok — don't rush it
  • The dog/cat/car example: similar things are close together in the space. Unrelated things are far apart.
  • You don't need to know the math — you just need to know that meaning gets encoded as distance
  • The model does all the work — you give it text, it gives you numbers
  • "You've been using embeddings every time you use Google Translate or Spotify recommendations"
08
Vectors as coordinates
  • The visualization is doing the heavy lifting here — let them look at it
  • Point out the clusters: similar words are near each other even though they weren't explicitly linked
  • "King minus man plus woman equals queen" — the classic example of vector arithmetic capturing meaning
  • In reality, vectors are 768+ dimensions, not 2D — but the same principle applies
  • Pause here — this is the "aha" slide for a lot of people
09
How text becomes numbers
  • Walk through the pipeline: text → embedding model → array of floats → stored in vector DB
  • The same pipeline runs at query time — your search phrase gets embedded the same way
  • The numbers themselves are meaningless to humans — what matters is the distance between them
  • Emphasize: you never see or manipulate the numbers directly — CF's API abstracts all of it
10
Similarity scores
  • Cosine similarity is the most common metric — it measures angle, not distance
  • Score of 1.0 = identical. Score near 0 = unrelated. Negative scores = opposite meaning.
  • In practice: 0.85+ is a strong match, 0.70–0.85 is relevant, below 0.70 is often noise
  • You set minScore in CF — start at 0.70 and tune based on your data
  • Show the demo scores later — let them see what "0.92 vs 0.71" feels like in practice
11
Embeddings recap
  • Quick consolidation before moving to the vector store — four key takeaways on screen
  • The transition line is important: "Now that we know what vectors are — where do they live?"
  • Don't spend more than 60 seconds here — it's a bridge slide
12
What is a Vector Store
  • A vector store is a database optimized for nearest-neighbor search — not exact match
  • Regular DB: "give me the row where id=42". Vector store: "give me the 5 rows closest to this query vector"
  • ANN (approximate nearest neighbor) is the key algorithm — fast because it trades perfect accuracy for speed
  • The five providers CF supports: InMemory, Milvus, Pinecone, Qdrant, Chroma — same API for all of them
13
What is Ollama
  • Ollama is the local model runner — it's what converts text to vectors on your own hardware
  • No cloud, no API keys, no data leaving your network — huge selling point for enterprise CF shops
  • Two types of models: LLMs (chat/generation) and embedding models — we're using embedding models here
  • nomic-embed-text is the recommended embedding model — fast, 768 dimensions, runs well on CPU
  • Install is a single command — it's not a big infrastructure ask
14
CF 2025 Vector Features
  • CF 2025 Update 8 is the minimum — make sure they know to check their update level
  • The single VectorStore() API is the key innovation — one interface, five backends
  • InMemory is perfect for dev/testing — zero setup, but data doesn't survive restarts
  • The five providers are all first-class — Adobe isn't treating Qdrant as an afterthought
15
CF + Ollama Architecture
  • Let them read the diagram — it clicks for visual learners
  • CF is the orchestrator: it takes user input, calls Ollama to embed the query, calls the vector store to search
  • Ollama sits between CF and the vector store — it's not optional, you always need an embedding model
  • The vector store just stores and searches — it has no idea what the numbers mean
  • This is all on your own infrastructure — no cloud dependency in the critical path
16
CF 2025 embedding code
  • Walk through the config struct — each key maps directly to an infrastructure component
  • provider: "inmemory" means vectors live in RAM — swap to "qdrant" for production
  • The embedding model config is nested inside the store config — CF wires them together automatically
  • Point out: this is the only setup code you write. Everything else is add() and search().
  • Good time to say: "If you can write a struct, you can connect to a vector store"
17
Two Providers Explained
  • The two providers are completely independent — stress this
  • Provider 1 = WHERE vectors are stored (Qdrant, InMemory, Milvus, etc.)
  • Provider 2 = HOW text becomes vectors (Ollama, OpenAI, Azure, etc.)
  • You can mix and match freely: InMemory + OpenAI, Qdrant + Ollama, Pinecone + Azure — CF doesn't care
  • "Think of it like a database connection vs a password hasher — separate concerns, same config struct"
18
VectorStore Config Code
  • This is the production config — Qdrant + Ollama on localhost
  • dimension: 768 must match nomic-embed-text — Qdrant will reject inserts that don't match
  • Collection is auto-created on first VectorStore() call — no DDL, no manual Qdrant setup
  • The URL points to wherever Qdrant is running — local Docker, NAS, or remote server
  • Next slide shows what this collection actually looks like inside Qdrant's UI
18b
Qdrant Visualization
  • This is what your vector collection looks like inside Qdrant's dashboard after ingestion
  • Each dot is an embedded document chunk — proximity = semantic similarity
  • Qdrant has a built-in web UI at port 6333 — nothing to install, it's part of the Docker container
  • The clusters you can see here correspond to topic groupings in the source documents
  • You can browse, filter, and inspect individual vectors directly in the UI — great for debugging ingest
19
Add and Search
  • add() takes a string and a metadata struct — that's it. CF calls Ollama, gets the vector, stores it.
  • The metadata struct is yours — store whatever you need to reconstruct the result in your UI
  • search() takes text — CF embeds the query, finds nearest vectors, returns ranked results
  • topK = max results. minScore = relevance threshold. filter = metadata narrowing
  • Results are an array of structs: id, text, metadata, score, vector
20
Vector Search Result Dump
  • Show what a real result struct looks like — demystifies the output
  • The score field is the similarity score — this is how you rank and filter results
  • The metadata struct is exactly what you stored at ingest — source, type, docId etc.
  • You'll typically use text and metadata in your UI — ignore vector unless debugging
  • Good time to say: "In the demo you'll see these scores in real time"
21
Ingest Demo
  • Walk through the code before launching — left side is the ingest script
  • PDFs: extract text → chunk → index each chunk with source metadata
  • JSON: deserialize → concatenate title + body → index as a single document
  • JSON can't be ingested raw — it must be converted to a plain text string first
  • The metadata struct is your breadcrumb back to the source — always store type and source at minimum
  • Launch the demo — show the ingest running live
22
Ingest Deep Dive
  • Chunking: embedding models have token limits — too large = blurry or truncated vector
  • 500 words with 100-word overlap is a solid starting point — tune for your content
  • Overlap prevents ideas from being cut in half at chunk boundaries
  • Raw PDFs are binary — must extract text first with cfpdf action="extracttext"
  • Raw JSON is a data structure — must deserialize and concatenate fields into a string
  • Can't ingest: images, audio/video, spreadsheets, compiled code, pure numeric data, encrypted files
  • Can ingest (after conversion): PDFs, JSON, DB rows, Markdown/HTML, emails, transcripts
23
GPU vs No GPU
  • Personal war story — keep it light and self-deprecating
  • Synology NAS, CF 2025, Ollama — no GPU. Indexing a modest PDF set took nearly 20 minutes.
  • GPU runs matrix multiplications in parallel — CPU runs them sequentially. The difference is dramatic.
  • First query after cold start: 30-second wait while Ollama loads the model into RAM
  • 8GB shared between CF, Ollama, and Qdrant — memory pressure is real on low-spec hardware
  • Fix: moved Ollama to a separate machine with a GPU — now indexing is fast enough to demo live
  • For CPU-only: use nomic-embed-text or all-minilm, batch index offline, never real-time
  • Don't confuse LLMs (llama3, mistral) with embedding models — they are not the same thing
24
Hybrid: Vector + Metadata Filter
  • The filter narrows the candidate pool before semantic scoring — it doesn't replace it
  • Use case: multi-tenant data — one collection, filter by clientId per request
  • Use case: scoped search — PDFs only, or "admin" category only
  • minScore still applies within the filtered results — combine both for precision
  • You can only filter on metadata fields you stored at ingest time — plan your schema upfront
  • Multiple filter conditions are supported — type AND category together
25
Hybrid: Vector + SQL
  • The vector store finds what's relevant — your DB has everything else
  • The docId in metadata is the bridge — store it at ingest, use it to join at query time
  • Step 1: semantic search returns doc IDs and scores. Step 2: SQL query fetches full records.
  • Access control lives in your DB — apply row-level security in the SQL query, not in the vector store
  • Freshness: update DB records freely. Only re-index in the vector store if the text content changes.
  • "Let each system do what it's designed for" — this is the key takeaway for this pattern
26
Gotchas Part 1
  • Reveal one at a time — pause after each and let it sink in
  • Same model always: change models = all vectors become meaningless, must re-index everything from scratch
  • InMemory doesn't persist: CF app pool recycles, server restarts — everything gone. Use Qdrant in production.
  • Metadata schema: adding a new field later means re-indexing the entire collection. Design it once.
  • Duplicate detection: the store has no deduplication — track indexed docs in your own DB with a content hash
  • These are the four things that will cost you the most time if you don't plan for them
27
Final Live Demo
  • This is the payoff — the full semantic search demo over pre-indexed CF how-to docs
  • Try queries that would fail with keyword search — "CF won't start", "service not responding", "connection pool issues"
  • Point out the similarity scores in the results — show the difference between a 0.92 and a 0.71
  • Try a query with zero keyword overlap to the source docs — show it still finds the right answer
  • If something goes wrong: "And that's why we have a GPU slide" — keeps it light
28
Gotchas Part 2
  • minScore: unset = results for every query including garbage matches. Start at 0.70, log scores, tune it.
  • Don't index noise: boilerplate text (copyright notices, headers, footers) pollutes embeddings badly
  • Semantic search isn't SQL: looking up a user by email? That's a DB query. Semantic search is for natural language.
  • Short text: single words or very short phrases don't embed well — concatenate related fields for more signal
  • Good moment to ask: "Any of these feel familiar from your own projects?"
29
Recap
  • Six cards — walk through them quickly, one sentence each
  • This slide is good to leave up while people are thinking of questions
  • Reinforce the throughline: CF 2025 makes all of this native — no external frameworks, no extra install beyond Ollama and Qdrant
  • Transition to final slide: "GitHub link is on the next slide — grab the code before you leave"
30
Thank You / Q&A
  • Leave this slide up for the entire Q&A — QR code lets people scan the GitHub repo right now
  • Repo has the ingest scripts, demo app, and the slide deck itself
  • @DFGrumpy on X for follow-up questions after the conference
  • If someone asks something you don't know: "Great question — put it in a GitHub issue and I'll dig into it"
  • Watch the time — CFSummit sessions run tight. If you're running long, cut from Gotchas Part 2 first.
← swipe to navigate →