--- title: 'Sammlungen anlegen' description: 'Eigene Collection in api/collections definieren: Schema, Meta, Indizes, Suche.' --- import { Aside, Steps, FileTree } from '@astrojs/starlight/components'; Eine **Sammlung** (Collection) ist eine Tabelle deines Projekts. Du legst sie als YAML-Datei in `api/collections/` an. Der Dateiname ist frei, entscheidend ist das Feld `name`. Beispiel-Sammlungen zum Abschauen: `content`, `navigation`, `medialib`, `tag`, `comment`. - api/ - collections/ - content.yml - navigation.yml - medialib.yml - tag.yml - comment.yml ## Minimal-Beispiel Die kleinste Sammlung braucht `name` **und `permissions`** (beides Pflicht laut Schema) — `fields` ist dagegen optional. Abgeschaut von `tag.yml` (vereinfacht): ```yaml title="api/collections/notiz.yml — Minimal-Beispiel (vereinfacht aus tag.yml)" # api/collections/notiz.yml # yaml-language-server: $schema=../../tibi-types/schemas/config/collection.schema.json name: notiz uploadPath: ../media/notiz audit: enabled: true permissions: public: methods: get: true user: methods: get: true post: true put: true delete: true indexes: - name: name_unique key: - name unique: true background: true search: - name: default mode: regex fields: [name, text] fields: - name: name type: string meta: label: { de: "Name", en: "Name" } - name: text type: string meta: label: { de: "Text", en: "Text" } ``` 1. Datei `api/collections/.yml` anlegen, Schema-Zeile aus dem Beispiel kopieren. 2. `name` kleinschreiben, keine Umlaute und keine Leerzeichen (`content`, `tag`, `comment` als Vorbild). 3. `permissions` setzen — sonst liest niemand die Daten (Details: [Rechte und Tokens](/projekt/rechte-tokens/)). 4. `uploadPath` setzen, auch wenn die Sammlung keine Dateien enthält (`../media/` wie in allen Starter-Sammlungen). 5. Projekt neu laden, damit der tibi-server die Datei einliest. 6. Testen: `POST` anlegen, doppelten Wert schicken → `409` bei Unique-Index. ## Meta: Label, Gruppe, Icon, Vorschau `meta` steuert, wie tibi-admin die Sammlung anzeigt. Pflicht ist nichts davon — aber ohne `label` rätst du im Admin. | Schlüssel | Wirkung | Beispiel aus dem Starter | |---|---|---| | `label` | Anzeigename (`de`/`en`) | `tag.yml`: `{ de: "Schlagwörter", en: "Tags" }` | | `group` | Gruppierung in der Navigation | `content`, `content`, `community` (`comment.yml`) | | `muiIcon` | Icon im Admin | `label`, `menu`, `chat`, `folder-multiple-image` | | `imageUrl` | Vorschaubild für Karten | Unsplash-URL in `content.yml` | | `preview` | Listenvorschau: `label`, `secondary`, `tertiary`, `image` | `content.yml`: `label: name`, `secondary: path`, `tertiary: lang` | | `viewHint` | Darstellungsart im Admin | siehe unten | | `sidebar` | Eigene Seitenleiste mit Gruppen | `content.yml`: `publishing`, `seo` | | `i18n.entry` | Übersetzungs-Kopplung | `content.yml`: `languageField: lang`, `groupField: translationKey` | Felder positionierst du mit `meta.position`: `main`, `sidebar:` oder `sidebar:settings`. Beispiel: `lang` liegt in `content.yml` auf `sidebar:settings`, `active` auf `sidebar:publishing`. `viewHint` aus dem Starter (geprüft, alle drei Varianten): - `viewHint: cards` — Kartenansicht (`content.yml`). - `viewHint.media` — Medienansicht mit Upload und KI-Alt-Text (`medialib.yml`). - `viewHint.navigation` — Baumansicht mit `nodesField` und `declaredTrees` (`navigation.yml`, Bäume: Header/Footer je Sprache). ## Indizes und Suche ### Indizes Jeder Eintrag in `indexes` braucht nur `key` (`name` ist freiwillig). `key` ist eine Liste von Feldnamen, Text-Index mit `"$text:$**"`. ```yaml title="content.yml — Text-Index plus zwei Unique-Indizes" # aus content.yml — Text-Index plus zwei Unique-Indizes indexes: - name: content_text key: - "$text:$**" defaultLanguage: none background: true - name: path_lang key: - path - lang unique: true background: true - name: translation key: - translationKey - lang unique: true background: true ``` `unique: true` schützt vor Doppelten: der zweite `POST` mit gleichem `path` + `lang` scheitert — modern mit `409`, im Legacy-Default (Standard) mit `500` (siehe [Fehlerbilder](/reference/fehlercodes/)). So erzwingt der Starter auch einzelne Navigationsbäume (`type_language` in `navigation.yml`) und einzelne Tag-Namen (`name_unique` in `tag.yml`). ### Suche Jeder Eintrag in `search` braucht nur `name` und `mode` (`fields` ist freiwillig). Vier Modi kommen im Starter vor: | Modus | Wofür | Beispiel | |---|---|---| | `regex` | Einfache Teilstringsuche | `tag.yml`, `navigation.yml`, `comment.yml`: `default` | | `ngram` | Fehlertolerante Fuzzy-Suche | `content.yml`: `fuzzy` mit `autoRegenerate: true` | | `vector` | Semantische Vektorsuche | `content.yml`: `semantic` mit `vector: { provider: bge_m3 }` | | `combined` | Hybrid aus semantisch + fuzzy (RRF) | `content.yml`: `combined` mit `rrf: { k: 60, topK: 100 }` | Abfrage dazu: `GET …/content?q=…&qName=combined`. Details stehen unter [Lesen und suchen](/projekt/abfragen-lesen/). ## Häufige Fehler | Fehlerbild | Ursache | Lösung | |---|---|---| | `409` beim Anlegen | `unique`-Index verletzt (`path`+`lang`, `translationKey`+`lang`, Tag-`name`) | Vor `POST` per `filter` prüfen, ob der Datensatz schon existiert | | `project not found` | Projekt nicht registriert oder Namespace falsch | Siehe [Neues Projekt](/einstieg/neues-projekt/) Schritt 5 | | Validierung schlägt fehl | Pflichtfeld fehlt oder `pattern` verletzt (z. B. `path` ohne führenden `/`) | Feldregeln stehen unter [Felder und Widgets](/projekt/felder-widgets/) | | Öffentlich leere Liste, mit Token voll | `active:true` fehlt — der `filter_public`-Hook filtert alles weg | Siehe [Rechte und Tokens](/projekt/rechte-tokens/) | | Datei-Upload landet falsch | `uploadPath` fehlt oder zeigt ins falsche Verzeichnis | `../media/` wie in allen Starter-Sammlungen setzen | Verifiziert am e3f92eb 2026-10-14 (tibi-svelte-starter), am 4c55f82 2026-10-12 (tibi-types).