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.
API-koppelvlak — algemene introductie tot de OpenWoo-API, authenticatie en datum-driven zichtbaarheid.
_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
publicationsis de slug van de catalog, geen vaste routenaam. Op een deployment zonder een catalog met deze slug krijg jeHTTP 404 — Catalog not found. Opopenwoo.commonground.nuis deze catalog standaard aanwezig.
Scope wordt bepaald door de catalog-configuratie. Endpoint 1 doorzoekt de schemas die in de catalog zijn geconfigureerd (
registers+schemasop het catalog-object). Standaard bevat een verse catalog alleen hetpublication-schema, dus krijg je alleen publicaties terug — passend bij het "publicatie-scoped"-karakter van dit endpoint. Voegt een beheerder ookdocument(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.schemaverschilt 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.
Zoeken in bestandsinhoud (content-search)
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 (
publicatiedatumin het verleden, geendepublicatiedatumof éé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
tsvectorGIN-index (metts_rank-scoring). Op MariaDB werkt de wire ook maar zonder ranking — eenLIKE-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 intypt | Wat er gebeurt |
|---|---|
_search=verzoek | Matcht "verzoek", "verzoeken", "Woo-verzoek", "aanvraagverzoeken" — substring op title/summary/description en overige tekst-velden |
_search=verzoek vergunning | Wordt als één string behandeld, niet als "beide woorden" |
_search="evenement vergunning" | Quotes zijn onderdeel van de match — geen phrase-operator |
_search=verzoek OR klacht | OR 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-insensitive —
verzoekmatchtVerzoek,VERZOEK. - Substring-match —
_search=enemmatchtevenement,bedrijvenemissies. - Combineerbaar met filters —
?_search=verzoek&publicatiedatum[gte]=2026-01-01&_limit=20&_order[publicatiedatum]=descwerkt 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.
Fuzzy 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.nuheeft dit aan staan). - Vergelijkt alleen op het naamveld — typos in
titel,samenvattingofbeschrijvingprofiteren 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
| Symptoom | Oorzaak / oplossing |
|---|---|
_search=verzoek vergunning geeft minder hits dan verwacht | Wordt 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 terug | Standaard 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-resultaten | Documenten hebben een geldige publication-verwijzing met id én slug nodig om in de envelope te verschijnen. |
_search=café matcht niet cafe | Diacritics-normalisatie is deployment-afhankelijk. Strip diacritics client-side voor consistent gedrag. |
Meervouden — verzoek vs verzoeken | Geen stemming, maar substring helpt: _search=verzoek matcht ook verzoeken. |
_search="evenement vergunning" doet niets bijzonders | Quotes zijn geen phrase-delimiter. Strip ze client-side. |
| Volgorde lijkt willekeurig op pagina 2 | Zonder 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.