Ga naar hoofdinhoud

Full-text search

De OpenWoo-API biedt twee endpoints voor tekstueel zoeken. Welke je gebruikt hangt af van wat je wilt terugvinden:

  • Endpoint 1 doorzoekt alleen publicaties (op titel, samenvatting, thema en andere publicatie-velden).
  • Endpoint 2 doorzoekt publicaties én documenten in één resultatenset — geschikt voor een centrale zoekbalk.

Beide endpoints respecteren dezelfde toegangs- en zichtbaarheidsregels: gebruikers krijgen alleen resultaten waar ze op basis van hun rol en de status van de publicatie recht op hebben.

Eerst lezen

API-koppelvlak — algemene introductie tot de OpenWoo-API, authenticatie en datum-driven zichtbaarheid.

Wat je in gedachten moet houden

_search doet een letterlijke substring-match. Er zijn geen booleaanse operatoren (AND/OR), geen wildcards (*), geen phrase-quotes, geen fuzzy-tilde (~) en geen relevantie-boosts (^n). Voor typo-tolerantie is er een aparte parameter — zie Fuzzy search hieronder.

Endpoint 1 — Zoeken binnen publicaties

GET https://openwoo.commonground.nu/apps/opencatalogi/api/publications?_search=<query>

Doorzoekt alle publicaties in de WOO-catalogus waar de gebruiker toegang toe heeft. Matcht op alle tekst-velden van het publicatie-schema (title, summary, description, themes, …) plus de algemene metadata-velden.

Een publicatie telt als hit zodra één van deze velden de zoekterm bevat. De sortering volgt _order[<veld>] als je die opgeeft; zonder expliciete sortering is de volgorde niet gegarandeerd.

Deze variant is ideaal wanneer je resultaten wilt binnen één catalogus-context, bijvoorbeeld voor de publicatie-overzichtspagina van een organisatie.

Let op: het pad-segment publications is de slug van de catalog, geen vaste routenaam. Op een deployment zonder een catalog met deze slug krijg je HTTP 404 — Catalog not found. Op openwoo.commonground.nu is deze catalog standaard aanwezig.

Scope wordt bepaald door de catalog-configuratie. Endpoint 1 doorzoekt de schemas die in de catalog zijn geconfigureerd (registers + schemas op het catalog-object). Standaard bevat een verse catalog alleen het publication-schema, dus krijg je alleen publicaties terug — passend bij het "publicatie-scoped"-karakter van dit endpoint. Voegt een beheerder ook document (of andere schemas) toe aan de catalog, dan verschijnen die object-types hier ook. Wil je bewust een mixed envelope met documenten? Gebruik Endpoint 2 — dat endpoint negeert de catalog-scope en zoekt altijd over publicaties én documenten in één antwoord.

Endpoint 2 — Brede zoekopdracht over publicaties én documenten

GET https://openwoo.commonground.nu/apps/opencatalogi/api/search?_search=<query>

Doorzoekt publicaties en documenten die daaraan hangen. Het resultaat is een gemengde lijst waarin beide soorten objecten samen voorkomen; het veld @self.schema geeft per rij aan wat het is — een publicatie of een document.

Elk document dat als hit terugkomt draagt een verwijzing naar de bijbehorende publicatie mee:

{
"id": "…",
"title": "…",
"publication": { "id": "…", "slug": "…", "title": "…" },
"@self": { "schema": "document", "…": "…" }
}

Zo kan een zoekpagina één lijst tonen en per resultaat correct doorlinken naar de publicatie waar het document bijhoort. Documenten die geen geldige publication-verwijzing hebben (id + slug) verschijnen niet in de resultaten.

Wat wordt doorzocht: standaard de metadata van publicaties én documenten — dus titels, samenvattingen, bestandsnamen, MIME-types en overige tekst-velden op het schema. De inhoud van PDF- of DOCX-bestanden wordt optioneel meegenomen door _content=true aan de query toe te voegen — zie Zoeken in bestandsinhoud hieronder.

Vorm van @self.schema verschilt per endpoint: endpoint 2 geeft de slug ("publication" / "document"), endpoint 1 geeft het numerieke schema-ID als string ("15", "16"). Bouw je één card-renderer voor beide endpoints? Normaliseer dan aan de client-kant.

Endpoint 2 kan optioneel ook zoeken in de inhoud van bijgehangen documenten — de tekst uit PDF-, DOCX-, XLSX- en andere ondersteunde bestandsformaten. Dit is een opt-in via _content=true:

GET https://openwoo.commonground.nu/apps/opencatalogi/api/search?_search=<query>&_content=true

