stratus-db 0.3.0 → 0.5.0

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.
Files changed (3) hide show
  1. package/README.md +10 -1
  2. package/package.json +1 -1
  3. package/stratus-db.js +42 -12
package/README.md CHANGED
@@ -57,7 +57,8 @@ Gateway's 403 Forbidden for a brand-new key, which works within a minute or two
57
57
  - Catalog: `listDbs({deleted})`, `dbByName(name)`, `dbRecord(id)`, `createDb({name, primaryKey})`,
58
58
  `renameDb(id, name)`, `deleteDb(id)`.
59
59
  - A database: `status(id)`, `transact(id, {tx, retract, delete, txId, match})`, `query(id, q)`,
60
- `queryAll(id, q)` (async iterator), `pull(id, pks, {asOf})` (and `asOf: t` in a query: the database as it stood after t), `requestIndex(id, att)`,
60
+ `queryAll(id, q)` (async iterator), `pull(id, pks, {asOf})` (and `asOf: t` in a query: the database as it stood after t), `requestIndex(id, att, {required})` (att an attribute, or an array of 2 to 8 for a
61
+ multi-attribute index: queried by tuples, `["=", ["a", "b"], [x, y]]`; docs/query.md),
61
62
  `awaitIndex(id, att)`, `dropIndex(id, att)`, `history(id, {from, limit})`, `historyAll`,
62
63
  `fork(id, {t, name})` (a new database, this one as of t), `excise(id, {rows,
63
64
  attributes})` (erasure: rows and {pk, att} out of the state, indexes, history and forks),
@@ -65,6 +66,14 @@ Gateway's 403 Forbidden for a brand-new key, which works within a minute or two
65
66
  - Keys (with a key reaching every database): `listKeys()`, `createKey({name, scope})`,
66
67
  `revokeKey(keyId)`.
67
68
  - Codec: `encode`, `decode` (typed-JSON text), `toJSONValue`, `fromJSONValue`.
69
+ - `rowId(n)`: a row named by its row id.
70
+
71
+ **Keys and row ids.** A primary key is a UUID (`squuid()`), a string or an integer (a long:
72
+ past 2^53, pass a `BigInt`). Every row also carries `"stratus/id"`, a row id the database assigns
73
+ when the row is made. `transact` answers `{ t, pks, ids }`. Where a key goes - `pull`, `delete`,
74
+ a row's key attribute - `rowId(n)` names a row by its id; a bare integer is always a primary key.
75
+ Queries return rows in row id order (the order they were made), `{ order: "desc" }` newest first;
76
+ `next` is a row id.
68
77
 
69
78
  Queries: docs/query.md ("JSON"). Requests go to `<url>/v1/dbs/<id>/...` with `Authorization:
70
79
  Bearer <apiKey>`.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "stratus-db",
3
- "version": "0.3.0",
3
+ "version": "0.5.0",
4
4
  "description": "The stratus-db service from JavaScript: typed JSON over fetch, no dependencies.",
5
5
  "homepage": "https://stratus-db.com",
6
6
  "license": "Apache-2.0",
package/stratus-db.js CHANGED
@@ -15,7 +15,7 @@
15
15
  // "Authorization: Bearer <key>".
16
16
 
17
17
  /** The client's version: package.json's, which release-js.yml checks against it. */
18
- export const VERSION = "0.3.0";
18
+ export const VERSION = "0.5.0";
19
19
 
20
20
  const USER_AGENT = `stratus-db-js/${VERSION}`;
21
21
  const IN_NODE = typeof process !== "undefined" && process.versions != null && process.versions.node != null;
@@ -269,6 +269,13 @@ export const localDate = (text) => LocalDate.parse(text);
269
269
  export const double = (v) => new Double(v);
270
270
  export const list = (...items) => List.from(items);
271
271
 
272
+ /**
273
+ * A row id, tagged: { "stratus/id": n }. Every row carries its row id (row["stratus/id"]), a
274
+ * long the database assigns when the row is made; a command or a pull names a row by it where a
275
+ * bare integer would be a primary key.
276
+ */
277
+ export const rowId = (n) => ({ "stratus/id": n });
278
+
272
279
  // ------------------------------------------------------------------ Typed JSON
273
280
  // Typed JSON (docs/json.md): stratus's encoding, MongoDB Extended JSON v2's tags where they
274
281
  // mean the same and stratus's own where it has none. encode/decode work on JSON text;
