Ga naar inhoud

Datastores

Gebruik Datastores voor gegevens die je organisatie zelf bijhoudt en die een agent moet kunnen lezen of bijwerken: een lijst klanten, de instellingen van een integratie, een kleine catalogus. Een datastore is een verzameling JSON-records, elk onder een eigen sleutel. Je kunt een schema vastleggen waaraan elk record moet voldoen, en aangeven dat een veld in een record naar een record in een andere datastore wijst.

Een datastore is geen database. Je zoekt een record op met zijn sleutel, met het begin van een sleutel, met zijn metadata, of met een zoekopdracht die de records van één datastore filtert en sorteert op hun velden. Een zoekopdracht combineert geen datastores en telt of sommeert niet. Wat een datastore bevat, bereik je op drie manieren: hier in de console, met een sleutel via de API, en als tool in een beurt van een agent.

Je hebt de capability datastores nodig: in het plafond van de organisatie om het onderdeel in de console te zien, en op de sleutel voor de API en de tools.

Een datastore toevoegen

Kies Datastore toevoegen. De lade opent met vier velden:

Veld Wat je invult
Naam uniek binnen de organisatie: kleine letters, cijfers, - en _, beginnend met een letter of cijfer, maximaal 64 tekens. De naam is later niet te wijzigen
Omschrijving waar deze datastore voor is. Optioneel
Metadata (JSON) een JSON-object dat je aan de datastore hangt, bijvoorbeeld {"eigenaar": "support"}. Core bewaart en toont het, maar doet er niets mee. Leeg laten is geen metadata
Bewaring Tijdelijk, de standaard, laat de records onder de bewaartermijn van de organisatie vallen; Bewaren tot verwijderd bewaart ze voorbij dat venster, voor records die je bijhoudt, zoals een catalogus, profielen of instellingen. Zie Bewaren en wissen

De naam ligt vast omdat andere instellingen een datastore bij zijn naam noemen: de policy van een sleutel en de verwijzingen in het schema van een andere datastore. Wil je een andere naam, maak dan een nieuwe datastore.

Na Opslaan staat de datastore in de lijst, met Nog geen schema in de kolom Laatste schemaversie. Klik op de naam om hem te openen. Zijn pagina heeft twee tabbladen: Schema en Records.

Een schema vastleggen

Zonder schema accepteert een datastore elk JSON-object als record. Met een schema controleert Core elk record voordat het wordt opgeslagen, en weigert een record dat er niet aan voldoet.

Op het tabblad Schema vul je onder Nieuwe schemaversie in:

Veld Wat je invult
JSON Schema een JSON Schema (Draft 2020-12) waaraan de data van elk record moet voldoen. Leeg laten valideert niets. Je bouwt het veld voor veld in de weergave Visueel (per veld een naam, een soort, Verplicht en regels zoals een patroon of een minimum), plakt een bestaand schema in de weergave JSON, of laat Beschrijf wat je nodig hebt er een voorzet voor maken, die je met Ongedaan maken weer terugdraait. Onder de velden staat hoe Core het schema leest en een voorbeeldrecord dat eraan voldoet
Verwijzingen per regel een Datastore en een JSON Pointer: welk veld in een record naar een record in welke datastore wijst. Zie Verwijzingen

Kies Schema opslaan. Elke keer dat je opslaat met een ander schema of andere verwijzingen, ontstaat een nieuwe versie; is alles gelijk aan de laatste versie, dan gebeurt er niets. Onder Schemaversies staan alle versies, de nieuwste bovenaan. Klik een versie open om het schema, de verwijzingen en de maker te zien. Een versie is achteraf niet te wijzigen.

Het formulier begint altijd leeg, ook als de datastore al een schema heeft. Een nieuwe versie vervangt het hele schema en alle verwijzingen. Wil je iets aanpassen, plak of bouw dan het volledige schema opnieuw en voeg de verwijzingen die moeten blijven opnieuw toe. Sla je het formulier leeg op, dan valideert de nieuwe versie niets meer.

Een voorbeeld: dit schema eist dat elk record een naam heeft en dat email een tekst is.

{
  "type": "object",
  "properties": {
    "naam": {"type": "string"},
    "email": {"type": "string"}
  },
  "required": ["naam"]
}

Bestaande records en een nieuwe versie

Een nieuwe schemaversie controleert de records die er al zijn niet opnieuw. Elk record onthoudt onder welke versie het is geschreven; dat zie je in de kolom Schemaversie van de records. Wil je dat oude records aan de nieuwe versie voldoen, sla ze dan opnieuw op.

Wat een schema niet mag

