|2.265Mins Read

Building a Semantic Search Engine with LangChain: A Step-by-Step Guide

Authors

LangChain is a flexible framework that makes it easy to build applications using LLMs, including semantic search engines. In this guide, you'll learn how to build a semantic search engine on top of PostgreSQL using LangChain and pgvector.


🧱 Overview

We'll use:

  • LangChain: For orchestrating LLM pipelines
  • pgvector: PostgreSQL extension for vector similarity search
  • Hugging Face Embeddings: To convert text into embeddings
  • PostgreSQL: As our structured data source and vector store

📦 Prerequisites

  1. Python ≥ 3.8

  2. A PostgreSQL database with data to search

  3. Install pgvector in PostgreSQL:

    CREATE EXTENSION IF NOT EXISTS vector;
    
  4. Install required Python packages:

    pip install langchain langchain-community pgvector psycopg2-binary
    pip install sentence-transformers
    

🗃️ Step 1: Connect to PostgreSQL

Use psycopg2 to connect to your PostgreSQL instance.

import psycopg2

connection = psycopg2.connect(
    dbname="mydb",
    user="user",
    password="password",
    host="localhost",
    port=5432
)

🧠 Step 2: Generate Embeddings

Use sentence-transformers to generate semantic embeddings.

from sentence_transformers import SentenceTransformer

model = SentenceTransformer('all-MiniLM-L6-v2')

texts = ["This is a semantic search example.", "Another document."]
embeddings = model.encode(texts).tolist()

📥 Step 3: Store Embeddings in PostgreSQL (pgvector)

Create a table with a vector column and insert data.

CREATE TABLE documents (
    id SERIAL PRIMARY KEY,
    content TEXT,
    embedding vector(384)
);
cursor = connection.cursor()
for text, emb in zip(texts, embeddings):
    cursor.execute(
        "INSERT INTO documents (content, embedding) VALUES (%s, %s)",
        (text, emb)
    )
connection.commit()

🔍 Step 4: Query Similar Documents

Use vector similarity with pgvector:

query_text = "Find examples of semantic search"
query_vector = model.encode([query_text])[0].tolist()

cursor.execute(
    "SELECT content FROM documents ORDER BY embedding <-> %s LIMIT 5",
    (query_vector,)
)
results = cursor.fetchall()
for result in results:
    print(result[0])

🔁 Optional: Add LLM for Summarization

Use OpenAI or Ollama with LangChain for summarizing the results.

from langchain_community.llms import Ollama
from langchain.prompts import PromptTemplate
from langchain.chains import LLMChain

llm = Ollama(model="mistral")

prompt = PromptTemplate.from_template("Summarize these results:
{results}")
chain = LLMChain(llm=llm, prompt=prompt)

response = chain.run(results="
".join(r[0] for r in results))
print(response)

🧰 Useful Tips

  • Create an index on the vector column for faster queries:

    CREATE INDEX ON documents USING ivfflat (embedding vector_cosine_ops) WITH (lists = 100);
    
  • Normalize embeddings for better cosine similarity:

    import numpy as np
    def normalize(v): return v / np.linalg.norm(v)
    

🚀 Conclusion

You've now built a simple yet powerful semantic search engine using LangChain, pgvector, and Hugging Face embeddings. Extend it with:

  • A web interface (Streamlit/FastAPI)
  • LangChain Retriever for full RAG pipelines
  • LLM integration for natural language querying

🔗 Resources