@antelopejs/interface-database 0.1.3 → 0.1.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,53 @@
1
+ # Query Types
2
+
3
+ AQL provides several query classes that form a hierarchy of capabilities. Each class encapsulates a different aspect of database interactions, from full table access down to individual value manipulation.
4
+
5
+ ## Query Type Hierarchy
6
+
7
+ All query types inherit from `StagedObject`, which records operations as a pipeline of stages. The hierarchy is:
8
+
9
+ ```
10
+ Query<T>
11
+ ├── Datum<T>
12
+ │ └── SingleSelection<T>
13
+ └── Stream<T>
14
+ └── Selection<T>
15
+ └── Table<T>
16
+
17
+ ValueProxy<T> (used within query callbacks)
18
+ ```
19
+
20
+ ## Common Features
21
+
22
+ All query types that extend `Query<T>` share these capabilities:
23
+
24
+ - **`run()`** - Execute the query and return a `Promise<T>` with the result.
25
+ - **`then()`** - `PromiseLike` support, so queries can be awaited directly.
26
+ - **`cursor()`** - Return an `AsyncGenerator` for streaming results one at a time.
27
+ - **`[Symbol.asyncIterator]()`** - Enable `for await...of` loops over query results.
28
+
29
+ ```typescript
30
+ import { Schema } from "@antelopejs/interface-database";
31
+
32
+ // Execute a query explicitly
33
+ const result = await schema.instance().table("users").run();
34
+
35
+ // Await the query directly (uses PromiseLike)
36
+ const users = await schema.instance().table("users");
37
+
38
+ // Iterate with async generator
39
+ for await (const user of schema.instance().table("users")) {
40
+ console.log(user.name);
41
+ }
42
+ ```
43
+
44
+ ## Available Query Types
45
+
46
+ - [**Table**](./2.table.md) - Represents a database table with insert, get, getAll, and between operations
47
+ - [**Selection**](./3.selection.md) - A set of documents supporting update, replace, and delete
48
+ - [**SingleSelection**](./4.single_selection.md) - A single document with update, replace, delete, and change feed support
49
+ - [**Stream**](./5.stream.md) - A sequence of results with map, filter, join, group, aggregate, and sort operations
50
+ - [**Change Feeds**](./6.feed.md) - Real-time change notifications via the `changes()` method
51
+ - [**Query**](./7.query.md) - The base class providing execution and async iteration
52
+ - [**Datum**](./8.datum.md) - A single value with field access, transformation, and lookup capabilities
53
+ - [**ValueProxy**](./9.valueproxy.md) - Proxy for in-query value operations (arithmetic, string, date, array, object)
@@ -0,0 +1,108 @@
1
+ # Table
2
+
3
+ The `Table<T>` class represents a database table and provides methods for inserting documents and retrieving them by primary key, secondary index, or range. It extends `Selection<T>`, inheriting all multi-document operations and stream capabilities.
4
+
5
+ ## Class Definition
6
+
7
+ ```typescript
8
+ class Table<T> extends Selection<T> {
9
+ insert(obj: DeepPartial<T> | DeepPartial<T>[], options?: InsertOptions): Query<string[]>;
10
+ get(key: string): SingleSelection<T>;
11
+ getAll(keys: string | number | boolean | (string | number | boolean)[], index?: string): Selection<T>;
12
+ between<TK extends keyof T>(index: TK, low: T[TK], high: T[TK]): Selection<T>;
13
+ }
14
+ ```
15
+
16
+ ## Access a Table
17
+
18
+ Tables are accessed through a `SchemaInstance`. Define a schema, create an instance, then call `table()`:
19
+
20
+ ```typescript
21
+ import { Schema } from "@antelopejs/interface-database";
22
+
23
+ const schema = new Schema("myapp", {
24
+ users: {
25
+ fields: { name: "string", email: "string" },
26
+ indexes: { email: {} },
27
+ },
28
+ });
29
+
30
+ await schema.createInstance();
31
+ const users = schema.instance().table("users");
32
+ ```
33
+
34
+ ## Insert Documents
35
+
36
+ The `insert` method adds one or more documents to the table and returns the generated IDs.
37
+
38
+ ```typescript
39
+ // Insert a single document
40
+ const ids = await users.insert({
41
+ name: "Alice Johnson",
42
+ email: "alice@example.com",
43
+ });
44
+ console.log("Inserted ID:", ids[0]);
45
+
46
+ // Insert multiple documents
47
+ const batchIds = await users.insert([
48
+ { name: "Bob", email: "bob@example.com" },
49
+ { name: "Carol", email: "carol@example.com" },
50
+ ]);
51
+
52
+ // Insert with conflict handling
53
+ const upsertIds = await users.insert(
54
+ { _id: "user-123", name: "Updated Name" },
55
+ { conflict: "update" },
56
+ );
57
+ ```
58
+
59
+ ### `InsertOptions`
60
+
61
+ | Option | Type | Description |
62
+ | ---------- | ------------------------ | --------------------------------------------------- |
63
+ | `conflict` | `"update" \| "replace"` | Behavior when a document with the same ID exists. |
64
+
65
+ ## Retrieve Documents
66
+
67
+ ### `get(key)`
68
+
69
+ Retrieve a single document by its primary key. Returns a `SingleSelection<T>`.
70
+
71
+ ```typescript
72
+ const user = await users.get("user-123");
73
+
74
+ if (user) {
75
+ console.log("Found:", user.name);
76
+ }
77
+ ```
78
+
79
+ ### `getAll(keys, index?)`
80
+
81
+ Retrieve multiple documents by key values. When `index` is provided, the lookup uses the specified secondary index instead of the primary key.
82
+
83
+ ```typescript
84
+ // By primary key
85
+ const selected = await users.getAll(["user-1", "user-2", "user-3"]);
86
+
87
+ // By secondary index
88
+ const admins = await users.getAll("admin", "role");
89
+ ```
90
+
91
+ ### `between(index, low, high)`
92
+
93
+ Retrieve documents where the specified index value falls within a range. The lower bound is inclusive, and the upper bound is exclusive.
94
+
95
+ ```typescript
96
+ const recent = await users.between(
97
+ "createdAt",
98
+ new Date("2024-01-01"),
99
+ new Date(),
100
+ );
101
+ ```
102
+
103
+ ## Inherited Methods
104
+
105
+ Since `Table` extends `Selection`, which extends `Stream`, it inherits:
106
+
107
+ - **Selection methods**: `update()`, `replace()`, `delete()` -- see [Selection](./3.selection.md)
108
+ - **Stream methods**: `map()`, `filter()`, `orderBy()`, `count()`, `join()`, and more -- see [Stream](./5.stream.md)
@@ -0,0 +1,94 @@
1
+ # Selection
2
+
3
+ The `Selection<T>` class represents a set of documents and provides methods for bulk mutation operations. It extends `Stream<T>`, inheriting all transformation, filtering, and aggregation capabilities.
4
+
5
+ ## Class Definition
6
+
7
+ ```typescript
8
+ class Selection<T> extends Stream<T> {
9
+ update(document: DeepPartial<T>): Query<number>;
10
+ update<U>(document: (val: ValueProxy<T>) => U): Query<number>;
11
+ replace(document: DeepPartial<T>): Query<number>;
12
+ delete(): Query<number>;
13
+ }
14
+ ```
15
+
16
+ ## Create a Selection
17
+
18
+ Selections are produced by table methods such as `getAll()` and `between()`, or by chaining stream operations like `filter()`:
19
+
20
+ ```typescript
21
+ import { Schema } from "@antelopejs/interface-database";
22
+
23
+ const schema = Schema.get("myapp")!;
24
+ const users = schema.instance().table("users");
25
+
26
+ // Selection from getAll
27
+ const admins = users.getAll("admin", "role");
28
+
29
+ // Selection from between
30
+ const recentUsers = users.between("createdAt", new Date("2024-01-01"), new Date());
31
+ ```
32
+
33
+ ## Update Documents
34
+
35
+ The `update` method modifies matching documents with the provided partial data. Fields not included in the update object remain unchanged. It returns the number of modified documents.
36
+
37
+ ```typescript
38
+ // Update with a partial object
39
+ const count = await users
40
+ .getAll("inactive", "status")
41
+ .update({ status: "active" });
42
+
43
+ console.log(`Activated ${count} users`);
44
+
45
+ // Update with a function for computed values
46
+ await users
47
+ .getAll("active", "status")
48
+ .update((user) => ({
49
+ loginCount: user.key("loginCount").add(1),
50
+ }));
51
+ ```
52
+
53
+ ## Replace Documents
54
+
55
+ The `replace` method substitutes entire documents with the provided data. Unlike `update`, all fields not present in the replacement are removed.
56
+
57
+ ```typescript
58
+ const count = await users
59
+ .getAll("user-123")
60
+ .replace({
61
+ _id: "user-123",
62
+ name: "New Name",
63
+ email: "new@example.com",
64
+ role: "user",
65
+ });
66
+
67
+ console.log(`Replaced ${count} documents`);
68
+ ```
69
+
70
+ ## Delete Documents
71
+
72
+ The `delete` method removes all documents in the selection and returns the number of deleted documents.
73
+
74
+ ```typescript
75
+ const count = await users
76
+ .getAll("inactive", "status")
77
+ .delete();
78
+
79
+ console.log(`Deleted ${count} inactive users`);
80
+ ```
81
+
82
+ ## Inherited Methods
83
+
84
+ Since `Selection` extends `Stream`, it inherits all stream operations:
85
+
86
+ - Transformation: `map()`, `pluck()`, `without()`
87
+ - Filtering: `filter()`
88
+ - Aggregation: `count()`, `sum()`, `avg()`, `min()`, `max()`
89
+ - Joining: `join()`, `joinInner()`, `lookup()`
90
+ - Ordering: `orderBy()`, `slice()`, `nth()`
91
+ - Grouping: `group()`
92
+ - Deduplication: `distinct()`
93
+
94
+ See [Stream](./5.stream.md) for complete details on these methods.
@@ -0,0 +1,121 @@
1
+ # SingleSelection
2
+
3
+ The `SingleSelection<T>` class represents a reference to a single document in a table. It extends `Datum<T>` and provides methods for updating, replacing, deleting, and monitoring changes to an individual document.
4
+
5
+ ## Class Definition
6
+
7
+ ```typescript
8
+ class SingleSelection<T> extends Datum<T> {
9
+ update(document: DeepPartial<T>): Query<number>;
10
+ update<U>(document: (val: ValueProxy<T>) => U): Query<number>;
11
+ replace(document: DeepPartial<T>): Query<number>;
12
+ delete(): Query<number>;
13
+ changes(): Query<Changes<T>[]>;
14
+ }
15
+ ```
16
+
17
+ ## Create a SingleSelection
18
+
19
+ SingleSelection objects are produced by calling `get()` on a table:
20
+
21
+ ```typescript
22
+ import { Schema } from "@antelopejs/interface-database";
23
+
24
+ const schema = Schema.get("myapp")!;
25
+ const users = schema.instance().table("users");
26
+
27
+ // Get a document by primary key
28
+ const user = users.get("user-123");
29
+ ```
30
+
31
+ ## Read a Document
32
+
33
+ Since `SingleSelection` extends `Datum`, you can execute it directly to retrieve the document value:
34
+
35
+ ```typescript
36
+ const user = await users.get("user-123");
37
+
38
+ if (user) {
39
+ console.log("Found:", user.name);
40
+ }
41
+
42
+ // Access specific fields
43
+ const email = await users.get("user-123").key("email");
44
+ console.log("Email:", email);
45
+
46
+ // Transform with do()
47
+ const display = await users.get("user-123").do((u) => ({
48
+ label: u.key("name"),
49
+ contact: u.key("email"),
50
+ }));
51
+ ```
52
+
53
+ ## Update a Document
54
+
55
+ The `update` method modifies fields in the document. Unmentioned fields remain unchanged. Returns the number of modified documents.
56
+
57
+ ```typescript
58
+ // Update with a partial object
59
+ const count = await users.get("user-123").update({
60
+ status: "active",
61
+ lastLogin: new Date(),
62
+ });
63
+
64
+ // Update with a function for computed values
65
+ await users.get("user-123").update((user) => ({
66
+ loginCount: user.key("loginCount").add(1),
67
+ lastLogin: new Date(),
68
+ }));
69
+ ```
70
+
71
+ ## Replace a Document
72
+
73
+ The `replace` method substitutes the entire document with new data.
74
+
75
+ ```typescript
76
+ const count = await users.get("user-123").replace({
77
+ _id: "user-123",
78
+ name: "New Name",
79
+ email: "new@example.com",
80
+ role: "user",
81
+ });
82
+ ```
83
+
84
+ ## Delete a Document
85
+
86
+ The `delete` method removes the document from the table.
87
+
88
+ ```typescript
89
+ const count = await users.get("user-123").delete();
90
+
91
+ if (count === 1) {
92
+ console.log("User deleted successfully");
93
+ }
94
+ ```
95
+
96
+ ## Monitor Changes
97
+
98
+ The `changes` method returns change events for the document. Each event contains a `changeType` and the `oldValue`/`newValue` of the document.
99
+
100
+ ```typescript
101
+ const changes = await users.get("user-123").changes();
102
+
103
+ for (const change of changes) {
104
+ console.log("Type:", change.changeType); // "added" | "removed" | "modified"
105
+ console.log("Old:", change.oldValue);
106
+ console.log("New:", change.newValue);
107
+ }
108
+ ```
109
+
110
+ ## Inherited Methods
111
+
112
+ Since `SingleSelection` extends `Datum`, it inherits:
113
+
114
+ - **`key()`** - Access a field by name
115
+ - **`default()`** - Provide a fallback value for null
116
+ - **`do()`** - Apply a transformation function
117
+ - **`lookup()`** - Perform a foreign key lookup
118
+ - **`pluck()`** - Select specific fields
119
+ - **`cast()`** - Change the TypeScript type
120
+
121
+ See [Datum](./8.datum.md) for complete details.
@@ -0,0 +1,237 @@
1
+ # Stream
2
+
3
+ The `Stream<T>` class represents a sequence of documents that can be transformed, filtered, joined, grouped, and aggregated. It extends `Query<T[]>` and serves as the foundation for collection-based operations in AQL.
4
+
5
+ ## Class Definition
6
+
7
+ ```typescript
8
+ class Stream<T> extends Query<T[]> {
9
+ // Type casting
10
+ cast<U>(): Stream<U>;
11
+
12
+ // Field access
13
+ key<K extends keyof T>(key: K, def?: unknown): Stream<T[K]>;
14
+ default<U>(val: Value<U>): Stream<Exclude<T, undefined | null> | U>;
15
+
16
+ // Transformation
17
+ map<U>(mapper: (val: ValueProxy<T>) => U): Stream<ExtractType<U>>;
18
+ filter(predicate: (val: ValueProxy<T>) => ValueProxyOrValue<boolean>): this;
19
+ pluck(...fields: string[]): Stream<Partial<T>>;
20
+ without(...fields: string[]): Stream<Partial<T>>;
21
+
22
+ // Combining
23
+ union<U>(other: Stream<U>): Stream<T | U>;
24
+
25
+ // Joining
26
+ join<U, V>(right: Stream<U>, predicate, mapper): Stream<ExtractType<V>>;
27
+ joinInner<U, V>(right: Stream<U>, predicate, mapper): Stream<ExtractType<V>>;
28
+ lookup<U, TK extends keyof T>(right: Selection<U>, localKey: TK, otherKey: keyof U): Stream<...>;
29
+
30
+ // Grouping
31
+ group<U, K>(index: K, mapper: (stream: Stream<T>, group: ValueProxy) => U): Stream<ExtractType<U>>;
32
+
33
+ // Ordering and pagination
34
+ orderBy(index: string, direction?: "asc" | "desc"): this;
35
+ slice(offset: Value<number>, count?: Value<number>): this;
36
+ nth(n: Value<number>): Datum<T | null>;
37
+
38
+ // Aggregation
39
+ count(field?: keyof T): Datum<number>;
40
+ sum(field?: keyof T): Datum<number>;
41
+ avg(field?: keyof T): Datum<number>;
42
+ min(field?: keyof T): Datum<number>;
43
+ max(field?: keyof T): Datum<number>;
44
+ distinct(): Datum<T[]>;
45
+ distinct<TK extends keyof T>(field: TK): Stream<T[TK]>;
46
+
47
+ // Change feeds
48
+ changes(): Query<Changes<T>[]>;
49
+ }
50
+ ```
51
+
52
+ ## Field Access
53
+
54
+ Access a specific field from all documents in the stream with `key()`, or provide a default value for null fields with `default()`.
55
+
56
+ ```typescript
57
+ const users = schema.instance().table("users");
58
+
59
+ // Extract a single field
60
+ const emails = await users.key("email");
61
+
62
+ // Provide a default value
63
+ const phones = await users.key("phone").default("N/A");
64
+ ```
65
+
66
+ ## Transform Data
67
+
68
+ ### `map()`
69
+
70
+ Transform each document using a mapping function. The callback receives a `ValueProxy<T>` for constructing expressions.
71
+
72
+ ```typescript
73
+ const profiles = await users.map((user) => ({
74
+ id: user.key("_id"),
75
+ displayName: user.key("name"),
76
+ contact: user.key("email"),
77
+ }));
78
+ ```
79
+
80
+ ### `filter()`
81
+
82
+ Select documents matching a predicate. The callback returns a boolean expression.
83
+
84
+ ```typescript
85
+ const active = await users.filter((user) =>
86
+ user.key("status").eq("active")
87
+ .and(user.key("age").ge(18)),
88
+ );
89
+ ```
90
+
91
+ ### `pluck()` and `without()`
92
+
93
+ Select or exclude specific fields from all documents.
94
+
95
+ ```typescript
96
+ // Keep only specific fields
97
+ const slim = await users.pluck("_id", "name", "email");
98
+
99
+ // Remove sensitive fields
100
+ const safe = await users.without("password", "ssn");
101
+ ```
102
+
103
+ ## Combine Streams
104
+
105
+ ### `union()`
106
+
107
+ Concatenate two streams without deduplication.
108
+
109
+ ```typescript
110
+ const allPremium = await users
111
+ .filter((u) => u.key("plan").eq("premium"))
112
+ .union(
113
+ users.filter((u) => u.key("plan").eq("enterprise")),
114
+ );
115
+ ```
116
+
117
+ ## Join Data
118
+
119
+ ### `join()` - Left Join
120
+
121
+ Perform a left join between two streams. Every document from the left stream appears in the result, with `null` for the right side when no match exists.
122
+
123
+ ```typescript
124
+ const usersWithOrders = await users.join(
125
+ schema.instance().table("orders"),
126
+ (user, order) => order.key("userId").eq(user.key("_id")),
127
+ (user, order) => ({
128
+ userName: user.key("name"),
129
+ orderTotal: order.key("amount").default(0),
130
+ }),
131
+ );
132
+ ```
133
+
134
+ ### `joinInner()` - Inner Join
135
+
136
+ Perform an inner join. Only documents with matches on both sides appear in the result.
137
+
138
+ ```typescript
139
+ const matched = await users.joinInner(
140
+ schema.instance().table("orders"),
141
+ (user, order) => order.key("userId").eq(user.key("_id")),
142
+ (user, order) => ({
143
+ userName: user.key("name"),
144
+ amount: order.key("amount"),
145
+ }),
146
+ );
147
+ ```
148
+
149
+ ### `lookup()`
150
+
151
+ Replace a foreign key field with the referenced document from another table. See [Lookup](../2.operations/7.lookup.md) for detailed usage.
152
+
153
+ ```typescript
154
+ const postsWithAuthors = await schema
155
+ .instance()
156
+ .table("posts")
157
+ .lookup(schema.instance().table("users"), "authorId", "_id");
158
+ ```
159
+
160
+ ## Group Data
161
+
162
+ The `group` method partitions the stream by an index value, then applies a mapping function to each group. The mapper receives the group's sub-stream and the group key.
163
+
164
+ ```typescript
165
+ const statsByRole = await users.group("role", (stream, role) => ({
166
+ role: role,
167
+ total: stream.count(),
168
+ avgAge: stream.avg("age"),
169
+ }));
170
+ ```
171
+
172
+ ## Order and Paginate
173
+
174
+ ### `orderBy()`
175
+
176
+ Sort the stream by an index field.
177
+
178
+ ```typescript
179
+ const sorted = await users.orderBy("name", "asc");
180
+ const newest = await users.orderBy("createdAt", "desc");
181
+ ```
182
+
183
+ ### `slice()`
184
+
185
+ Extract a subsection of the stream for pagination.
186
+
187
+ ```typescript
188
+ const page1 = await users.orderBy("createdAt").slice(0, 10);
189
+ const page2 = await users.orderBy("createdAt").slice(10, 10);
190
+ ```
191
+
192
+ ### `nth()`
193
+
194
+ Retrieve a single document by its position in the stream. Returns a `Datum<T | null>`.
195
+
196
+ ```typescript
197
+ const first = await users.orderBy("createdAt").nth(0);
198
+ ```
199
+
200
+ ## Aggregate Data
201
+
202
+ Stream provides several aggregation methods that return a `Datum<number>`:
203
+
204
+ ```typescript
205
+ const totalUsers = await users.count();
206
+ const distinctEmails = await users.count("email");
207
+ const totalSales = await schema.instance().table("orders").sum("amount");
208
+ const avgOrder = await schema.instance().table("orders").avg("amount");
209
+ const maxOrder = await schema.instance().table("orders").max("amount");
210
+ const minOrder = await schema.instance().table("orders").min("amount");
211
+ ```
212
+
213
+ ### `distinct()`
214
+
215
+ Retrieve deduplicated documents or distinct values of a specific field.
216
+
217
+ ```typescript
218
+ // All distinct documents
219
+ const unique = await users.distinct();
220
+
221
+ // Distinct values of a field
222
+ const roles = await users.distinct("role");
223
+ ```
224
+
225
+ ## Change Feeds
226
+
227
+ The `changes` method subscribes to real-time change notifications on the stream.
228
+
229
+ ```typescript
230
+ const changes = await users.changes();
231
+
232
+ for (const change of changes) {
233
+ console.log(change.changeType, change.oldValue, change.newValue);
234
+ }
235
+ ```
236
+
237
+ See [Change Feeds](./6.feed.md) for more details.
@@ -0,0 +1,82 @@
1
+ # Change Feeds
2
+
3
+ Change feeds provide real-time notifications when documents are inserted, updated, or deleted. Both `Stream<T>` and `SingleSelection<T>` expose a `changes()` method that returns change events.
4
+
5
+ ## The Changes Interface
6
+
7
+ ```typescript
8
+ interface Changes<T> {
9
+ changeType: "added" | "removed" | "modified";
10
+ oldValue?: T;
11
+ newValue?: T;
12
+ }
13
+ ```
14
+
15
+ | Property | Description |
16
+ | ------------ | ------------------------------------------------------------------ |
17
+ | `changeType` | The type of change: `"added"`, `"removed"`, or `"modified"`. |
18
+ | `oldValue` | The document value before the change. Undefined for new documents. |
19
+ | `newValue` | The document value after the change. Undefined for deletions. |
20
+
21
+ ## Create a Change Feed
22
+
23
+ Call `changes()` on any stream or single selection to subscribe to change events:
24
+
25
+ ```typescript
26
+ import { Schema } from "@antelopejs/interface-database";
27
+
28
+ const schema = Schema.get("myapp")!;
29
+ const users = schema.instance().table("users");
30
+
31
+ // Monitor all changes to a table
32
+ const allChanges = await users.changes();
33
+
34
+ // Monitor changes to a specific document
35
+ const userChanges = await users.get("user-123").changes();
36
+ ```
37
+
38
+ ## Process Change Events
39
+
40
+ Each change event includes a `changeType` field that indicates the nature of the change:
41
+
42
+ ```typescript
43
+ const changes = await users.changes();
44
+
45
+ for (const change of changes) {
46
+ switch (change.changeType) {
47
+ case "added":
48
+ console.log("New document:", change.newValue);
49
+ break;
50
+ case "modified":
51
+ console.log("Updated from:", change.oldValue);
52
+ console.log("Updated to:", change.newValue);
53
+ break;
54
+ case "removed":
55
+ console.log("Deleted document:", change.oldValue);
56
+ break;
57
+ }
58
+ }
59
+ ```
60
+
61
+ ## Use with Async Iteration
62
+
63
+ Since `changes()` returns a `Query`, you can use the cursor for streaming results:
64
+
65
+ ```typescript
66
+ const feed = users.changes();
67
+
68
+ for await (const batch of feed) {
69
+ for (const change of batch) {
70
+ console.log(`${change.changeType}:`, change.newValue ?? change.oldValue);
71
+ }
72
+ }
73
+ ```
74
+
75
+ ## Use Cases
76
+
77
+ Change feeds are well suited for:
78
+
79
+ - **Real-time UI updates** - Push changes to connected clients as they happen.
80
+ - **Event-driven architectures** - React to data changes with downstream processing.
81
+ - **Audit logging** - Record every modification to a collection.
82
+ - **Cache invalidation** - Clear or update caches when the underlying data changes.