Zonder _content=true blijft het gedrag ongewijzigd (metadata-only). Met de flag worden documenten waarvan de body-tekst matcht toegevoegd aan het resultaat — dezelfde platte envelope, dezelfde @self.schema-discriminator, geen extra response-velden.

Hoe het werkt: OpenRegister extraheert de tekst uit elke document-upload via zijn eigen text-extractie-pipeline en indexeert de resulterende chunks. Endpoint 2 forward _content=true als _content_search=true naar OpenRegister; de matchende chunks worden terug-gemapt naar het bijbehorende document-object. OpenCatalogi doet zelf geen extractie of indexering.

Gedrag:

  • Dedup — een document dat zowel op metadata (titel, samenvatting) als op body-tekst matcht verschijnt éénmalig in de resultaten.
  • Zichtbaarheid — dezelfde zichtbaarheidsregel als de metadata-only variant: een document verschijnt alleen als de gelinkte publicatie op dit moment gepubliceerd is (publicatiedatum in het verleden, geen depublicatiedatum of één die nog in de toekomst ligt).
  • Extractie loopt asynchroon — vlak na upload kan een document nog niet doorzoekbaar zijn omdat de OR-indexeer-job nog niet gedraaid heeft. Retry na ~1 minuut.
  • Ranking database-afhankelijk — content-search draait op OR's PostgreSQL tsvector GIN-index (met ts_rank-scoring). Op MariaDB werkt de wire ook maar zonder ranking — een LIKE-fallback levert dezelfde matches, alleen ongesorteerd.

Voorbeeld:

GET https://openwoo.commonground.nu/apps/opencatalogi/api/search
?_search=stikstof
&_content=true
&_limit=10

Retourneert publicaties én documenten waarvan óf metadata óf body-tekst "stikstof" bevat.

Query-vorm & gedrag

De volgende regels gelden voor beide endpoints:

Wat je intyptWat er gebeurt
_search=verzoekMatcht "verzoek", "verzoeken", "Woo-verzoek", "aanvraagverzoeken" — substring op title/summary/description en overige tekst-velden
_search=verzoek vergunningWordt als één string behandeld, niet als "beide woorden"
_search="evenement vergunning"Quotes zijn onderdeel van de match — geen phrase-operator
_search=verzoek OR klachtOR is gewone tekst, geen operator
_search=evenem** is gewone tekst; zonder * matcht al "evenement", "evenementen", "evenementenvergunning"
_search=verzoek~~ is gewone tekst, geen fuzzy-operator

Wat wél klopt:

  • Case-insensitiveverzoek matcht Verzoek, VERZOEK.
  • Substring-match_search=enem matcht evenement, bedrijvenemissies.
  • Combineerbaar met filters?_search=verzoek&publicatiedatum[gte]=2026-01-01&_limit=20&_order[publicatiedatum]=desc werkt zoals verwacht.

Praktische tips voor consumenten:

  • Wil de gebruiker "beide woorden" matchen? Splits de query client-side of laat de UI meerdere zoektermen aanbieden — server-side ondersteunt dit niet.
  • Voor "lijkt op"-zoeken (typo-tolerantie): zie Fuzzy search.
  • Voor filtering op categorie of datum: gebruik echte query-parameters (@self[schema]=<id>, publicatiedatum[gte]=…) náást _search.

Voor typo-tolerantie is er een aparte parameter _fuzzy=true:

GET https://openwoo.commonground.nu/apps/opencatalogi/api/publications?_search=evenemnt&_fuzzy=true

Voegt een trigram-similariteit toe op het naamveld van elk object. Een rij komt terug als óf de gewone substring-match slaagt óf de naam voldoende lijkt op de zoekterm. Elke hit krijgt een @self.relevance-veld (geheel getal 0–100) — de score is de trigram-similariteit tussen zoekterm en het naamveld, dus zelfs een exacte substring-match kan een lagere score krijgen wanneer de zoekterm maar een klein deel van de volledige naam beslaat. Bij een actieve _search wordt standaard al op relevance aflopend gesorteerd; wil je expliciet forceren of omdraaien: _order[_relevance]=desc of _order[_relevance]=asc.

Beperkingen:

  • Werkt alleen op deployments met PostgreSQL en de pg_trgm-extensie ingeschakeld (openwoo.commonground.nu heeft dit aan staan).
  • Vergelijkt alleen op het naamveld — typos in titel, samenvatting of beschrijving profiteren niet.

Concrete voorbeelden

Zoekbalk met paginatie