Core haalt nooit iets op van een ander adres. Een $ref in je schema mag daarom alleen naar een deel van hetzelfde schema wijzen (zoals #/$defs/adres). Core weigert bij het opslaan een schema:

  • met een $ref naar een ander document of een URL;
  • met een $id ergens binnen het schema;
  • met een pattern dat een terugverwijzing (\1) of een lookaround ((?=...)) gebruikt;
  • dat geen geldig JSON Schema is;
  • dat groter is dan wat je installatie toestaat (standaard 64 KiB).

De foutmelding staat onder het veld JSON Schema.

Verwijzingen

Een verwijzing zegt dat een veld in een record de sleutel van een record in een andere datastore bevat. Heeft een datastore bestellingen de verwijzing Datastore klanten en JSON Pointer /klant, dan moet in elk bestelrecord de waarde van klant de sleutel zijn van een bestaand record in klanten.

  • Een JSON Pointer begint met / en volgt het pad in de data: /klant, of /levering/adres voor een genest veld.
  • De datastore waarnaar je verwijst, moet al bestaan. Een datastore mag naar zichzelf verwijzen.
  • Een schemaversie heeft maximaal 32 verwijzingen.
  • Een record waarin het veld leeg is of ontbreekt, wordt niet gecontroleerd.
  • Staat er een sleutel die niet bestaat, dan weigert Core het record, met de melding onder Data (JSON).

Wordt het record waarnaar verwezen wordt later verwijderd, dan blijft de verwijzing staan zoals ze geschreven is. In de lijst met records staat dan Wijst naar niets meer in de kolom Verwijzingen, en in het record zelf staat hetzelfde label bij die verwijzing.

Records bekijken

Het tabblad Records toont de records op volgorde van sleutel, met per record:

Kolom Wat er staat
Sleutel de sleutel van het record; klik erop om het record te openen
Schemaversie de versie waaraan het record voldeed toen het werd geschreven, of Nog geen schema
Gewijzigd wanneer het record voor het laatst is geschreven
Verwijzingen hoeveel verwijzingen het record heeft, Geen verwijzingen gedeclareerd., of Wijst naar niets meer als er minstens één naar een verwijderd record wijst

Een geopend record toont bovenaan wie het betreft (Subject), onder welke schemaversie het is geschreven, wanneer het is aangemaakt en gewijzigd, en door wie (Door: de sleutel of de beheerder die het schreef). Daaronder staan Data, Metadata en Verwijzingen, elke verwijzing met de sleutel en de datastore waar ze naartoe wijst.

Elke keer dat je records bekijkt, legt Core in de audit vast welke records je te zien kreeg.

Filteren en sorteren

Boven de lijst staan de velden waarmee je de lijst inperkt en ordent: één regel per filter, één regel voor de sortering, en de knoppen Filter toevoegen, Toepassen en Wissen. Ze voeren een zoekopdracht uit; de regels voor velden, typen en bewerkingen staan in dat hoofdstuk.

  1. Kies Filter toevoegen. Er verschijnt een regel met drie velden, van links naar rechts: het veld, de bewerking en de waarde.
  2. Kies het veld. Declareert de laatste schemaversie velden, dan kies je uit een lijst: Sleutel, Aangemaakt en Gewijzigd voor de sleutel van het record en zijn twee tijdstippen, gevolgd door elk veld op het hoogste niveau van het schema. Anders typ je de naam van het veld; $key, $created_at en $updated_at staan voor de sleutel en de twee tijdstippen.
  3. Kies de bewerking, zoals is, bevat of is minstens. De lijst bevat alleen de bewerkingen die bij het type van het veld passen; de tabel met bewerkingen legt ze uit.
  4. Vul de waarde in. Wat je invult, hangt af van het veld en de bewerking:

    Veld of bewerking Wat je invult
    een tekstveld de tekst
    een getalveld een getal
    een datumveld een datum
    een veld met datum en tijd, Aangemaakt of Gewijzigd een datum en een tijd, in je eigen tijdzone
    een waar-of-onwaar-veld Ja of Nee
    de bewerking is een van de waarden, gescheiden door komma's
    de bewerking bestaat Heeft een waarde of Heeft geen waarde
  5. Wil je de records ordenen, kies dan in de sorteerregel, die begint op Sleutel (standaard), een veld en Oplopend of Aflopend. Zonder schema typ je daar de naam van het veld; laat het leeg voor sleutelvolgorde.

  6. Kies Toepassen.

De lijst verandert pas als je Toepassen kiest. Bij meerdere filters moet een record aan alle filters voldoen. Een regel zonder veld of zonder waarde telt niet mee. Eén filter weghalen: kies Filter verwijderen (de prullenbak aan het eind van de regel) en daarna opnieuw Toepassen. Wissen haalt alle filters en de sortering weg en toont alle records op volgorde van sleutel.

De toegepaste filters en sortering staan in het adres van de pagina. Een gekopieerde link of een bladwijzer opent dus dezelfde selectie.

Voldoet geen record aan de filters, dan staat er Geen record komt overeen. Weigert Core een deel van de zoekopdracht, bijvoorbeeld een waarde die geen getal is bij een getalveld, dan staat de melding bij het veld waar het om gaat. Een zoekopdracht die te lang duurt, wordt op de plek van de lijst gemeld; voeg een filter toe dat de zoekopdracht inperkt, of sorteer op sleutel.

Records van één persoon

Een record is van één persoon wanneer het is geschreven in een verzoek of beurt voor die persoon (zie Bouwen voor personen). Subject toont dan het id van die persoon. Een record dat niet aan één persoon is gebonden, heet Gedeeld.

Een sleutel is uniek per persoon. Anna en Bob kunnen elk een eigen record voorkeuren hebben, naast een gedeeld record voorkeuren. In de console zie je ze alle drie, als drie rijen met dezelfde sleutel; open een rij om te zien van wie hij is. De lade van het record toont ook zijn Labels, één badge per waarde, die regels over Datastore-rijen lezen.

De console schrijft en verwijdert alleen gedeelde records. Record schrijven, Bewerken als JSON en Verwijderen werken altijd op het gedeelde record onder die sleutel. Bij een record van één persoon maakt Bewerken als JSON daarom een nieuw gedeeld record met dezelfde sleutel, en Verwijderen verwijdert het gedeelde record of meldt dat er geen is. Het record van de persoon blijft ongewijzigd. Wijzig of verwijder zo'n record via de API, met een verzoek voor die persoon.

Een record schrijven

Kies Record schrijven. De lade Nieuw record opent met:

Veld Wat je invult
Sleutel de sleutel van het record: letters, cijfers, _, ., : en -, maximaal 128 tekens, niet . of ..
Data (JSON) het record zelf, als JSON-object. Verplicht
Metadata (JSON) een optioneel JSON-object naast het record. De tekstwaarden erin werken als labels voor de toegangsregels per persoon; zie Labels op alles

Bestaat de sleutel al als gedeeld record, dan wordt dat record in zijn geheel vervangen; anders wordt het aangemaakt. Core controleert de data tegen de laatste schemaversie en haar verwijzingen. Voldoet het record niet, dan staat onder Data (JSON) wat er mis is, met per fout het pad in de data (bijvoorbeeld data.email), maximaal tien fouten tegelijk. Een record is standaard maximaal 256 KiB; je installatie kan een andere grens hebben. Een datastore die zijn maximale aantal records bevat, accepteert geen record onder een nieuwe sleutel; zie Grenzen.

Een record bewerken: open het en kies Bewerken als JSON. De sleutel ligt dan vast; je past Data (JSON) en Metadata (JSON) aan en kiest Opslaan. Het record wordt in zijn geheel vervangen door wat in de velden staat.

Een record verwijderen

Kies Verwijderen in het rijmenu van het record en bevestig. Dat kan niet ongedaan worden gemaakt. Records in andere datastores die naar dit record verwezen, houden hun verwijzing; die staat daarna als Wijst naar niets meer.

Een datastore bewerken of verwijderen

Bewerken in het rijmenu van de lijst opent een lade met Omschrijving, Metadata (JSON) en Bewaring. De naam is niet te wijzigen.

Verwijderen in het rijmenu verwijdert de datastore met al zijn schemaversies en records. Dat kan niet ongedaan worden gemaakt, en in de bevestiging typ je de naam van de datastore. Verwijst de laatste schemaversie van een andere datastore nog naar deze, dan weigert Core het verwijderen: pas eerst het schema van die andere datastore aan, zodat de verwijzing verdwijnt.

Zoeken op velden

Een zoekopdracht geeft de records van één datastore die aan elk filter voldoen dat je meegeeft, in de volgorde die je vraagt. De filters in de console, de tool query en de zoekroute van de API voeren dezelfde zoekopdracht uit, onder dezelfde regels.

Velden

Een filter of een sortering noemt één veld:

  • Een veld op het hoogste niveau van de data van het record, bij zijn naam: status voor een record {"status": "open", "adres": {"plaats": "Utrecht"}}. Een veld binnen een object of een lijst is niet te noemen: adres.plaats wordt gelezen als een veld op het hoogste niveau met precies die naam.
  • $key, de sleutel van het record.
  • $created_at en $updated_at, wanneer het record voor het eerst en voor het laatst is geschreven.

Een naam die met $ begint, betekent altijd een van deze drie; elke andere naam met $ weigert Core met 422. Een veld in je eigen data waarvan de naam met $ begint, is daardoor niet in een zoekopdracht te gebruiken.

Typen

De laatste schemaversie van de datastore bepaalt als wat een veld wordt vergeleken:

Het schema declareert het veld als Het wordt vergeleken als
"type": "boolean" waar of onwaar
"type": "integer" of "type": "number" een getal, exact
"type": "string" tekst
"type": "string" met "format": "date" een datum, zoals 2026-11-01
"type": "string" met "format": "date-time" een tijdstip, zoals 2026-11-01T09:00:00Z; zonder UTC-afwijking wordt het als UTC gelezen
een array, een object of meerdere typen, of het veld is niet gedeclareerd tekst

Een type dat als lijst is geschreven met null naast één ander type, zoals ["integer", "null"], telt als dat andere type. $key is tekst; $created_at en $updated_at zijn tijdstippen.

In een datastore zonder schema is elk veld tekst. Een string wordt vergeleken als zijn tekens, en elke andere waarde zoals JSON die schrijft. Tekst wordt teken voor teken geordend: een opgeslagen 100 komt dus vóór 9. Declareer het veld in het schema als getal om het als getal te vergelijken.

De waarde in een filter moet als het type van het veld te lezen zijn: een getal bij een getalveld, een tekst met een datum bij een datumveld. Een waarde die dat niet is, weigert Core met 422, met het filter erbij genoemd.

Opgeslagen records laten een zoekopdracht nooit mislukken. Een record dat is geschreven voordat het schema het veld declareerde, kan een waarde van een ander type bevatten, bijvoorbeeld tekst waar een getal wordt verwacht. Zo'n record voldoet aan geen enkele vergelijking op dat veld, en een sortering zet het bij de records die geen waarde voor het veld hebben. Voor exists telt het wel als een waarde: het record voldoet aan exists met true en niet met false.

Bewerkingen

Bewerking In de console Een record voldoet als Typen veld
eq is het veld gelijk is aan de waarde alle
ne is niet het veld een waarde heeft en die verschilt alle
lt, lte is kleiner dan, is hoogstens het veld vóór de waarde komt, of er bij lte gelijk aan is alle behalve waar-of-onwaar
gt, gte is groter dan, is minstens het veld na de waarde komt, of er bij gte gelijk aan is alle behalve waar-of-onwaar
contains bevat de waarde ergens in de tekst staat tekst
starts_with begint met de tekst met de waarde begint tekst
ends_with eindigt op de tekst op de waarde eindigt tekst
in is een van het veld gelijk is aan een van een lijst van maximaal 100 waarden alle
exists bestaat met true: het veld een waarde heeft; met false: het ontbreekt of null is alle

De drie tekstbewerkingen maken geen onderscheid tussen hoofdletters en kleine letters en nemen de waarde letterlijk: %, _ en * zijn gewone tekens, geen jokers.

Een record zonder het veld voldoet aan eq noch aan ne. Zoek je de records waarin een veld ontbreekt, gebruik dan exists met false.

Een zoekopdracht heeft maximaal 20 filters, en een record moet aan alle voldoen. Er is geen of tussen filters. Gebruik in voor meerdere waarden van één veld; voer voor alternatieven over meerdere velden per alternatief een zoekopdracht uit.

Sorteren en bladeren

Een zoekopdracht sorteert op één veld, oplopend (asc) of aflopend (desc). Zonder sortering komen de records op volgorde van sleutel. Records zonder waarde voor het sorteerveld komen in beide richtingen achteraan, en records met dezelfde waarde komen op volgorde van sleutel. Een tekstveld wordt gesorteerd op zijn eerste 256 tekens.

Een zoekopdracht geeft één pagina tegelijk terug. Via de API bevat een pagina 20 records, tenzij je limit meegeeft (1 tot en met 100), en het antwoord bevat next_cursor als er meer zijn (Paginering met cursors). Stuur voor de volgende pagina dezelfde filters en sortering met die cursor. Een cursor hoort bij de sortering waaronder hij is uitgegeven; met een andere sortering weigert Core hem met 422. Bladeren houdt zijn plaats als er tussen twee pagina's records worden verwijderd: je kunt dus een pagina lezen, verwijderen wat je hebt afgehandeld en de volgende pagina vragen.

Wat een zoekopdracht niet doet

  • Datastores combineren. Een zoekopdracht leest één datastore. Volg een verwijzing door het record waarnaar ze wijst met zijn sleutel te lezen.
  • Tellen, optellen of groeperen. Het antwoord bestaat uit records, nooit uit een getal daarover.
  • Of tussen filters toepassen. Elk filter moet kloppen.
  • Geneste velden bereiken. Alleen velden op het hoogste niveau zijn te noemen. Zet een waarde waarop je wilt filteren op het hoogste niveau van het record.
  • Filteren op metadata. Dat doet de recordlijst van de API, met de parameter metadata.
  • Velden selecteren. Elk record dat voldoet, komt in zijn geheel terug.

Een voorbeeld

Een datastore bestellingen heeft een schema dat status declareert als string, bedrag als getal en vervaldatum als string met het formaat date. Dit verzoek vraagt de open bestellingen die vóór 1 november 2026 vervallen, het grootste bedrag eerst, twee per pagina:

curl -s -X POST "$CORE_URL/datastores/ds_7Kp2.../records/query?limit=2" \
    -H "Authorization: Bearer $CORE_KEY" -H "Content-Type: application/json" \
    -d '{"filters": [{"field": "status", "op": "eq", "value": "open"},
                     {"field": "vervaldatum", "op": "lt", "value": "2026-11-01"}],
         "sort": {"field": "bedrag", "direction": "desc"}}'
{
  "items": [
    {
      "object": "datastore.record",
      "datastore_id": "ds_7Kp2...",
      "key": "bestelling-1042",
      "data": {"status": "open", "bedrag": 1250, "vervaldatum": "2026-10-20"},
      "metadata": {},
      "schema_version": 2,
      "references": [],
      "subject": null,
      "created_by": "key_4Hd9...",
      "created_at": "2026-09-30T08:12:44Z",
      "updated_at": "2026-10-02T14:03:10Z"
    },
    {
      "object": "datastore.record",
      "datastore_id": "ds_7Kp2...",
      "key": "bestelling-0977",
      "data": {"status": "open", "bedrag": 480.5, "vervaldatum": "2026-10-28"},
      "metadata": {},
      "schema_version": 2,
      "references": [],
      "subject": null,
      "created_by": "key_4Hd9...",
      "created_at": "2026-09-12T10:40:02Z",
      "updated_at": "2026-09-12T10:40:02Z"
    }
  ],
  "next_cursor": "eyJ2IjoxLCJr..."
}

