Posted on
Artificial Intelligence

Artificial Intelligence Vector Databases Explained

Author
  • User
    linuxbash
    Posts by this author
    Posts by this author

Artificial Intelligence Vector Databases Explained (for Bash-Loving Linux Users)

If grep and SQL feel clumsy for “what did I mean?” questions, you’re not alone. Traditional string or keyword search can’t catch “restart the web server” when your note says “bounce nginx.” Vector databases fix that by searching via meaning, not exact words—unlocking powerful semantic search and retrieval for your scripts, docs, logs, and CLIs.

This post explains vector databases in plain terms, shows how to run one locally on Linux, and gives copy-paste Bash you can use today.


Why this matters (and why it’s valid)

  • Words are messy. Humans say the same thing in many ways. Full-text search and SQL LIKE struggle with “synonyms” and context.

  • Embeddings turn text into numbers. A language model maps sentences into high-dimensional vectors (e.g., 384 or 768 floats) so semantically similar text ends up numerically close.

  • Vector databases store those embeddings and answer “find the nearest vectors” extremely fast using approximate nearest neighbor (ANN) indexes like HNSW or IVF.

  • Real value for Linux users: add “semantic grep” to your toolkit, enrich shell scripts with smarter answers, and build fast local Retrieval-Augmented Generation (RAG) workflows—no vendor lock-in required.


What is a vector database?

  • A vector DB stores vectors (lists of floats) plus optional metadata (payload).

  • It indexes vectors using specialized structures (e.g., HNSW graphs) for sub-millisecond nearest-neighbor queries on millions of items.

  • It supports distance metrics (Cosine, Dot, Euclidean) and filters (e.g., only notes tagged “bash”).

  • It exposes simple APIs (often REST/gRPC), so you can drive it with curl from Bash.

We’ll use Qdrant (open source, great REST API) for examples.


Prerequisites (install via your package manager)

We’ll use curl and jq for HTTP + JSON, and optionally Docker/Podman or native packages for Qdrant. If you want to generate real embeddings locally, we’ll use Python.

  • Ubuntu/Debian (apt):

    sudo apt update
    sudo apt install -y curl jq
    # Optional: Docker
    sudo apt install -y docker.io
    # Optional: Python + pip + venv
    sudo apt install -y python3 python3-pip python3-venv
    
  • Fedora/RHEL/CentOS (dnf):

    sudo dnf install -y curl jq
    # Optional: Podman (recommended on Fedora) or Docker
    sudo dnf install -y podman
    # or
    sudo dnf install -y docker
    # Optional: Python + pip
    sudo dnf install -y python3 python3-pip
    
  • openSUSE (zypper):

    sudo zypper refresh
    sudo zypper install -y curl jq
    # Optional: Podman or Docker
    sudo zypper install -y podman
    # or
    sudo zypper install -y docker
    # Optional: Python + pip
    sudo zypper install -y python3 python3-pip
    

If you install Docker, make sure it’s running:

# Systemd-based distros
sudo systemctl enable --now docker
# Add your user to the docker group to run without sudo (log out/in after)
sudo usermod -aG docker "$USER"

Podman runs rootless out of the box:

podman --version

Option A: Run Qdrant via container (fastest path)

  • Docker:

    docker run -d --name qdrant \
    -p 6333:6333 \
    -v "$(pwd)/qdrant_storage:/qdrant/storage" \
    qdrant/qdrant:latest
    
    curl -fsS http://localhost:6333/healthz && echo "Qdrant is healthy"
    
  • Podman:

    podman run -d --name qdrant \
    -p 6333:6333 \
    -v "$(pwd)/qdrant_storage:/qdrant/storage" \
    qdrant/qdrant:latest
    
    curl -fsS http://localhost:6333/healthz && echo "Qdrant is healthy"
    

