Docs
  • 0.38.1latest
  • 0.37.1
  • 0.37.0
  • 0.36.2
  • 0.36.1
  • 0.36.0
  • 0.35.3
  • 0.35.2
  • 0.35.1
  • 0.35.0
  • 0.34.4
  • 0.34.3-beta
  • 0.34.2-beta
  • 0.34.1-beta
  • 0.34.0-beta
  • 0.33.4-beta
  • 0.33.3-beta
  • 0.33.2-beta
  • 0.33.1-beta
  • 0.33.0-beta
  • 0.32.0-beta

Lists, hashes, sets and rankings

A key may hold a list, a hash, a set of members or members ranked by score, each changed by a statement that reads and writes the key atomically.

A key in a space may hold a container: a list, a hash of fields, a set of members, or members ranked by a score. Each is changed by a statement of its own, which reads the key and writes it atomically, the way INCR does. These are the statements the RESP surface runs for LPUSH, HSET, SADD and ZADD.

DEFINE NAMESPACE app;
USE NAMESPACE app;
DEFINE DATABASE web;
USE DATABASE web;
DEFINE SPACE data;

Lists

LIST PUSH data:'jobs' BACK 'a', 'b';
LIST PUSH data:'jobs' FRONT 'z';
LIST POP data:'jobs' FRONT;
LIST INSERT data:'jobs' 'x' BEFORE 'b';
GET data:'jobs';

['a', 'x', 'b']. LIST PUSH answers the new length, LIST POP the element, or an array of them with LIMIT n. FRONT pushes each value in turn, so the last one named ends first.

Indexes count from zero at the front and from -1 at the back: LIST SET data:'jobs' AT -1 = 'last', LIST TRIM data:'jobs' FROM 0 TO 99 (both ends kept). LIST REMOVE … LIMIT n FROM BACK removes matching elements. LIST MOVE data:'jobs' BACK TO data:'done' FRONT moves one element between two keys in one statement.

Hashes

HASH SET data:'cart:u1' 'apples' = 3, 'pears' = 1;
HASH INCR data:'cart:u1' 'apples' BY 2;
HASH SET data:'cart:u1' 'plums' = 2 IF ABSENT;
HASH REMOVE data:'cart:u1' 'pears';
GET data:'cart:u1';

{ apples: 5, plums: 2 }. A hash is an object of fields, so (GET data:'cart:u1').apples reads one field, and HASH INCR answers the new number.

A field may expire on its own:

HASH EXPIRE data:'cart:u1' 'plums' AT 30m IF NONE;
HASH TTL data:'cart:u1' 'plums', 'apples';

HASH EXPIRE answers one number per field: -2 no such field, 0 the condition did not hold, 1 the instant was set, 2 removed at once because the instant was not in the future. IF NONE, IF SOME, IF LATER and IF SOONER are the conditions. HASH TTL answers the time left, NULL for a field that never expires and NONE for no field.

A field past its instant is left out of every read, and a hash whose fields have all expired is no key at all. Writing a field again drops its instant. Giving a field an instant needs a store at format 9.

Sets of members

MEMBERS ADD data:'tags:post:7' 'rust', 'db', 'search';
MEMBERS REMOVE data:'tags:post:7' 'search';
MEMBERS MOVE data:'tags:post:7' 'rust' TO data:'tags:post:8';
GET data:'tags:post:7';

ADD and REMOVE answer how many members they added or removed, and MOVE whether it moved one. MEMBERS POP … LIMIT n takes members at random.

Rankings

SCORES ADD data:'board' 'ada' = 10, 'bob' = 7, 'cy' = 1;
SCORES ADD data:'board' 'bob' = 9 IF GREATER CHANGED;
SCORES INCR data:'board' 'ada' BY 1.5;
SCORES POP data:'board' HIGHEST LIMIT 2;

[['ada', 11.5], ['bob', 9.0]]. A ranking is a set of [score, member] pairs, so its own order — score, then member — is the ranking, lowest first.

SCORES REMOVE removes by member, or by range: RANK FROM 0 TO -11, SCORE FROM NONE TO 2 EXCLUSIVE or MEMBER FROM 'a' TO 'c'. NONE leaves an end open, and EXCLUSIVE leaves it out.

Rules all four share

  • A container is an ordinary value. GET reads the whole of it, and array::len(GET data:'jobs') a list's length, in any TessariQL expression.
  • Emptying a container deletes the key. No key holds an empty list, hash or set. A write keeps the expiry the key had, as INCR does.
  • Two writers cannot both win. Run on its own, a statement that loses a race runs again on a fresh snapshot, so eight writers pushing onto one list land every element, none of them twice.
  • The wrong kind is refused. A statement on a key holding another kind is refused as KeyHoldsAnotherKind, naming both kinds.
  • A container is one value. Each write re-encodes the whole container. The statements suit the containers a cache keeps, not ones of millions of elements.
LIST PUSH data:'cart:u1' BACK 'x';

KeyHoldsAnotherKind: cart:u1 holds a hash.