Stuur voor de volgende twee bestellingen hetzelfde verzoek met &cursor=eyJ2IjoxLCJr... achter het adres. Een next_cursor van null betekent dat er geen meer zijn.

Grenzen

Grens Wat er bij de grens gebeurt
Een datastore bevat standaard maximaal 50.000 records, de gedeelde records en die van elke persoon samen. Je installatie kan een ander aantal hebben Core weigert een record onder een nieuwe sleutel met 409 en de melding dat de datastore het maximale aantal records bevat. Een bestaand record vervangen kan altijd. Verwijder records om ruimte te maken
Een zoekopdracht mag standaard 5 seconden duren; je installatie kan een andere tijd hebben Core breekt de zoekopdracht af en antwoordt 422 met het advies de zoekopdracht in te perken met een filter of op $key te sorteren
Een zoekopdracht heeft maximaal 20 filters, en in accepteert maximaal 100 waarden Core weigert de zoekopdracht met 422
Een opgeslagen getal dat met meer dan 1.000 tekens is geschreven het wordt niet als getal gelezen: het record voldoet aan geen enkele vergelijking op dat veld en wordt gesorteerd bij de records zonder waarde. Voor exists telt het wel als een waarde

De eerste grens houdt een zoekopdracht snel: een zoekopdracht leest alle records van de datastore, dus het aantal records begrenst het werk van de zoekopdracht. Gegevens die onbeperkt groeien, zoals een logboek, horen niet in één datastore.

