# ekoDB Python Client
High-performance Python client for ekoDB, built with Rust for speed and safety.
This package wraps the `ekodb_client` Rust library using PyO3 to provide a
native Python interface.
## Features
- ✅ **Fast**: Built with Rust, leveraging the same client library as the Rust
SDK
- ✅ **Type-safe**: Strong typing with Python type hints
- ✅ **Async/await**: Full async support using Python's asyncio
- ✅ **Easy to use**: Pythonic API that feels natural
- ✅ **Complete**: All ekoDB features supported
- ✅ **Query Builder** - Fluent API for complex queries with operators, sorting,
and pagination
- ✅ **Search** - Full-text search, fuzzy search, and field-specific search with
scoring
- ✅ **Schema Management** - Define and enforce data schemas with validation
- ✅ **Join Operations** - Single and multi-collection joins with queries
- ✅ **Rate limiting with automatic retry** (429, 503, network errors)
- ✅ **Rate limit tracking** (`X-RateLimit-*` headers)
- ✅ **Configurable retry behavior**
- ✅ **Retry-After header support**
## Installation
```bash
pip install ekodb
```
Or install from source:
```bash
cd ekodb-py
pip install maturin
maturin develop
```
## Quick Start
```python
import asyncio
from ekodb_client import Client, RateLimitError
async def main():
# Create client with configuration
client = Client.new(
"http://localhost:8080",
"your-api-key",
should_retry=True, # Enable automatic retries (default: True)
max_retries=3, # Maximum retry attempts (default: 3)
timeout_secs=30 # Request timeout in seconds (default: 30)
)
try:
# Insert a document
record = await client.insert("users", {
"name": "John Doe",
"age": 30,
"email": "john@example.com",
"active": True
})
print(f"Inserted: {record['id']}")
# Find by ID
user = await client.find_by_id("users", record["id"])
print(f"Found: {user}")
# Find with query
results = await client.find("users", limit=10)
print(f"Found {len(results)} users")
# Update
updated = await client.update("users", record["id"], {
"age": 31
})
print(f"Updated: {updated}")
# Delete
await client.delete("users", record["id"])
print("Deleted")
except RateLimitError as e:
print(f"Rate limited! Retry after {e.retry_after_secs} seconds")
asyncio.run(main())
```
## Usage Examples
### Query Builder
```python
from ekodb_client import Client, QueryBuilder
async def main():
client = Client.new("http://localhost:8080", "your-api-key")
# Simple query with operators
query = QueryBuilder() \
.eq("status", "active") \
.gte("age", 18) \
.lt("age", 65) \
.limit(10) \
.build()
results = await client.find("users", query)
# Complex query with sorting and pagination
query = QueryBuilder() \
.in_array("status", ["active", "pending"]) \
.contains("email", "@example.com") \
.sort_desc("created_at") \
.skip(20) \
.limit(10) \
.build()
results = await client.find("users", query)
```
### Search Operations
```python
# Basic text search
search_query = {
"query": "programming",
"min_score": 0.1,
"limit": 10
}
results = await client.search("articles", search_query)
for result in results["results"]:
print(f"Score: {result['score']:.4f} - {result['record']['title']}")
# Search with field weights
search_query = {
"query": "rust database",
"fields": ["title", "description"],
"weights": {"title": 2.0},
"limit": 5
}
results = await client.search("articles", search_query)
```
### Schema Management
```python
# Create a collection with schema
schema = {
"fields": {
"name": {
"field_type": "String",
"required": True,
"regex": "^[a-zA-Z ]+$"
},
"email": {
"field_type": "String",
"required": True,
"unique": True
},
"age": {
"field_type": "Integer",
"min": 0,
"max": 150
}
}
}
await client.create_collection("users", schema)
# Get collection schema
schema = await client.get_schema("users")
```
### Join Operations
```python
# Single collection join
query = {
"join": {
"collections": ["departments"],
"local_field": "department_id",
"foreign_field": "id",
"as_field": "department"
},
"limit": 10
}
results = await client.find("users", query)
# Multi-collection join
query = {
"join": [
{
"collections": ["departments"],
"local_field": "department_id",
"foreign_field": "id",
"as_field": "department"
},
{
"collections": ["profiles"],
"local_field": "id",
"foreign_field": "id",
"as_field": "profile"
}
],
"limit": 10
}
results = await client.find("users", query)
```
## API Reference
### Client
#### `Client.new(base_url: str, api_key: str, should_retry: bool = True, max_retries: int = 3, timeout_secs: int = 30) -> Client`
Create a new ekoDB client.
**Parameters:**
- `base_url`: The base URL of the ekoDB server
- `api_key`: Your API key
- `should_retry`: Enable automatic retries (default: True)
- `max_retries`: Maximum number of retry attempts (default: 3)
- `timeout_secs`: Request timeout in seconds (default: 30)
**Returns:**
- A new `Client` instance
### RateLimitInfo
Rate limit information is automatically tracked and logged by the client. The
client will automatically retry on rate limit errors using the server's
`Retry-After` header.
#### Properties
- `limit: int` - Maximum requests allowed per window
- `remaining: int` - Requests remaining in current window
- `reset: int` - Unix timestamp when the rate limit resets
#### Methods
- `is_near_limit() -> bool` - Check if approaching rate limit (<10% remaining)
- `is_exceeded() -> bool` - Check if the rate limit has been exceeded
- `remaining_percentage() -> float` - Get the percentage of requests remaining
### RateLimitError
Exception raised when rate limit is exceeded (if retries are disabled or
exhausted).
#### Properties
- `retry_after_secs: int` - Number of seconds to wait before retrying
#### `await client.insert(collection: str, record: dict) -> dict`
Insert a document into a collection.
**Parameters:**
- `collection`: The collection name
- `record`: A dictionary representing the document
**Returns:**
- The inserted document with ID
#### `await client.find_by_id(collection: str, id: str) -> dict`
Find a document by ID.
**Parameters:**
- `collection`: The collection name
- `id`: The document ID
**Returns:**
- The found document
#### `await client.find(collection: str, limit: Optional[int] = None) -> List[dict]`
Find documents in a collection.
**Parameters:**
- `collection`: The collection name
- `limit`: Optional limit on number of results
**Returns:**
- List of matching documents
#### `await client.update(collection: str, id: str, updates: dict) -> dict`
Update a document.
**Parameters:**
- `collection`: The collection name
- `id`: The document ID
- `updates`: Dictionary of fields to update
**Returns:**
- The updated document
#### `await client.delete(collection: str, id: str) -> None`
Delete a document.
**Parameters:**
- `collection`: The collection name
- `id`: The document ID
#### `await client.list_collections() -> List[str]`
List all collections.
**Returns:**
- List of collection names
#### `await client.delete_collection(collection: str) -> None`
Delete a collection.
**Parameters:**
- `collection`: The collection name to delete
#### `await client.search(collection: str, query: dict) -> dict`
Perform full-text search on a collection.
**Parameters:**
- `collection`: The collection name
- `query`: Search query dictionary with fields like `query`, `fields`,
`weights`, `min_score`, `limit`
**Returns:**
- Search results with scores and matched records
#### `await client.create_collection(collection: str, schema: dict) -> None`
Create a collection with a schema.
**Parameters:**
- `collection`: The collection name
- `schema`: Schema definition dictionary
#### `await client.get_schema(collection: str) -> dict`
Get the schema for a collection.
**Parameters:**
- `collection`: The collection name
**Returns:**
- Schema definition dictionary
#### `await client.get_collection(collection: str) -> dict`
Get collection metadata including schema.
**Parameters:**
- `collection`: The collection name
**Returns:**
- Collection metadata dictionary
## Examples
See the
[examples directory](https://github.com/ekoDB/ekodb-client/tree/main/examples/python)
for complete working examples:
- `client_simple_crud.py` - Basic CRUD operations
- `client_query_builder.py` - Complex queries with QueryBuilder
- `client_search.py` - Full-text search operations
- `client_schema.py` - Schema management
- `client_joins.py` - Join operations
- `client_batch_operations.py` - Batch operations
- `client_kv_operations.py` - Key-value operations
- And more...
## Development
### Building
```bash
# Install maturin
pip install maturin
# Build and install in development mode
maturin develop
# Build release wheel
maturin build --release
```
### Testing
```bash
# Run Python tests
pytest
# Run with coverage
pytest --cov=ekodb
```
## License
MIT OR Apache-2.0
## Links
- [GitHub](https://github.com/ekoDB/ekodb-client)
- [Examples](https://github.com/ekoDB/ekodb-client/tree/main/examples/python)
- [PyPI Package](https://pypi.org/project/ekodb-client/)
Raw data
{
"_id": null,
"home_page": null,
"name": "ekodb-client",
"maintainer": null,
"docs_url": null,
"requires_python": ">=3.8",
"maintainer_email": null,
"keywords": "database, ekodb, document-database, nosql",
"author": "ekoDB Team",
"author_email": null,
"download_url": "https://files.pythonhosted.org/packages/85/27/8993556edbb5a65c2563fef5449ce8696955584277d38b1efb32f54aee3f/ekodb_client-0.3.0.tar.gz",
"platform": null,
"description": "# ekoDB Python Client\n\nHigh-performance Python client for ekoDB, built with Rust for speed and safety.\n\nThis package wraps the `ekodb_client` Rust library using PyO3 to provide a\nnative Python interface.\n\n## Features\n\n- \u2705 **Fast**: Built with Rust, leveraging the same client library as the Rust\n SDK\n- \u2705 **Type-safe**: Strong typing with Python type hints\n- \u2705 **Async/await**: Full async support using Python's asyncio\n- \u2705 **Easy to use**: Pythonic API that feels natural\n- \u2705 **Complete**: All ekoDB features supported\n- \u2705 **Query Builder** - Fluent API for complex queries with operators, sorting,\n and pagination\n- \u2705 **Search** - Full-text search, fuzzy search, and field-specific search with\n scoring\n- \u2705 **Schema Management** - Define and enforce data schemas with validation\n- \u2705 **Join Operations** - Single and multi-collection joins with queries\n- \u2705 **Rate limiting with automatic retry** (429, 503, network errors)\n- \u2705 **Rate limit tracking** (`X-RateLimit-*` headers)\n- \u2705 **Configurable retry behavior**\n- \u2705 **Retry-After header support**\n\n## Installation\n\n```bash\npip install ekodb\n```\n\nOr install from source:\n\n```bash\ncd ekodb-py\npip install maturin\nmaturin develop\n```\n\n## Quick Start\n\n```python\nimport asyncio\nfrom ekodb_client import Client, RateLimitError\n\nasync def main():\n # Create client with configuration\n client = Client.new(\n \"http://localhost:8080\",\n \"your-api-key\",\n should_retry=True, # Enable automatic retries (default: True)\n max_retries=3, # Maximum retry attempts (default: 3)\n timeout_secs=30 # Request timeout in seconds (default: 30)\n )\n\n try:\n # Insert a document\n record = await client.insert(\"users\", {\n \"name\": \"John Doe\",\n \"age\": 30,\n \"email\": \"john@example.com\",\n \"active\": True\n })\n print(f\"Inserted: {record['id']}\")\n\n # Find by ID\n user = await client.find_by_id(\"users\", record[\"id\"])\n print(f\"Found: {user}\")\n\n # Find with query\n results = await client.find(\"users\", limit=10)\n print(f\"Found {len(results)} users\")\n\n # Update\n updated = await client.update(\"users\", record[\"id\"], {\n \"age\": 31\n })\n print(f\"Updated: {updated}\")\n\n # Delete\n await client.delete(\"users\", record[\"id\"])\n print(\"Deleted\")\n\n except RateLimitError as e:\n print(f\"Rate limited! Retry after {e.retry_after_secs} seconds\")\n\nasyncio.run(main())\n```\n\n## Usage Examples\n\n### Query Builder\n\n```python\nfrom ekodb_client import Client, QueryBuilder\n\nasync def main():\n client = Client.new(\"http://localhost:8080\", \"your-api-key\")\n\n # Simple query with operators\n query = QueryBuilder() \\\n .eq(\"status\", \"active\") \\\n .gte(\"age\", 18) \\\n .lt(\"age\", 65) \\\n .limit(10) \\\n .build()\n\n results = await client.find(\"users\", query)\n\n # Complex query with sorting and pagination\n query = QueryBuilder() \\\n .in_array(\"status\", [\"active\", \"pending\"]) \\\n .contains(\"email\", \"@example.com\") \\\n .sort_desc(\"created_at\") \\\n .skip(20) \\\n .limit(10) \\\n .build()\n\n results = await client.find(\"users\", query)\n```\n\n### Search Operations\n\n```python\n# Basic text search\nsearch_query = {\n \"query\": \"programming\",\n \"min_score\": 0.1,\n \"limit\": 10\n}\n\nresults = await client.search(\"articles\", search_query)\nfor result in results[\"results\"]:\n print(f\"Score: {result['score']:.4f} - {result['record']['title']}\")\n\n# Search with field weights\nsearch_query = {\n \"query\": \"rust database\",\n \"fields\": [\"title\", \"description\"],\n \"weights\": {\"title\": 2.0},\n \"limit\": 5\n}\n\nresults = await client.search(\"articles\", search_query)\n```\n\n### Schema Management\n\n```python\n# Create a collection with schema\nschema = {\n \"fields\": {\n \"name\": {\n \"field_type\": \"String\",\n \"required\": True,\n \"regex\": \"^[a-zA-Z ]+$\"\n },\n \"email\": {\n \"field_type\": \"String\",\n \"required\": True,\n \"unique\": True\n },\n \"age\": {\n \"field_type\": \"Integer\",\n \"min\": 0,\n \"max\": 150\n }\n }\n}\n\nawait client.create_collection(\"users\", schema)\n\n# Get collection schema\nschema = await client.get_schema(\"users\")\n```\n\n### Join Operations\n\n```python\n# Single collection join\nquery = {\n \"join\": {\n \"collections\": [\"departments\"],\n \"local_field\": \"department_id\",\n \"foreign_field\": \"id\",\n \"as_field\": \"department\"\n },\n \"limit\": 10\n}\n\nresults = await client.find(\"users\", query)\n\n# Multi-collection join\nquery = {\n \"join\": [\n {\n \"collections\": [\"departments\"],\n \"local_field\": \"department_id\",\n \"foreign_field\": \"id\",\n \"as_field\": \"department\"\n },\n {\n \"collections\": [\"profiles\"],\n \"local_field\": \"id\",\n \"foreign_field\": \"id\",\n \"as_field\": \"profile\"\n }\n ],\n \"limit\": 10\n}\n\nresults = await client.find(\"users\", query)\n```\n\n## API Reference\n\n### Client\n\n#### `Client.new(base_url: str, api_key: str, should_retry: bool = True, max_retries: int = 3, timeout_secs: int = 30) -> Client`\n\nCreate a new ekoDB client.\n\n**Parameters:**\n\n- `base_url`: The base URL of the ekoDB server\n- `api_key`: Your API key\n- `should_retry`: Enable automatic retries (default: True)\n- `max_retries`: Maximum number of retry attempts (default: 3)\n- `timeout_secs`: Request timeout in seconds (default: 30)\n\n**Returns:**\n\n- A new `Client` instance\n\n### RateLimitInfo\n\nRate limit information is automatically tracked and logged by the client. The\nclient will automatically retry on rate limit errors using the server's\n`Retry-After` header.\n\n#### Properties\n\n- `limit: int` - Maximum requests allowed per window\n- `remaining: int` - Requests remaining in current window\n- `reset: int` - Unix timestamp when the rate limit resets\n\n#### Methods\n\n- `is_near_limit() -> bool` - Check if approaching rate limit (<10% remaining)\n- `is_exceeded() -> bool` - Check if the rate limit has been exceeded\n- `remaining_percentage() -> float` - Get the percentage of requests remaining\n\n### RateLimitError\n\nException raised when rate limit is exceeded (if retries are disabled or\nexhausted).\n\n#### Properties\n\n- `retry_after_secs: int` - Number of seconds to wait before retrying\n\n#### `await client.insert(collection: str, record: dict) -> dict`\n\nInsert a document into a collection.\n\n**Parameters:**\n\n- `collection`: The collection name\n- `record`: A dictionary representing the document\n\n**Returns:**\n\n- The inserted document with ID\n\n#### `await client.find_by_id(collection: str, id: str) -> dict`\n\nFind a document by ID.\n\n**Parameters:**\n\n- `collection`: The collection name\n- `id`: The document ID\n\n**Returns:**\n\n- The found document\n\n#### `await client.find(collection: str, limit: Optional[int] = None) -> List[dict]`\n\nFind documents in a collection.\n\n**Parameters:**\n\n- `collection`: The collection name\n- `limit`: Optional limit on number of results\n\n**Returns:**\n\n- List of matching documents\n\n#### `await client.update(collection: str, id: str, updates: dict) -> dict`\n\nUpdate a document.\n\n**Parameters:**\n\n- `collection`: The collection name\n- `id`: The document ID\n- `updates`: Dictionary of fields to update\n\n**Returns:**\n\n- The updated document\n\n#### `await client.delete(collection: str, id: str) -> None`\n\nDelete a document.\n\n**Parameters:**\n\n- `collection`: The collection name\n- `id`: The document ID\n\n#### `await client.list_collections() -> List[str]`\n\nList all collections.\n\n**Returns:**\n\n- List of collection names\n\n#### `await client.delete_collection(collection: str) -> None`\n\nDelete a collection.\n\n**Parameters:**\n\n- `collection`: The collection name to delete\n\n#### `await client.search(collection: str, query: dict) -> dict`\n\nPerform full-text search on a collection.\n\n**Parameters:**\n\n- `collection`: The collection name\n- `query`: Search query dictionary with fields like `query`, `fields`,\n `weights`, `min_score`, `limit`\n\n**Returns:**\n\n- Search results with scores and matched records\n\n#### `await client.create_collection(collection: str, schema: dict) -> None`\n\nCreate a collection with a schema.\n\n**Parameters:**\n\n- `collection`: The collection name\n- `schema`: Schema definition dictionary\n\n#### `await client.get_schema(collection: str) -> dict`\n\nGet the schema for a collection.\n\n**Parameters:**\n\n- `collection`: The collection name\n\n**Returns:**\n\n- Schema definition dictionary\n\n#### `await client.get_collection(collection: str) -> dict`\n\nGet collection metadata including schema.\n\n**Parameters:**\n\n- `collection`: The collection name\n\n**Returns:**\n\n- Collection metadata dictionary\n\n## Examples\n\nSee the\n[examples directory](https://github.com/ekoDB/ekodb-client/tree/main/examples/python)\nfor complete working examples:\n\n- `client_simple_crud.py` - Basic CRUD operations\n- `client_query_builder.py` - Complex queries with QueryBuilder\n- `client_search.py` - Full-text search operations\n- `client_schema.py` - Schema management\n- `client_joins.py` - Join operations\n- `client_batch_operations.py` - Batch operations\n- `client_kv_operations.py` - Key-value operations\n- And more...\n\n## Development\n\n### Building\n\n```bash\n# Install maturin\npip install maturin\n\n# Build and install in development mode\nmaturin develop\n\n# Build release wheel\nmaturin build --release\n```\n\n### Testing\n\n```bash\n# Run Python tests\npytest\n\n# Run with coverage\npytest --cov=ekodb\n```\n\n## License\n\nMIT OR Apache-2.0\n\n## Links\n\n- [GitHub](https://github.com/ekoDB/ekodb-client)\n- [Examples](https://github.com/ekoDB/ekodb-client/tree/main/examples/python)\n- [PyPI Package](https://pypi.org/project/ekodb-client/)\n\n",
"bugtrack_url": null,
"license": "MIT OR Apache-2.0",
"summary": "Python client for ekoDB - a high-performance document database",
"version": "0.3.0",
"project_urls": {
"Documentation": "https://github.com/ekoDB/ekodb/tree/main/documentation",
"Homepage": "https://github.com/ekoDB/ekodb",
"Repository": "https://github.com/ekoDB/ekodb"
},
"split_keywords": [
"database",
" ekodb",
" document-database",
" nosql"
],
"urls": [
{
"comment_text": null,
"digests": {
"blake2b_256": "fda0b9d0a401b4cd787cc18d34be198b929d908fca1ee201d37b9fc13dee4612",
"md5": "8eacd5016ff28da92cbdda182e5d0a29",
"sha256": "9bf7c96640b340c69162fe9d476ba6d877ebc2ca00402354787056e7b424b3dd"
},
"downloads": -1,
"filename": "ekodb_client-0.3.0-cp38-abi3-macosx_11_0_arm64.whl",
"has_sig": false,
"md5_digest": "8eacd5016ff28da92cbdda182e5d0a29",
"packagetype": "bdist_wheel",
"python_version": "cp38",
"requires_python": ">=3.8",
"size": 3334515,
"upload_time": "2025-11-05T06:11:32",
"upload_time_iso_8601": "2025-11-05T06:11:32.578720Z",
"url": "https://files.pythonhosted.org/packages/fd/a0/b9d0a401b4cd787cc18d34be198b929d908fca1ee201d37b9fc13dee4612/ekodb_client-0.3.0-cp38-abi3-macosx_11_0_arm64.whl",
"yanked": false,
"yanked_reason": null
},
{
"comment_text": null,
"digests": {
"blake2b_256": "8d586c212cb17596ba826c1f49489ec6e399afac2636b7f846f67701ace1627c",
"md5": "e5b51edc42bb2fc8896d49d31a64d8a1",
"sha256": "dcba928bcaf95abbe3941e236115ff4a703c9ff531e06b3feef641504288fe2a"
},
"downloads": -1,
"filename": "ekodb_client-0.3.0-cp38-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl",
"has_sig": false,
"md5_digest": "e5b51edc42bb2fc8896d49d31a64d8a1",
"packagetype": "bdist_wheel",
"python_version": "cp38",
"requires_python": ">=3.8",
"size": 3689985,
"upload_time": "2025-11-05T06:11:34",
"upload_time_iso_8601": "2025-11-05T06:11:34.330054Z",
"url": "https://files.pythonhosted.org/packages/8d/58/6c212cb17596ba826c1f49489ec6e399afac2636b7f846f67701ace1627c/ekodb_client-0.3.0-cp38-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl",
"yanked": false,
"yanked_reason": null
},
{
"comment_text": null,
"digests": {
"blake2b_256": "85278993556edbb5a65c2563fef5449ce8696955584277d38b1efb32f54aee3f",
"md5": "f0ee4113a9aee467b25a304a799f87a1",
"sha256": "6d59176b8aa833f9969f213337efe5a9a1585a4db4f8ce923db7b48a2078a56a"
},
"downloads": -1,
"filename": "ekodb_client-0.3.0.tar.gz",
"has_sig": false,
"md5_digest": "f0ee4113a9aee467b25a304a799f87a1",
"packagetype": "sdist",
"python_version": "source",
"requires_python": ">=3.8",
"size": 72073,
"upload_time": "2025-11-05T06:11:35",
"upload_time_iso_8601": "2025-11-05T06:11:35.915899Z",
"url": "https://files.pythonhosted.org/packages/85/27/8993556edbb5a65c2563fef5449ce8696955584277d38b1efb32f54aee3f/ekodb_client-0.3.0.tar.gz",
"yanked": false,
"yanked_reason": null
}
],
"upload_time": "2025-11-05 06:11:35",
"github": true,
"gitlab": false,
"bitbucket": false,
"codeberg": false,
"github_user": "ekoDB",
"github_project": "ekodb",
"github_not_found": true,
"lcname": "ekodb-client"
}