Option B: Install Qdrant natively (apt/dnf)

  • Ubuntu/Debian (apt):

    curl -fsSL https://apt.qdrant.tech/qdrant-apt-repo.gpg | sudo tee /usr/share/keyrings/qdrant-archive-keyring.gpg >/dev/null
    echo "deb [signed-by=/usr/share/keyrings/qdrant-archive-keyring.gpg] https://apt.qdrant.tech/ stable main" | sudo tee /etc/apt/sources.list.d/qdrant.list
    sudo apt update
    sudo apt install -y qdrant
    sudo systemctl enable --now qdrant
    curl -fsS http://localhost:6333/healthz && echo "Qdrant is healthy"
    
  • Fedora/RHEL/CentOS (dnf):

    sudo dnf config-manager --add-repo https://rpm.qdrant.tech/qdrant.repo
    sudo dnf install -y qdrant
    sudo systemctl enable --now qdrant
    curl -fsS http://localhost:6333/healthz && echo "Qdrant is healthy"
    
  • openSUSE (zypper): Qdrant does not provide an official openSUSE repo at the time of writing. Use the container method above with Podman or Docker.


Core: Create a collection and do a semantic search with just curl

First, we’ll use tiny 3D vectors to keep it simple. In practice, you’ll use embeddings from a model (see next section).

1) Create a collection named “notes” (3-dimensional vectors, Cosine distance):

curl -sS -X PUT "http://localhost:6333/collections/notes" \
  -H "Content-Type: application/json" \
  -d '{
    "vectors": { "size": 3, "distance": "Cosine" },
    "hnsw_config": { "m": 16, "ef_construct": 100 }
  }' | jq

2) Upsert a few points (id, vector, payload):

curl -sS -X PUT "http://localhost:6333/collections/notes/points" \
  -H "Content-Type: application/json" \
  -d '{
    "points": [
      { "id": 1, "vector": [0.1, 0.3, 0.5], "payload": { "text": "How to restart nginx on Linux", "tags": ["linux", "nginx", "ops"] } },
      { "id": 2, "vector": [0.09, 0.29, 0.52], "payload": { "text": "Systemd service restart commands", "tags": ["linux", "systemd"] } },
      { "id": 3, "vector": [0.8, 0.1, 0.1], "payload": { "text": "SSH key forwarding tips", "tags": ["ssh", "security"] } }
    ]
  }' | jq

3) Search: “restart the web server” (pretend its 3D embedding is close to [0.1,0.3,0.5]):

curl -sS -X POST "http://localhost:6333/collections/notes/points/search" \
  -H "Content-Type: application/json" \
  -d '{
    "vector": [0.11, 0.28, 0.5],
    "limit": 3
  }' | jq '.result[] | {id, score, text: .payload.text}'

4) Filtered search: only notes tagged “linux”:

curl -sS -X POST "http://localhost:6333/collections/notes/points/search" \
  -H "Content-Type: application/json" \
  -d '{
    "vector": [0.11, 0.28, 0.5],
    "limit": 3,
    "filter": {
      "must": [
        { "key": "tags", "match": { "value": "linux" } }
      ]
    }
  }' | jq '.result[] | {id, score, text: .payload.text, tags: .payload.tags}'

You now have semantic search via curl. Next, let’s switch from toy vectors to real embeddings.


Optional: Generate real embeddings locally (Python) and upsert

1) Create a Python virtual environment and install a lightweight embedding model:

  • Ubuntu/Debian (apt already above):

    python3 -m venv .venv
    . .venv/bin/activate
    pip install --upgrade pip
    pip install sentence-transformers
    
  • Fedora/RHEL/CentOS (dnf already above):

    python3 -m venv .venv
    . .venv/bin/activate
    pip install --upgrade pip
    pip install sentence-transformers
    
  • openSUSE (zypper already above):

    python3 -m venv .venv
    . .venv/bin/activate
    pip install --upgrade pip
    pip install sentence-transformers
    

2) Encode your text and upsert to Qdrant:

cat > embed_and_upsert.py << 'PY'
import json, sys
import requests
from sentence_transformers import SentenceTransformer

