ReMe/docs/vector_store_api_guide.md
2025-10-27 17:02:44 +08:00

27 KiB

jupytext kernelspec
formats text_representation
md:myst
extension format_name format_version jupytext_version
.md myst 0.13 1.11.5
display_name language name
Python 3 python python3

Vector Store API Guide

This guide covers the vector store implementations available in ReMe, their APIs, and how to use them effectively.

📋 Overview

ReMe provides multiple vector store backends for different use cases:

  • LocalVectorStore (backend=local) - 📁 Simple file-based storage for development and small datasets
  • ChromaVectorStore (backend=chroma) - 🔮 Embedded vector database for moderate scale
  • EsVectorStore (backend=elasticsearch) - 🔍 Elasticsearch-based storage for production and large scale
  • QdrantVectorStore (backend=qdrant) - 🎯 High-performance vector database with advanced filtering
  • MemoryVectorStore (backend=memory) - ⚡ In-memory storage for ultra-fast access and testing

All vector stores implement the BaseVectorStore interface, providing a consistent API across implementations.

📊 Comparison Table

Feature LocalVectorStore ChromaVectorStore EsVectorStore QdrantVectorStore MemoryVectorStore
Storage File (JSONL) Embedded DB Elasticsearch Qdrant Server In-Memory
Performance Medium Good Excellent Excellent Ultra-Fast
Scalability < 10K vectors < 1M vectors > 1M vectors > 10M vectors < 1M vectors
Persistence ✅ Auto ✅ Auto ✅ Auto ✅ Auto ⚠️ Manual
Setup Complexity 🟢 Simple 🟡 Medium 🔴 Complex 🟡 Medium 🟢 Simple
Dependencies None ChromaDB Elasticsearch Qdrant None
Filtering ❌ Basic ✅ Metadata ✅ Advanced ✅ Advanced ❌ Basic
Concurrency ❌ Limited ✅ Good ✅ Excellent ✅ Excellent ❌ Single Process
Async Support ❌ No ❌ No ❌ No ✅ Native ❌ No
Best For Development Local Apps Production Production/Cloud Testing

🔄 Common API Methods

All vector store implementations share these core methods:

🔄 Async Support

All vector stores provide both synchronous and asynchronous versions of every method:

# Synchronous methods
store.search(query="example", workspace_id="workspace", top_k=5)
store.insert(nodes, workspace_id="workspace")

# Asynchronous methods (with async_ prefix)
await store.async_search(query="example", workspace_id="workspace", top_k=5)
await store.async_insert(nodes, workspace_id="workspace")

Workspace Management

# Check if workspace exists
store.exist_workspace(workspace_id: str) -> bool

# Create a new workspace
store.create_workspace(workspace_id: str, **kwargs)

# Delete a workspace
store.delete_workspace(workspace_id: str, **kwargs)

# Copy a workspace
store.copy_workspace(src_workspace_id: str, dest_workspace_id: str, **kwargs)

Data Operations

# Insert nodes (single or list)
store.insert(nodes: VectorNode | List[VectorNode], workspace_id: str, **kwargs)

# Delete nodes by ID
store.delete(node_ids: str | List[str], workspace_id: str, **kwargs)

# Search for similar nodes
store.search(query: str, workspace_id: str, top_k: int = 1, **kwargs) -> List[VectorNode]

# Iterate through workspace nodes
for node in store.iter_workspace_nodes(workspace_id: str, **kwargs):
    # Process each node

Import/Export

# Export workspace to file
store.dump_workspace(workspace_id: str, path: str | Path = "", callback_fn=None, **kwargs)

# Import workspace from file
store.load_workspace(workspace_id: str, path: str | Path = "", nodes: List[VectorNode] = None, 
                    callback_fn=None, **kwargs)

⚡ Vector Store Implementations

1. 📁 LocalVectorStore (backend=local)

A simple file-based vector store that saves data to local JSONL files.

💡 When to Use

  • Development and testing - No external dependencies required 🛠️
  • Small datasets - Suitable for datasets with < 10,000 vectors 📊
  • Single-user applications - Limited concurrent access support 👤

⚙️ Configuration

from flowllm.storage.vector_store import LocalVectorStore
from flowllm.embedding_model import OpenAICompatibleEmbeddingModel
from flowllm.utils.common_utils import load_env

