Specialized Databases - Vector, Spatial, Embedded, and Ledger
Learning Objectives: By the end of this lesson, you will be able to:
- Understand vector databases and their role in AI/ML applications
- Work with spatial databases for geolocation and mapping applications
- Implement embedded databases for local-first applications
- Understand blockchain and ledger databases for immutable audit trails
- Choose the appropriate specialized database for niche requirements
1. Welcome and Introduction (5 minutes)
Icebreaker Activity
You’re building an AI-powered image search. Users upload photos and expect to find similar images instantly. Traditional databases can’t efficiently search for “images that look like this.” You need a database that understands embeddings—high-dimensional vectors representing image features. Welcome to the world of specialized databases, each designed for unique data types and query patterns that general-purpose databases handle poorly.
Lesson Overview
- Duration: 90 minutes
- Prerequisites: Understanding of basic database concepts, familiarity with vectors and coordinates
- Materials Needed: Docker, Python environment with relevant client libraries
2. Learning Objectives and Session Outline (5 minutes)
What We’ll Cover Today:
- Vector Databases - Pinecone, Milvus, Weaviate for AI/ML embeddings
- Spatial Databases - PostGIS, MongoDB Geospatial for location data
- Embedded Databases - SQLite, RocksDB, LevelDB for local storage
- Ledger Databases - Amazon QLDB, ImmuDB for immutable records
- In-Memory Databases - Beyond caching with Redis and SAP HANA
- Selection Criteria - Matching specialized databases to requirements
Success Criteria:
By the end of this session, you should be able to identify when specialized databases are needed and implement basic operations with each type.
3. Foundation Knowledge (15-20 minutes)
Core Concepts
Vector Databases
Vector databases store and search high-dimensional vectors (embeddings) efficiently. These embeddings represent semantic meaning of text, images, audio, or any data that can be encoded.
How Embeddings Work:
"Happy dog playing" → Model → [0.23, -0.45, 0.89, 0.12, ..., 0.67]
(1536-dimensional vector)
Why Specialized Search is Needed:
- Traditional databases use exact matching or B-tree indexes
- Vector similarity requires distance calculations (cosine, Euclidean)
- Approximate Nearest Neighbor (ANN) algorithms enable fast search
Use Cases:
- Semantic search (search by meaning, not keywords)
- Image/video similarity
- Recommendation systems
- RAG (Retrieval Augmented Generation) for LLMs
- Anomaly detection
Spatial Databases
Spatial databases are optimized for geographic and geometric data with specialized indexes and query operations.
Key Capabilities:
- Store points, lines, polygons, and complex geometries
- Spatial indexes (R-tree, Quadtree) for fast lookups
- Distance calculations, containment, intersection queries
- Support for multiple coordinate systems (WGS84, UTM, etc.)
Use Cases:
- Location-based services (find nearby)
- Mapping and navigation
- Urban planning and GIS
- Delivery routing optimization
Embedded Databases
Embedded databases run within the application process without a separate server, providing local data persistence.
Key Characteristics:
- No network overhead
- No separate process to manage
- Single-file storage (typically)
- Ideal for mobile, desktop, and edge devices
Use Cases:
- Mobile applications
- Desktop software
- IoT and edge computing
- Browser-based storage
- Testing and development
Ledger Databases
Ledger databases provide cryptographically verifiable, immutable append-only logs of data changes.
Key Characteristics:
- Immutable history
- Cryptographic verification (hash chains)
- Complete audit trail
- Tamper-evident design
Use Cases:
- Financial transaction history
- Regulatory compliance (audit logs)
- Supply chain tracking
- Healthcare records
- Legal document management
Key Terminology
- Embedding: Dense vector representation of data
- Similarity Search: Finding vectors closest to a query vector
- ANN (Approximate Nearest Neighbor): Fast approximate similarity search
- Spatial Index: Data structure optimized for geometric queries
- Geofence: Virtual boundary defined by geographic coordinates
- WAL (Write-Ahead Log): Durability mechanism for embedded databases
- Hash Chain: Cryptographic link between sequential records
- Merkle Tree: Tree structure for efficient data verification
4. Detailed Explanation and Examples (25-30 minutes)
Vector Databases Deep Dive
Popular Vector Database Systems
| Database | Type | Index Types | Best For |
|---|---|---|---|
| Pinecone | Managed SaaS | Proprietary | Production AI apps |
| Milvus | Open Source | HNSW, IVF, etc. | Large-scale similarity |
| Weaviate | Open Source | HNSW | Semantic search |
| Qdrant | Open Source | HNSW | Performance focus |
| Chroma | Open Source | HNSW | LLM applications |
| pgvector | PostgreSQL ext. | IVF, HNSW | SQL integration |
Similarity Metrics
# Cosine Similarity (angle between vectors)
# Range: -1 to 1, where 1 = identical direction
cosine_sim = dot(A, B) / (norm(A) * norm(B))
# Euclidean Distance (straight-line distance)
# Range: 0 to infinity, where 0 = identical
euclidean_dist = sqrt(sum((A - B)^2))
# Dot Product (magnitude-weighted similarity)
# Range: -infinity to infinity
dot_product = sum(A * B)
Spatial Database Capabilities
PostGIS (PostgreSQL Extension)
PostGIS adds spatial capabilities to PostgreSQL:
- Geometry types (point, line, polygon, multipolygon)
- Geography types (spherical calculations)
- Spatial functions (ST_Distance, ST_Contains, ST_Intersects)
- Spatial indexes (GIST, SP-GIST)
MongoDB Geospatial
MongoDB supports:
- GeoJSON objects (Point, Polygon, LineString)
- 2d indexes (flat geometry)
- 2dsphere indexes (spherical geometry)
- Geospatial queries ($near, $geoWithin, $geoIntersects)
Embedded Database Options
| Database | Language | Storage | Best For |
|---|---|---|---|
| SQLite | SQL | Single file | General purpose |
| LevelDB | Key-Value | Directory | Fast writes |
| RocksDB | Key-Value | Directory | High performance |
| Realm | Object | Single file | Mobile apps |
| DuckDB | SQL/Analytics | Single file | OLAP embedded |
| LMDB | Key-Value | Memory-mapped | Read-heavy |
Common Misconceptions
-
Misconception: “Vector databases are just for AI chatbots” Reality: They’re useful for any similarity-based search: images, products, users, fraud patterns.
-
Misconception: “Spatial queries work fine with regular indexes” Reality: Geographic queries require specialized indexes; regular B-tree indexes can’t efficiently handle 2D range queries.
-
Misconception: “SQLite is just for small projects” Reality: SQLite handles terabytes of data and billions of rows when properly configured.
5. Live Coding Demonstration (20-25 minutes)
Example 1: Vector Database (Pinecone/Milvus Style)
# Using a vector database for semantic search
from sentence_transformers import SentenceTransformer
import numpy as np
# In practice, use actual Pinecone/Milvus client
# This demonstrates the concepts
# Load embedding model
model = SentenceTransformer('all-MiniLM-L6-v2')
# Sample documents
documents = [
{"id": "1", "text": "How to train a machine learning model"},
{"id": "2", "text": "Best practices for deep learning"},
{"id": "3", "text": "Introduction to neural networks"},
{"id": "4", "text": "Cooking delicious pasta recipes"},
{"id": "5", "text": "Italian cuisine fundamentals"},
]
# Generate embeddings
for doc in documents:
doc["embedding"] = model.encode(doc["text"]).tolist()
# Pinecone example (pseudo-code)
"""
import pinecone
# Initialize
pinecone.init(api_key="your-key", environment="us-west1-gcp")
# Create index
pinecone.create_index(
name="documents",
dimension=384, # MiniLM dimension
metric="cosine"
)
# Connect to index
index = pinecone.Index("documents")
# Upsert vectors
vectors = [
(doc["id"], doc["embedding"], {"text": doc["text"]})
for doc in documents
]
index.upsert(vectors=vectors)
# Query
query_text = "How do I build AI models?"
query_embedding = model.encode(query_text).tolist()
results = index.query(
vector=query_embedding,
top_k=3,
include_metadata=True
)
for match in results["matches"]:
print(f"Score: {match['score']:.4f} - {match['metadata']['text']}")
"""
# Milvus example
"""
from pymilvus import connections, Collection, FieldSchema, CollectionSchema, DataType
# Connect
connections.connect(host="localhost", port="19530")
# Define schema
fields = [
FieldSchema(name="id", dtype=DataType.INT64, is_primary=True),
FieldSchema(name="embedding", dtype=DataType.FLOAT_VECTOR, dim=384),
FieldSchema(name="text", dtype=DataType.VARCHAR, max_length=1000)
]
schema = CollectionSchema(fields)
collection = Collection(name="documents", schema=schema)
# Insert
collection.insert([
[1, 2, 3], # ids
[[0.1, 0.2, ...], [0.3, 0.4, ...], [0.5, 0.6, ...]], # embeddings
["text1", "text2", "text3"] # texts
])
# Create index
collection.create_index(
field_name="embedding",
index_params={
"metric_type": "COSINE",
"index_type": "HNSW",
"params": {"M": 16, "efConstruction": 200}
}
)
# Search
collection.load()
results = collection.search(
data=[query_embedding],
anns_field="embedding",
param={"metric_type": "COSINE", "params": {"ef": 100}},
limit=3,
output_fields=["text"]
)
"""
# pgvector example (PostgreSQL extension)
"""
-- Enable extension
CREATE EXTENSION vector;
-- Create table with vector column
CREATE TABLE documents (
id SERIAL PRIMARY KEY,
text TEXT,
embedding vector(384)
);
-- Insert with embedding
INSERT INTO documents (text, embedding)
VALUES ('How to train a machine learning model', '[0.1, 0.2, ...]');
-- Create index for fast similarity search
CREATE INDEX ON documents USING ivfflat (embedding vector_cosine_ops)
WITH (lists = 100);
-- Or HNSW index (better accuracy)
CREATE INDEX ON documents USING hnsw (embedding vector_cosine_ops);
-- Similarity search
SELECT id, text, 1 - (embedding <=> '[0.1, 0.2, ...]') AS similarity
FROM documents
ORDER BY embedding <=> '[0.1, 0.2, ...]'
LIMIT 5;
-- Cosine distance: <=>
-- Euclidean distance: <->
-- Inner product: <#>
"""
Example 2: Spatial Database (PostGIS)
-- Enable PostGIS extension
CREATE EXTENSION postgis;
-- Create a table with location data
CREATE TABLE stores (
id SERIAL PRIMARY KEY,
name VARCHAR(100) NOT NULL,
location GEOGRAPHY(POINT, 4326),
service_area GEOGRAPHY(POLYGON, 4326)
);
-- Insert store locations
INSERT INTO stores (name, location) VALUES
('Downtown Store', ST_SetSRID(ST_MakePoint(-73.985428, 40.748817), 4326)),
('Midtown Store', ST_SetSRID(ST_MakePoint(-73.978873, 40.756947), 4326)),
('Brooklyn Store', ST_SetSRID(ST_MakePoint(-73.986229, 40.693254), 4326));
-- Create spatial index
CREATE INDEX idx_stores_location ON stores USING GIST (location);
-- Find stores within 2km of a point
SELECT
name,
ST_Distance(
location,
ST_SetSRID(ST_MakePoint(-73.980000, 40.750000), 4326)::geography
) AS distance_meters
FROM stores
WHERE ST_DWithin(
location,
ST_SetSRID(ST_MakePoint(-73.980000, 40.750000), 4326)::geography,
2000 -- 2km radius
)
ORDER BY distance_meters;
-- Find the nearest store
SELECT
name,
ST_Distance(
location,
ST_SetSRID(ST_MakePoint(-73.980000, 40.750000), 4326)::geography
) AS distance_meters
FROM stores
ORDER BY location <-> ST_SetSRID(ST_MakePoint(-73.980000, 40.750000), 4326)::geography
LIMIT 1;
-- Create delivery zones (polygons)
CREATE TABLE delivery_zones (
id SERIAL PRIMARY KEY,
zone_name VARCHAR(100),
area GEOGRAPHY(POLYGON, 4326)
);
INSERT INTO delivery_zones (zone_name, area) VALUES
('Manhattan', ST_SetSRID(ST_GeomFromText('POLYGON((-74.02 40.70, -74.02 40.80, -73.93 40.80, -73.93 40.70, -74.02 40.70))'), 4326));
-- Check if a point is within delivery zone
SELECT zone_name
FROM delivery_zones
WHERE ST_Contains(
area::geometry,
ST_SetSRID(ST_MakePoint(-73.985428, 40.748817), 4326)
);
-- Find stores within a polygon
SELECT s.name
FROM stores s
JOIN delivery_zones dz ON ST_Within(s.location::geometry, dz.area::geometry)
WHERE dz.zone_name = 'Manhattan';
-- Calculate area of delivery zone in square kilometers
SELECT
zone_name,
ST_Area(area) / 1000000 AS area_sq_km
FROM delivery_zones;
-- Find routes (LineString operations)
CREATE TABLE routes (
id SERIAL PRIMARY KEY,
route_name VARCHAR(100),
path GEOGRAPHY(LINESTRING, 4326)
);
INSERT INTO routes (route_name, path) VALUES
('Route A', ST_SetSRID(ST_MakeLine(ARRAY[
ST_MakePoint(-73.985428, 40.748817),
ST_MakePoint(-73.978873, 40.756947),
ST_MakePoint(-73.986229, 40.693254)
]), 4326));
-- Calculate route length
SELECT
route_name,
ST_Length(path) / 1000 AS length_km
FROM routes;
Example 3: Embedded Database (SQLite)
import sqlite3
import json
# SQLite basic operations
conn = sqlite3.connect('app_data.db')
cursor = conn.cursor()
# Create tables
cursor.execute('''
CREATE TABLE IF NOT EXISTS users (
id INTEGER PRIMARY KEY AUTOINCREMENT,
email TEXT UNIQUE NOT NULL,
name TEXT NOT NULL,
preferences TEXT, -- JSON stored as TEXT
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
)
''')
cursor.execute('''
CREATE TABLE IF NOT EXISTS offline_queue (
id INTEGER PRIMARY KEY AUTOINCREMENT,
action TEXT NOT NULL,
payload TEXT NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
synced INTEGER DEFAULT 0
)
''')
# Create indexes
cursor.execute('CREATE INDEX IF NOT EXISTS idx_users_email ON users(email)')
cursor.execute('CREATE INDEX IF NOT EXISTS idx_queue_synced ON offline_queue(synced)')
# Insert with JSON
user_prefs = json.dumps({"theme": "dark", "notifications": True})
cursor.execute(
'INSERT INTO users (email, name, preferences) VALUES (?, ?, ?)',
('alice@example.com', 'Alice Smith', user_prefs)
)
# Offline-first pattern: queue operations for sync
def queue_action(action, payload):
cursor.execute(
'INSERT INTO offline_queue (action, payload) VALUES (?, ?)',
(action, json.dumps(payload))
)
conn.commit()
queue_action('create_order', {'product_id': 123, 'quantity': 2})
# Sync pending operations
def get_pending_sync():
cursor.execute(
'SELECT id, action, payload FROM offline_queue WHERE synced = 0 ORDER BY created_at'
)
return cursor.fetchall()
def mark_synced(ids):
cursor.execute(
f'UPDATE offline_queue SET synced = 1 WHERE id IN ({",".join("?" * len(ids))})',
ids
)
conn.commit()
# JSON operations (SQLite 3.38+)
cursor.execute('''
SELECT
name,
json_extract(preferences, '$.theme') as theme
FROM users
WHERE json_extract(preferences, '$.notifications') = 1
''')
# Full-text search with FTS5
cursor.execute('''
CREATE VIRTUAL TABLE IF NOT EXISTS documents_fts USING fts5(
title,
content,
content='documents',
content_rowid='id'
)
''')
# Transaction management
try:
cursor.execute('BEGIN TRANSACTION')
cursor.execute('UPDATE users SET name = ? WHERE id = ?', ('Alice Johnson', 1))
cursor.execute('INSERT INTO offline_queue (action, payload) VALUES (?, ?)',
('update_user', '{"id": 1, "name": "Alice Johnson"}'))
conn.commit()
except Exception as e:
conn.rollback()
raise
# Performance settings for SQLite
cursor.execute('PRAGMA journal_mode = WAL') # Write-ahead logging
cursor.execute('PRAGMA synchronous = NORMAL') # Balance safety/speed
cursor.execute('PRAGMA cache_size = -64000') # 64MB cache
cursor.execute('PRAGMA temp_store = MEMORY') # Temp tables in memory
conn.close()
// DuckDB for embedded analytics (JavaScript example)
import * as duckdb from 'duckdb';
const db = new duckdb.Database(':memory:');
const conn = db.connect();
// Create and load data
conn.run(`
CREATE TABLE sales (
date DATE,
product VARCHAR,
region VARCHAR,
amount DECIMAL(10,2)
);
INSERT INTO sales VALUES
('2024-01-01', 'Widget', 'US', 100.00),
('2024-01-01', 'Gadget', 'US', 150.00),
('2024-01-02', 'Widget', 'EU', 120.00),
('2024-01-02', 'Gadget', 'EU', 180.00);
`);
// Analytical queries on local data
const result = conn.all(`
SELECT
region,
SUM(amount) as total_sales,
COUNT(*) as transaction_count
FROM sales
GROUP BY region
ORDER BY total_sales DESC
`);
// Import from Parquet (local file analytics)
conn.run(`
CREATE TABLE logs AS
SELECT * FROM read_parquet('logs/*.parquet');
`);
// Export results
conn.run(`
COPY (SELECT * FROM sales) TO 'sales_export.csv' (HEADER, DELIMITER ',');
`);
Example 4: Ledger Database (Immutable Records)
-- Amazon QLDB-style operations (conceptual SQL)
-- QLDB uses PartiQL (SQL-compatible)
-- Create a ledger table
CREATE TABLE accounts;
CREATE TABLE transactions;
-- Insert initial account
INSERT INTO accounts VALUE {
'AccountId': 'ACC001',
'Owner': 'Alice Smith',
'Balance': 1000.00,
'Status': 'Active'
};
-- Transfer funds (creates immutable history)
-- Step 1: Debit source account
UPDATE accounts SET Balance = Balance - 100.00
WHERE AccountId = 'ACC001';
-- Step 2: Credit destination account
UPDATE accounts SET Balance = Balance + 100.00
WHERE AccountId = 'ACC002';
-- Step 3: Record transaction
INSERT INTO transactions VALUE {
'TransactionId': 'TXN001',
'FromAccount': 'ACC001',
'ToAccount': 'ACC002',
'Amount': 100.00,
'Timestamp': `2024-01-15T10:30:00Z`
};
-- Query current state
SELECT * FROM accounts WHERE AccountId = 'ACC001';
-- Query history (QLDB-specific)
SELECT * FROM history(accounts) AS h
WHERE h.data.AccountId = 'ACC001';
-- Verify document integrity (get document hash)
SELECT h.metadata.id, h.hash, h.data
FROM history(accounts) AS h
WHERE h.data.AccountId = 'ACC001';
-- ImmuDB example (immutable key-value + SQL)
/*
# Using immudb Python client
from immudb import ImmudbClient
client = ImmudbClient()
client.login("immudb", "immudb")
client.useDatabase("defaultdb")
# Set with verified proof
client.verifiedSet(b"account:ACC001", b"1000.00")
# Get with proof verification
entry = client.verifiedGet(b"account:ACC001")
print(f"Value: {entry.value}, Verified: {entry.verified}")
# SQL interface
client.sqlExec("""
CREATE TABLE IF NOT EXISTS audit_log (
id INTEGER AUTO_INCREMENT,
action VARCHAR[50],
user_id VARCHAR[50],
details VARCHAR[500],
timestamp TIMESTAMP,
PRIMARY KEY (id)
)
""")
# Insert audit entry (immutable)
client.sqlExec("""
INSERT INTO audit_log (action, user_id, details, timestamp)
VALUES ('LOGIN', 'user123', 'Successful login from IP 192.168.1.1', NOW())
""")
# Query with time travel
result = client.sqlQuery("""
SELECT * FROM audit_log
BEFORE TX 1000
WHERE user_id = 'user123'
""")
# History of a specific key
history = client.history(b"account:ACC001")
for entry in history:
print(f"TX: {entry.tx}, Value: {entry.value}")
*/
6. Hands-On Practice (30-40 minutes)
Exercise 1: Build a Semantic Search System (15 minutes)
Objective: Create a document search using vector embeddings
Requirements:
- Store documents with embeddings
- Search by semantic similarity
- Filter by metadata
Solution:
# Using pgvector with PostgreSQL
import psycopg2
from sentence_transformers import SentenceTransformer
# Setup
model = SentenceTransformer('all-MiniLM-L6-v2')
conn = psycopg2.connect("postgresql://user:pass@localhost/vectordb")
cur = conn.cursor()
# Create table
cur.execute("""
CREATE EXTENSION IF NOT EXISTS vector;
CREATE TABLE IF NOT EXISTS documents (
id SERIAL PRIMARY KEY,
title TEXT NOT NULL,
content TEXT NOT NULL,
category VARCHAR(50),
embedding vector(384),
created_at TIMESTAMP DEFAULT NOW()
);
CREATE INDEX IF NOT EXISTS idx_docs_embedding
ON documents USING hnsw (embedding vector_cosine_ops);
""")
# Insert documents
documents = [
("Machine Learning Basics", "Introduction to supervised and unsupervised learning algorithms", "tech"),
("Deep Learning with Python", "Building neural networks using TensorFlow and PyTorch", "tech"),
("Healthy Cooking Tips", "How to prepare nutritious meals at home", "lifestyle"),
("Database Optimization", "Techniques for improving SQL query performance", "tech"),
]
for title, content, category in documents:
embedding = model.encode(f"{title} {content}").tolist()
cur.execute(
"INSERT INTO documents (title, content, category, embedding) VALUES (%s, %s, %s, %s)",
(title, content, category, embedding)
)
conn.commit()
# Search function
def semantic_search(query, category=None, limit=5):
query_embedding = model.encode(query).tolist()
if category:
cur.execute("""
SELECT title, content, 1 - (embedding <=> %s::vector) AS similarity
FROM documents
WHERE category = %s
ORDER BY embedding <=> %s::vector
LIMIT %s
""", (query_embedding, category, query_embedding, limit))
else:
cur.execute("""
SELECT title, content, 1 - (embedding <=> %s::vector) AS similarity
FROM documents
ORDER BY embedding <=> %s::vector
LIMIT %s
""", (query_embedding, query_embedding, limit))
return cur.fetchall()
# Test search
results = semantic_search("How do I build AI models?", category="tech")
for title, content, similarity in results:
print(f"{similarity:.4f}: {title}")
Exercise 2: Location-Based Service (15 minutes)
Objective: Build a store finder with delivery zone checking
Solution:
-- Create schema
CREATE EXTENSION IF NOT EXISTS postgis;
CREATE TABLE stores (
id SERIAL PRIMARY KEY,
name VARCHAR(100) NOT NULL,
address TEXT,
location GEOGRAPHY(POINT, 4326),
delivery_radius_km FLOAT DEFAULT 5.0,
is_open BOOLEAN DEFAULT TRUE
);
CREATE TABLE delivery_zones (
id SERIAL PRIMARY KEY,
store_id INTEGER REFERENCES stores(id),
zone_type VARCHAR(20), -- 'express', 'standard', 'extended'
area GEOGRAPHY(POLYGON, 4326),
delivery_fee DECIMAL(10,2)
);
-- Create indexes
CREATE INDEX idx_stores_location ON stores USING GIST (location);
CREATE INDEX idx_zones_area ON delivery_zones USING GIST (area);
-- Insert sample stores
INSERT INTO stores (name, address, location, delivery_radius_km) VALUES
('Central Kitchen', '123 Main St', ST_SetSRID(ST_MakePoint(-73.985, 40.748), 4326), 5.0),
('East Side Kitchen', '456 East Ave', ST_SetSRID(ST_MakePoint(-73.960, 40.755), 4326), 4.0),
('Brooklyn Hub', '789 Brooklyn Blvd', ST_SetSRID(ST_MakePoint(-73.986, 40.693), 4326), 6.0);
-- Create delivery zones from radius
INSERT INTO delivery_zones (store_id, zone_type, area, delivery_fee)
SELECT
id,
'standard',
ST_Buffer(location::geometry, delivery_radius_km * 1000)::geography,
2.99
FROM stores;
-- Function: Find nearest open stores
CREATE OR REPLACE FUNCTION find_nearest_stores(
user_lat FLOAT,
user_lng FLOAT,
max_distance_km FLOAT DEFAULT 10.0,
limit_count INT DEFAULT 5
)
RETURNS TABLE (
store_id INT,
store_name VARCHAR,
distance_km FLOAT,
can_deliver BOOLEAN
) AS $$
BEGIN
RETURN QUERY
SELECT
s.id,
s.name,
ST_Distance(
s.location,
ST_SetSRID(ST_MakePoint(user_lng, user_lat), 4326)::geography
) / 1000 AS dist_km,
ST_DWithin(
s.location,
ST_SetSRID(ST_MakePoint(user_lng, user_lat), 4326)::geography,
s.delivery_radius_km * 1000
) AS can_deliver
FROM stores s
WHERE s.is_open = TRUE
AND ST_DWithin(
s.location,
ST_SetSRID(ST_MakePoint(user_lng, user_lat), 4326)::geography,
max_distance_km * 1000
)
ORDER BY dist_km
LIMIT limit_count;
END;
$$ LANGUAGE plpgsql;
-- Use the function
SELECT * FROM find_nearest_stores(40.750, -73.980);
-- Check delivery zone and fee
CREATE OR REPLACE FUNCTION get_delivery_options(
user_lat FLOAT,
user_lng FLOAT
)
RETURNS TABLE (
store_id INT,
store_name VARCHAR,
zone_type VARCHAR,
delivery_fee DECIMAL,
estimated_time_min INT
) AS $$
BEGIN
RETURN QUERY
SELECT
s.id,
s.name,
dz.zone_type,
dz.delivery_fee,
CASE dz.zone_type
WHEN 'express' THEN 15
WHEN 'standard' THEN 30
WHEN 'extended' THEN 45
END AS est_time
FROM stores s
JOIN delivery_zones dz ON s.id = dz.store_id
WHERE s.is_open = TRUE
AND ST_Contains(
dz.area::geometry,
ST_SetSRID(ST_MakePoint(user_lng, user_lat), 4326)
)
ORDER BY dz.delivery_fee;
END;
$$ LANGUAGE plpgsql;
SELECT * FROM get_delivery_options(40.750, -73.980);
7. Common Pitfalls and Troubleshooting (10 minutes)
Frequent Mistakes
-
Problem: Poor vector search recall Cause: Wrong distance metric or index parameters Solution: Match metric to embedding model (cosine for normalized, L2 for unnormalized)
-
Problem: Slow spatial queries Cause: Missing or wrong spatial index type Solution: Use GIST index; ensure queries use index-friendly operators
-
Problem: SQLite locking issues Cause: Multiple writers without WAL mode Solution: Enable WAL mode; use connection pooling carefully
-
Problem: Ledger database performance Cause: Every write is cryptographically verified Solution: Batch writes; use async verification where acceptable
Debugging Tips
- Vector search: Compare exact vs approximate results; tune index parameters
- Spatial queries: Use EXPLAIN ANALYZE; verify SRID matches
- Embedded databases: Check file permissions; monitor file size
- Ledger databases: Understand verification vs trust trade-offs
8. Best Practices and Professional Tips (5 minutes)
Industry Standards
- Vector DBs: Pre-compute embeddings; batch insert operations
- Spatial DBs: Use appropriate SRID for your region; simplify complex geometries
- Embedded DBs: Enable WAL; set appropriate page sizes; vacuum regularly
- Ledger DBs: Plan for verification overhead; consider hybrid approaches
When to Use Specialized Databases
| Use Case | Recommended Database |
|---|---|
| AI/ML similarity search | Pinecone, Milvus, pgvector |
| Location-based services | PostGIS, MongoDB Geospatial |
| Mobile/desktop apps | SQLite, Realm, LevelDB |
| Audit/compliance | Amazon QLDB, ImmuDB |
| Real-time analytics | ClickHouse, DuckDB |
| IoT edge | SQLite, RocksDB |
Next Steps in Your Learning Journey
- Vector search optimization: Quantization, dimensionality reduction
- Spatial analysis: Advanced GIS operations, routing algorithms
- Embedded performance: Custom storage engines, memory mapping
- Cryptographic verification: Zero-knowledge proofs, blockchain integration
9. Summary and Review (5 minutes)
Key Takeaways
- Vector databases enable semantic similarity search for AI applications
- Spatial databases provide optimized storage and queries for geographic data
- Embedded databases offer serverless local storage for applications
- Ledger databases provide immutable, verifiable record keeping
Quick Review Questions
-
What is an embedding and why do we need special databases for them? Answer: An embedding is a dense vector representation; specialized databases provide ANN algorithms and distance-based indexes that regular databases lack
-
When would you use PostGIS over MongoDB’s geospatial features? Answer: When you need complex spatial operations (intersections, buffers), precise distance calculations, or SQL integration
-
What makes a ledger database different from a regular database with audit logging? Answer: Ledger databases provide cryptographic proof of data integrity; records are immutable and tamper-evident, not just append-only
For Instructors
Teaching Tips
- Use visual examples for vector similarity (word embeddings, image search)
- Show maps for spatial queries (interactive if possible)
- Demonstrate mobile app scenarios for embedded databases
- Explain blockchain concepts briefly for ledger databases
Assessment Ideas
- Build a semantic search feature
- Design a location-based service schema
- Implement offline-first data sync
- Create an audit trail system
Adaptation Notes
- For beginners: Focus on one specialized type per session
- For advanced students: Cover index internals and optimization
- Online delivery: Use cloud services with free tiers
- Time constraints: Each specialized type can be covered in 30 minutes