Normaliser une adresse avec ElasticSearch et la base adresse nationale

Il y a fort fort longtemps, j’intégrais la base adresse nationale dans elasticsearch via logstash. Je ne suis pas allé plus loin faute de temps et peut-être d’envie.

Que peut-on faire avec ces quelques gigas de données me direz vous?

  • Faire des recherches pour avoir les coordonnées géographiques d’une adresse donnée
  • Faire une recherche pour avoir l’adresse normalisée
  • Faire un rapprochement avec d’autres données (avec Spark par exemple)
  • Sans doute plein d’autres use cases

Je vais m’attarder sur le deuxième point. A quoi ça sert ? et bien à avoir une adresse « propre » et utilisable par un système d’information (ex. ce que font les impôts, ou amazon). Il existe quelques solutions propriétaires qui réalisent ceci et sont assez chers. Je me suis donc mis dans la tête de le faire via elasticsearch.

Rappels des épisodes précédents

Voici le schéma de l’architecture

Présentation1

Pré-requis

Configuration Logstash

Voici ma configuration Logstash

input {
  file {
    codec =>plain{
    charset => "UTF-8"
    }
    path => ["./data/*.csv"]
    sincedb_path => "logstash/.sincedb_path"
    start_position => "beginning"
  }
}

filter {
	if [message] =~ /^"id";"nom_voie"/ {
	drop { }
	}
 	csv {
    columns => ["id","nom_voie","id_fantoir","numero","rep","code_insee","code_post","alias","nom_ld","nom_afnor","libelle_acheminement","x","y","lon","lat","nom_commune"]
    separator => ";"
	source => message
  }

  mutate {
    rename => [ "lat", "[location][lat]", "lon", "[location][lon]" ]
  }
   mutate {
    convert => [ "[location][lat]", "float" ]
    convert => [ "[location][lon]", "float" ]
    convert => [ "[lon]", "float" ]
    convert => [ "[lat]", "float" ]
    convert => ["[location]","float"]
    }
}


output {
        stdout{
        codec => "json"
        }
    elasticsearch {
    hosts => ["localhost"]
    index => "ban"
    document_type => "address"
    workers => 8
    document_id => "%{id}"
  }
}

J’ai configuré LOGSTASH pour réaliser les actions suivantes :

  • extraction des données d’un fichier
  • suppression de la ligne d’en-tête
  • gestion de la localisation avec un champ de type ‘geo_point’.

C’est assez simple (pour l’instant)

Mapping ELASTICSEARCH

Analyzers

J’ai configuré les analyzers de la manière suivante:

"analysis": {
      "analyzer": {
        "ngram_analyzer": {
          "type": "custom",
          "tokenizer": "whitespace",
          "filter": [
            "lowercase",
            "asciifolding",
            "ngram_filter"
          ]
        },
        "whitespace_analyzer": {
          "type": "custom",
          "tokenizer": "whitespace",
          "filter": [
            "lowercase",
            "asciifolding"
          ]
        }
      },
      "filter": {
        "ngram_filter": {
          "type": "nGram",
          "min_gram": 3,
          "max_gram": 15,
          "token_chars": [
            "letter",
            "digit",
            "punctuation",
            "symbol"
          ]
        }
      }
    }

J’ analyse tous les mots (whitespace tokenizer) en minuscule en appliquant un filtre (ngram) permet de rechercher par caractère (ex. imp au lieu de impasse).

La c’est super couteux en espace disque et impose un plus gros traitement lors de l’insertion mais permet d’ alléger le temps de traitement des requêtes ( le gros du travail est fait lors du chargement).

Mapping

La je n’ai pas fait grand chose de particulier si ce n’est le typage des différents champs (ex. la localisation )

"location": {
               "type": "geo_point"
       
          }

Création de l’index

Dans sense ou via cUrl, lancer la commande suivante :

PUT /ban{...}

Pour le mapping complet, voir sur mon compte github

Chargement

voir mon article précédent

Interrogation

Maintenant je peux interroger mon index

Imaginons que je fasse saisir le code postal, la ville et l’adresse et que j’interroge elasticsearch pour obtenir une adresse normalisée

#/GET /ban/address/_search
{
  "query": {
    "bool": {
      "must": [
        {
          "multi_match": {
            "query": "rue",
            "type": "best_fields",
            "fields": [
              "nom_ld",
              "nom_afnor"
            ]
          }
        },
        {
          "match": {
            "nom_commune": {
              "query": "ville"
            }
          }
        }
      ],
      "filter": [
        {
          "term": {
            "code_post": "00000"
          }
        }
      ]
    }
  }
}

Conclusion

Ce n’est qu’un début. Ce n’est sans doute pas encore très performant, si vous avez des remarques, n’hésitez pas. En tout cas, les briques et outils décrits ci-dessus permettent de normaliser des adresses a minima avec des logiciels libres, ce qui n’est pas rien.

Les sources sont disponibles sur GITHUB. Je ne pense pas que je vais en faire un projet à proprement parler mais plutôt d’une boîte à outils pour manipuler ce genre de données. J’y ajouterai sans doute quelques recherches géospatiales et peut être l’ intégration de graph.

Vus : 1135
Publié par Littlewing : 368