Een datastore gebruiken vanuit een agent

Elke datastore levert vijf tools die een model in een beurt kan aanroepen:

Tool Wat het doet
datastore__<id>__get één record lezen met zijn sleutel
datastore__<id>__list records opsommen op volgorde van sleutel, eventueel alleen sleutels met een bepaald begin; maximaal 50 per aanroep
datastore__<id>__query records zoeken op de waarden van hun velden, eventueel gesorteerd, als zoekopdracht; 10 per aanroep tenzij het model er meer vraagt, maximaal 50
datastore__<id>__put een record schrijven of in zijn geheel vervangen; zonder sleutel maakt Core er een aan
datastore__<id>__delete één record verwijderen met zijn sleutel

<id> is het id van de datastore, niet zijn naam. De tools staan niet in het Overzicht van Tools, dat voor datastores naar Datastores verwijst; in de keuzelijst Tools toevoegen eindigt de omschrijving van elke tool met de naam van de datastore tussen haakjes. Geef een agent een datastore door de tools te kiezen bij Tools in het formulier van de agent, of neem ze op in een toolset, bijvoorbeeld met het patroon datastore__<id>__*. Een agent die alleen mag lezen, krijgt alleen get, list en query.

Het model ziet de naam en de omschrijving van de datastore niet. Leg daarom in de instructies van de agent uit wat er in de datastore staat en hoe de records eruitzien.

