Retour d’expérience

Une API Elasticsearch Spring Boot contre les doublettes de plaques

Sur une autoroute en flux libre, la barrière a disparu : un portique lit la plaque, et l’usager règle son passage ensuite. Ce modèle repose sur une hypothèse fragile, à savoir que la plaque lue correspond bien au véhicule qui roule. Voici comment une API Elasticsearch Spring Boot, alimentée par un flux Talend, aide à repérer les fausses plaques en temps réel.

8 min de lecture

  • Flux libre
  • Talend
  • Elasticsearch
  • Spring Boot
  • RGPD
  • CI/CD GitLab
Portique autoroutier équipé de caméras de lecture de plaques au-dessus de voies où circulent des voitures
  • 72 hpour régler son passage sous un portique en flux libre
  • 3verdicts renvoyés par l’API : cohérent, incohérent, plaque inconnue
  • 2régimes de chargement : initial et incrémental
  • 2étages de tests dans la chaîne GitLab CI

Sous un portique, la plaque devient presque la seule preuve d’identité du véhicule. Tout le projet décrit ici part de ce constat.

01

Le flux libre et le problème des doublettes

Une doublette : deux véhicules pour une seule plaque, et l’avis de paiement qui arrive chez la mauvaise personne.

Le principe est simple. Des portiques équipés de caméras remplacent les cabines, la lecture automatisée de plaques (LAPI) identifie chaque véhicule qui passe à pleine vitesse, et le conducteur sans badge paie après coup, en ligne ou chez un buraliste agréé, dans un délai de 72 heures. Plusieurs axes français fonctionnent déjà ainsi.

Le revers est connu des automobilistes : la doublette. Un véhicule circule avec la plaque d’un autre, et c’est le titulaire légitime qui reçoit l’avis de paiement. Avec une barrière, un agent ou une caméra de cabine voyait la voiture de près. Sous un portique, la plaque devient presque la seule preuve d’identité du véhicule.

D’où une idée de bon sens. Si la caméra reconnaît une berline d’une marque donnée et que la plaque appartient, selon le fichier officiel, à un utilitaire d’une autre marque, quelque chose cloche. Encore faut-il pouvoir interroger ce fichier en quelques millisecondes, à chaque passage.

02

Le cas : une API de vérification pour un concessionnaire autoroutier

Le cas se déroule chez un concessionnaire autoroutier qui déploie le flux libre. L’objectif du projet était précis : exposer une API REST capable de renvoyer, à partir d’une plaque, les caractéristiques du véhicule immatriculé, pour les confronter à ce que les portiques observent.

L’équipe a construit la chaîne complète :

  1. Talend DMPintégration du catalogue issu du fichier ministériel
  2. Normalisationredressement des marques et des modèles
  3. Elasticsearchindexation des véhicules
  4. Spring Bootbackend Java qui expose l’API REST
  5. GitLab CI/CDtests unitaires et fonctionnels, conteneur Docker

Sur le papier, la partie la plus visible est l’API. Dans les faits, la qualité de la réponse dépend presque entièrement de ce qui se passe en amont, dans le flux de données.

03

Nettoyer le fichier ministériel avec Talend

Un fichier administratif de véhicules a été alimenté pendant des années par des sources multiples. Le même constructeur y apparaît sous plusieurs graphies, les modèles mélangent nom commercial et désignation technique, les accents vont et viennent. Comparer « ce que voit la caméra » à « ce que dit le fichier » sans nettoyage préalable produirait surtout des faux positifs.

Le flux Talend a donc porté l’essentiel du redressement :

  • mise en majuscules, suppression des accents et des caractères parasites ;
  • table de correspondance des marques, pour ramener toutes les variantes vers un libellé de référence ;
  • regroupement des modèles par famille, quand la désignation technique est trop fine pour être comparée à une classification caméra ;
  • rejet tracé et visible des lignes inexploitables.
Exemple illustratif : quatre graphies d’un même constructeur ramenées à un seul libellé.

Nos experts recommandent toujours de garder la valeur brute à côté de la valeur normalisée. Le jour où un utilisateur conteste un résultat, on veut pouvoir montrer exactement ce que contenait la source.

Chargement initial et mises à jour incrémentales

Le projet a distingué deux régimes. Le chargement initial reprend tout le catalogue et construit l’index de zéro. Les mises à jour incrémentales, elles, n’apportent que les créations, modifications et radiations survenues depuis le dernier passage.

Le piège classique se cache dans les suppressions.Un fichier incrémental qui ne contient que les nouveautés oublie facilement les véhicules retirés de la circulation, et l’index garde alors des fiches fantômes.

Il faut un indicateur explicite de radiation dans le flux, ou un rapprochement périodique complet pour purger ce qui a disparu. Les deux approches se combinent bien : l’incrémental au quotidien, une réconciliation complète à intervalle régulier.

04

Indexer dans Elasticsearch pour des recherches rapides

Pourquoi Elasticsearch, alors qu’une base relationnelle sait très bien chercher une clé ? Pour la tolérance. Une plaque lue par une caméra arrive avec ou sans tirets, parfois avec des espaces, en majuscules ou non. Le normalizer d’Elasticsearch permet de ramener toutes ces formes à une seule clé, à l’indexation comme à la recherche.