# Load environment variables (for API keys)
load_env()

# Initialize embedding model
embedding_model = OpenAICompatibleEmbeddingModel(dimensions=64, model_name="text-embedding-v4")

# Initialize vector store
vector_store = LocalVectorStore(
    embedding_model=embedding_model,
    store_dir="./file_vector_store",  # Directory to store JSONL files
    batch_size=1024                   # Batch size for operations
)

💻 Example Usage

from flowllm.schema.vector_node import VectorNode

# Create workspace
workspace_id = "my_workspace"
vector_store.create_workspace(workspace_id)

# Create nodes
nodes = [
    VectorNode(
        unique_id="node1",
        workspace_id=workspace_id,
        content="Artificial intelligence is revolutionizing technology",
        metadata={"category": "tech", "source": "article1"}
    ),
    VectorNode(
        unique_id="node2",
        workspace_id=workspace_id,
        content="Machine learning enables data-driven insights",
        metadata={"category": "tech", "source": "article2"}
    )
]

# Insert nodes
vector_store.insert(nodes, workspace_id)

# Search
results = vector_store.search("What is AI?", workspace_id, top_k=2)
for result in results:
    print(f"Content: {result.content}")
    print(f"Metadata: {result.metadata}")
    print(f"Score: {result.metadata.get('score', 'N/A')}")

2. 🔮 ChromaVectorStore (backend=chroma)

An embedded vector database that provides persistent storage with advanced features.

💡 When to Use

  • Local development with persistence requirements 🏠
  • Medium-scale applications (10K - 1M vectors) 📈
  • Applications requiring metadata filtering 🔍

⚙️ Configuration

from flowllm.storage.vector_store import ChromaVectorStore
from flowllm.embedding_model import OpenAICompatibleEmbeddingModel
from flowllm.utils.common_utils import load_env

# Load environment variables
load_env()

# Initialize embedding model
embedding_model = OpenAICompatibleEmbeddingModel(dimensions=64, model_name="text-embedding-v4")

# Initialize vector store
vector_store = ChromaVectorStore(
    embedding_model=embedding_model,
    store_dir="./chroma_vector_store",  # Directory for Chroma database
    batch_size=1024                     # Batch size for operations
)

💻 Example Usage

from flowllm.schema.vector_node import VectorNode

workspace_id = "chroma_workspace"

# Check if workspace exists and create if needed
if not vector_store.exist_workspace(workspace_id):
    vector_store.create_workspace(workspace_id)

# Create nodes with metadata
nodes = [
    VectorNode(
        unique_id="node1",
        workspace_id=workspace_id,
        content="Deep learning models require large datasets",
        metadata={
            "category": "AI", 
            "difficulty": "advanced", 
            "topic": "deep_learning"
        }
    ),
    VectorNode(
        unique_id="node2",
        workspace_id=workspace_id,
        content="Transformer architecture revolutionized NLP",
        metadata={
            "category": "AI",
            "difficulty": "intermediate",
            "topic": "transformers"
        }
    )
]

# Insert nodes
vector_store.insert(nodes, workspace_id)

# Search
results = vector_store.search("deep learning", workspace_id, top_k=5)
for result in results:
    print(f"Content: {result.content}")
    print(f"Metadata: {result.metadata}")

3. 🔍 EsVectorStore (backend=elasticsearch)

Production-grade vector search using Elasticsearch with advanced filtering and scaling capabilities.

💡 When to Use

  • Production environments requiring high availability 🏭
  • Large-scale applications (1M+ vectors) 🚀
  • Complex filtering requirements on metadata 🎯

🛠️ Setup Elasticsearch

Before using EsVectorStore, set up Elasticsearch:

Option 1: Docker Run
# Pull the latest Elasticsearch image
docker pull docker.elastic.co/elasticsearch/elasticsearch-wolfi:9.0.0

# Run Elasticsearch container
docker run -p 9200:9200 \
  -e "discovery.type=single-node" \
  -e "xpack.security.enabled=false" \
  -e "xpack.license.self_generated.type=trial" \
  -e "http.host=0.0.0.0" \
  docker.elastic.co/elasticsearch/elasticsearch-wolfi:9.0.0
Environment Configuration
export FLOW_ES_HOSTS=http://localhost:9200

