@thadeu/ndb 0.1.0 → 0.1.1
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/README.md +25 -1
- package/dist/index.cjs +47 -6
- package/dist/index.d.cts +21 -5
- package/dist/index.d.ts +21 -5
- package/dist/index.js +46 -6
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -70,7 +70,8 @@ const calls = ndb.model('calls', {
|
|
|
70
70
|
- `time` is the collection time field. Used by zone maps and `$dateTrunc`.
|
|
71
71
|
- The model declares its collection on first use (`ensure_collection`). You can call `await calls.ready()` to do it at once.
|
|
72
72
|
- Types come from the schema. Rows have the schema fields and `_v`, the document version. A `date` field is a `Date` in rows. Input takes `Date` values and ISO strings in filters.
|
|
73
|
-
-
|
|
73
|
+
- `expires` is the `date` field that holds when a document expires. It turns on `expireIn`, `expire`, `persist` and `ttl` (below).
|
|
74
|
+
- The client has no `ttl` catalog option and no blob API. Use the HTTP API for those.
|
|
74
75
|
|
|
75
76
|
## Insert, find, update, delete
|
|
76
77
|
|
|
@@ -111,6 +112,28 @@ try {
|
|
|
111
112
|
|
|
112
113
|
An insert with a duplicate id throws `DuplicateIdError`. A duplicate `unique` value throws `UniqueViolationError`. An insert without the id when the schema names one fails with code `missing_id`.
|
|
113
114
|
|
|
115
|
+
### Expiry (TTL like Redis)
|
|
116
|
+
|
|
117
|
+
```ts
|
|
118
|
+
const sessions = db.model('sessions', {
|
|
119
|
+
id: 'sid',
|
|
120
|
+
expires: 'exp',
|
|
121
|
+
fields: { sid: { type: 'string' }, exp: { type: 'date', optional: true } },
|
|
122
|
+
})
|
|
123
|
+
|
|
124
|
+
await sessions.insert({ sid: 's1' }, { expireIn: '1h' }) // or 3600; all docs of the call
|
|
125
|
+
await sessions.update('s1', { user: 'u' }, { expireIn: '30m' })
|
|
126
|
+
|
|
127
|
+
const { v, expiresAt } = await sessions.expire('s1', '30m') // EXPIRE
|
|
128
|
+
await sessions.expireAt('s1', new Date(Date.now() + 3_600_000))
|
|
129
|
+
await sessions.ttl('s1') // seconds left, -1 = never expires, null = missing or expired
|
|
130
|
+
await sessions.persist('s1') // PERSIST
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
- A duration is seconds or a string such as `"90m"`, `"1h30m"`, `"1d"`. `expireIn` wins over a value of the `expires` field in the same call.
|
|
134
|
+
- `expire` and `persist` throw `NotFoundError` for a missing or expired document. `TtlError` (`ttl_not_enabled`, `ttl_derived`) means the schema has no `expires` field, or the expiry comes from `ttl` + `time`.
|
|
135
|
+
- Disk space of expired documents returns after compaction, and fully only with `wal_retention`; see [Freeing disk](https://github.com/thadeu/ndb/blob/main/docs/dev/configuration.md#freeing-disk).
|
|
136
|
+
|
|
114
137
|
## Query
|
|
115
138
|
|
|
116
139
|
A query is lazy and immutable. It is also thenable: `await` runs it.
|
|
@@ -252,6 +275,7 @@ Daemon errors are `NdbError` or a subclass. They have `code` (the daemon code) a
|
|
|
252
275
|
| `CursorError` | `cursor_invalid`, `cursor_mismatch`, `cursor_expired` |
|
|
253
276
|
| `WatchLaggedError` | `watch_lagged` |
|
|
254
277
|
| `HistoryTruncatedError` | `history_truncated` |
|
|
278
|
+
| `TtlError` | `ttl_not_enabled`, `ttl_derived` |
|
|
255
279
|
| `NdbError` | every other code: `not_ready`, `catalog_conflict`, `catalog_invalid`, `direct_collection`, `invalid_request`, `group_limit_exceeded`, `request_too_large`, `line_too_large`, `closed`, `invalid_position`, `http_status`, ... |
|
|
256
280
|
|
|
257
281
|
```ts
|
package/dist/index.cjs
CHANGED
|
@@ -29,6 +29,7 @@ __export(index_exports, {
|
|
|
29
29
|
NdbError: () => NdbError,
|
|
30
30
|
NotFoundError: () => NotFoundError,
|
|
31
31
|
Query: () => Query,
|
|
32
|
+
TtlError: () => TtlError,
|
|
32
33
|
UnauthorizedError: () => UnauthorizedError,
|
|
33
34
|
UniqueViolationError: () => UniqueViolationError,
|
|
34
35
|
VersionConflictError: () => VersionConflictError,
|
|
@@ -67,6 +68,8 @@ var WatchLaggedError = class extends NdbError {
|
|
|
67
68
|
};
|
|
68
69
|
var HistoryTruncatedError = class extends NdbError {
|
|
69
70
|
};
|
|
71
|
+
var TtlError = class extends NdbError {
|
|
72
|
+
};
|
|
70
73
|
var subclasses = {
|
|
71
74
|
unauthorized: UnauthorizedError,
|
|
72
75
|
not_found: NotFoundError,
|
|
@@ -80,7 +83,9 @@ var subclasses = {
|
|
|
80
83
|
cursor_mismatch: CursorError,
|
|
81
84
|
cursor_expired: CursorError,
|
|
82
85
|
watch_lagged: WatchLaggedError,
|
|
83
|
-
history_truncated: HistoryTruncatedError
|
|
86
|
+
history_truncated: HistoryTruncatedError,
|
|
87
|
+
ttl_not_enabled: TtlError,
|
|
88
|
+
ttl_derived: TtlError
|
|
84
89
|
};
|
|
85
90
|
function makeError(code, message, status = 0) {
|
|
86
91
|
const Cls = subclasses[code] ?? NdbError;
|
|
@@ -274,26 +279,60 @@ var Model = class {
|
|
|
274
279
|
});
|
|
275
280
|
return this.ensured;
|
|
276
281
|
}
|
|
277
|
-
async insert(docs) {
|
|
282
|
+
async insert(docs, opts = {}) {
|
|
278
283
|
await this.ready();
|
|
279
284
|
const list = Array.isArray(docs) ? docs : [docs];
|
|
280
285
|
const out = await this.db.transport.request("POST", this.path("insert"), {
|
|
281
|
-
insert: list
|
|
286
|
+
insert: list,
|
|
287
|
+
expire_in: opts.expireIn
|
|
282
288
|
});
|
|
283
289
|
return out.results;
|
|
284
290
|
}
|
|
285
|
-
create(docs) {
|
|
286
|
-
return this.insert(docs);
|
|
291
|
+
create(docs, opts = {}) {
|
|
292
|
+
return this.insert(docs, opts);
|
|
287
293
|
}
|
|
288
294
|
async update(id, set, opts = {}) {
|
|
289
295
|
await this.ready();
|
|
290
|
-
const update = { id, set, unset: opts.unset, if_version: opts.ifVersion };
|
|
296
|
+
const update = { id, set, unset: opts.unset, if_version: opts.ifVersion, expire_in: opts.expireIn };
|
|
291
297
|
return this.db.transport.request("POST", this.path("update"), { update });
|
|
292
298
|
}
|
|
293
299
|
async delete(id, opts = {}) {
|
|
294
300
|
await this.ready();
|
|
295
301
|
await this.db.transport.request("POST", this.path("delete"), { delete: { id, if_version: opts.ifVersion } });
|
|
296
302
|
}
|
|
303
|
+
// Makes a document expire, like EXPIRE in Redis: seconds or "30m". It throws
|
|
304
|
+
// NotFoundError for a missing or expired document, and TtlError when the
|
|
305
|
+
// schema has no expires field.
|
|
306
|
+
expire(id, duration, opts = {}) {
|
|
307
|
+
return this.setExpiry({ id, in: duration, if_version: opts.ifVersion });
|
|
308
|
+
}
|
|
309
|
+
expireAt(id, at, opts = {}) {
|
|
310
|
+
return this.setExpiry({ id, at: at.toISOString(), if_version: opts.ifVersion });
|
|
311
|
+
}
|
|
312
|
+
// Removes the expiry of a document (PERSIST in Redis).
|
|
313
|
+
async persist(id) {
|
|
314
|
+
await this.ready();
|
|
315
|
+
return this.db.transport.request("POST", this.path("persist"), { id });
|
|
316
|
+
}
|
|
317
|
+
// The seconds left (rounded up), -1 when the document never expires, null
|
|
318
|
+
// when it is missing or expired.
|
|
319
|
+
async ttl(id) {
|
|
320
|
+
await this.ready();
|
|
321
|
+
try {
|
|
322
|
+
const out = await this.db.transport.request("GET", `${this.path("ttl")}/${encodeURIComponent(id)}`);
|
|
323
|
+
return out.ttl;
|
|
324
|
+
} catch (err) {
|
|
325
|
+
if (err instanceof NotFoundError) {
|
|
326
|
+
return null;
|
|
327
|
+
}
|
|
328
|
+
throw err;
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
async setExpiry(body) {
|
|
332
|
+
await this.ready();
|
|
333
|
+
const out = await this.db.transport.request("POST", this.path("expire"), body);
|
|
334
|
+
return { id: out.id, v: out.v, expiresAt: new Date(out.expires_at) };
|
|
335
|
+
}
|
|
297
336
|
find(id) {
|
|
298
337
|
return this.query().where({ [this.idField]: id }).first();
|
|
299
338
|
}
|
|
@@ -374,6 +413,7 @@ var Model = class {
|
|
|
374
413
|
return {
|
|
375
414
|
id: this.schema.id,
|
|
376
415
|
time: this.schema.time,
|
|
416
|
+
expires_field: this.schema.expires,
|
|
377
417
|
indexes: entries.filter(([, d]) => d.index).map(([n]) => n),
|
|
378
418
|
unique: entries.filter(([, d]) => d.unique).map(([n]) => n)
|
|
379
419
|
};
|
|
@@ -589,6 +629,7 @@ function connect(addr, opts) {
|
|
|
589
629
|
NdbError,
|
|
590
630
|
NotFoundError,
|
|
591
631
|
Query,
|
|
632
|
+
TtlError,
|
|
592
633
|
UnauthorizedError,
|
|
593
634
|
UniqueViolationError,
|
|
594
635
|
VersionConflictError,
|
package/dist/index.d.cts
CHANGED
|
@@ -18,6 +18,7 @@ type Fields = Record<string, FieldDef>;
|
|
|
18
18
|
interface Schema<F extends Fields> {
|
|
19
19
|
id?: Extract<keyof F, string>;
|
|
20
20
|
time?: Extract<keyof F, string>;
|
|
21
|
+
expires?: Extract<keyof F, string>;
|
|
21
22
|
fields: F;
|
|
22
23
|
}
|
|
23
24
|
type ValueOf<D extends FieldDef> = TypeMap[D['type']];
|
|
@@ -68,8 +69,16 @@ interface WriteResult {
|
|
|
68
69
|
interface WriteOptions {
|
|
69
70
|
ifVersion?: number;
|
|
70
71
|
}
|
|
72
|
+
type Duration = number | string;
|
|
71
73
|
interface UpdateOptions extends WriteOptions {
|
|
72
74
|
unset?: string[];
|
|
75
|
+
expireIn?: Duration;
|
|
76
|
+
}
|
|
77
|
+
interface InsertOptions {
|
|
78
|
+
expireIn?: Duration;
|
|
79
|
+
}
|
|
80
|
+
interface ExpireResult extends WriteResult {
|
|
81
|
+
expiresAt: Date;
|
|
73
82
|
}
|
|
74
83
|
interface AggregateOptions {
|
|
75
84
|
limit?: number;
|
|
@@ -139,10 +148,15 @@ declare class Model<F extends Fields> {
|
|
|
139
148
|
get idField(): string;
|
|
140
149
|
path(op?: string): string;
|
|
141
150
|
ready(): Promise<void>;
|
|
142
|
-
insert(docs: Input<F> | Input<F>[]): Promise<WriteResult[]>;
|
|
143
|
-
create(docs: Input<F> | Input<F>[]): Promise<WriteResult[]>;
|
|
151
|
+
insert(docs: Input<F> | Input<F>[], opts?: InsertOptions): Promise<WriteResult[]>;
|
|
152
|
+
create(docs: Input<F> | Input<F>[], opts?: InsertOptions): Promise<WriteResult[]>;
|
|
144
153
|
update(id: string, set: Input<F>, opts?: UpdateOptions): Promise<WriteResult>;
|
|
145
154
|
delete(id: string, opts?: WriteOptions): Promise<void>;
|
|
155
|
+
expire(id: string, duration: Duration, opts?: WriteOptions): Promise<ExpireResult>;
|
|
156
|
+
expireAt(id: string, at: Date, opts?: WriteOptions): Promise<ExpireResult>;
|
|
157
|
+
persist(id: string): Promise<WriteResult>;
|
|
158
|
+
ttl(id: string): Promise<number | null>;
|
|
159
|
+
private setExpiry;
|
|
146
160
|
find(id: string): Promise<Row<F> | null>;
|
|
147
161
|
query(): Query<F>;
|
|
148
162
|
where(w: Where<F>): Query<F>;
|
|
@@ -201,7 +215,7 @@ declare class Ndb {
|
|
|
201
215
|
}
|
|
202
216
|
declare function connect(addr?: string, opts?: ConnectOptions): Ndb;
|
|
203
217
|
|
|
204
|
-
type ErrorCode = 'unauthorized' | 'not_found' | 'version_conflict' | 'duplicate_id' | 'unique_violation' | 'catalog_conflict' | 'catalog_invalid' | 'unknown_collection' | 'direct_collection' | 'invalid_request' | 'invalid_query' | 'invalid_pipeline' | 'group_limit_exceeded' | 'cursor_invalid' | 'cursor_mismatch' | 'cursor_expired' | 'request_too_large' | 'line_too_large' | 'not_ready' | 'closed' | 'watch_lagged' | 'invalid_position' | 'history_truncated' | 'http_status' | (string & {});
|
|
218
|
+
type ErrorCode = 'unauthorized' | 'not_found' | 'version_conflict' | 'duplicate_id' | 'unique_violation' | 'catalog_conflict' | 'catalog_invalid' | 'unknown_collection' | 'direct_collection' | 'invalid_request' | 'invalid_query' | 'invalid_pipeline' | 'group_limit_exceeded' | 'cursor_invalid' | 'cursor_mismatch' | 'cursor_expired' | 'request_too_large' | 'line_too_large' | 'not_ready' | 'closed' | 'watch_lagged' | 'invalid_position' | 'history_truncated' | 'ttl_not_enabled' | 'ttl_derived' | 'http_status' | (string & {});
|
|
205
219
|
declare class NdbError extends Error {
|
|
206
220
|
readonly code: ErrorCode;
|
|
207
221
|
readonly status: number;
|
|
@@ -225,7 +239,9 @@ declare class WatchLaggedError extends NdbError {
|
|
|
225
239
|
}
|
|
226
240
|
declare class HistoryTruncatedError extends NdbError {
|
|
227
241
|
}
|
|
242
|
+
declare class TtlError extends NdbError {
|
|
243
|
+
}
|
|
228
244
|
declare function makeError(code: ErrorCode, message: string, status?: number): NdbError;
|
|
229
245
|
|
|
230
|
-
export { CursorError, DuplicateIdError, HistoryTruncatedError, InvalidQueryError, Model, Ndb, NdbError, NotFoundError, Query, UnauthorizedError, UniqueViolationError, VersionConflictError, WatchLaggedError, connect, makeError };
|
|
231
|
-
export type { AggregateOptions, AggregateResult, ConnectOptions, Doc, ErrorCode, FieldDef, FieldType, Fields, Input, Ops, Row, Schema, SortSpec, TypeMap, UpdateOptions, WatchEvent, WatchOptions, Where, WriteOptions, WriteResult };
|
|
246
|
+
export { CursorError, DuplicateIdError, HistoryTruncatedError, InvalidQueryError, Model, Ndb, NdbError, NotFoundError, Query, TtlError, UnauthorizedError, UniqueViolationError, VersionConflictError, WatchLaggedError, connect, makeError };
|
|
247
|
+
export type { AggregateOptions, AggregateResult, ConnectOptions, Doc, Duration, ErrorCode, ExpireResult, FieldDef, FieldType, Fields, Input, InsertOptions, Ops, Row, Schema, SortSpec, TypeMap, UpdateOptions, WatchEvent, WatchOptions, Where, WriteOptions, WriteResult };
|
package/dist/index.d.ts
CHANGED
|
@@ -18,6 +18,7 @@ type Fields = Record<string, FieldDef>;
|
|
|
18
18
|
interface Schema<F extends Fields> {
|
|
19
19
|
id?: Extract<keyof F, string>;
|
|
20
20
|
time?: Extract<keyof F, string>;
|
|
21
|
+
expires?: Extract<keyof F, string>;
|
|
21
22
|
fields: F;
|
|
22
23
|
}
|
|
23
24
|
type ValueOf<D extends FieldDef> = TypeMap[D['type']];
|
|
@@ -68,8 +69,16 @@ interface WriteResult {
|
|
|
68
69
|
interface WriteOptions {
|
|
69
70
|
ifVersion?: number;
|
|
70
71
|
}
|
|
72
|
+
type Duration = number | string;
|
|
71
73
|
interface UpdateOptions extends WriteOptions {
|
|
72
74
|
unset?: string[];
|
|
75
|
+
expireIn?: Duration;
|
|
76
|
+
}
|
|
77
|
+
interface InsertOptions {
|
|
78
|
+
expireIn?: Duration;
|
|
79
|
+
}
|
|
80
|
+
interface ExpireResult extends WriteResult {
|
|
81
|
+
expiresAt: Date;
|
|
73
82
|
}
|
|
74
83
|
interface AggregateOptions {
|
|
75
84
|
limit?: number;
|
|
@@ -139,10 +148,15 @@ declare class Model<F extends Fields> {
|
|
|
139
148
|
get idField(): string;
|
|
140
149
|
path(op?: string): string;
|
|
141
150
|
ready(): Promise<void>;
|
|
142
|
-
insert(docs: Input<F> | Input<F>[]): Promise<WriteResult[]>;
|
|
143
|
-
create(docs: Input<F> | Input<F>[]): Promise<WriteResult[]>;
|
|
151
|
+
insert(docs: Input<F> | Input<F>[], opts?: InsertOptions): Promise<WriteResult[]>;
|
|
152
|
+
create(docs: Input<F> | Input<F>[], opts?: InsertOptions): Promise<WriteResult[]>;
|
|
144
153
|
update(id: string, set: Input<F>, opts?: UpdateOptions): Promise<WriteResult>;
|
|
145
154
|
delete(id: string, opts?: WriteOptions): Promise<void>;
|
|
155
|
+
expire(id: string, duration: Duration, opts?: WriteOptions): Promise<ExpireResult>;
|
|
156
|
+
expireAt(id: string, at: Date, opts?: WriteOptions): Promise<ExpireResult>;
|
|
157
|
+
persist(id: string): Promise<WriteResult>;
|
|
158
|
+
ttl(id: string): Promise<number | null>;
|
|
159
|
+
private setExpiry;
|
|
146
160
|
find(id: string): Promise<Row<F> | null>;
|
|
147
161
|
query(): Query<F>;
|
|
148
162
|
where(w: Where<F>): Query<F>;
|
|
@@ -201,7 +215,7 @@ declare class Ndb {
|
|
|
201
215
|
}
|
|
202
216
|
declare function connect(addr?: string, opts?: ConnectOptions): Ndb;
|
|
203
217
|
|
|
204
|
-
type ErrorCode = 'unauthorized' | 'not_found' | 'version_conflict' | 'duplicate_id' | 'unique_violation' | 'catalog_conflict' | 'catalog_invalid' | 'unknown_collection' | 'direct_collection' | 'invalid_request' | 'invalid_query' | 'invalid_pipeline' | 'group_limit_exceeded' | 'cursor_invalid' | 'cursor_mismatch' | 'cursor_expired' | 'request_too_large' | 'line_too_large' | 'not_ready' | 'closed' | 'watch_lagged' | 'invalid_position' | 'history_truncated' | 'http_status' | (string & {});
|
|
218
|
+
type ErrorCode = 'unauthorized' | 'not_found' | 'version_conflict' | 'duplicate_id' | 'unique_violation' | 'catalog_conflict' | 'catalog_invalid' | 'unknown_collection' | 'direct_collection' | 'invalid_request' | 'invalid_query' | 'invalid_pipeline' | 'group_limit_exceeded' | 'cursor_invalid' | 'cursor_mismatch' | 'cursor_expired' | 'request_too_large' | 'line_too_large' | 'not_ready' | 'closed' | 'watch_lagged' | 'invalid_position' | 'history_truncated' | 'ttl_not_enabled' | 'ttl_derived' | 'http_status' | (string & {});
|
|
205
219
|
declare class NdbError extends Error {
|
|
206
220
|
readonly code: ErrorCode;
|
|
207
221
|
readonly status: number;
|
|
@@ -225,7 +239,9 @@ declare class WatchLaggedError extends NdbError {
|
|
|
225
239
|
}
|
|
226
240
|
declare class HistoryTruncatedError extends NdbError {
|
|
227
241
|
}
|
|
242
|
+
declare class TtlError extends NdbError {
|
|
243
|
+
}
|
|
228
244
|
declare function makeError(code: ErrorCode, message: string, status?: number): NdbError;
|
|
229
245
|
|
|
230
|
-
export { CursorError, DuplicateIdError, HistoryTruncatedError, InvalidQueryError, Model, Ndb, NdbError, NotFoundError, Query, UnauthorizedError, UniqueViolationError, VersionConflictError, WatchLaggedError, connect, makeError };
|
|
231
|
-
export type { AggregateOptions, AggregateResult, ConnectOptions, Doc, ErrorCode, FieldDef, FieldType, Fields, Input, Ops, Row, Schema, SortSpec, TypeMap, UpdateOptions, WatchEvent, WatchOptions, Where, WriteOptions, WriteResult };
|
|
246
|
+
export { CursorError, DuplicateIdError, HistoryTruncatedError, InvalidQueryError, Model, Ndb, NdbError, NotFoundError, Query, TtlError, UnauthorizedError, UniqueViolationError, VersionConflictError, WatchLaggedError, connect, makeError };
|
|
247
|
+
export type { AggregateOptions, AggregateResult, ConnectOptions, Doc, Duration, ErrorCode, ExpireResult, FieldDef, FieldType, Fields, Input, InsertOptions, Ops, Row, Schema, SortSpec, TypeMap, UpdateOptions, WatchEvent, WatchOptions, Where, WriteOptions, WriteResult };
|
package/dist/index.js
CHANGED
|
@@ -27,6 +27,8 @@ var WatchLaggedError = class extends NdbError {
|
|
|
27
27
|
};
|
|
28
28
|
var HistoryTruncatedError = class extends NdbError {
|
|
29
29
|
};
|
|
30
|
+
var TtlError = class extends NdbError {
|
|
31
|
+
};
|
|
30
32
|
var subclasses = {
|
|
31
33
|
unauthorized: UnauthorizedError,
|
|
32
34
|
not_found: NotFoundError,
|
|
@@ -40,7 +42,9 @@ var subclasses = {
|
|
|
40
42
|
cursor_mismatch: CursorError,
|
|
41
43
|
cursor_expired: CursorError,
|
|
42
44
|
watch_lagged: WatchLaggedError,
|
|
43
|
-
history_truncated: HistoryTruncatedError
|
|
45
|
+
history_truncated: HistoryTruncatedError,
|
|
46
|
+
ttl_not_enabled: TtlError,
|
|
47
|
+
ttl_derived: TtlError
|
|
44
48
|
};
|
|
45
49
|
function makeError(code, message, status = 0) {
|
|
46
50
|
const Cls = subclasses[code] ?? NdbError;
|
|
@@ -234,26 +238,60 @@ var Model = class {
|
|
|
234
238
|
});
|
|
235
239
|
return this.ensured;
|
|
236
240
|
}
|
|
237
|
-
async insert(docs) {
|
|
241
|
+
async insert(docs, opts = {}) {
|
|
238
242
|
await this.ready();
|
|
239
243
|
const list = Array.isArray(docs) ? docs : [docs];
|
|
240
244
|
const out = await this.db.transport.request("POST", this.path("insert"), {
|
|
241
|
-
insert: list
|
|
245
|
+
insert: list,
|
|
246
|
+
expire_in: opts.expireIn
|
|
242
247
|
});
|
|
243
248
|
return out.results;
|
|
244
249
|
}
|
|
245
|
-
create(docs) {
|
|
246
|
-
return this.insert(docs);
|
|
250
|
+
create(docs, opts = {}) {
|
|
251
|
+
return this.insert(docs, opts);
|
|
247
252
|
}
|
|
248
253
|
async update(id, set, opts = {}) {
|
|
249
254
|
await this.ready();
|
|
250
|
-
const update = { id, set, unset: opts.unset, if_version: opts.ifVersion };
|
|
255
|
+
const update = { id, set, unset: opts.unset, if_version: opts.ifVersion, expire_in: opts.expireIn };
|
|
251
256
|
return this.db.transport.request("POST", this.path("update"), { update });
|
|
252
257
|
}
|
|
253
258
|
async delete(id, opts = {}) {
|
|
254
259
|
await this.ready();
|
|
255
260
|
await this.db.transport.request("POST", this.path("delete"), { delete: { id, if_version: opts.ifVersion } });
|
|
256
261
|
}
|
|
262
|
+
// Makes a document expire, like EXPIRE in Redis: seconds or "30m". It throws
|
|
263
|
+
// NotFoundError for a missing or expired document, and TtlError when the
|
|
264
|
+
// schema has no expires field.
|
|
265
|
+
expire(id, duration, opts = {}) {
|
|
266
|
+
return this.setExpiry({ id, in: duration, if_version: opts.ifVersion });
|
|
267
|
+
}
|
|
268
|
+
expireAt(id, at, opts = {}) {
|
|
269
|
+
return this.setExpiry({ id, at: at.toISOString(), if_version: opts.ifVersion });
|
|
270
|
+
}
|
|
271
|
+
// Removes the expiry of a document (PERSIST in Redis).
|
|
272
|
+
async persist(id) {
|
|
273
|
+
await this.ready();
|
|
274
|
+
return this.db.transport.request("POST", this.path("persist"), { id });
|
|
275
|
+
}
|
|
276
|
+
// The seconds left (rounded up), -1 when the document never expires, null
|
|
277
|
+
// when it is missing or expired.
|
|
278
|
+
async ttl(id) {
|
|
279
|
+
await this.ready();
|
|
280
|
+
try {
|
|
281
|
+
const out = await this.db.transport.request("GET", `${this.path("ttl")}/${encodeURIComponent(id)}`);
|
|
282
|
+
return out.ttl;
|
|
283
|
+
} catch (err) {
|
|
284
|
+
if (err instanceof NotFoundError) {
|
|
285
|
+
return null;
|
|
286
|
+
}
|
|
287
|
+
throw err;
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
async setExpiry(body) {
|
|
291
|
+
await this.ready();
|
|
292
|
+
const out = await this.db.transport.request("POST", this.path("expire"), body);
|
|
293
|
+
return { id: out.id, v: out.v, expiresAt: new Date(out.expires_at) };
|
|
294
|
+
}
|
|
257
295
|
find(id) {
|
|
258
296
|
return this.query().where({ [this.idField]: id }).first();
|
|
259
297
|
}
|
|
@@ -334,6 +372,7 @@ var Model = class {
|
|
|
334
372
|
return {
|
|
335
373
|
id: this.schema.id,
|
|
336
374
|
time: this.schema.time,
|
|
375
|
+
expires_field: this.schema.expires,
|
|
337
376
|
indexes: entries.filter(([, d]) => d.index).map(([n]) => n),
|
|
338
377
|
unique: entries.filter(([, d]) => d.unique).map(([n]) => n)
|
|
339
378
|
};
|
|
@@ -548,6 +587,7 @@ export {
|
|
|
548
587
|
NdbError,
|
|
549
588
|
NotFoundError,
|
|
550
589
|
Query,
|
|
590
|
+
TtlError,
|
|
551
591
|
UnauthorizedError,
|
|
552
592
|
UniqueViolationError,
|
|
553
593
|
VersionConflictError,
|