Kijkje in de keuken: Hoe Walter Living binnen miliseconden zoekt in negen miljoen adressen

Kijkje in de keuken: Hoe Walter Living binnen miliseconden zoekt in negen miljoen adressen
Walter Living vertelt je alles wat er over een specifiek huis te weten valt, geplaatst in de context van buurt, wijk en stad.

Met Walter Living doe je een haarscherp bod op je volgende huis. Wij vertellen je alles wat er over een specifiek huis te weten valt, geplaatst in de context van buurt, wijk en stad. Om dat te doen combineren en analyseren we meerdere grote datasets in een paar seconden. Maar hoe doen we dat eigenlijk?

In deze serie technische artikelen nemen we je mee achter de schermen bij Walter, om een kijkje te geven in onze geografische datakeuken. In dit eerste deel: hoe doorzoek je zo snel mogelijk negen miljoen adressen?

De Basis Registratie Adressen en Gebouwen

Het Kadaster is verantwoordelijk voor de registratie van alle grond en vastgoed in Nederland. Zij houden bij wie de eigenaar is van elk stukje grond en van de gebouwen die op die grond staan. Deze informatie wordt vastgelegd in de Basisregistratie Adressen en Gebouwen, oftewel de BAG.

Omdat Walter Living over elk adres in Nederland iets wil zeggen, is het cruciaal om een lijst met alle adressen te hebben en up-to-date te houden. Het beginpunt van elke zoektocht bij Walter Living is namelijk een autocomplete zoekbalk die elk adres van Nederland binnen een paar tellen uit de lijst, de BAG, tevoorschijn tovert.

De uitdaging is dat het hier gaat om meer dan negen miljoen adressen. Hoe krijg je al deze data in een fatsoenlijk formaat ingelezen en hoe zorg je vervolgens dat je er betrouwbare zoekresultaten uit kan halen met minder dan een paar milliseconden latency?

Er zijn een paar specifieke uitdagingen aan dit probleem:

  1. De BAG is een open dataset die voor iedereen in Nederland gratis te gebruiken is. Helaas is het formaat niet gemaakt om doorzoekbaar te zijn in een webapplicatie. Er is dus wat magie nodig om de databestanden te converteren en importeren naar een bruikbaar formaat.
  2. Als de BAG-bestanden eenmaal zijn ingelezen, moeten ze worden geïndexeerd om de bestanden doorzoekbaar te maken. Dit is de eenvoudigste stap – met behulp van Elasticsearch en default index mappings komen we een heel eind.
  3. De grootste uitdaging is het daadwerkelijk doorzoeken van adressen. De conventies van adressen kunnen nogal verschillen tussen woonplaatsen, buurten en zelfs straten. Ondanks dappere pogingen van het Kadaster, zijn adressen in Nederland niet zo netjes en duidelijk aangeduid als je zou hopen.

Dataconversie en import

Het voorbereiden van meer dan negen miljoen XML-objecten om te importeren in PostgreSQL is niet bepaald onze hobby. Gelukkig hebben de knappe koppen van NLExtract open source tools beschikbaar gemaakt waarmee de ruwe BAG-bestanden van het Kadaster kunnen worden geconverteerd en geïmporteerd in een Postgres-database, compleet voorzien van de juiste PostGIS-informatie en indices. Ze bieden zelfs elke maand een compleet geprepareerde Postgres-dump aan die je direct in je eigen database kan inlezen.

Omdat wij nu alleen interesse hebben in het doorzoekbaar maken van alle adressen, beperken wij ons tot de bagactueel tabel uit deze NLExtract dataset. Momenteel zijn dat 9.152.803 adressen in Nederland, om precies te zijn. Op naar de volgende stap: indexeren in Elasticsearch.

De uitdaging zit hem dan ook niet in het doorzoeken, maar in het stellen van de juiste vraag.

Indexeren in Elasticsearch

Negen miljoen records doorzoeken is een eitje voor Elasticsearch. De uitdaging zit hem dan ook niet in het doorzoeken, maar in het stellen van de juiste vraag. Omdat wij je helpen bij het doen van een haarscherp bod op je volgende huis, moeten we adressen kunnen vinden van woningen die te koop staan, bijvoorbeeld op Funda of JAAP.

We kwamen er al snel achter dat lang niet alle makelaars netjes de adressen uit de BAG gebruiken als ze een huis te koop aanbieden. Zo kan de “Dorpstraat 23-3 in Alphen aan den Rijn” online te vinden zijn als de “Dorpstraat 23-III in Alphen a/d Rijn”, of “Prinseneiland 12-H in Amsterdam” als “Prinseneiland 12-huis in A’dam”.