Voici un mapping qui va dans ce sens :

JSON
PUT /vehicules
{
  "settings": {
    "analysis": {
      "char_filter": {
        "sans_separateurs": {
          "type": "pattern_replace",
          "pattern": "[\\s\\-\\.]",
          "replacement": ""
        }
      },
      "normalizer": {
        "plaque_normalizer": {
          "type": "custom",
          "char_filter": ["sans_separateurs"],
          "filter": ["lowercase", "asciifolding"]
        }
      }
    }
  },
  "mappings": {
    "properties": {
      "plaque":             { "type": "keyword", "normalizer": "plaque_normalizer" },
      "marque_brute":       { "type": "keyword" },
      "marque_normalisee":  { "type": "keyword" },
      "famille_modele":     { "type": "keyword" },
      "categorie":          { "type": "keyword" },
      "date_mise_a_jour":   { "type": "date" },
      "radie":              { "type": "boolean" }
    }
  }
}

Le normalizer s’applique à un champ keyword : la plaque reste une valeur exacte, simplement débarrassée de ses séparateurs et de sa casse. Une recherche sur « ab-123-cd » ou « AB 123 CD » tombe sur le même document.

Le normalizer en action

Trois lectures, une seule clé d’index

Ce que fait plaque_normalizer : retrait des espaces, tirets et points, passage en minuscules, suppression des accents. Saisissez une plaque pour voir la clé réellement comparée.

ab123cdclé indexée et recherchée
  • ab-123-cd→ ab123cd
  • AB 123 CD→ ab123cd
  • AB.123.CD→ ab123cd

Un détail compte pour les performances : on interroge en term sur ce champ, en contexte de filtre. Pas de calcul de score, des résultats mis en cache, et des temps de réponse compatibles avec un portique qui envoie des lectures en continu.

Recharger l’index sans interrompre le service

Le chargement initial pose une question pratique : comment reconstruire tout le catalogue pendant que les portiques continuent d’appeler l’API ? La réponse passe par un alias. L’API passe toujours par l’alias vehicules, quel que soit l’index qui se trouve derrière. Le flux Talend construit un nouvel index daté, l’équipe vérifie les volumes et quelques plaques témoins, puis l’alias bascule d’un index à l’autre en une seule opération atomique.

API Spring Bootappelée par les portiques
Alias vehiculesseul nom connu de l’API
Index en servicepointé par l’alias
Nouvel index datéen construction par Talend
Pendant la reconstruction : l’alias pointe toujours vers l’index en service, Talend remplit le nouvel index daté à côté.

En cas de problème, on repointe l’alias vers l’ancien index et le service n’a rien vu. Ce mécanisme coûte quelques lignes de script et évite les nuits blanches.

05

Construire l’API Elasticsearch Spring Boot

Le backend Spring Boot reste volontairement mince. Il reçoit une plaque, interroge l’index et renvoie une fiche véhicule réduite au strict nécessaire.

Java
@RestController
@RequestMapping("/api/v1/vehicules")
public class VehiculeController {

    private final VehiculeService vehiculeService;

    public VehiculeController(VehiculeService vehiculeService) {
        this.vehiculeService = vehiculeService;
    }

    @GetMapping("/{plaque}")
    public ResponseEntity<VehiculeDto> rechercher(@PathVariable String plaque) {
        return vehiculeService.trouverParPlaque(plaque)
                .map(ResponseEntity::ok)
                .orElse(ResponseEntity.notFound().build());
    }

    @PostMapping("/verification")
    public VerificationDto verifier(@Valid @RequestBody ObservationDto observation) {
        return vehiculeService.verifier(observation);
    }
}

La seconde route est celle qui compte pour la fraude. Elle reçoit ce que le portique a observé (plaque, marque reconnue, catégorie) et renvoie un verdict : cohérent, incohérent, ou plaque inconnue. La logique de comparaison peut être portée par une requête Elasticsearch qui cherche la plaque tout en excluant les caractéristiques observées :

JSON
GET /vehicules/_search
{
  "query": {
    "bool": {
      "filter": [
        { "term": { "plaque": "AB-123-CD" } },
        { "term": { "radie": false } }
      ],
      "must_not": [
        { "term": { "marque_normalisee": "MARQUE_OBSERVEE" } },
        { "term": { "categorie": "CATEGORIE_OBSERVEE" } }
      ]
    }
  }
}

Si la plaque existe et que cette requête renvoie un document, la marque ou la catégorie enregistrées diffèrent de l’observation : la doublette devient une hypothèse sérieuse. Le mot hypothèse est important. Une erreur de lecture caméra ou une classification approximative peuvent produire le même signal. L’API alimente donc une file de contrôle, et la décision finale revient à un traitement humain.

Observation du portiqueplaque, marque reconnue, catégorie

Recherche de la plaque dans l’indexpuis comparaison des caractéristiques

  • Plaque absente de l’indexPlaque inconnue
  • Marque et catégorie identiquesCohérent
  • Marque ou catégorie différenteIncohérentEnvoi en file de contrôle, décision humaine