De tool query vertelt het model welke bewerkingen er zijn. Heeft de datastore een schema, dan noemt de tool ook maximaal vijftig van de gedeclareerde velden met hun type. Een veld wordt daar alleen genoemd als zijn naam begint met een gewone letter (a tot en met z, A tot en met Z) of een underscore, gevolgd door maximaal 63 gewone letters, cijfers, underscores, punten of koppeltekens. Een veld met een andere naam, bijvoorbeeld met een letter met accent of een valutateken, is nog steeds in een zoekopdracht te gebruiken, dus noem het in de instructies. Heeft een zoekopdracht meer records dan één aanroep teruggeeft, dan krijgt het model een cursor om de volgende op te vragen.

Een schrijfactie van een tool voert dezelfde controles uit als de console: schema, verwijzingen en grootte. Het model krijgt het antwoord meteen, maar het record wordt pas opgeslagen aan het eind van de beurt. Verandert er in de tussentijd iets waardoor de schrijfactie alsnog niet kan (iemand verwijdert het record waarnaar verwezen wordt, bijvoorbeeld), dan wordt alleen die schrijfactie overgeslagen en in de audit vastgelegd. De rest van de beurt blijft staan.

Elk record dat de tools tonen draagt zijn etag. Het model kan die als if_match meegeven aan put of delete, en put neemt ook if_absent: true om een record alleen aan te maken. Core weigert zo'n schrijfactie meteen als het record is gewijzigd sinds het model het las, en nogmaals aan het eind van de beurt als iemand anders het intussen wijzigde; dan wordt alleen die schrijfactie overgeslagen, zoals hierboven.