⚙️ Configuration

from flowllm.storage.vector_store import EsVectorStore
from flowllm.embedding_model import OpenAICompatibleEmbeddingModel
from flowllm.utils.common_utils import load_env
import os

# Load environment variables
load_env()

# Initialize embedding model
embedding_model = OpenAICompatibleEmbeddingModel(dimensions=64, model_name="text-embedding-v4")

# Initialize vector store
vector_store = EsVectorStore(
    embedding_model=embedding_model,
    hosts=os.getenv("FLOW_ES_HOSTS", "http://localhost:9200"),  # Elasticsearch hosts
    basic_auth=None,                                           # ("username", "password") for auth
    batch_size=1024                                           # Batch size for bulk operations
)

🎯 Advanced Filtering

EsVectorStore supports advanced filtering capabilities through the filter_dict parameter:

# Term filters (exact match)
term_filter = {
    "category": "technology",
    "author": "research_team"
}

# Range filters (numeric and date ranges)
range_filter = {
    "score": {"gte": 0.8},  # Score >= 0.8
    "confidence": {"gte": 0.5, "lte": 0.9},  # Between 0.5 and 0.9
    "timestamp": {"gte": "2024-01-01", "lte": "2024-12-31"}
}

# Combined filters (filters are combined with AND logic)
combined_filter = {
    "category": "AI",
    "confidence": {"gte": 0.9}
}

# Search with filters applied
results = vector_store.search("machine learning", workspace_id, top_k=10, filter_dict=combined_filter)

⚡ Performance Optimization

# Refresh index for immediate availability (useful after bulk inserts)
vector_store.insert(nodes, workspace_id, refresh=True)  # Auto-refresh
vector_store.refresh(workspace_id)  # Manual refresh

# Bulk operations with custom batch size
vector_store.insert(large_node_list, workspace_id, refresh=False)  # Skip refresh for speed
vector_store.refresh(workspace_id)  # Refresh once after all inserts

💻 Example Usage

from flowllm.schema.vector_node import VectorNode

# Define workspace
workspace_id = "production_workspace"

# Create workspace if needed
if not vector_store.exist_workspace(workspace_id):
    vector_store.create_workspace(workspace_id)

# Create nodes with rich metadata
nodes = [
    VectorNode(
        unique_id="doc1",
        workspace_id=workspace_id,
        content="Transformer architecture revolutionized NLP",
        metadata={
            "category": "AI",
            "subcategory": "NLP",
            "author": "research_team",
            "timestamp": "2024-01-15",
            "confidence": 0.95,
            "tags": ["transformer", "nlp", "attention"]
        }
    )
]

# Insert with refresh for immediate availability
vector_store.insert(nodes, workspace_id, refresh=True)

# Advanced search with filters
filter_dict = {
    "category": "AI",
    "confidence": {"gte": 0.9}
}

results = vector_store.search("transformer models", workspace_id, top_k=5, filter_dict=filter_dict)

for result in results:
    print(f"Score: {result.metadata.get('score', 'N/A')}")
    print(f"Content: {result.content}")
    print(f"Metadata: {result.metadata}")

4. 🎯 QdrantVectorStore (backend=qdrant)

A high-performance vector database designed for production workloads with native async support and advanced filtering.

💡 When to Use

  • Production environments requiring high performance and reliability 🏭
  • Large-scale applications (10M+ vectors) with excellent horizontal scaling 🚀
  • Applications requiring native async operations for better concurrency ⚡
  • Complex filtering and metadata queries on large datasets 🎯
  • Cloud-native deployments with Qdrant Cloud support ☁️

🛠️ Setup Qdrant

Before using QdrantVectorStore, set up Qdrant:

# Pull the latest Qdrant image
docker pull qdrant/qdrant

# Run Qdrant container
docker run -p 6333:6333 -p 6334:6334 \
  -v $(pwd)/qdrant_storage:/qdrant/storage:z \
  qdrant/qdrant
Option 2: Qdrant Cloud

For production, you can use Qdrant Cloud for managed hosting.

Environment Configuration
# For local setup
export FLOW_QDRANT_HOST=localhost
export FLOW_QDRANT_PORT=6333

# For cloud setup (optional)
export FLOW_QDRANT_API_KEY=your-api-key

⚙️ Configuration