Onze Elasticsearch-index heeft dus een aardig setje filters en normalizers nodig om dit soort inconsistenties zo goed mogelijk te filteren, zodat we in de meeste gevallen een resultaat kunnen vinden voor je zoekopdracht. We doen al dit werk vóór het indexeren, zodat we de search queries naar Elasticsearch betrekkelijk eenvoudig kunnen houden.

Een willekeurige greep:

  • Het roman_number_filter maakt van Romeinse cijfers (I, IX, XII) de gangbare decimale getallen (1, 9, 12). Daardoor kan je bij Walter zoeken op “Dorpstraat 23-III” en vinden we de “Dorpstraat 23-3” voor je;
  • Het number_unit_filter splitst huisnummers en toevoegingen en zorgt dat ze altijd in eenzelfde formaat worden geïndexeerd. Hierdoor kan je zoeken op “Dorpstraat 23 III” en vinden wij “Dorpstraat 23-3” voor je;
  • Het dash_filter haalt liggende streepjes uit adressen en vervangt ze door een spatie. Dat maakt het makkelijker om adressen te vinden die streepjes bevatten, zonder dat jij eraan hoeft te denken om die streepjes op de juiste plek te tikken;

Tenslotte splitsen we zelf het adres op in een aantal combinaties en slaan we een geolocatie op bij het adres. Hiermee kunnen we makkelijker adressen vinden binnen een bepaald gebied in onze infrastructuur.

Het indexeren van de BAG in Elasticsearch duurt op deze manier een uur. Zodra dat klaar is hebben we krap 11 GB aan data die we binnen milliseconden kunnen doorzoeken.

Doorzoekbaar maken

Nu we alle adressen uit de BAG geïndexeerd hebben, kunnen we zoekopdrachten op ons Elasticsearch-cluster gaan afvuren.

Tijdens het zoeken proberen we een zo goed mogelijke query voor Elasticsearch op te stellen. Als we geen cijfers in je input vinden, heeft het geen zin om een query te doen die het huisnummer een boost geeft. En als we een postcode aantreffen, kunnen we daar juist een flinke boost aan geven om alleen adressen in die postcode te vinden.

Uiteindelijk bestaat onze query uit een bool query met een aantal must en should clauses daarin, met match op één of meer van de velden die we tijdens het indexeren hebben gedefinieerd en opgeslagen.

Daarna maken we eenheidsworst van de BAG.

Converteer, indexeer, importeer, zoek, repeat

Een goede inrichting van onze data helpt ons met het doorzoeken van alle 9.152.803 adressen in Nederland met zo min mogelijk latency. We doen dit door alle beschikbare data met NLExtract-tools klaar te maken, zodat ze zo snel mogelijk geanalyseerd kunnen worden. Dankzij het gebruik van een aantal filters maken we daarna eenheidsworst van de BAG, een database die niet gemaakt is om snel doorzocht te worden.

De BAG is maar één van de grote datasets die we importeren. We gebruiken data van tientallen bronnen om waardevolle inzichten te kunnen geven over een huis. Maar daar de volgende keer meer over.

Meer verhalen van Walter Living

Wees slim met bieden en onderhandelen (huis kopen deel 3)
Marcel de Graaf |

Wees slim met bieden en onderhandelen (huis kopen deel 3)

Onderhandelen, een cruciale stap als je een huis gaat kopen. In Huis Kopen deel III geven je onze tips op een rijtje.

Huis kopen & bieden · 6 minuten leestijd
Koffie met Walter: Hoe wij van data een goed onderbouwd verhaal brouwen in 5 stappen.
Edwin Hans |

Koffie met Walter: Hoe wij van data een goed onderbouwd verhaal brouwen in 5 stappen.

Bij Walter Living zetten we alles in om je te helpen slimme keuzes te maken tijdens je huizenjacht. Daarvoor verzamelen en verwerken we alle relevante data over de huizenmarkt. Hoe dat gaat, is een beetje zoals het zetten van een kopje koffie.

Opiniestukken · 5 minuten leestijd

Klaar om Walter te proberen?

Maak alle juiste huizenjacht keuzes — Geen BS. Beloofd.
Probeer Walter gratis
Alle verhalen
Volgend verhaal
Wees slim met bieden en onderhandelen (huis kopen deel 3)