Les trois verdicts de la route de vérification. Un verdict incohérent reste une hypothèse, tranchée par un traitement humain.

Petit conseil de terrainExposez dans la réponse le niveau de correspondance (marque seule, catégorie seule, les deux). Les équipes métier ajustent beaucoup mieux leurs règles quand elles voient pourquoi un passage a été signalé.

06

Une plaque est une donnée personnelle

Une plaque d’immatriculation permet de remonter au titulaire du certificat. Les traitements qui la manipulent relèvent donc du RGPD, et la CNIL encadre de près les dispositifs de lecture automatisée, notamment sur leurs finalités et leurs durées de conservation.

Plaque d’immatriculation vierge posée sur un bureau à côté d’un cadenas fermé et d’un carnet
Une plaque permet de remonter au titulaire : les accès à l’API se protègent en conséquence.

Pour une API de ce type, quelques réflexes s’imposent :

  • renvoyer uniquement les attributs utiles à la vérification (marque, famille de modèle, catégorie), sans aucune donnée sur le titulaire ;
  • authentifier les appelants et journaliser les accès ;
  • définir avec le délégué à la protection des données une durée de conservation pour les traces de vérification ;
  • éviter de recopier des plaques réelles dans les jeux de tests, et générer des données fictives à la place.

Ce dernier point concerne directement la chaîne de livraison.

07

Tester et déployer : CI/CD GitLab et Docker

La chaîne GitLab CI a été pensée en deux étages de tests. Les tests unitaires vérifient la normalisation et la logique de verdict, sans dépendance externe. Les tests fonctionnels démarrent un Elasticsearch éphémère, chargent un petit jeu de véhicules fictifs et appellent l’API comme le ferait un portique.

C’est là que se jouent les régressions les plus sournoises. Une modification anodine de la table des marques peut faire basculer des milliers de verdicts. Un jeu de cas de référence, rejoué à chaque merge request, sert de filet : plaque avec tirets, plaque sans tirets, marque écrite de trois façons, véhicule radié.

  1. Merge requestdéclenche la chaîne
  2. Tests unitairesnormalisation et logique de verdict
  3. Tests fonctionnelsElasticsearch éphémère, véhicules fictifs
  4. Cas de référencerejoués à chaque merge request
  5. Conteneur Dockerla version testée est celle déployée
  • plaque avec tirets
  • plaque sans tirets
  • marque écrite de trois façons
  • véhicule radié
La chaîne de livraison et son filet de cas de référence.

L’application est livrée en conteneur Docker, ce qui garantit que la version testée en intégration est exactement celle déployée. Pour un service appelé en continu par des équipements de terrain, cette reproductibilité vaut cher.

08

Ce qu’on retient

Ce projet rappelle une évidence que l’on oublie vite : une API rapide ne vaut que par la donnée qu’elle sert. Le temps passé à redresser les marques et à fiabiliser les mises à jour incrémentales a plus pesé sur la qualité des verdicts que n’importe quel réglage de l’API. Elasticsearch apporte la tolérance de saisie et la vitesse, Spring Boot une couche de service simple à tester, et la CI/CD la confiance nécessaire pour faire évoluer les règles.

À retenir

  • La donnée source fait la qualité du verdict. Redresser marques et modèles a plus pesé que tout réglage de l’API.
  • Valeur brute et valeur normalisée côte à côte : c’est ce qui permet de justifier un résultat contesté.
  • Les radiations sont le point faible de l’incrémental. Un indicateur explicite ou une réconciliation complète régulière évite les fiches fantômes.
  • Un normalizer sur un champ keyword rend la recherche tolérante aux tirets, espaces et majuscules, avec une requête term en contexte de filtre.
  • Un alias d’index permet de reconstruire le catalogue sans coupure et de revenir en arrière en une opération.
  • Une doublette signalée reste une hypothèse, confirmée par un contrôle humain, et l’API n’expose aucune donnée sur le titulaire.

En résumé : la fiabilité d’un contrôle de plaque en temps réel se joue d’abord dans le flux de données, puis dans un index bien pensé et une chaîne de tests qui protège les règles métier.

Ntico Projets Digitaux

Du flux de données jusqu’à l’API qui le valorise

Chez Ntico, nous concevons ce type de chaîne de bout en bout, du flux d’intégration de données jusqu’à l’API qui la valorise, avec une attention forte à la qualité du code et à la conformité. Nos équipes Projets Digitaux couvrent l’architecture, le développement et le DevOps, du cadrage jusqu’à la mise en production.

Nos experts interviennent en renfort d’équipe, en squad produit ou en Build & Run, toujours avec un référent unique.

Vous avez un référentiel à exposer ou un contrôle à automatiser en temps réel ?

Article rédigé par la communauté SmartData Alexandre Millet-Bassez Leader de la communauté SmartData chez Ntico

SmartData réunit plus de 70 talents Data & IA chez Ntico et a déjà accompagné plus de 40 entreprises.

Toutes les actualités