Skip to main content

Lightweight local vector database with persistence to disk, supporting multiple similarity metrics and easy-to-use API.

Project description

microvector

Lightweight local vector database with persistence to disk, supporting multiple similarity metrics and an easy-to-use API.

A refactor and repackaging of HyperDB optimized for CPU-only environments with improved type safety and developer experience.

Features

  • 🚀 Simple API: Clean, intuitive interface with PartitionStore pattern
  • 💾 Persistent Storage: Automatically caches vector stores to .pickle.gz files
  • 🔍 Multiple Similarity Metrics: Choose from cosine, dot product, Euclidean, or Derrida distance
  • 🎯 Type Safe: Full type annotations with strict pyright compliance
  • CPU Optimized: Designed for CPU-only environments (no CUDA required)
  • 🔄 Flexible Caching: Use persistent stores or create temporary in-memory collections
  • 📦 Easy Installation: One-command setup with automatic PyTorch CPU configuration
  • Partition-level Operations: Add, remove, and search documents through dedicated store objects

Installation

pip install microvector

Or for development:

git clone https://github.com/loganpowell/microvector.git
cd microvector
uv sync

Quick Start

from microvector import Client

# Initialize the client
client = Client()

# Save a collection (by default, in-memory only)
store = client.save(
    partition="my_documents",
    collection=[
        {"text": "Python is a popular programming language", "category": "tech"},
        {"text": "Machine learning models learn from data", "category": "ai"},
        {"text": "The quick brown fox jumps over the lazy dog", "category": "example"},
    ],
    key="text"
)

# Search using the store
results = store.search(
    term="artificial intelligence and ML",
)

for result in results:
    print(f"Score: {result['similarity_score']:.4f} - {result['text']}")

# Add more documents (also in-memory by default)
store.add([
    {"text": "Deep learning uses neural networks", "category": "ai"}
])

# Search again with the updated store
results = store.search("neural networks", top_k=3)

# Persist to disk when ready
client.save(
    partition="my_documents",
    collection=store.to_dict(),
    cache=True  # Now save to disk
)

API Reference

Client

The main interface for creating and managing vector stores.

Client(
    model_cache: str = "./.cached_models",
    vector_cache: str = "./.vector_cache",
    embedding_model: str = "avsolatorio/GIST-small-Embedding-v0",
    search_algo: str = "cosine"
)

Parameters:

Parameter Description Default
model_cache Directory for caching downloaded embedding models "./.cached_models"
vector_cache Directory for persisting vector stores "./.vector_cache"
embedding_model HuggingFace model name for generating embeddings "avsolatorio/GIST-small-Embedding-v0"
search_algo "cosine", "dot", "euclidean", or "derrida" "cosine"

Note: The search_algo is set at the client level and applies to all partitions created by that client. This ensures consistency and prevents issues with switching algorithms on already-normalized vectors.

save()

Create or update a vector store and return a PartitionStore for operations.

store = client.save(
    partition: str,
    collection: list[dict[str, Any]],
    key: str = "text",
    cache: bool = False,
    append: bool = False
) -> PartitionStore

Parameters:

Parameter Description Default
partition Unique identifier for this vector store -
collection List of documents (dictionaries) to vectorize -
key Field name to use for embedding "text"
cache If True, persist to disk; if False, keep in-memory only False
append If True, add to existing store; if False, replace False

Returns: PartitionStore object with methods for searching and managing documents

Example:

store = client.save(
    partition="products",
    collection=[
        {"description": "Wireless headphones", "price": 99.99},
        {"description": "Smart watch", "price": 299.99},
    ],
    key="description",
    cache=True  # Persist to disk
)

print(f"Partition: {store.partition}")
print(f"Size: {store.size}")
print(f"Algorithm: {store.algo}")

PartitionStore

A partition-specific interface for vector operations returned by client.save().

Attributes:

Attribute Type Description
partition str Name of the partition
key str Field being vectorized
algo str Similarity algorithm in use ("cosine", "dot", "euclidean", or "derrida")
size int Number of documents in the store (read-only property)

Methods:

search()

Search this partition for similar documents.

results = store.search(
    term: str,
    top_k: int = 5
) -> list[dict[str, Any]]

Parameters:

Parameter Description Default
term Search query string -
top_k Maximum number of results to return 5

Returns: List of documents with similarity scores