from flowllm.storage.vector_store import QdrantVectorStore
from flowllm.embedding_model import OpenAICompatibleEmbeddingModel
from flowllm.utils.common_utils import load_env
import os

# Load environment variables
load_env()

# Initialize embedding model
embedding_model = OpenAICompatibleEmbeddingModel(dimensions=64, model_name="text-embedding-v4")

# Option 1: Use localhost with environment variables
vector_store = QdrantVectorStore(
    embedding_model=embedding_model,
    host=os.getenv("FLOW_QDRANT_HOST", "localhost"),
    port=int(os.getenv("FLOW_QDRANT_PORT", "6333")),
    batch_size=1024
)

# Option 2: Use URL (for Qdrant Cloud or remote servers)
vector_store = QdrantVectorStore(
    embedding_model=embedding_model,
    url="http://your-qdrant-server:6333",
    api_key="your-api-key",  # Optional, for cloud
    batch_size=1024
)

# Option 3: Specify custom distance metric
from qdrant_client.http.models import Distance

vector_store = QdrantVectorStore(
    embedding_model=embedding_model,
    host="localhost",
    port=6333,
    distance=Distance.COSINE,  # or Distance.EUCLIDEAN, Distance.DOT
    batch_size=1024
)

🎯 Advanced Filtering

QdrantVectorStore supports advanced filtering capabilities similar to Elasticsearch:

# Term filters (exact match)
term_filter = {
    "category": "AI",
    "node_type": "research"
}

# Range filters (numeric)
range_filter = {
    "confidence": {"gte": 0.8, "lte": 1.0},  # Between 0.8 and 1.0
    "score": {"gt": 0.5}  # Greater than 0.5
}

# Combined filters (all conditions must match - AND logic)
combined_filter = {
    "category": "AI",
    "confidence": {"gte": 0.9},
    "node_type": "research"
}

# Search with filters
results = vector_store.search(
    query="machine learning",
    workspace_id=workspace_id,
    top_k=10,
    filter_dict=combined_filter
)
Filter Operations Supported:
  • Exact match: {"field": "value"}
  • Range queries:
    • gte: Greater than or equal
    • lte: Less than or equal
    • gt: Greater than
    • lt: Less than

⚡ Async Operations

QdrantVectorStore provides native async support for all operations:

import asyncio

async def main():
    # All operations have async equivalents
    
    # Check if workspace exists
    exists = await vector_store.async_exist_workspace(workspace_id)
    
    # Create workspace
    if not exists:
        await vector_store.async_create_workspace(workspace_id)
    
    # Insert nodes with async embedding
    await vector_store.async_insert(nodes, workspace_id)
    
    # Search with async embedding
    results = await vector_store.async_search(
        query="AI research",
        workspace_id=workspace_id,
        top_k=5,
        filter_dict={"category": "AI"}
    )
    
    # Delete nodes
    await vector_store.async_delete(node_ids, workspace_id)
    
    # Delete workspace
    await vector_store.async_delete_workspace(workspace_id)
    
    # Close client
    await vector_store.async_close()

# Run async operations
asyncio.run(main())

💻 Example Usage

from flowllm.schema.vector_node import VectorNode

workspace_id = "qdrant_workspace"

# Check and create workspace
if not vector_store.exist_workspace(workspace_id):
    vector_store.create_workspace(workspace_id)

# Create nodes with rich metadata
nodes = [
    VectorNode(
        unique_id="node1",
        workspace_id=workspace_id,
        content="Artificial intelligence is revolutionizing technology",
        metadata={
            "category": "AI",
            "node_type": "research",
            "confidence": 0.95,
            "author": "research_team"
        }
    ),
    VectorNode(
        unique_id="node2",
        workspace_id=workspace_id,
        content="Machine learning models require large datasets",
        metadata={
            "category": "AI",
            "node_type": "tutorial",
            "confidence": 0.85,
            "author": "education_team"
        }
    ),
    VectorNode(
        unique_id="node3",
        workspace_id=workspace_id,
        content="Deep learning excels at image recognition",
        metadata={
            "category": "AI",
            "node_type": "research",
            "confidence": 0.92,
            "author": "research_team"
        }
    )
]

# Insert nodes (upsert - creates or updates)
vector_store.insert(nodes, workspace_id)

