Queries & Live-Daten
Einmal lesen. Oder live bleiben.
Abschnitt betitelt „Einmal lesen. Oder live bleiben.“Erstelle einen Client mit deinem Schema, der Worker-URL, einer Workspace-ID und einem Token-Callback. Deine Anwendung liefert diese Verbindungswerte.
import { createClient } from "tapedb";import schema from "./tapedb.schema";
const db = createClient({ schema, url, workspace, getToken });
const open = await db.task.findMany({ where: { done: false, due: { lt: Date.now() } }, orderBy: { due: "asc" }, take: 50, include: { column: { include: { board: true } } },});findMany gibt jede passende Zeile zurück, sofern du kein take übergibst; take und findPage haben keine Obergrenze. Breite Reads laufen unter der Haube als begrenzte Statements: Keyset-Chunks von 1.000 Zeilen, wobei Includes 1.000 Eltern gleichzeitig hydratisieren.
Eine Zeile, ein Count, eine Seite
Abschnitt betitelt „Eine Zeile, ein Count, eine Seite“const one = await db.task.findUnique({ where: { id } }); // Zeile oder null
const total = await db.task.count({ where: { done: false } });
const page = await db.task.findPage({ orderBy: { due: "asc" }, take: 50 });// { rows, hasMore, cursor } — Cursor als `after` zurückgeben
const first = await db.task.findFirst({ where: { done: false } }); // erster Treffer oder nullPaginierung ist Keyset, daher kann ein Insert oberhalb des Fensters keine Zeile wiederholen.
Select und Include
Abschnitt betitelt „Select und Include“select gibt nur die benannten Felder zurück; Relationen in select bekommen ihr eigenes select:
const titles = await db.task.findMany({ select: { title: true, column: { select: { name: true } } },});// { title: string; column: { name: string } | null }[]Die Engine liest und sendet nur diese Spalten, plus die ID der Zeile, auf die Live-Updates schlüsseln. Ein Live-Select bekommt nur dann ein Update, wenn sich ein ausgewähltes Feld geändert hat. select und include werden nicht auf derselben Ebene kombiniert.
Aggregates
Abschnitt betitelt „Aggregates“Aggregates nehmen dieselbe where und geben nur die gefragten Maße zurück:
const stats = await db.task.aggregate({ where: { done: false }, _count: true, // oder { _all: true, due: true } für Non-Null-Counts _sum: { points: true }, _avg: { points: true }, _min: { due: true }, _max: { due: true },});
const perColumn = await db.task.groupBy({ by: ["columnId"], _count: true, _sum: { points: true }, orderBy: { columnId: "asc" }, take: 20,});Sie laufen als ein GROUP BY-Statement über die Zeilen, die der Aufrufer lesen darf. Eine filter()-Leseregel wird hineinkompiliert; ein Modell mit einer Funktion als Leseregel wird Zeile für Zeile angewendet, nachdem die Regel lief. Counts und Aggregates sind live mit subscribe.
Filter unterstützen not, in, notIn, gt, gte, lt, lte sowie AND / OR / NOT über ganze Filter.
Eine to-one-Relation nimmt einen Filter auf die Zeile, auf die sie zeigt; eine to-many-Relation nimmt some, every oder none:
await db.column.findMany({ where: { board: { archived: false } } });await db.board.findMany({ where: { columns: { some: { tasks: { none: {} } } } } });await db.task.findMany({ where: { column: null } }); // kein ParentDiese kompilieren zu korrelierten Subqueries, verknüpfte Zeilen werden also nie abgerufen. Die Leseregel des verknüpften Modells wird in die Subquery kompiliert; ein Modell mit einer Funktion als Leseregel wird als Filterziel abgelehnt. Typen folgen einem Link; weitere Hops prüft die Engine. Relationsfilter verschachteln bis zur Ausdruckstiefe von SQLite, etwa acht Hops; breite Filter sind nicht begrenzt.
Alles, was die Engine entscheidet (Rechte, eine fehlende Zeile, ein Konflikt), kommt beim Await der Query oder im error einer Live-Query hoch.
Eine Query wird live, wenn du sie abonnierst:
const stop = db.task .findMany({ where: { done: false }, orderBy: { due: "asc" } }) .subscribe(({ data }) => renderTasks(data));
// Wenn die Ansicht verworfen wird:stop();Nach einem Commit laufen nur Subscriptions erneut, deren Query ein geändertes Modell berührt, gebündelt alle 50 ms, und Clients erhalten Diffs pro Zeile. Counts und Aggregates bleiben ebenfalls live. Ein Live-select bekommt nur dann ein Update, wenn sich ein ausgewähltes Feld geändert hat.
React-Bindings (useLive, useLivePage, usePresence, useBroadcast) stehen in React.