[
    {
        "text": "Machine learning is awesome",
        "category": "ai",
        "similarity_score": 0.923
    },
    ...
]

Example:

results = store.search("laptop computers", top_k=3)
for result in results:
    print(f"{result['similarity_score']:.3f} - {result['description']}")

add()

Add new documents to this partition.

success = store.add(
    collection: list[dict[str, Any]] | dict[str, Any],
    cache: bool = False
) -> bool

Parameters:

Parameter Description Default
collection Single document or list of documents to add -
cache If True, persist changes; if False, keep in-memory False

Returns: True if successful, False otherwise

Example:

# Add a single document
store.add({"description": "USB-C cable", "price": 12.99})

# Add multiple documents
store.add([
    {"description": "Keyboard", "price": 79.99},
    {"description": "Mouse pad", "price": 19.99}
], cache=True)

print(f"Updated size: {store.size}")

remove()

Remove a document from this partition by index or content.

success = store.remove(
    item: int | dict[str, Any],
    cache: bool = False
) -> bool

Parameters:

Parameter Description Default
item Document index (int) or document content (dict) to remove -
cache If True, persist changes; if False, keep in-memory False

Returns: True if successful, False otherwise

Example:

# Remove by index
store.remove(0)

# Remove by document content
store.remove({"description": "Wireless headphones", "price": 99.99})

delete()

Delete this partition's cache file from disk.

success = store.delete() -> bool

Returns: True if successful, False otherwise

Example:

if store.delete():
    print(f"Partition '{store.partition}' deleted from disk")

Similarity Algorithms

Algorithm Best For Range
cosine General text similarity (default) 0-1 (higher is more similar)
dot When magnitude matters Unbounded
euclidean Spatial distance 0-∞ (lower is more similar)
derrida Experimental alternative distance 0-∞ (lower is more similar)

Advanced Usage

Using Different Algorithms

The similarity algorithm is set at the client level and applies to all partitions:

# Create clients with different algorithms
cosine_client = Client(search_algo="cosine")
dot_client = Client(search_algo="dot")
euclidean_client = Client(search_algo="euclidean")

# Each client's partitions use its algorithm
cosine_store = cosine_client.save("docs_cosine", documents)
dot_store = dot_client.save("docs_dot", documents)

# Different algorithms, different results
cosine_results = cosine_store.search("query")
dot_results = dot_store.search("query")

Why client-level? Vectors are normalized based on the algorithm. Switching algorithms on existing vectors would produce incorrect results, so we lock the algorithm at creation time for consistency.

Custom Embedding Models

Use any HuggingFace sentence-transformer model:

client = Client(
    embedding_model="intfloat/e5-small-v2",
    search_algo="cosine"
)

Nested Key Paths

Access nested fields using dot notation:

collection = [
    {
        "product": {
            "name": "Laptop",
            "specs": {"cpu": "Intel i7"}
        }
    },
    {
        "product": {
            "name": "Mouse",
            "specs": {"cpu": None}
        }
    }
]

store = client.save(
    partition="products",
    collection=collection,
    key="product.name"  # Extract "Laptop", "Mouse" from nested structure
)

# Search works on the nested field
results = store.search("computer", top_k=1)
print(results[0]["product"]["name"])  # "Laptop"

Working with Multiple Partitions

Organize different datasets in separate partitions:

# Create stores for different content types
news_store = client.save("news_articles", news_data, key="content")
review_store = client.save("product_reviews", review_data, key="review_text")
ticket_store = client.save("support_tickets", tickets, key="description")

# Search each independently
news_results = news_store.search("economy")
review_results = review_store.search("quality")
ticket_results = ticket_store.search("login issue")

Incremental Updates

Add new documents to existing stores without replacing them:

# Create initial store (in-memory by default)
store = client.save(
    partition="knowledge_base",
    collection=[
        {"text": "Python is a programming language"},
        {"text": "JavaScript runs in browsers"},
    ]
)

print(f"Initial size: {store.size}")  # 2

# Add more documents using the store's add() method
store.add([
    {"text": "TypeScript adds types to JavaScript"},
    {"text": "Rust is memory-safe"},
])

print(f"Updated size: {store.size}")  # 4

# Or use save() with append=True
client.save(
    partition="knowledge_base",
    collection=[{"text": "Go is designed for concurrency"}],
    append=True
)

# Persist all changes to disk when ready
store.add([], cache=True)  # Flush to disk

Persistent Storage