# Simple search
results = vector_store.search("What is AI?", workspace_id, top_k=3)
for result in results:
    print(f"Content: {result.content}")
    print(f"Score: {result.metadata.get('score', 'N/A')}")
    print(f"Metadata: {result.metadata}")
    print("-" * 50)

# Advanced search with filters
filter_dict = {
    "node_type": "research",
    "confidence": {"gte": 0.9}
}

filtered_results = vector_store.search(
    query="AI technology",
    workspace_id=workspace_id,
    top_k=5,
    filter_dict=filter_dict
)

print(f"Found {len(filtered_results)} filtered results")
for result in filtered_results:
    print(f"Content: {result.content}")
    print(f"Metadata: {result.metadata}")

# Iterate through all nodes
print("\nAll nodes in workspace:")
for node in vector_store.iter_workspace_nodes(workspace_id, limit=100):
    print(f"ID: {node.unique_id}, Content: {node.content[:50]}...")

# Update a node (delete + insert)
updated_node = VectorNode(
    unique_id="node1",
    workspace_id=workspace_id,
    content="Artificial intelligence is transforming industries worldwide",
    metadata={
        "category": "AI",
        "node_type": "research",
        "confidence": 0.98,
        "author": "research_team",
        "updated": True
    }
)
vector_store.delete("node1", workspace_id)
vector_store.insert(updated_node, workspace_id)

# Export workspace for backup
vector_store.dump_workspace(workspace_id, path="./qdrant_backup")

# Clean up
vector_store.close()

🔄 Async Example

import asyncio
from flowllm.schema.vector_node import VectorNode

async def async_example():
    workspace_id = "async_qdrant_workspace"
    
    # Create workspace
    if not await vector_store.async_exist_workspace(workspace_id):
        await vector_store.async_create_workspace(workspace_id)
    
    # Create nodes
    nodes = [
        VectorNode(
            unique_id="async_node1",
            workspace_id=workspace_id,
            content="Async operations enable better performance",
            metadata={"type": "performance", "async": True}
        ),
        VectorNode(
            unique_id="async_node2",
            workspace_id=workspace_id,
            content="Concurrent requests improve throughput",
            metadata={"type": "performance", "async": True}
        )
    ]
    
    # Insert with async embedding
    await vector_store.async_insert(nodes, workspace_id)
    
    # Search with async embedding
    results = await vector_store.async_search(
        query="performance optimization",
        workspace_id=workspace_id,
        top_k=2,
        filter_dict={"async": True}
    )
    
    for result in results:
        print(f"Score: {result.metadata['score']:.4f}")
        print(f"Content: {result.content}")
    
    # Cleanup
    await vector_store.async_delete_workspace(workspace_id)
    await vector_store.async_close()

# Run async example
asyncio.run(async_example())

🌟 Key Features

  • Native Async Support - All operations have async equivalents for better concurrency
  • Upsert Operations - Insert automatically updates existing nodes with the same ID
  • Advanced Filtering - Support for term and range filters on metadata
  • High Performance - Optimized for large-scale vector similarity search
  • Horizontal Scaling - Supports clustering for distributed deployments
  • Multiple Distance Metrics - Cosine, Euclidean, and Dot Product similarity
  • Persistent Storage - Data is automatically persisted to disk
  • Efficient Iteration - Scroll through large collections with pagination

🚨 Important Notes

  • Collection = Workspace - Qdrant uses "collections" which map to workspace_id
  • Automatic Embedding - Nodes without vectors are automatically embedded
  • ID-based Upsert - Using the same unique_id will update existing nodes
  • Metadata Indexing - All metadata fields are automatically indexed for filtering
  • Connection Management - Call close() or async_close() to cleanup connections

📊 Performance Tips

  1. Batch Operations - Insert multiple nodes at once for better performance
  2. Use Async - For high-concurrency scenarios, use async methods
  3. Optimize Filters - Use indexed metadata fields for faster filtering
  4. Pagination - Use iter_workspace_nodes() with appropriate limit for large collections
  5. Distance Metric - Choose appropriate distance metric for your use case (COSINE for normalized vectors)

5. ⚡ MemoryVectorStore (backend=memory)

An ultra-fast in-memory vector store that keeps all data in RAM for maximum performance.

