Skip to main content

Python client for KotaDB - PostgreSQL-level ease of use for document database

Project description

KotaDB Python Client

A simple, PostgreSQL-level easy-to-use Python client for KotaDB with type safety and builder patterns.

PyPI version

Features

  • Type Safety: Runtime-validated types (ValidatedPath, ValidatedDocumentId, etc.)
  • Builder Patterns: Fluent APIs for safe document and query construction
  • Full API Coverage: Support for all KotaDB operations (CRUD, search, metadata)
  • Connection Management: Automatic retries, connection pooling, timeout handling
  • Multiple Search Types: Text, semantic, and hybrid search capabilities

Installation

pip install kotadb-client

Quick Start

from kotadb import KotaDB

# Connect to KotaDB
db = KotaDB("http://localhost:8080")

# Insert a document
doc_id = db.insert({
    "path": "/notes/meeting.md",
    "title": "Team Meeting Notes",
    "content": "Discussed project roadmap and next steps...",
    "tags": ["work", "meeting", "planning"]
})

# Search for documents
results = db.query("project roadmap")
for result in results.results:
    print(f"Found: {result.document.title} (score: {result.score})")

# Get a specific document
doc = db.get(doc_id)
print(f"Document: {doc.title}")

# Update a document
updated_doc = db.update(doc_id, {
    "content": "Updated meeting notes with action items..."
})

# Delete a document
db.delete(doc_id)

Type Safety & Builder Patterns

Validated Types

Prevent errors at runtime with validated types that mirror the Rust implementation:

from kotadb import ValidatedPath, ValidatedDocumentId, ValidatedTitle

# These will raise ValidationError if invalid
path = ValidatedPath("/notes/meeting.md")  # Validates: no null bytes, no parent dir refs, etc.
doc_id = ValidatedDocumentId.parse("123e4567-e89b-12d3-a456-426614174000")
title = ValidatedTitle("My Document Title")  # Validates: non-empty, length limits

# Use in document operations
db.insert({
    "path": path.as_str(),
    "title": title.as_str(), 
    "content": "Document content..."
})

Document Builder Pattern

Build documents safely with validation at each step:

from kotadb import DocumentBuilder

# Fluent API with validation
doc_id = db.insert_with_builder(
    DocumentBuilder()
    .path("/knowledge/python-guide.md")
    .title("Python Best Practices")
    .content("# Python Guide\n\nBest practices for Python development...")
    .add_tag("python")
    .add_tag("documentation")
    .add_metadata("author", "user@example.com")
    .add_metadata("priority", "high")
)

Query Builder Pattern

Build complex queries with type safety:

from kotadb import QueryBuilder

# Text search with filters
results = db.query_with_builder(
    QueryBuilder()
    .text("machine learning algorithms")
    .limit(20)
    .offset(10)
    .tag_filter("ai")
    .path_filter("/research/*")
)

# Semantic search
results = db.semantic_search_with_builder(
    QueryBuilder()
    .text("neural network architectures")
    .limit(10)
)

# Hybrid search
results = db.hybrid_search_with_builder(
    QueryBuilder()
    .text("database optimization techniques")
    .semantic_weight(0.8)  # 80% semantic, 20% text
    .limit(15)
)

Update Builder Pattern

Safely update documents with fine-grained control:

from kotadb import UpdateBuilder

# Update with builder pattern
updated_doc = db.update_with_builder(doc_id,
    UpdateBuilder()
    .title("Updated Python Guide")
    .add_tag("updated")
    .add_tag("2024")
    .remove_tag("draft")
    .add_metadata("last_modified_by", "user123")
    .add_metadata("version", "2.0")
)

Connection Options

Environment Variable

export KOTADB_URL="http://localhost:8080"
# Will use KOTADB_URL automatically
db = KotaDB()

Connection String

# PostgreSQL-style connection string
db = KotaDB("kotadb://localhost:8080/myapp")

# Direct HTTP URL
db = KotaDB("http://localhost:8080")

Context Manager

with KotaDB("http://localhost:8080") as db:
    results = db.query("search term")
    # Connection automatically closed

Search Options

Text Search

results = db.query("rust programming patterns", limit=10)

Semantic Search

results = db.semantic_search("machine learning concepts", limit=5)

Hybrid Search

results = db.hybrid_search(
    "database optimization",
    limit=10,
    semantic_weight=0.7  # 70% semantic, 30% text
)

Document Operations

Create Document

# Using dictionary
doc_id = db.insert({
    "path": "/docs/guide.md",
    "title": "User Guide",
    "content": "How to use the system...",
    "tags": ["documentation", "guide"],
    "metadata": {"author": "jane@example.com"}
})

# Using CreateDocumentRequest
from kotadb.types import CreateDocumentRequest

doc_request = CreateDocumentRequest(
    path="/docs/api.md",
    title="API Documentation",
    content="API endpoints and usage...",
    tags=["api", "docs"]
)
doc_id = db.insert(doc_request)

List Documents

# Get all documents
all_docs = db.list_all()

# With pagination
docs = db.list_all(limit=50, offset=100)

Database Health

# Check health
health = db.health()
print(f"Status: {health['status']}")

# Get statistics
stats = db.stats()
print(f"Document count: {stats['document_count']}")