Enable caching to persist vector stores to disk:

# Save directly to disk
store = client.save(
    partition="permanent_docs",
    collection=documents,
    cache=True  # Persist immediately
)

# Add documents and persist
store.add(new_documents, cache=True)

# Remove documents and persist
store.remove(0, cache=True)

# Later, load from cache
loaded_store = client.save(
    partition="permanent_docs",
    collection=[],  # Empty collection loads from cache
    cache=True
)

In-Memory Operations

Work with documents without persisting to disk (default behavior):

# Create a temporary store (cache=False is the default)
temp_store = client.save(
    partition="temp_analysis",
    collection=documents
)

# Add documents without caching (default behavior)
temp_store.add(more_documents)

# Search as normal
results = temp_store.search("query")

# Since cache=False (default), changes aren't persisted

Document Management

# Create store (in-memory by default)
store = client.save("products", initial_products)

# Add new products (in-memory)
store.add([
    {"name": "New Product", "price": 49.99}
])

# Remove by index (in-memory)
store.remove(0)

# Remove by content (in-memory)
store.remove({"name": "Old Product", "price": 19.99})

# Check current size
print(f"Current inventory: {store.size} items")

# Persist changes to disk
store.add([], cache=True)

# Or delete entire partition from disk
if store.delete():
    print("Partition deleted")

Development Setup

This project uses uv for dependency management and automatically configures CPU-only PyTorch.

Quick Start

  1. Install dependencies:

    uv sync
    
  2. Verify setup:

    uv run python setup_dev.py
    
  3. Run tests:

    uv run pytest
    
  4. Type checking:

    uv run pyright
    

What Gets Installed

  • PyTorch (CPU-only): Automatically from PyTorch CPU index
  • Transformers: HuggingFace transformers library
  • Sentence Transformers: For embedding generation
  • NumPy: Numerical computing

No special flags or manual PyTorch installation needed - just uv sync and go!

Performance Tips

  1. Reuse Client instances - Model loading is expensive
  2. Use persistent caching - Vector computation is cached automatically
  3. Batch your saves - Save collections together when possible
  4. Choose the right algorithm - Cosine is fastest for most use cases
  5. Adjust top_k - Lower values are faster

Architecture

microvector/
├── main.py              # Client API
├── partition_store.py   # PartitionStore class for partition operations
├── store.py             # Vector storage and similarity search
├── cache.py             # Persistence layer
├── embed.py             # Embedding generation
├── search.py            # Search utilities
├── algos.py             # Similarity algorithms
└── utils.py             # Helper functions

License

MIT License - see LICENSE file for details.

Credits

Based on HyperDB by John Dagdelen. Refactored and maintained by Logan Powell.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

microvector-0.3.11.tar.gz (176.4 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

microvector-0.3.11-py3-none-any.whl (36.7 kB view details)

Uploaded Python 3

File details

Details for the file microvector-0.3.11.tar.gz.

File metadata

  • Download URL: microvector-0.3.11.tar.gz
  • Upload date:
  • Size: 176.4 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.7

File hashes

Hashes for microvector-0.3.11.tar.gz
Algorithm Hash digest
SHA256 5c61bb38ec0763b9d58b4cbea485c36e60e3197d5dc003ee8aa24cdba7a4abdf
MD5 fdc72fb5057a4623383e470568199610
BLAKE2b-256 d8ac675bdfbaa9b0f349f38e09882037b94394aed43226fa7f22f6b82f945891

See more details on using hashes here.

Provenance

The following attestation bundles were made for microvector-0.3.11.tar.gz:

Publisher: publish.yml on loganpowell/microvector

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file microvector-0.3.11-py3-none-any.whl.

File metadata

  • Download URL: microvector-0.3.11-py3-none-any.whl
  • Upload date:
  • Size: 36.7 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.7

File hashes

Hashes for microvector-0.3.11-py3-none-any.whl
Algorithm Hash digest
SHA256 0df811506b651b85fbb4521af4c81367caf05a78f12fc345ad200e99bff10f4a
MD5 4b3a96f0b37efcf2ba2725d41269ee06
BLAKE2b-256 f70f71af8de2718cd7f0c1f87015849b493cad890e9d5d0d5b24b44aebaa0d31

See more details on using hashes here.

Provenance

The following attestation bundles were made for microvector-0.3.11-py3-none-any.whl:

Publisher: publish.yml on loganpowell/microvector

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page