@antelopejs/interface-database 0.1.3 → 0.1.5
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.
- package/dist/antelope.test.js +2 -2
- package/dist/antelope.test.js.map +1 -1
- package/dist/atomic.d.ts +41 -0
- package/dist/atomic.js +135 -0
- package/dist/atomic.js.map +1 -0
- package/dist/common.js.map +1 -1
- package/dist/datum.d.ts +2 -2
- package/dist/datum.js.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.js +15 -0
- package/dist/index.js.map +1 -1
- package/dist/query.js +1 -1
- package/dist/query.js.map +1 -1
- package/dist/schema.d.ts +1 -1
- package/dist/schema.js +1 -1
- package/dist/schema.js.map +1 -1
- package/dist/selection.d.ts +4 -1
- package/dist/selection.js +7 -0
- package/dist/selection.js.map +1 -1
- package/dist/stream.d.ts +1 -1
- package/dist/stream.js.map +1 -1
- package/dist/tests/aggregation_operations.test.js +1 -1
- package/dist/tests/aggregation_operations.test.js.map +1 -1
- package/dist/tests/atomic_mutation.test.d.ts +1 -0
- package/dist/tests/atomic_mutation.test.js +185 -0
- package/dist/tests/atomic_mutation.test.js.map +1 -0
- package/dist/tests/basic_operations.test.js +1 -1
- package/dist/tests/basic_operations.test.js.map +1 -1
- package/dist/tests/between_operations.test.js +1 -1
- package/dist/tests/between_operations.test.js.map +1 -1
- package/dist/tests/change_feeds.test.js +1 -1
- package/dist/tests/change_feeds.test.js.map +1 -1
- package/dist/tests/datasets/codec_fields.js.map +1 -1
- package/dist/tests/date_operations.test.js +1 -1
- package/dist/tests/date_operations.test.js.map +1 -1
- package/dist/tests/do_operations.test.js +1 -1
- package/dist/tests/do_operations.test.js.map +1 -1
- package/dist/tests/filters.test.js +1 -1
- package/dist/tests/filters.test.js.map +1 -1
- package/dist/tests/group_operations.test.js +1 -1
- package/dist/tests/group_operations.test.js.map +1 -1
- package/dist/tests/instance_modes.test.js +1 -1
- package/dist/tests/instance_modes.test.js.map +1 -1
- package/dist/tests/joins.test.js +2 -2
- package/dist/tests/joins.test.js.map +1 -1
- package/dist/tests/lookup_operations.test.js +2 -2
- package/dist/tests/lookup_operations.test.js.map +1 -1
- package/dist/tests/merge_operations.test.js +2 -2
- package/dist/tests/merge_operations.test.js.map +1 -1
- package/dist/tests/sorting.test.js +1 -1
- package/dist/tests/sorting.test.js.map +1 -1
- package/dist/tests/union_operations.test.js +2 -2
- package/dist/tests/union_operations.test.js.map +1 -1
- package/docs/1.query_types/1.index.md +53 -0
- package/docs/1.query_types/2.table.md +114 -0
- package/docs/1.query_types/3.selection.md +92 -0
- package/docs/1.query_types/4.single_selection.md +121 -0
- package/docs/1.query_types/5.stream.md +234 -0
- package/docs/1.query_types/6.feed.md +82 -0
- package/docs/1.query_types/7.query.md +82 -0
- package/docs/1.query_types/8.datum.md +102 -0
- package/docs/1.query_types/9.valueproxy.md +232 -0
- package/docs/2.operations/1.index.md +34 -0
- package/docs/2.operations/2.database_management.md +151 -0
- package/docs/2.operations/3.table_operations.md +117 -0
- package/docs/2.operations/4.crud.md +214 -0
- package/docs/2.operations/5.indexes.md +110 -0
- package/docs/2.operations/6.filtering.md +172 -0
- package/docs/2.operations/7.lookup.md +135 -0
- package/docs/2.operations/8.atomic_mutations.md +71 -0
- package/docs/3.results/1.index.md +38 -0
- package/docs/3.results/2.write_results.md +86 -0
- package/docs/3.results/3.index_changes.md +82 -0
- package/docs/3.results/4.table_changes.md +105 -0
- package/docs/3.results/5.database_changes.md +96 -0
- package/docs/3.results/6.value_changes.md +75 -0
- package/docs/index.md +51 -0
- package/package.json +62 -42
- package/skills/database-interface/SKILL.md +101 -0
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
# Datum
|
|
2
|
+
|
|
3
|
+
The `Datum<T>` class represents a single value in the query system. It extends `Query<T>` and provides methods for accessing fields, applying transformations, and performing foreign key lookups on individual values.
|
|
4
|
+
|
|
5
|
+
## Class Definition
|
|
6
|
+
|
|
7
|
+
```typescript
|
|
8
|
+
class Datum<T> extends Query<T> {
|
|
9
|
+
cast<U>(): Datum<U>;
|
|
10
|
+
key<K extends keyof NonNullable<T>>(key: K, def?: unknown): Datum<NonNullable<T>[K]>;
|
|
11
|
+
default<U>(val: Value<U>): Datum<Exclude<T, undefined | null> | U>;
|
|
12
|
+
do<U>(mapper: (val: ValueProxy<T>) => U): Datum<ExtractType<U>>;
|
|
13
|
+
lookup<U>(other: Selection<U>, localKey: keyof T, otherKey: keyof U): Datum<...>;
|
|
14
|
+
pluck(...fields: string[]): Datum<Partial<T>>;
|
|
15
|
+
}
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
## Field Access
|
|
19
|
+
|
|
20
|
+
The `key` method retrieves a field from a document datum. You can chain `key` calls for nested field access. An optional default value can be provided for the case when the field is undefined.
|
|
21
|
+
|
|
22
|
+
```typescript
|
|
23
|
+
import { Schema } from "@antelopejs/interface-database";
|
|
24
|
+
|
|
25
|
+
const schema = Schema.get("myapp")!;
|
|
26
|
+
const users = schema.instance().table("users");
|
|
27
|
+
|
|
28
|
+
// Access a field
|
|
29
|
+
const name = await users.get("user-123").key("name");
|
|
30
|
+
|
|
31
|
+
// Access a nested field
|
|
32
|
+
const city = await users.get("user-123").key("address").key("city");
|
|
33
|
+
|
|
34
|
+
// Provide a default value
|
|
35
|
+
const phone = await users.get("user-123").key("phone", "N/A");
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
## Default Values
|
|
39
|
+
|
|
40
|
+
The `default` method provides a fallback value when the datum is null or undefined.
|
|
41
|
+
|
|
42
|
+
```typescript
|
|
43
|
+
const user = await users.get("maybe-missing").default({
|
|
44
|
+
_id: "guest",
|
|
45
|
+
name: "Guest User",
|
|
46
|
+
role: "visitor",
|
|
47
|
+
});
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
## Transform with `do()`
|
|
51
|
+
|
|
52
|
+
The `do` method applies a transformation function to the datum. The callback receives a `ValueProxy<T>` that provides operators for building expressions.
|
|
53
|
+
|
|
54
|
+
```typescript
|
|
55
|
+
const formatted = await users.get("user-123").do((user) => ({
|
|
56
|
+
displayName: user.key("name"),
|
|
57
|
+
contact: user.key("email"),
|
|
58
|
+
hasPhone: user.key("phone").ne(undefined),
|
|
59
|
+
}));
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
## Foreign Key Lookup
|
|
63
|
+
|
|
64
|
+
The `lookup` method replaces a foreign key field with the referenced document from another table.
|
|
65
|
+
|
|
66
|
+
```typescript
|
|
67
|
+
interface Post {
|
|
68
|
+
_id: string;
|
|
69
|
+
title: string;
|
|
70
|
+
authorId: string;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
interface User {
|
|
74
|
+
_id: string;
|
|
75
|
+
name: string;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
const post = await schema
|
|
79
|
+
.instance()
|
|
80
|
+
.table<Post>("posts")
|
|
81
|
+
.get("post-1")
|
|
82
|
+
.lookup(schema.instance().table<User>("users"), "authorId", "_id");
|
|
83
|
+
|
|
84
|
+
// post.authorId is now the full User object
|
|
85
|
+
console.log(post.authorId.name);
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
## Field Selection
|
|
89
|
+
|
|
90
|
+
The `pluck` method selects specific fields from a document datum, discarding the rest.
|
|
91
|
+
|
|
92
|
+
```typescript
|
|
93
|
+
const profile = await users.get("user-123").pluck("name", "email", "avatar");
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
## Type Casting
|
|
97
|
+
|
|
98
|
+
The `cast` method changes the TypeScript type of the datum without performing any runtime conversion. Use this when you know the actual type differs from what the compiler infers.
|
|
99
|
+
|
|
100
|
+
```typescript
|
|
101
|
+
const raw = users.get("user-123").cast<{ name: string; role: string }>();
|
|
102
|
+
```
|
|
@@ -0,0 +1,232 @@
|
|
|
1
|
+
# ValueProxy
|
|
2
|
+
|
|
3
|
+
The `ValueProxy<T>` class acts as a proxy to a database value. Operations on a ValueProxy are not executed immediately but are recorded as query stages. ValueProxy objects appear inside callback functions for `map`, `filter`, `do`, `update`, `join`, and other query methods.
|
|
4
|
+
|
|
5
|
+
## Class Definition
|
|
6
|
+
|
|
7
|
+
ValueProxy provides type-specific methods based on the underlying value type. The available operations are organized by category.
|
|
8
|
+
|
|
9
|
+
## Create a ValueProxy
|
|
10
|
+
|
|
11
|
+
ValueProxy instances are created automatically when you use callback-based query methods. You can also create one from a constant value:
|
|
12
|
+
|
|
13
|
+
```typescript
|
|
14
|
+
import { ValueProxy } from "@antelopejs/interface-database";
|
|
15
|
+
|
|
16
|
+
const constant = ValueProxy.constant(42);
|
|
17
|
+
const strConst = ValueProxy.constant("hello");
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
## General Operations
|
|
21
|
+
|
|
22
|
+
These methods are available on all ValueProxy types.
|
|
23
|
+
|
|
24
|
+
### `default(value)`
|
|
25
|
+
|
|
26
|
+
Return the given value if the proxy is null.
|
|
27
|
+
|
|
28
|
+
```typescript
|
|
29
|
+
const phone = user.key("phone").default("N/A");
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
### `eq(value)` / `ne(value)`
|
|
33
|
+
|
|
34
|
+
Equality and inequality comparison.
|
|
35
|
+
|
|
36
|
+
```typescript
|
|
37
|
+
const isActive = user.key("status").eq("active");
|
|
38
|
+
const isNotAdmin = user.key("role").ne("admin");
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
### `and(value)` / `or(value)` / `not()`
|
|
42
|
+
|
|
43
|
+
Logical operators for combining boolean expressions.
|
|
44
|
+
|
|
45
|
+
```typescript
|
|
46
|
+
const canAccess = user.key("active").and(user.key("verified"));
|
|
47
|
+
const isSpecial = user
|
|
48
|
+
.key("role")
|
|
49
|
+
.eq("admin")
|
|
50
|
+
.or(user.key("plan").eq("premium"));
|
|
51
|
+
const isInactive = user.key("active").not();
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
### `cast<U>()`
|
|
55
|
+
|
|
56
|
+
Change the TypeScript type without runtime conversion.
|
|
57
|
+
|
|
58
|
+
## Number Operations
|
|
59
|
+
|
|
60
|
+
Available when the proxy wraps a numeric value.
|
|
61
|
+
|
|
62
|
+
### Arithmetic
|
|
63
|
+
|
|
64
|
+
```typescript
|
|
65
|
+
const next = age.add(1);
|
|
66
|
+
const diff = total.sub(discount);
|
|
67
|
+
const doubled = price.mul(2);
|
|
68
|
+
const half = amount.div(2);
|
|
69
|
+
const remainder = count.mod(10);
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
### Rounding
|
|
73
|
+
|
|
74
|
+
```typescript
|
|
75
|
+
const rounded = score.round();
|
|
76
|
+
const ceiling = value.ceil();
|
|
77
|
+
const floored = value.floor();
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
### Bitwise
|
|
81
|
+
|
|
82
|
+
```typescript
|
|
83
|
+
const bitwiseAnd = flags.band(0xff);
|
|
84
|
+
const bitwiseOr = flags.bor(0x01);
|
|
85
|
+
const bitwiseXor = flags.bxor(mask);
|
|
86
|
+
const bitwiseNot = flags.bnot();
|
|
87
|
+
const shifted = flags.blshift(2);
|
|
88
|
+
const unshifted = flags.brshift(2);
|
|
89
|
+
const signPreserved = flags.brshiftPreserveSign(2);
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
## Comparison Operations
|
|
93
|
+
|
|
94
|
+
Available for `number`, `string`, and `Date` proxy values.
|
|
95
|
+
|
|
96
|
+
```typescript
|
|
97
|
+
const isAdult = age.gt(17);
|
|
98
|
+
const isMinor = age.le(17);
|
|
99
|
+
const isRecent = date.ge(new Date("2024-01-01"));
|
|
100
|
+
const isBefore = date.lt(deadline);
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
| Method | Description |
|
|
104
|
+
| ------- | --------------------- |
|
|
105
|
+
| `gt(v)` | Greater than |
|
|
106
|
+
| `ge(v)` | Greater than or equal |
|
|
107
|
+
| `lt(v)` | Less than |
|
|
108
|
+
| `le(v)` | Less than or equal |
|
|
109
|
+
|
|
110
|
+
## Date Operations
|
|
111
|
+
|
|
112
|
+
Available when the proxy wraps a `Date` value. All date component methods accept an optional `timezone` parameter.
|
|
113
|
+
|
|
114
|
+
### Arithmetic
|
|
115
|
+
|
|
116
|
+
```typescript
|
|
117
|
+
const threeDaysLater = created.add(3 * 24 * 60 * 60);
|
|
118
|
+
const elapsed = now.sub(created); // difference in seconds
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
### Components
|
|
122
|
+
|
|
123
|
+
```typescript
|
|
124
|
+
const y = created.year();
|
|
125
|
+
const m = created.month();
|
|
126
|
+
const d = created.day();
|
|
127
|
+
const dow = created.dayofweek();
|
|
128
|
+
const doy = created.dayofyear();
|
|
129
|
+
const h = created.hours();
|
|
130
|
+
const min = created.minutes();
|
|
131
|
+
const sec = created.seconds();
|
|
132
|
+
const tod = created.timeofday(); // seconds since midnight
|
|
133
|
+
const epoch = created.epoch(); // seconds since UNIX epoch
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
### Range Check
|
|
137
|
+
|
|
138
|
+
```typescript
|
|
139
|
+
const inRange = created.during(
|
|
140
|
+
ValueProxy.constant(new Date("2024-01-01")),
|
|
141
|
+
ValueProxy.constant(new Date("2025-01-01")),
|
|
142
|
+
);
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
## String Operations
|
|
146
|
+
|
|
147
|
+
Available when the proxy wraps a `string` value.
|
|
148
|
+
|
|
149
|
+
```typescript
|
|
150
|
+
const upper = name.upcase();
|
|
151
|
+
const lower = name.downcase();
|
|
152
|
+
const parts = email.split("@");
|
|
153
|
+
const full = first.concat(last);
|
|
154
|
+
const len = name.strlen();
|
|
155
|
+
const matches = email.match("^.*@gmail\\.com$");
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
| Method | Description |
|
|
159
|
+
| ------------------ | ------------------------------- |
|
|
160
|
+
| `upcase()` | Convert to uppercase |
|
|
161
|
+
| `downcase()` | Convert to lowercase |
|
|
162
|
+
| `split(sep, max?)` | Split by separator |
|
|
163
|
+
| `concat(other)` | Concatenate with another string |
|
|
164
|
+
| `strlen()` | Number of Unicode codepoints |
|
|
165
|
+
| `match(regex)` | Test against a regex pattern |
|
|
166
|
+
|
|
167
|
+
## Array Operations
|
|
168
|
+
|
|
169
|
+
Available when the proxy wraps an array value.
|
|
170
|
+
|
|
171
|
+
```typescript
|
|
172
|
+
const first = tags.index(0);
|
|
173
|
+
const hasAdmin = tags.includes("admin");
|
|
174
|
+
const sub = tags.slice(0, 3);
|
|
175
|
+
const upper = tags.map((t) => t.upcase());
|
|
176
|
+
const long = tags.filter((t) => t.strlen().gt(5));
|
|
177
|
+
const empty = tags.isempty();
|
|
178
|
+
const len = tags.count();
|
|
179
|
+
const total = scores.sum();
|
|
180
|
+
const average = scores.avg();
|
|
181
|
+
const lowest = scores.min();
|
|
182
|
+
const highest = scores.max();
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
| Method | Description |
|
|
186
|
+
| --------------- | ----------------------------------- |
|
|
187
|
+
| `index(n)` | Element at position `n` |
|
|
188
|
+
| `includes(val)` | Check if the array contains a value |
|
|
189
|
+
| `slice(s, e?)` | Extract a sub-array |
|
|
190
|
+
| `map(fn)` | Transform each element |
|
|
191
|
+
| `filter(fn)` | Keep elements matching a predicate |
|
|
192
|
+
| `isempty()` | Check if the array is empty |
|
|
193
|
+
| `count()` | Number of elements |
|
|
194
|
+
| `sum()` | Sum of numeric elements |
|
|
195
|
+
| `avg()` | Average of numeric elements |
|
|
196
|
+
| `min()` | Minimum of numeric elements |
|
|
197
|
+
| `max()` | Maximum of numeric elements |
|
|
198
|
+
|
|
199
|
+
## Object Operations
|
|
200
|
+
|
|
201
|
+
Available when the proxy wraps a record/object value.
|
|
202
|
+
|
|
203
|
+
```typescript
|
|
204
|
+
const email = user.key("email");
|
|
205
|
+
const emailOrDefault = user.key("email", "none@example.com");
|
|
206
|
+
const merged = user.merge({ lastSeen: new Date() });
|
|
207
|
+
const fieldNames = user.keys();
|
|
208
|
+
const fieldValues = user.values();
|
|
209
|
+
const hasRequired = user.hasfields("email", "name");
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
| Method | Description |
|
|
213
|
+
| ------------------ | ------------------------------------- |
|
|
214
|
+
| `key(k, def?)` | Access a field, with optional default |
|
|
215
|
+
| `merge(other)` | Merge with another object |
|
|
216
|
+
| `keys()` | Array of field names |
|
|
217
|
+
| `values()` | Array of field values |
|
|
218
|
+
| `hasfields(...fs)` | Check if all specified fields exist |
|
|
219
|
+
|
|
220
|
+
## Compose Expressions
|
|
221
|
+
|
|
222
|
+
ValueProxy methods are chainable, enabling complex expressions within query callbacks:
|
|
223
|
+
|
|
224
|
+
```typescript
|
|
225
|
+
const result = await users.filter((user) =>
|
|
226
|
+
user
|
|
227
|
+
.key("age")
|
|
228
|
+
.ge(18)
|
|
229
|
+
.and(user.key("status").eq("active"))
|
|
230
|
+
.and(user.key("role").eq("admin").or(user.key("plan").eq("premium"))),
|
|
231
|
+
);
|
|
232
|
+
```
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
# Operations
|
|
2
|
+
|
|
3
|
+
This section covers practical operations you can perform with the AQL interface. These guides walk through schema management, table access, document CRUD, index usage, filtering, and foreign key lookups.
|
|
4
|
+
|
|
5
|
+
## Schema and Instance Management
|
|
6
|
+
|
|
7
|
+
AQL organizes data through schemas and instances:
|
|
8
|
+
|
|
9
|
+
- [2. Schema Management](./2.database_management.md) - Define schemas, create and destroy instances
|
|
10
|
+
|
|
11
|
+
## Table Access
|
|
12
|
+
|
|
13
|
+
Tables live inside schema instances:
|
|
14
|
+
|
|
15
|
+
- [3. Table Operations](./3.table_operations.md) - Access tables and work with their contents
|
|
16
|
+
|
|
17
|
+
## Document Operations
|
|
18
|
+
|
|
19
|
+
Documents are the fundamental unit of data in AQL:
|
|
20
|
+
|
|
21
|
+
- [4. CRUD Operations](./4.crud.md) - Insert, read, update, replace, and delete documents
|
|
22
|
+
|
|
23
|
+
## Index Operations
|
|
24
|
+
|
|
25
|
+
Indexes accelerate queries by enabling efficient lookups:
|
|
26
|
+
|
|
27
|
+
- [5. Index Management](./5.indexes.md) - Define secondary indexes in schema definitions
|
|
28
|
+
|
|
29
|
+
## Query Operations
|
|
30
|
+
|
|
31
|
+
Transform and filter data with the query builder:
|
|
32
|
+
|
|
33
|
+
- [6. Filtering and Querying](./6.filtering.md) - Filter, compare, and transform documents
|
|
34
|
+
- [7. Lookup](./7.lookup.md) - Foreign key joins and data population
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
# Schema Management
|
|
2
|
+
|
|
3
|
+
AQL organizes data through schemas and instances. A `Schema` defines the structure of your database (tables, fields, and indexes). Each schema can have multiple instances, and the underlying module implementation determines how instances map to physical storage.
|
|
4
|
+
|
|
5
|
+
## Define a Schema
|
|
6
|
+
|
|
7
|
+
Create a schema by providing an ID and a definition of tables with their fields and indexes:
|
|
8
|
+
|
|
9
|
+
```typescript
|
|
10
|
+
import { Schema } from "@antelopejs/interface-database";
|
|
11
|
+
|
|
12
|
+
const schema = new Schema("myapp", {
|
|
13
|
+
users: {
|
|
14
|
+
fields: { name: "string", email: "string", role: "string" },
|
|
15
|
+
indexes: {
|
|
16
|
+
email: {},
|
|
17
|
+
role: {},
|
|
18
|
+
},
|
|
19
|
+
},
|
|
20
|
+
posts: {
|
|
21
|
+
fields: { title: "string", content: "string", authorId: "string" },
|
|
22
|
+
indexes: {
|
|
23
|
+
authorId: {},
|
|
24
|
+
},
|
|
25
|
+
},
|
|
26
|
+
});
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
### `SchemaDefinition`
|
|
30
|
+
|
|
31
|
+
A schema definition is a record mapping table names to their `TableDefinition`:
|
|
32
|
+
|
|
33
|
+
```typescript
|
|
34
|
+
interface TableDefinition {
|
|
35
|
+
fields: Record<string, FieldType>;
|
|
36
|
+
indexes: Record<string, IndexDefinition>;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
interface IndexDefinition {
|
|
40
|
+
fields?: string[]; // For compound indexes
|
|
41
|
+
multi?: boolean; // Multi-valued index
|
|
42
|
+
}
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
## Create an Instance
|
|
46
|
+
|
|
47
|
+
After defining a schema, create an instance to start working with data. The optional `id` parameter distinguishes multiple instances of the same schema.
|
|
48
|
+
|
|
49
|
+
```typescript
|
|
50
|
+
// Create the default instance
|
|
51
|
+
await schema.createInstance();
|
|
52
|
+
|
|
53
|
+
// Create a named instance
|
|
54
|
+
await schema.createInstance("tenant-abc");
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
## Access an Instance
|
|
58
|
+
|
|
59
|
+
Retrieve a `SchemaInstance` to access tables:
|
|
60
|
+
|
|
61
|
+
```typescript
|
|
62
|
+
// Default instance
|
|
63
|
+
const db = schema.instance();
|
|
64
|
+
|
|
65
|
+
// Named instance
|
|
66
|
+
const tenantDb = schema.instance("tenant-abc");
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
To operate across every instance of the schema, pass the `CROSS_INSTANCE` sentinel:
|
|
70
|
+
|
|
71
|
+
```typescript
|
|
72
|
+
import { CROSS_INSTANCE } from "@antelopejs/interface-database";
|
|
73
|
+
|
|
74
|
+
const everything = schema.instance(CROSS_INSTANCE).table("users");
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
## Destroy an Instance
|
|
78
|
+
|
|
79
|
+
Remove an instance and its data:
|
|
80
|
+
|
|
81
|
+
```typescript
|
|
82
|
+
await schema.destroyInstance("tenant-abc");
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
## List Instances
|
|
86
|
+
|
|
87
|
+
List the IDs of named instances of a schema:
|
|
88
|
+
|
|
89
|
+
```typescript
|
|
90
|
+
const ids = await schema.listInstances();
|
|
91
|
+
// ["tenant-abc", "tenant-xyz"]
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
The default (unnamed) instance is not included.
|
|
95
|
+
|
|
96
|
+
## Retrieve a Schema
|
|
97
|
+
|
|
98
|
+
Use the static `Schema.get()` method to look up a previously defined schema by its ID:
|
|
99
|
+
|
|
100
|
+
```typescript
|
|
101
|
+
const schema = Schema.get("myapp");
|
|
102
|
+
|
|
103
|
+
if (schema) {
|
|
104
|
+
const users = schema.instance().table("users");
|
|
105
|
+
}
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
## Work with Tables
|
|
109
|
+
|
|
110
|
+
Once you have a `SchemaInstance`, access tables by name. The generic type parameter provides type safety for your documents:
|
|
111
|
+
|
|
112
|
+
```typescript
|
|
113
|
+
interface User {
|
|
114
|
+
_id: string;
|
|
115
|
+
name: string;
|
|
116
|
+
email: string;
|
|
117
|
+
role: string;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
interface Post {
|
|
121
|
+
_id: string;
|
|
122
|
+
title: string;
|
|
123
|
+
content: string;
|
|
124
|
+
authorId: string;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
const db = schema.instance();
|
|
128
|
+
const users = db.table<User>("users");
|
|
129
|
+
const posts = db.table<Post>("posts");
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
## Multi-Tenant Pattern
|
|
133
|
+
|
|
134
|
+
Schemas with multiple instances support multi-tenant architectures. Each tenant gets an isolated instance of the same schema:
|
|
135
|
+
|
|
136
|
+
```typescript
|
|
137
|
+
const schema = new Schema("saas", {
|
|
138
|
+
projects: {
|
|
139
|
+
fields: { name: "string" },
|
|
140
|
+
indexes: {},
|
|
141
|
+
},
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
// Create one instance per tenant
|
|
145
|
+
await schema.createInstance("tenant-1");
|
|
146
|
+
await schema.createInstance("tenant-2");
|
|
147
|
+
|
|
148
|
+
// Access tenant-specific data
|
|
149
|
+
const tenant1Projects = schema.instance("tenant-1").table("projects");
|
|
150
|
+
const tenant2Projects = schema.instance("tenant-2").table("projects");
|
|
151
|
+
```
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
# Table Operations
|
|
2
|
+
|
|
3
|
+
Tables are the primary containers for documents in AQL. They are accessed through a `SchemaInstance` and provide methods for inserting, retrieving, and querying documents.
|
|
4
|
+
|
|
5
|
+
## Access a Table
|
|
6
|
+
|
|
7
|
+
Retrieve a table reference from a schema instance:
|
|
8
|
+
|
|
9
|
+
```typescript
|
|
10
|
+
import { Schema } from "@antelopejs/interface-database";
|
|
11
|
+
|
|
12
|
+
const schema = Schema.get("myapp")!;
|
|
13
|
+
const db = schema.instance();
|
|
14
|
+
const users = db.table("users");
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
## Type-Safe Tables
|
|
18
|
+
|
|
19
|
+
Provide a generic type parameter to get type-safe operations:
|
|
20
|
+
|
|
21
|
+
```typescript
|
|
22
|
+
interface User {
|
|
23
|
+
_id: string;
|
|
24
|
+
name: string;
|
|
25
|
+
email: string;
|
|
26
|
+
status: string;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
const users = db.table<User>("users");
|
|
30
|
+
|
|
31
|
+
// TypeScript now validates field names and types
|
|
32
|
+
const user = await users.get("user-123"); // user is User | null
|
|
33
|
+
const emails = await users.key("email"); // string[]
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
## Basic Operations
|
|
37
|
+
|
|
38
|
+
### Retrieve All Documents
|
|
39
|
+
|
|
40
|
+
A table reference doubles as a stream of all its documents:
|
|
41
|
+
|
|
42
|
+
```typescript
|
|
43
|
+
const allUsers = await users;
|
|
44
|
+
console.log(`Total users: ${allUsers.length}`);
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
### Retrieve by Primary Key
|
|
48
|
+
|
|
49
|
+
```typescript
|
|
50
|
+
const user = await users.get("user-123");
|
|
51
|
+
|
|
52
|
+
if (user) {
|
|
53
|
+
console.log("Found:", user.name);
|
|
54
|
+
}
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
### Retrieve by Secondary Index
|
|
58
|
+
|
|
59
|
+
```typescript
|
|
60
|
+
const admins = await users.getAll("admin", "role");
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
### Retrieve by Range
|
|
64
|
+
|
|
65
|
+
```typescript
|
|
66
|
+
const recent = await users.between(
|
|
67
|
+
"createdAt",
|
|
68
|
+
new Date("2024-01-01"),
|
|
69
|
+
new Date(),
|
|
70
|
+
);
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
## Chain Operations
|
|
74
|
+
|
|
75
|
+
Since `Table` extends `Selection` and `Stream`, you can chain any stream operation directly:
|
|
76
|
+
|
|
77
|
+
```typescript
|
|
78
|
+
// Filter and sort
|
|
79
|
+
const activeByName = await users
|
|
80
|
+
.filter((u) => u.key("status").eq("active"))
|
|
81
|
+
.orderBy("name");
|
|
82
|
+
|
|
83
|
+
// Paginate
|
|
84
|
+
const page = await users.orderBy("createdAt").slice(0, 10);
|
|
85
|
+
|
|
86
|
+
// Aggregate
|
|
87
|
+
const count = await users.count();
|
|
88
|
+
const avgAge = await users.avg("age");
|
|
89
|
+
|
|
90
|
+
// Transform
|
|
91
|
+
const names = await users.map((u) => u.key("name"));
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
## Insert Documents
|
|
95
|
+
|
|
96
|
+
```typescript
|
|
97
|
+
// Single document
|
|
98
|
+
const ids = await users.insert({
|
|
99
|
+
name: "Alice",
|
|
100
|
+
email: "alice@example.com",
|
|
101
|
+
status: "active",
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
// Multiple documents
|
|
105
|
+
const batchIds = await users.insert([
|
|
106
|
+
{ name: "Bob", email: "bob@example.com", status: "active" },
|
|
107
|
+
{ name: "Carol", email: "carol@example.com", status: "active" },
|
|
108
|
+
]);
|
|
109
|
+
|
|
110
|
+
// Upsert (update on conflict)
|
|
111
|
+
await users.insert(
|
|
112
|
+
{ _id: "user-123", name: "Updated" },
|
|
113
|
+
{ conflict: "update" },
|
|
114
|
+
);
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
For complete CRUD documentation, see [CRUD Operations](./4.crud.md).
|