Error Handling

from kotadb.exceptions import KotaDBError, NotFoundError, ConnectionError

try:
    doc = db.get("non-existent-id")
except NotFoundError:
    print("Document not found")
except ConnectionError:
    print("Failed to connect to database")
except KotaDBError as e:
    print(f"Database error: {e}")

Configuration

db = KotaDB(
    url="http://localhost:8080",
    timeout=30,  # Request timeout in seconds
    retries=3    # Number of retry attempts
)

Data Types

Core Types

@dataclass
class Document:
    id: str
    path: str
    title: str
    content: str
    tags: List[str]
    created_at: datetime
    updated_at: datetime
    size: int
    metadata: Optional[Dict[str, Any]]

@dataclass
class QueryResult:
    results: List[Document]
    total_count: int
    query_time_ms: Optional[int]

Validated Types

class ValidatedPath:
    """Path with validation for safety (no traversal, null bytes, etc.)"""
    def __init__(self, path: str) -> None: ...
    def as_str(self) -> str: ...

class ValidatedDocumentId:
    """Document ID with UUID validation"""
    @classmethod
    def new(cls) -> 'ValidatedDocumentId': ...
    @classmethod
    def parse(cls, s: str) -> 'ValidatedDocumentId': ...
    def as_str(self) -> str: ...

class ValidatedTitle:
    """Title with length and content validation"""
    def __init__(self, title: str) -> None: ...
    def as_str(self) -> str: ...

class ValidatedTimestamp:
    """Timestamp with range validation"""
    @classmethod
    def now(cls) -> 'ValidatedTimestamp': ...
    def as_secs(self) -> int: ...

class NonZeroSize:
    """Size value that must be positive"""
    def __init__(self, size: int) -> None: ...
    def get(self) -> int: ...

Builder Types

class DocumentBuilder:
    """Fluent API for safe document construction"""
    def path(self, path: Union[str, ValidatedPath]) -> 'DocumentBuilder': ...
    def title(self, title: Union[str, ValidatedTitle]) -> 'DocumentBuilder': ...
    def content(self, content: Union[str, bytes, List[int]]) -> 'DocumentBuilder': ...
    def add_tag(self, tag: str) -> 'DocumentBuilder': ...
    def add_metadata(self, key: str, value: Any) -> 'DocumentBuilder': ...
    def build(self) -> CreateDocumentRequest: ...

class QueryBuilder:
    """Fluent API for building search queries"""
    def text(self, query: str) -> 'QueryBuilder': ...
    def limit(self, limit: int) -> 'QueryBuilder': ...
    def offset(self, offset: int) -> 'QueryBuilder': ...
    def tag_filter(self, tag: str) -> 'QueryBuilder': ...
    def build(self) -> Dict[str, Any]: ...

class UpdateBuilder:
    """Fluent API for safe document updates"""
    def title(self, title: Union[str, ValidatedTitle]) -> 'UpdateBuilder': ...
    def content(self, content: Union[str, bytes, List[int]]) -> 'UpdateBuilder': ...
    def add_tag(self, tag: str) -> 'UpdateBuilder': ...
    def remove_tag(self, tag: str) -> 'UpdateBuilder': ...
    def add_metadata(self, key: str, value: Any) -> 'UpdateBuilder': ...
    def build(self) -> Dict[str, Any]: ...

Development

Install development dependencies:

pip install -e ".[dev]"

Run tests:

pytest

Format code:

black kotadb/

Type checking:

mypy kotadb/

License

MIT License - see LICENSE file for details.

Contributing

See CONTRIBUTING.md for contribution guidelines.

Support

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

kotadb_client-0.4.0.tar.gz (36.4 kB view details)

Uploaded Source

Built Distribution

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

kotadb_client-0.4.0-py3-none-any.whl (37.4 kB view details)

Uploaded Python 3

File details

Details for the file kotadb_client-0.4.0.tar.gz.

File metadata

  • Download URL: kotadb_client-0.4.0.tar.gz
  • Upload date:
  • Size: 36.4 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.1.0 CPython/3.11.13

File hashes

Hashes for kotadb_client-0.4.0.tar.gz
Algorithm Hash digest
SHA256 5ef668374b86971b7a546f526742cc051453d9eecf85c167a438fa67580a9444
MD5 b769c375a8259422025a3d745238cdc1
BLAKE2b-256 ddb17ce617b5e6a2cd78d1f3820b584b6ef70af3b5a72fb2bc8d1de6520694b4

See more details on using hashes here.

File details

Details for the file kotadb_client-0.4.0-py3-none-any.whl.

File metadata

  • Download URL: kotadb_client-0.4.0-py3-none-any.whl
  • Upload date:
  • Size: 37.4 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.1.0 CPython/3.11.13

File hashes

Hashes for kotadb_client-0.4.0-py3-none-any.whl
Algorithm Hash digest
SHA256 d63b18ac18ae9e899130210f48a1211e72f099dfa1c516889da38ed0b778c093
MD5 759d810d01fa9180ec406fdc9059f4d6
BLAKE2b-256 4e4959de2c9c4a74ebdc137ea299103730d408702b4c77b7e7437818d8d80c6b

See more details on using hashes here.

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