You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: site/capabilities/live-queries.md
+34-35Lines changed: 34 additions & 35 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -13,7 +13,7 @@ You get back a *live view* of your query — in realtime.
13
13
Live queries are a first-class concept in LinkedQL.
14
14
They happen over the same `client.query()` API.
15
15
16
-
The `.query()` method does what you expect. But for a `SELECT`query, it also works in **live mode**:
16
+
The `client.query()` method simply additionally supports an `options.live` parameter for `SELECT`queries:
17
17
18
18
```js
19
19
constresult=awaitclient.query(`SELECT * FROM posts`, { live:true });
@@ -153,7 +153,7 @@ Essentially, LinkedQL extends reactivity to the full semantic surface of `SELECT
153
153
154
154
## Live Views in Detail
155
155
156
-
`result.rows` is a self-updating array of objects — each element reflecting a row in real time.
156
+
`result.rows` is a self-updating array of objects — each result row in the array reflecting the database in real time.
157
157
158
158
For a query like the below:
159
159
@@ -194,7 +194,7 @@ UPDATE posts SET title = 'Hello Again' WHERE title = 'Hello World';
194
194
[A, B, C^, D, E] // C updated
195
195
```
196
196
197
-
When a row is deleted in the database, the corresponding row in the view disappears:
197
+
When a row is deleted in the database, the corresponding row in the view leaves the view:
198
198
199
199
```sql
200
200
DELETEFROM posts WHERE title ='Hello Again';
@@ -218,19 +218,18 @@ const result = await client.query(
218
218
);
219
219
```
220
220
221
-
Database-level mutations — `INSERT`, `UPDATE`, or `DELETE` — on either side of a join can affect the **join relationship** itself.<br>
222
-
A right-hand or left-hand side that once matched may suddenly match no more, or the reverse may be the case.
221
+
Database-level mutations — `INSERT`, `UPDATE`, or `DELETE` — have a deeper semantic effect in the view. Given that rows are composed of data from multiple tables, each row in the view has to obey the join semantics as mutations happen on any of the underlying tables. A right-hand or left-hand side of a join that once matched may suddenly match no more, and the reverse may be the case.
223
222
224
223
This means the result of an event like `INSERT` or `DELETE` may not always mean “add”, or “remove”, a row in the view.
225
-
It might instead mean: *a row has transited from "no matching right-hand side" to "fully materialized"*, or the reverse.<br>
226
-
This is **Join Transition**.
224
+
It might instead mean: *a row has transited from "no matching right-hand side" to "a fully materialized join"*, or the reverse.<br>
225
+
This is treated in LinkedQL as **Join Transition**.
227
226
228
-
Join transitions would normally be observed as a "delete" + "add" effect — existing composition dissolves and a new one emerges.
227
+
If interpreted naively, join transitions would be observed as a "delete" + "add" effect — that is, the existing composition dissolves and a new one emerges.
229
228
But LinkedQL is designed to detect these phenomena and properly communicate them as **in-place updates**, preserving continuity and identity.
230
229
231
230
Observers see an in-place update, not a *teardown + recreate* sequence:
232
231
233
-
#### Example 1 — `INSERT` causes a join to materialize
232
+
#### Scenario 1 — An`INSERT` causes a join to materialize
Essentially, LinkedQL interprets database mutations through the lens of query semantics —
278
-
thus, join compositions remain continuous relationships over time.
274
+
*Overal:* identity persists. The view stays true the join relationships without letting the underlying mutaions leak into the view.
279
275
280
276
### Frames and Ordinality
281
277
282
-
Queries that have ordering, limits, or offsets applied are materialized with the semantics of each modifier automatically maintained in the view.
278
+
Queries that have ordering, limits, or offsets applied materialize in the view with the semantics of each modifier fully maintained.
283
279
284
280
```js
285
281
consttop5= (awaitclient.query(
@@ -299,13 +295,13 @@ Initially:
299
295
[A, B, C, D, E] // initial result
300
296
```
301
297
302
-
Then on "_`INSERT` a new row `N`_":
298
+
Then on "_`INSERT`ing a new row `N`_":
303
299
304
300
```text
305
301
[N, A, B, C, D] // N enters the view; E falls off because it’s now #6
306
302
```
307
303
308
-
Then on "_`UPDATE` a post’s `created_at`_":
304
+
Then on "_`UPDATE`ing a post’s `created_at` field and promoting it one step higher in the list_":
309
305
310
306
```text
311
307
[N, A, C, B, D] // C and B swap places without initiating a full re-ordering
@@ -315,9 +311,7 @@ Essentially, ordering and slicing remain stable relationships — they evolve as
315
311
316
312
### Precision and Granularity
317
313
318
-
Live updates apply the smallest possible change needed to keep the view correct. This reflects a key design goal in LinkedQL: precision and granularity.
319
-
320
-
This guarantees two things:
314
+
Live updates apply the smallest possible change needed to keep the view correct. This is a key design goal in LinkedQL.
321
315
322
316
**(a) Field-level updates**
323
317
@@ -349,7 +343,7 @@ After: [A, C, B, D, E]
349
343
350
344
**Why that matters:**
351
345
352
-
Precision and granularity help keep costs low across consumers bound to the view. When rendering on the UI, for example:
346
+
Precision and granularity keeps the system – all the way to the consumers bound to the view – highly efficient. When rendering on the UI, for example:
353
347
354
348
* The UI maintains state, avoids unnecessary rerenders, and never flickers.
355
349
* Components keyed by row identity keep their state.
@@ -361,23 +355,25 @@ Live views are not just auto-updating — they are also **observable**.
361
355
362
356
LinkedQL exposes them through the [Observer API](https://github.com/webqit/observer). Observer is a general-purpose JavaScript API for observing object and array-level mutations.
363
357
364
-
You pass a callback to observe root-level changes — which, for `result.rows`, would mean row additions and deletions:
358
+
This makes `result.rows` observable like any object.
Observer guarantees that events are delivered with the atomicity of the underlying database transactions. In other words, all mutations that happen inside a single database transaction arrive together in one callback turn.
376
+
LinkedQL leverages Observer's batching feature to preserve the atomicity of the database transactions behind the emitted events. It guarantees that all mutations that happen inside a single database transaction arrive together in one callback turn.
Essentially, transactions aren't torn across multiple emissions.
407
403
408
404
### Live Bindings
409
405
410
406
LinkedQL’s live views are ordinary JavaScript objects and arrays. They simply happen to mutate over time as the database changes.
411
407
412
-
And here’s how that plays out across runtimes: because they use the [Observer API](https://github.com/webqit/observer) protocol, you get automatic binding and mutation-based reactivity across contexts or runtimes where mutations are a first-class concept.
408
+
Those mutations themselves are the basis for reactivity in the design. Because they happen via the [Observer API](https://github.com/webqit/observer) protocol, you get automatic binding and mutation-based reactivity across contexts or runtimes where mutations are a first-class concept.
413
409
414
-
For example, with the[ Webflo framework](https://github.com/webqit/webflo)’s *[live response](https://webflo.netlify.app/docs/concepts/realtime#live-responses)* capability, `result.rows` — like any object — can be returned from a route, with reactivity preserved over the wire.
410
+
For example, with the[ Webflo framework](https://github.com/webqit/webflo)’s *[live response](https://webflo.netlify.app/docs/concepts/realtime#live-responses)* capability, `result.rows` — like any object — can be returned from a route as live response, with reactivity preserved over the wire.
If the goal is to render, that, too, comes automatic: [OOHTML](https://github.com/webqit/oohtml) gives you automatic data-binding over arbitrary objects and arrays — without a compile step:
432
+
These live objects automatically bind to UI in any mutation-based data-binding framework like [OOHTML](https://github.com/webqit/oohtml). OOHTML is an addition to the DOM that brings mutation-based reactivity to the UI — without a compile step:
The UI updates as posts are added or removed — with no glue code. (List rendering has been omitted here for brevity. Try updating the posts table from a terminal to see the UI update the total count.)
439
+
The UI in this example updates as posts are added or removed — with no glue code. (List rendering has been omitted here for brevity.)
440
+
441
+
> [!TIP]
442
+
> Try updating the posts table from a terminal to see the UI update the total count.
444
443
445
444
Essentially, with the Observer protocol as the shared vocabulary of change, continuity stays intact from database to DOM. Each layer in the chain — LinkedQL → Webflo → OOHTML — simply makes or reacts to mutations.
446
445
@@ -453,12 +452,12 @@ That event stream is made of three event types:
|`result`| A full snapshot of the query result – for when diffrential updates aren't feasible for the qiven query – typically queries with aggregates. |
457
456
|`diff`| Incremental inserts, updates, and deletes. |
458
457
|`swap`| Positional swaps that satisfy an `ORDER BY` clause |
459
458
460
459
You can subscribe to these events directly and maintain your own state store.
461
-
This is useful if you’re building a custom cache, animation layer, or replication layer.
460
+
This is useful if you’re building a custom cache, or replication layer.
462
461
463
462
```js
464
463
// Get a handle to the live query
@@ -536,11 +535,11 @@ That logic is **conceptually** what the built-in [`RealtimeResult`](../docs/quer
536
535
* preserves ordering and LIMIT/OFFSET semantics;
537
536
* exposes the final live state as `result.rows`.
538
537
539
-
Compared to the default live view concept, custom event handling sits closer to the wire — meant for systems that need explicit control, like caches or replication layers.
538
+
Compared to the default live view concept, custom event handling sits closer to the wire.
540
539
541
540
## Query Inheritance
542
541
543
-
Live queries are efficient because LinkedQL does not treat each subscription as an isolated process.
542
+
Live queries are highly efficient because LinkedQL does not treat each subscription as an isolated process.
544
543
Instead, LinkedQL groups overlapping queries into a shared structure called a **query inheritance tree**.
0 commit comments