Een beurt voor een persoon leest de eigen records van die persoon en de gedeelde, en schrijft in de records van die persoon. Een beurt zonder persoon leest en schrijft de gedeelde records.

Toegang van een sleutel

Wat een sleutel met datastores mag, stel je in bij Sleutels:

  • De capability datastores moet aanstaan op de sleutel. Zonder die capability antwoorden de routes met 404 en kan een beurt de tools niet gebruiken.
  • De regels over Datastores in de policy van de sleutel bepalen welke datastores de sleutel bereikt, met naampatronen zoals klanten-*. Een datastore die geen regel toestaat ontbreekt in lijsten, en zijn tools zijn niet te gebruiken.
  • De regels over Datastore-rijen (Regels over een persoon) bepalen welke records een persoon bereikt. Onder de standaardregels zijn dat de eigen records van die persoon en de gedeelde.

De capability heeft twee instellingen die de console niet toont: read_tools (de tools get, list en query) en write_tools (de tools put en delete). Beide staan standaard aan. Een sleutel waarop write_tools uit staat, kan in een beurt alleen lezen.

Via de API

Een sleutel met de capability datastores kan alles wat deze pagina beschrijft ook via de API, onder /datastores: datastores aanmaken, schemaversies schrijven en records lezen, schrijven en verwijderen. De recordlijst filtert ook op het begin van een sleutel of op metadata, en je kunt een record laten aanmaken onder een sleutel die Core kiest. Een verzoek voor een persoon leest dan de records van die persoon en de gedeelde, en schrijft en verwijdert de records van die persoon.

POST /datastores/{datastore_id}/records/query voert een zoekopdracht uit: filters en sort in de body, limit en cursor in het adres, en een pagina records als antwoord. Het voorbeeld toont één verzoek met zijn antwoord.

De routes, hun velden en elke status staan in de API-referentie.

Schrijven zonder een ander te overschrijven

Elk record dat je leest draagt een etag, die GET en PUT ook als header ETag meesturen. Stuur hem terug in If-Match bij de volgende PUT of DELETE:

curl -s -X PUT "$CORE_URL/datastores/ds_7Kp2.../records/wagen-9" \
  -H "Authorization: Bearer $CORE_KEY" -H 'Content-Type: application/json' \
  -H 'If-Match: "dsr_01J....3"' \
  -d '{"data": {"artikelen": ["TSHIRT-ROOD-M"]}}'

Heeft iemand het record geschreven of verwijderd sinds je het las, dan antwoordt Core met 412 en schrijft niets. Lees het record opnieuw en beslis op wat het nu bevat. Stuur in plaats daarvan If-None-Match: * om een record alleen aan te maken zolang de sleutel vrij is; een bezette sleutel geeft ook 412.

Meerdere schrijfacties tegelijk

POST /datastores/batch neemt tot honderd bewerkingen en voert ze allemaal uit of geen enkele. Een bewerking is een put of delete van een record, elk eventueel met if_match (en een put met if_absent), of een counter die het volgende nummer neemt. Een put kan zijn sleutel met key_from uit een eerdere teller halen. Een bestelling plaatsen, bijvoorbeeld:

{"operations": [
  {"op": "counter", "datastore_id": "ds_orders", "counter": "orders"},
  {"op": "put", "datastore_id": "ds_orders", "key_from": {"operation": 0, "prefix": "O-"},
   "data": {"sku": "TSHIRT-ROOD-M", "aantal": 1}, "if_absent": true},
  {"op": "put", "datastore_id": "ds_stock", "key": "TSHIRT-ROOD-M",
   "data": {"over": 4}, "if_match": "\"dsr_01J....7\""}
]}