GET https://openwoo.commonground.nu/apps/opencatalogi/api/publications
?_search=evenementenvergunning
&_order[publicatiedatum]=desc
&_limit=10
&_page=1

Zoekbalk met datumfilter

GET https://openwoo.commonground.nu/apps/opencatalogi/api/publications
?_search=evenementenvergunning
&publicatiedatum[gte]=2026-01-01
&publicatiedatum[lte]=2026-12-31
&_limit=20

Zoeken binnen één informatiecategorie

GET https://openwoo.commonground.nu/apps/opencatalogi/api/publications
?_search=convenant
&@self[schema]=<schema-id>
&_limit=10

@self[schema] filtert op één schema en verwacht het numerieke schema-ID (geen slug). Het ID is omgevings-specifiek — vraag op via een facet-call op je eigen omgeving:

GET https://openwoo.commonground.nu/apps/opencatalogi/api/publications
?_facetable=true
&_facets[@self][schema][type]=terms
&_limit=0

De buckets in het facets-blok geven per voorkomend schema-ID de count.

Centrale zoekbalk (publicaties + documenten)

GET https://openwoo.commonground.nu/apps/opencatalogi/api/search
?_search=evenementenvergunning
&_limit=10

Retourneert gemengde resultaten. Onderscheid maken tussen publicaties en documenten kan via het @self.schema-veld op elke rij.

Type-ahead met lichte payload

GET https://openwoo.commonground.nu/apps/opencatalogi/api/publications
?_search=evenem
&_limit=5
&_unset=attachments,beschrijving,bevindingen,conclusies

_unset laat de opgesomde velden weg uit elke resultaat-rij — handig om response-grootte klein te houden voor real-time suggesties.

Typo-tolerant zoeken

GET https://openwoo.commonground.nu/apps/opencatalogi/api/publications
?_search=evenemnt
&_fuzzy=true
&_order[_relevance]=desc
&_limit=10

Faceted-search UI

GET https://openwoo.commonground.nu/apps/opencatalogi/api/publications
?_search=evenementenvergunning
&_facetable=true
&_facets[@self][schema][type]=terms
&_facets[publicatiedatum][type]=date_histogram
&_facets[publicatiedatum][interval]=year
&_limit=10

Response bevat een facets-blok met buckets per veld, geschikt voor filter-checkboxes met counts.

Gotchas

SymptoomOorzaak / oplossing
_search=verzoek vergunning geeft minder hits dan verwachtWordt als één substring behandeld, niet als twee termen. Splits client-side of laat de UI losse velden aanbieden.
_search=WOZ matcht ook losse 'w', 'o', 'z'Substring-match is letterlijk; korte termen produceren veel false positives. Eis minimaal 3 karakters in de UI.
Inhoud van een PDF-bijlage komt niet terugStandaard wordt alleen metadata (bestandsnaam, MIME) doorzocht. Voeg _content=true toe aan de query om ook body-tekst mee te nemen — zie Zoeken in bestandsinhoud. Werkt de flag maar krijg je nog steeds niks? De OR-extractie loopt asynchroon; retry na ~1 min.
Document verschijnt niet in /api/search-resultatenDocumenten hebben een geldige publication-verwijzing met id én slug nodig om in de envelope te verschijnen.
_search=café matcht niet cafeDiacritics-normalisatie is deployment-afhankelijk. Strip diacritics client-side voor consistent gedrag.
Meervouden — verzoek vs verzoekenGeen stemming, maar substring helpt: _search=verzoek matcht ook verzoeken.
_search="evenement vergunning" doet niets bijzondersQuotes zijn geen phrase-delimiter. Strip ze client-side.
Volgorde lijkt willekeurig op pagina 2Zonder expliciete sortering is de volgorde niet gegarandeerd. Voeg altijd &_order[<veld>]=… toe.

Schrijfacties (POST / PUT / DELETE)

Anonieme toegang geldt alleen voor lezen. Voor schrijfacties (bijvoorbeeld publiceren namens een organisatie) is standaard Nextcloud-authenticatie nodig — Basic-auth, OAuth of een app-token. Neem contact op met info@conduction.nl voor productie-toegang.

OpenAPI

De volledige API-specificatie leeft onder /api/publications/ en /api/. Zie API-overzicht voor de sync-details.

Referentie-implementaties

  • woo-website-template-apiv2 — de publieke WOO-publicatiepagina; gebruikt beide endpoints met een faceted-search UI.
  • api-koppelvlak — generiek koppelvlak-overzicht inclusief metadata-schema's, datum-driven zichtbaarheid en de architectuur achter de API-lagen.