💡 When to Use

  • Testing and development - Fastest possible operations for unit tests 🧪
  • Small to medium datasets that fit in memory (< 1M vectors) 💾
  • Applications requiring ultra-low latency search operations ⚡
  • Temporary workspaces that don't need persistence 🚀

⚙️ Configuration

from flowllm.storage.vector_store import MemoryVectorStore
from flowllm.embedding_model import OpenAICompatibleEmbeddingModel
from flowllm.utils.common_utils import load_env

# Load environment variables
load_env()

# Initialize embedding model
embedding_model = OpenAICompatibleEmbeddingModel(dimensions=64, model_name="text-embedding-v4")

# Initialize vector store
vector_store = MemoryVectorStore(
    embedding_model=embedding_model,
    store_dir="./memory_vector_store",  # Directory for backup/restore operations
    batch_size=1024                     # Batch size for operations
)

💻 Example Usage

from flowllm.schema.vector_node import VectorNode

workspace_id = "memory_workspace"

# Create workspace in memory
vector_store.create_workspace(workspace_id)

# Create nodes
nodes = [
    VectorNode(
        unique_id="mem_node1",
        workspace_id=workspace_id,
        content="Memory stores provide ultra-fast access to data",
        metadata={
            "category": "performance", 
            "type": "memory",
            "speed": "ultra_fast"
        }
    ),
    VectorNode(
        unique_id="mem_node2",
        workspace_id=workspace_id,
        content="In-memory databases excel at low-latency operations",
        metadata={
            "category": "performance",
            "type": "database",
            "latency": "low"
        }
    )
]

# Insert nodes (stored in memory)
vector_store.insert(nodes, workspace_id)

# Ultra-fast search
results = vector_store.search("fast memory access", workspace_id, top_k=2)
for result in results:
    print(f"Content: {result.content}")
    print(f"Score: {result.metadata.get('score', 'N/A')}")

# Optional: Save to disk for backup
vector_store.dump_workspace(workspace_id, path="./backup")

# Optional: Load from disk to memory
vector_store.load_workspace(workspace_id, path="./backup")

⚡ Performance Benefits

  • Zero I/O latency - All operations happen in RAM
  • Instant search results - No disk or network overhead
  • Perfect for testing - Fast setup and teardown
  • Memory efficient - Only stores what you need

🚨 Important Notes

  • Data is volatile - Lost when process ends unless explicitly saved
  • Memory usage - Entire dataset must fit in available RAM
  • No persistence - Use dump_workspace() to save to disk
  • Single process - Not suitable for distributed applications

📝 Working with VectorNode

The VectorNode class is the fundamental data unit for all vector stores:

from flowllm.schema.vector_node import VectorNode

# Create a node
node = VectorNode(
    unique_id="unique_identifier",     # Unique ID for the node (required)
    workspace_id="my_workspace",       # Workspace ID (required)
    content="Text content to embed",   # Content to be embedded (required)
    metadata={                         # Optional metadata
        "source": "document1",
        "category": "technology",
        "timestamp": "2024-08-29"
    },
    vector=None                        # Vector will be generated automatically if None
)

🔄 Import/Export Example

Export and import workspaces for backup or transfer:

# Export workspace to file
vector_store.dump_workspace(
    workspace_id="my_workspace",
    path="./backup_data"  # Directory to store the exported data
)

# Import workspace from file
vector_store.load_workspace(
    workspace_id="new_workspace",
    path="./backup_data"  # Directory containing the exported data
)

# Copy workspace within the same store
vector_store.copy_workspace(
    src_workspace_id="original_workspace",
    dest_workspace_id="copied_workspace"
)

🧩 Integration with Embedding Models

All vector stores require an embedding model to function:

from flowllm.embedding_model import OpenAICompatibleEmbeddingModel

# Initialize embedding model
embedding_model = OpenAICompatibleEmbeddingModel(
    dimensions=64,               # Embedding dimensions
    model_name="text-embedding-v4",  # Model name
    batch_size=32                # Batch size for embedding generation
)

# Pass to vector store (example with LocalVectorStore)
# You can also use: ChromaVectorStore, EsVectorStore, QdrantVectorStore, or MemoryVectorStore
vector_store = LocalVectorStore(
    embedding_model=embedding_model,
    store_dir="./vector_store"
)