Zo krijgt de bestelling een nummer (O-1, O-2, ...), wordt ze opgeslagen en daalt de voorraad, of gebeurt er niets als de voorraad veranderde sinds je hem las. Elke bewerking wordt gecontroleerd zoals haar eigen route dat doet. Een weigering noemt de bewerking, als operations.2.data.over of een melding die begint met Operation 2:. Het antwoord geeft per bewerking de sleutel, de etag en het nummer van een teller.

Nummeren

POST /datastores/{datastore_id}/counters/{name}/next geeft het volgende nummer van een teller van de datastore: de eerste keer 1, daarna 2, 3 enzovoort. Een verzoek dat mislukt geeft zijn nummer terug, dus de nummers hebben geen gaten. De naam van een teller volgt de regels van een datastorenaam.

Een uniek veld

Een schemaversie kan unique op één veld op het hoogste niveau zetten, zoals "unique": "sku". Zolang dat de laatste versie is, hebben geen twee records daar dezelfde waarde: een tweede geeft 409 met de naam van het veld. Het geldt per eigenaar: de gedeelde records en de eigen records van elke persoon mogen een waarde elk één keer hebben. De waarde moet tekst of een geheel getal zijn, en een record zonder het veld is vrij. unique instellen op records die al een waarde delen, wordt geweigerd met 409.

Een functie die elke schrijfactie beoordeelt

Een datastore kan een functie noemen die elke put en delete van zijn records ziet voordat er iets wordt opgeslagen, zoals de regels van een bestelling: een betaalde bestelling mag worden verzonden maar nooit terug naar de winkelwagen, en haar total is altijd de som van haar regels. Stel write_function in bij het aanmaken van de datastore of met PATCH:

{"write_function": {"name": "order_rules", "version": null}}

version legt één gepubliceerde versie vast; null draait bij elke schrijfactie de laatste gepubliceerde versie. De functie moet gepubliceerd zijn en geen reads en geen writes declareren, en de sleutel die hem instelt heeft tooling nodig en een policy die function__<name> laat zien en gebruiken; anders wordt het verzoek geweigerd met 422. Een functie die de sleutel niet mag zien, geeft dezelfde 422 als een functie die niet bestaat. Een sleutel die alleen records schrijft, heeft geen tooling nodig. null voor write_function haalt de schrijffunctie weg.

De argumenten van de functie zijn de schrijfactie en het record dat ze vervangt:

{"operation": "put", "datastore": "orders", "key": "O-1042",
 "data": {"status": "paid", "lines": [{"price": 5, "quantity": 2}]},
 "previous": {"data": {"status": "cart", "lines": [{"price": 5, "quantity": 2}], "total": 10},
              "etag": "\"dsr_01J….3\""}}

Een delete heeft geen data; previous is null voor een nieuwe sleutel. Bij een put voor een persoon is previous het eigen record van die persoon, nooit het gedeelde; bij een delete is het het record dat de delete zou verwijderen. key is null bij een put in een batch waarvan een teller in dezelfde batch de sleutel maakt. De eigen parameters van de functie toetsen dit document; een document dat ze weigeren geeft invalid_arguments. De functie antwoordt een van deze:

Antwoord Wat er gebeurt
{"data": {...}} een put slaat dit record op; het wordt getoetst aan het schema zoals elk record, dus de functie mag velden zoals total invullen
{} een delete gaat door
{"refuse": {"reason": "Een betaalde bestelling kan niet terug naar de winkelwagen."}} er wordt niets geschreven; het antwoord is 422 met de reden in refusal. De reden is 1 tot 500 tekens; een andere geeft invalid_response

Beslist de functie niets, dan is het antwoord 422 zonder dat er iets is geschreven, met refusal null en error_code dat zegt hoe ze eindigde:

error_code De functie
trapped stopte op een fout
fuel_exhausted, memory_exceeded, timeout heeft geen brandstof, geheugen of tijd meer
invalid_arguments kreeg een document dat haar parameters weigeren
invalid_response antwoordde iets dat geen van de antwoorden hierboven is, of dat haar output_schema weigert
executor_failed kon niet draaien: ze is verwijderd, heeft geen gepubliceerde versie met het vastgelegde nummer, haar laatste versie leest of schrijft nu, of Core had op dat moment geen ruimte om haar te draaien

De functie draait op elke weg waarlangs geschreven wordt: een losse schrijfactie, een batch, de tools put en delete van een agent en de eigen schrijfacties van een functie. Bij een losse schrijfactie en een batch draait Core haar opnieuw op wat nu is opgeslagen als iemand anders het record wijzigt terwijl ze draait -- of als de datastore intussen een andere schrijffunctie krijgt --, tot drie keer extra, en antwoordt daarna 409. De tools van een agent draaien de functie wanneer de agent ze aanroept: een weigering bereikt het model als resultaat van de tool, met de reden erin, en een record dat verandert voordat de beurt eindigt, laat de schrijfactie vallen als conflict, zonder de functie opnieuw te draaien.