notes = [
  {"id": 101, "text": "Restart nginx with systemd on Ubuntu", "tags": ["linux","nginx","ops"]},
  {"id": 102, "text": "How to tail and rotate logs", "tags": ["linux","logs"]},
  {"id": 103, "text": "Generate SSH keys and use ssh-agent", "tags": ["ssh","security"]}
]

model = SentenceTransformer("sentence-transformers/all-MiniLM-L6-v2")  # 384-dim
vecs = model.encode([n["text"] for n in notes], normalize_embeddings=True).tolist()

points = []
for n, v in zip(notes, vecs):
    points.append({"id": n["id"], "vector": v, "payload": {"text": n["text"], "tags": n["tags"]}})

# Create collection with correct vector size (384)
requests.put("http://localhost:6333/collections/notes384", json={
    "vectors": {"size": len(vecs[0]), "distance": "Cosine"},
    "hnsw_config": {"m": 16, "ef_construct": 128}
}).raise_for_status()

# Upsert
r = requests.put("http://localhost:6333/collections/notes384/points", json={"points": points})
r.raise_for_status()
print("Upserted:", r.json())
PY

python3 embed_and_upsert.py

3) Query with real embeddings (encode the query in Python and search via curl):

cat > encode_query.py << 'PY'
import sys, json
from sentence_transformers import SentenceTransformer
model = SentenceTransformer("sentence-transformers/all-MiniLM-L6-v2")
q = "restart the web server"
print(json.dumps(model.encode([q], normalize_embeddings=True).tolist()[0]))
PY

Q=$(python3 encode_query.py)

curl -sS -X POST "http://localhost:6333/collections/notes384/points/search" \
  -H "Content-Type: application/json" \
  -d "{
    \"vector\": $Q,
    \"limit\": 3,
    \"filter\": { \"must\": [ {\"key\":\"tags\",\"match\":{\"value\":\"linux\"}} ] }
  }" | jq '.result[] | {id, score, text: .payload.text}'

You’re now searching real semantic embeddings locally.


Real-world use cases you can build this week

  • Semantic grep for notes and wikis:

    • Ingest Markdown files into a vector DB; ask “How do I rotate logs?” and jump straight to your doc, even if it never says “rotate.”
  • Log triage:

    • Embed error messages; cluster or search by “similar failures” to group noisy alerts together.
  • Shell RAG:

    • Combine man pages, config snippets, and troubleshooting notes. Ask natural questions and retrieve the top passages for your LLM or scripts.

Performance and accuracy tips

  • Choose the right distance:

    • Cosine for normalized sentence embeddings (common default).
    • Dot for some models (especially if they’re trained for dot-product).
    • Euclidean often works too; test on your data.
  • Index tuning (Qdrant HNSW):

    • ef_construct (build time) and ef (query time, set via search params) trade speed vs. recall.
    • m controls graph connectivity; higher usually = better recall, more memory.
  • Filtering matters:

    • Use payload filters (tags, type, date) to limit search to relevant subsets.
  • Persistence and backups:

    • Mount Qdrant storage to a host dir or volume and snapshot it routinely.

Alternatives to consider

  • Milvus: High-scale distributed vector DB (often run via containers/Helm).

  • Weaviate: Feature-rich with modules and hybrid search.

  • PostgreSQL + pgvector: Great if you’re already on Postgres and your scale fits.

Each has pros/cons; the curl-first REST of Qdrant is excellent for Bash.


Conclusion and next steps

Vector databases let your Linux box answer “what did I mean?” instead of “what did I type?”. You’ve seen how to:

  • Run a vector DB locally (container or native).

  • Upsert points and payloads.

  • Query semantically with curl.

  • Generate real embeddings locally with Python.

Your move:

  • Pick a folder of notes or logs.

  • Spin up Qdrant.

  • Embed a handful of files and try your first semantic search.

  • Wrap it in a tiny Bash script and add it to your daily toolkit.

If you want a follow-up, ask for a “semantic-grep.sh” that ingests Markdown and searches via Qdrant—we’ll ship a drop-in script.