Lesen und suchen
Lesen geht per GET auf eine Sammlung.
Filtern, Sortieren, Blättern und Auflösen hängst du als Query-Parameter an.
Basis: GET /api/v1/_/<namespace>/<collection>.
GET-Liste
Abschnitt betitelt „GET-Liste“GET /api/v1/_/<namespace>/content?filter={"lang":"de"}&sort=-name&limit=20&offset=0Liste mit filter, sort, limit, offset.
GET /api/v1/_/<namespace>/content/<id>Einzelner Eintrag per ID.
Filtern, Sortieren, Blättern
Abschnitt betitelt „Filtern, Sortieren, Blättern“| Parameter | Format | Beispiel |
|---|---|---|
filter |
MongoDB-Filter als JSON | ?filter={"lang":"de","active":true} |
sort |
Feld, - heißt absteigend; Komma oder wiederholt |
?sort=-name, ?sort=type&sort=-language |
limit |
Zahl, max. Einträge | ?limit=20 |
offset |
Zahl, übersprungene Einträge | ?offset=40 |
count |
1 oder true |
?count=1 |
Filter-Operatoren wie $and, $or, $in, $lte, $gte, $exists funktionieren.
Der Starter nutzt sie selbst im publishedFilter (api/hooks/config.js): active: true plus Veröffentlichungsfenster publication.from/publication.to.
# nächste Seite, neueste zuerst (Annahme: Feld updatedAt existiert)GET …/comment?filter={"contentId":"<id>"}&sort=-updatedAt&limit=10&offset=10select und projection
Abschnitt betitelt „select und projection“select begrenzt die Felder — Komma-getrennt oder wiederholt.
Das spart Bandbreite und Kontext, vor allem bei Dateien.
GET …/medialib?select=title,alt,tagsGET …/content?select=name&select=path&select=langselectohnefile.dataabfragen: Datei-Inhalte (file.data) sind groß —file.srcreicht für Anzeige.- Der Starter-Hinweis dazu: Liste mit
selectohnefile.datalesen, sonst bläht der Kontext auf. projectionwählt eine vordefinierte Projektion aus der Sammlungs-YAML (projections-Block).- Unbekannte Felder oder fremde Projektionen →
400.
Lookup: Verweise auflösen
Abschnitt betitelt „Lookup: Verweise auflösen“lookup löst Fremdschlüssel auf und legt das Ergebnis unter _lookup ab.
Format: feld:sammlung, Rest mit Doppelpunkten (feld:sammlung:fremdfeld:projektion:select:als).
# Navigation mit Seitendaten (Feld page -> content)GET …/navigation?lookup=elements.page:content
# Content mit Hero- und Richtext-Bildern (Felder aus content.yml)GET …/content?lookup=blocks.heroImage.image:medialib,blocks.image:medialib- Ohne
lookupsteht im Feld nur die ID (z. B.page: "<content.id>"). - Mit
lookupsteht der Zieldatensatz in_lookup.page(Name, Pfad usw.). - Die App baut daraus den Link:
hrefaus_lookup.page.pathplushash. - Mehrere Lookups per Komma oder wiederholtem
?lookup=. - Ziel ohne Leserecht oder unbekannte Sammlung → Fehler (
lookup permission denied, Ziel nicht gefunden).
Facetten und Zählen (eigene Endpunkte!)
Abschnitt betitelt „Facetten und Zählen (eigene Endpunkte!)“| Endpunkt | Wirkung | Beispiel |
|---|---|---|
GET …/<collection>/_count?filter={…} |
Nur Anzahl, keine Einträge | GET …/comment/_count?filter={"contentId":"<id>"} |
GET …/<collection>/_count?estimate=1 |
Schnelle Schätzung statt exakter Zahl (nur ohne filter) |
GET …/comment/_count?estimate=1 |
GET …/<collection>/_facet?facet=<feld> |
Werte zählen je Feld | GET …/comment/_facet?facet=contentId |
…/_facet?facet=<feld>&limit=10 |
Nur Top N | GET …/comment/_facet?facet=contentId&limit=10 |
…/_facet?facet=<feld>&includeEmpty=1 |
Leere Werte mitzählen | GET …/comment/_facet?facet=tags&includeEmpty=1 |
…/_facet?facet=<feld>&q=<text>&qName=<block> |
Werte der Treffer zählen | GET …/productVariant/_facet?facet=brand&q=warme jacke&qName=combined |
Die Kommentar-Anzahl je Seite (commentCount) kommt aus so einer Abfrage über contentId.
Suchbewusste Facetten
Abschnitt betitelt „Suchbewusste Facetten“q und qName gelten auch für _facet: Die Zählung bezieht sich dann auf die Trefferliste, nicht auf die ganze Sammlung.
Damit passt sich eine Filterseitenleiste der Suche an.
| Parameter | Bedeutung |
|---|---|
q, qName |
Suchtext und Suchblock; gezählt werden nur noch die Treffer |
qLimit |
Zahl, 0 oder all: wie viele Treffer die Zählung abdeckt |
GET …/productVariant/_facet?facet=catalogProjection.brand&q=warme jacke&qName=combinedGET …/productVariant/_facet?facet=catalogProjection.brand&q=warme jacke&qName=combined&qLimit=all- Ohne
qLimitist das Fenster so groß wierrf.topKdes Suchblocks (ohne Angabe 500, siehe Suche) — also genau die Menge, die die Liste zeigt. qLimit=all(oderqLimit=0) zählt alle Treffer; der Server deckelt bei 20 000 IDs.qLimit=<zahl>setzt das Fenster selbst.- Ein größeres Fenster als
rrf.topKliefert bewusst mehr Treffer als die Liste zeigt: die Summe der Facettenwerte ist dann größer als die Trefferzahl der Liste. - Bei den filterbasierten Modi (
text,regex,ngram,eval,filter) wirdqLimitignoriert — dort zählt der Filter ohnehin exakt. - Nur bei den Modi
vectorundcombinedergänzt die Antwort einen Blocksearch, z. B.{"mode":"combined","resolved":500,"limit":500,"truncated":true}.truncated: trueheißt: Fenster voll, weitere Treffer sind möglich — auch wenn es exakt aufgeht, denn der Server holt kein Extra-Dokument. - Die Modi
text,regex,ngram,evalundfiltersetzenqals MongoDB-Filter um. Dort ist die Zählung exakt und es gibt keinensearch-Block. - Ohne Treffer kommt eine leere Facetten-Antwort ohne Datenbankzugriff.
Aggregate: Rückwärts zählen und rechnen (Listen-Route!)
Abschnitt betitelt „Aggregate: Rückwärts zählen und rechnen (Listen-Route!)“aggregate hängt an der normalen GET-Liste — kein eigener Endpunkt wie _count/_facet.
Es zählt oder verrechnet Dokumente einer anderen Sammlung, die auf deine Listeneinträge zeigen, und legt das Ergebnis unter _aggregate ab.
GET …/content?aggregate=comment:contentId:count:commentCount# Antwort je Eintrag: {"_id": "…", "name": "…", "_aggregate": {"commentCount": 3}}Baustein (ziel:feld:op:wert:als) |
Bedeutung | Beispiel |
|---|---|---|
ziel (Pflicht) |
Ziel-Sammlung, in der gezählt wird | comment |
feld (Pflicht) |
Feld dort, das auf deinen Eintrag zeigt | contentId (dein _id ist die Quelle) |
op |
count (Standard), exists, sum, avg, min, max |
count |
wert |
Nur für sum/avg/min/max Pflicht: Zahlenfeld im Ziel (number, float, int, any) |
price |
als |
Ergebnis-Schlüssel unter _aggregate; ohne Angabe erzeugt der Server einen (commentCount, commentPriceSum, …) |
commentCount |
Formate: Kurzform mit Doppelpunkten, mehrere per Komma (?aggregate=a:x:count,b:y:count), JSON-Objekt/Array, wiederholtes ?aggregate= oder ?aggregate[]=.
Nur lesend: Quell-Feld, Ziel-Feld (foreignField), valueField und Filter-Felder müssen für deine Rolle lesbar sein — sonst 400.
Fehlt dir das get-Recht auf der Ziel-Sammlung, kommt 403 (aggregate permission denied).
Unbekannte Sammlung, unbekanntes Feld oder unbekannte Op → 400; mehrere Specs mit gleichem Ergebnis-Schlüssel → 400 (aggregate result key conflict).
Leere Treffer haben feste Werte: count → 0, exists → false, sum → 0, avg/min/max → null.
q sucht, qName wählt den Suchindex aus dem search-Block der Sammlung.
Die vollständige Index-Tabelle (Modi, Gewichte, Aufbau) steht unter Suche — hier nur die Abfrage:
# kombinierte Suche in content (RRF aus semantisch + fuzzy)GET …/content?q=kontakt&qName=combined
# einfache Suche in Tags (Default-Index, regex)GET …/tag?q=hero?qExplain=1 erklärt, wie die Suche gewichtet hat — nützlich beim Debuggen.
Fehlerbilder
Abschnitt betitelt „Fehlerbilder“| Antwort | Ursache | Lösung |
|---|---|---|
400 |
Ungültiges filter-JSON, unbekanntes select-Feld, falsche lookup-Syntax |
JSON prüfen, Feldnamen gegen die Sammlungs-YAML halten |
permission denied |
Kein get-Recht für deine Rolle |
Rechte und Tokens prüfen |
invalid projection |
projection heißt anders als in der YAML |
Namen im projections-Block nachschlagen |
| Leere Liste trotz Daten | filter_public-Hook filtert active:true + Zeitraum (Rechte und Tokens) |
active:true setzen, publication-Fenster prüfen |
_lookup leer |
Zieldatensatz nicht lesbar (meist active:true fehlt, siehe Rechte und Tokens) |
Zieldatensatz prüfen, nicht die Abfrage |
Verifiziert am e3f92eb 2026-10-17 (tibi-svelte-starter), am 68b4278 2026-10-17 (tibi-server, collection_get/select/sort/facet/count/lookup), am 68b4278 2026-10-17 (tibi-server, aggregate: dbapi/aggregate.go, controller/collection_aggregate.go), am d0d699d 2026-10-20 (tibi-server: collection_facet.go, collection_facet_search.go, rrf.topK-Default)