Een batch schrijft één sleutel van zo'n datastore één keer; een tweede schrijfactie ervan geeft 422 op operations.<i>.key. Ook de beurt van een agent schrijft zo'n sleutel één keer; een tweede schrijfactie wordt geweigerd op het moment dat de agent hem doet. Een eigen termijn, de bewaartermijn en wissen verwijderen records zonder de functie te vragen.

Een eigen termijn en de boekhouding

record_ttl_days (1 tot en met 3650) op een datastore, in te stellen bij het aanmaken of met PATCH, verwijdert de records zodra ze zoveel dagen niet zijn gewijzigd, wat de bewaartermijn van de organisatie ook zegt. null zet het uit. retention: "keep" bewaart de records voorbij de bewaartermijn van de organisatie; "window", de standaard, laat ze eronder vallen. retained_for: "bookkeeping" is voor records die je moet bewaren, zoals bestellingen en facturen; het vereist record_ttl_days. Zie Bewaren en wissen.

Schrijfacties aankondigen

Zet emit_events: true bij het aanmaken van een datastore, of met PATCH, en elk geschreven of verwijderd record levert een gebeurtenis datastore.record.written of datastore.record.deleted op: datastore_id, datastore (de naam), key, revision en, bij een schrijfactie, created; nooit de gegevens van het record. Elke bewerking van een batch levert een eigen gebeurtenis op.

Een workflowtrigger neemt één event_kind, dus een trigger op datastore.record.written hoort geen verwijderingen; een tweede trigger op datastore.record.deleted wel. Het filter kiest de datastore op naam en de trigger start voor elke gebeurtenis een run:

curl -s -X POST "$CORE_URL/workflow_triggers" \
    -H "Authorization: Bearer $CORE_KEY" -H "Content-Type: application/json" \
    -d '{"workflow": "fulfil-order",
         "event_kind": "datastore.record.written",
         "filter": {"datastore": "orders"}}'

Een geweigerde schrijfactie en een teruggedraaide batch leveren geen gebeurtenis op. Records die verlopen, met de bewaartermijn van de organisatie verdwijnen of met een persoon worden gewist leveren er ook geen op, net als de records die verdwijnen als je de datastore verwijdert.

Bewaren en wissen

Records vallen onder de bewaartermijn van de organisatie. Een record dat langer dan die termijn niet is gewijzigd, wordt verwijderd; het tijdstip van Gewijzigd telt, niet dat van aanmaken. De datastores zelf en hun schemaversies blijven staan. Een datastore met een eigen record_ttl_days verwijdert zijn records ook op die termijn.

Een datastore met retention: "keep" (Bewaren tot verwijderd in de console) valt niet onder de bewaartermijn van de organisatie: gebruik hem voor records die je bijhoudt in plaats van ontvangt, zoals een productcatalogus, klantprofielen of instellingen, die niet mogen verdwijnen omdat de organisatie haar gesprekken bijvoorbeeld na dertig dagen verwijdert. Een eigen record_ttl_days geldt nog steeds, en wis je een persoon, dan verdwijnen haar records ook hier. Je stelt het in bij het aanmaken of wijzigt het met PATCH; laat je het veld weg uit een PATCH, dan blijft het zoals het was.

Wis je een persoon (Een persoon wissen), dan verdwijnen ook de records van die persoon, ook in een datastore met retention: "keep". Alleen een datastore met retained_for: "bookkeeping" houdt ze vast: die records blijven, nog altijd als de hare gemarkeerd, tot hun record_ttl_days is verstreken, en de bewaartermijn van de organisatie verwijdert ze niet, wat retention ook zegt. Het wissen telt ze als datastore_records_retained. Core houdt geen lijst van gewiste personen bij, dus een later verzoek met het id van dezelfde persoon bereikt die records weer. Zo'n datastore is niet te verwijderen (409) tot je retained_for met PATCH leegmaakt; de records worden dan weer gewone records.

Lege lijsten en toegangsbeperkingen

"Nog geen datastores" betekent dat er voor deze organisatie nog geen datastore is aangemaakt. "Nog geen records." betekent dat de datastore leeg is.

Zie je datastores staat uit voor deze organisatie, dan staat die capability uit in het plafond van de organisatie. De beheerder van de organisatie erboven zet het aan bij Toegestane capabilities op Instellingen; voor een organisatie zonder bovenliggende organisatie doet je operator dat.

"Datastore niet gevonden" betekent dat de datastore is verwijderd, of dat je organisatie er geen toegang toe heeft.