Coverage for src / lexigram / contracts / data / nosql / nosql.py: 0%
48 statements
« prev ^ index » next coverage.py v7.13.5, created at 2026-08-15 18:57 +0800
« prev ^ index » next coverage.py v7.13.5, created at 2026-08-15 18:57 +0800
1"""NoSQL database protocols for document-oriented storage.
3Provides driver-agnostic abstractions for document stores (MongoDB,
4DynamoDB, CouchDB, etc.) parallel to the SQL-centric
5``DatabaseProviderProtocol``.
7Key protocols:
9- :class:`CollectionProtocol` — CRUD, indexing, and aggregation on a
10 single collection / table.
11- :class:`DocumentStoreProtocol` — connection lifecycle, collection
12 access, and health checks.
13"""
15from __future__ import annotations
17from dataclasses import dataclass, field
18from typing import TYPE_CHECKING, Any, Protocol, runtime_checkable
20if TYPE_CHECKING:
21 from collections.abc import AsyncIterator
22 from contextlib import AbstractAsyncContextManager
24 from lexigram.contracts.core.health import HealthCheckResult
27# ============================================================
28# Result types
29# ============================================================
32@dataclass(frozen=True, slots=True)
33class DocumentResult:
34 """Result of a single document operation."""
36 document_id: str | None = None
37 matched_count: int = 0
38 modified_count: int = 0
39 upserted_id: str | None = None
40 acknowledged: bool = True
43@dataclass(frozen=True, slots=True)
44class BulkWriteResult:
45 """Result of a bulk write operation."""
47 inserted_count: int = 0
48 matched_count: int = 0
49 modified_count: int = 0
50 deleted_count: int = 0
51 upserted_ids: list[str] = field(default_factory=list)
54# ============================================================
55# Collection protocol
56# ============================================================
59@runtime_checkable
60class CollectionProtocol(Protocol):
61 """Protocol for a NoSQL collection / table abstraction.
63 Provides document-oriented CRUD operations without SQL
64 assumptions. Maps to MongoDB collections, DynamoDB tables, etc.
65 """
67 @property
68 def name(self) -> str:
69 """Collection / table name."""
70 ...
72 async def insert_one(self, document: dict[str, Any]) -> DocumentResult:
73 """Insert a single document."""
74 ...
76 async def insert_many(self, documents: list[dict[str, Any]]) -> BulkWriteResult:
77 """Insert multiple documents."""
78 ...
80 async def find_one(
81 self,
82 filter: dict[str, Any],
83 *,
84 projection: dict[str, Any] | None = None,
85 ) -> dict[str, Any] | None:
86 """Find a single document matching the filter."""
87 ...
89 async def find(
90 self,
91 filter: dict[str, Any],
92 *,
93 projection: dict[str, Any] | None = None,
94 sort: list[tuple[str, int]] | None = None,
95 skip: int = 0,
96 limit: int = 0,
97 ) -> AsyncIterator[dict[str, Any]]:
98 """Find documents matching the filter. Returns async iterator."""
99 ...
101 async def update_one(
102 self,
103 filter: dict[str, Any],
104 update: dict[str, Any],
105 *,
106 upsert: bool = False,
107 ) -> DocumentResult:
108 """Update a single document matching the filter."""
109 ...
111 async def update_many(
112 self,
113 filter: dict[str, Any],
114 update: dict[str, Any],
115 ) -> DocumentResult:
116 """Update all documents matching the filter."""
117 ...
119 async def delete_one(self, filter: dict[str, Any]) -> DocumentResult:
120 """Delete a single document matching the filter."""
121 ...
123 async def delete_many(self, filter: dict[str, Any]) -> DocumentResult:
124 """Delete all documents matching the filter."""
125 ...
127 async def replace_one(
128 self,
129 filter: dict[str, Any],
130 replacement: dict[str, Any],
131 *,
132 upsert: bool = False,
133 ) -> DocumentResult:
134 """Replace a single document matching the filter."""
135 ...
137 async def find_one_and_update(
138 self,
139 filter: dict[str, Any],
140 update: dict[str, Any],
141 *,
142 upsert: bool = False,
143 return_document: bool = True,
144 ) -> dict[str, Any] | None:
145 """Atomically find and update a document.
147 Args:
148 filter: Match criteria.
149 update: Update operations.
150 upsert: Insert if no match.
151 return_document: If True, return the updated document.
153 Returns:
154 The document (before or after update) or None.
155 """
156 ...
158 async def count_documents(self, filter: dict[str, Any] | None = None) -> int:
159 """Count documents matching the filter."""
160 ...
162 async def create_index(
163 self,
164 keys: list[tuple[str, int]],
165 *,
166 unique: bool = False,
167 name: str | None = None,
168 ) -> str:
169 """Create an index on the collection. Returns the index name."""
170 ...
172 async def aggregate(
173 self,
174 pipeline: list[dict[str, Any]],
175 ) -> AsyncIterator[dict[str, Any]]:
176 """Execute an aggregation pipeline."""
177 ...
179 async def list_indexes(self) -> list[dict[str, Any]]:
180 """List all indexes on the collection."""
181 ...
183 async def distinct(
184 self,
185 key: str,
186 filter: dict[str, Any] | None = None,
187 ) -> list[Any]:
188 """Get distinct values for a specified key."""
189 ...
191 async def bulk_write(self, operations: list[Any]) -> BulkWriteResult:
192 """Execute multiple write operations in a single batch.
194 Operations are driver-specific write models (e.g., UpdateOne, InsertOne).
195 """
196 ...
199# ============================================================
200# Document store protocol
201# ============================================================
204@runtime_checkable
205class DocumentStoreProtocol(Protocol):
206 """Protocol for a document-oriented database provider.
208 Parallel to ``DatabaseProviderProtocol`` but without SQL
209 assumptions. Provides collection access, session management,
210 and health checks.
211 """
213 async def connect(self) -> None:
214 """Establish connection to the document store."""
215 ...
217 async def disconnect(self) -> None:
218 """Close all connections."""
219 ...
221 def is_connected(self) -> bool:
222 """Check if the store is connected."""
223 ...
225 def collection(self, name: str) -> CollectionProtocol:
226 """Get a collection / table handle by name."""
227 ...
229 def session(self) -> AbstractAsyncContextManager[Any]:
230 """Create a session for multi-document transactions."""
231 ...
233 async def list_collections(self) -> list[str]:
234 """List all collection names in the database."""
235 ...
237 async def drop_collection(self, name: str) -> None:
238 """Drop a collection."""
239 ...
241 async def health_check(self, timeout: float = 5.0) -> HealthCheckResult:
242 """Check document store connectivity and health."""
243 ...
246__all__ = [
247 "BulkWriteResult",
248 "CollectionProtocol",
249 "DocumentResult",
250 "DocumentStoreProtocol",
251]