@@ -529,6 +536,15 @@ function queryString(params) {
529
536
 
530
537
  function attName(att) { return att instanceof Keyword ? att.fullName : String(att).replace(/^:/, ""); }
531
538
 
539
+ // An index's name: an attribute's, or a multi-attribute index's (an array) joined by commas -
540
+ // how the status keys it, and the DELETE path names it.
541
+ function indexName(att) { return Array.isArray(att) ? att.map(attName).join(",") : attName(att); }
542
+
543
+ // The DELETE path of an index: each name's parts encoded, its slashes and commas kept.
544
+ function indexPath(att) {
545
+ return indexName(att).split(",").map((n) => n.split("/").map(encodeURIComponent).join("/")).join(",");
546
+ }
547
+
532
548
  /** A client of the stratus service at opts.url (see the module's comment for opts). */
533
549
  export function connect(opts) {
534
550
  return new Client(opts);
@@ -664,9 +680,11 @@ export class Client {
664
680
  }
665
681
 
666
682
  /**
667
- * Transacts: { t, pks, duplicate? }. tx: rows (each with its primary key: squuid()),
668
- * retract: rows of attributes to remove, delete: primary keys, txId: makes a retry safe,
669
- * match: rows whose attributes must hold those values now (else a 409 "conflict").
683
+ * Transacts: { t, pks, ids, duplicate? } - pks and ids are tx's rows' primary keys and row ids,
684
+ * in order. tx: rows (each with its primary key: squuid(); or naming its row by rowId(n) in
685
+ * the key's place), retract: rows of attributes to remove, delete: primary keys (or rowId(n)),
686
+ * txId: makes a retry safe, match: rows whose attributes must hold those values now (else a
687
+ * 409 "conflict").
670
688
  */
671
689
  async transact(id, { tx, retract, delete: del, txId, match }) {
672
690
  const body = {};
@@ -678,7 +696,11 @@ export class Client {
678
696
  return Client.ok(await this.callDb(id, "POST", "/transact", body));
679
697
  }
680
698
 
681
- /** One page of query q (docs/query.md, "JSON"): { rows, next, t }. */
699
+ /**
700
+ * One page of query q (docs/query.md, "JSON"): { rows, next, t } - rows in row id order (the
701
+ * order they were made; { order: "desc" }: newest first), next the cursor (a row id) to pass
702
+ * as after.
703
+ */
682
704
  async query(id, q = {}) {
683
705
  const { asOf, ...rest } = q;
684
706
  return Client.ok(await this.callDb(id, "POST", "/query", asOf === undefined ? rest : { ...rest, "as-of": asOf }));
@@ -696,25 +718,33 @@ export class Client {
696
718
  }
697
719
 
698
720
  /**
699
- * The rows of primary keys pks, in order (null where there is none); { asOf: t } reads them as
700
- * they stood after transaction t.
721
+ * The rows of pks (primary keys, or rowId(n)), in order (null where there is none); { asOf: t }
722
+ * reads them as they stood after transaction t.
701
723
  */
702
724
  async pull(id, pks, { asOf } = {}) {
703
725
  return (Client.ok(await this.callDb(id, "POST", "/pull", asOf === undefined ? { pks } : { pks, "as-of": asOf }))).rows;
704
726
  }
705
727
 
706
- /** Requests a value index on att: { att, state } ("building" until a checkpoint builds it). */
707
- async requestIndex(id, att) {
708
- return Client.ok(await this.callDb(id, "POST", "/indexes", { att: attName(att) }));
728
+ /**
729
+ * Requests a value index on att: { att, state } ("building" until a checkpoint builds it).
730
+ * att is an attribute, or an array of 2 to 8 for a multi-attribute index (its rows keyed by
731
+ * the tuple of their values; queried by tuple predicates), with { required }: the attributes
732
+ * a row needs a value of to be in it.
733
+ */
734
+ async requestIndex(id, att, { required } = {}) {
735
+ const body = { att: Array.isArray(att) ? att.map(attName) : attName(att) };
736
+ if (required !== undefined) body.required = required.map(attName);
737
+ return Client.ok(await this.callDb(id, "POST", "/indexes", body));
709
738
  }
710
739
 
740
+ /** Drops att's index (an attribute, or a multi-attribute index's array). */
711
741
  async dropIndex(id, att) {
712
- return Client.ok(await this.callDb(id, "DELETE", `/indexes/${attName(att).split("/").map(encodeURIComponent).join("/")}`));
742
+ return Client.ok(await this.callDb(id, "DELETE", `/indexes/${indexPath(att)}`));
713
743
  }
714
744
 
715
745
  /** Waits until att's index is ready (rejects if it failed, or after timeoutMs). */
716
746
  async awaitIndex(id, att, { timeoutMs = 60000, intervalMs = 200 } = {}) {
717
- const name = attName(att);
747
+ const name = indexName(att);
718
748
  const deadline = Date.now() + timeoutMs;
719
749
  for (;;) {
720
750
  const s = await this.status(id);