Skip to content

GeoLeaf Search Engine — Guide de configuration et benchmark

Module : @geoleaf/core — lazy chunk search

Version : 2.0.0

Moteur par défaut : native (fallback includes(), zéro dépendance)

Dernière mise à jour : mars 2026


Sommaire

  1. Vue d'ensemble
  2. Activation de FlexSearch
  3. Configuration du profil
  4. API publique GeoLeaf.Search
  5. Champs indexés
  6. Gestion incrémentale
  7. Benchmark
  8. Sécurité
  9. Migration depuis la recherche native

Vue d'ensemble

GeoLeaf propose deux moteurs de recherche full-text activables via le profil JSON :

MoteurConfigAvantagesInconvénients
native(défaut, aucune config)Zéro dépendance, ~0 KB supplémentaireO(n) par requête, pas de normalisation des accents avancée
flexsearchsearch.engine: "flexsearch"Sub-milliseconde (O(1)), prefix match, normalisation diacritiques, indexation incrémentaleChunk JS supplémentaire ~6 KB gzip (lazy-loadé)

Le moteur flexsearch est lazy-loadé : aucun impact sur le bundle initial si non activé.


Activation de FlexSearch

Via le profil JSON

json
{
    "search": {
        "engine": "flexsearch"
    }
}

Via chargement dynamique (ESM)

js
// Force le chargement du module search (normalement déclenché par GeoLeaf._loadModule)
await GeoLeaf._loadModule("search");

Configuration du profil

Champs indexés personnalisés

Par défaut, les champs title, label, name sont indexés. Pour personnaliser, utilisez la propriété search: true dans panels.detail.layout :

json
{
    "panels": {
        "detail": {
            "layout": [
                { "field": "title", "search": true },
                { "field": "attributes.commune", "search": true },
                { "field": "attributes.tags", "search": true }
            ]
        }
    }
}

Ou via panels.search.filters :

json
{
    "panels": {
        "search": {
            "filters": [
                {
                    "type": "search",
                    "searchFields": ["title", "attributes.commune", "attributes.description"]
                }
            ]
        }
    }
}

Chemins imbriqués : utilisez la notation pointée (ex. attributes.commune). Tableaux : les valeurs de type array sont automatiquement concaténées.


Disponible après chargement du module search (lazy ou via _loadModule("search")).

GeoLeaf.Search.isReady()

ts
GeoLeaf.Search.isReady(): boolean

Retourne true quand l'index est construit et prêt à répondre aux requêtes.

GeoLeaf.Search.query(text)

ts
GeoLeaf.Search.query(text: string): Set<string>

Retourne un Set d'identifiants POI correspondant à la recherche. Retourne un Set vide si le moteur n'est pas prêt (fallback automatique dans les filtres).

GeoLeaf.Search.build(pois)

ts
GeoLeaf.Search.build(pois: object[]): void

Reconstruit l'index à partir d'un tableau de POI. Utile après un changement de profil.

GeoLeaf.Search.getEngine()

ts
GeoLeaf.Search.getEngine(): string // "flexsearch" | "native"

Retourne le nom du moteur actif : "flexsearch" si l'index FlexSearch est prêt, "native" sinon (fallback includes()).

GeoLeaf.Search.clear()

ts
GeoLeaf.Search.clear(): void

Vide l'index. Les requêtes suivantes retournent un Set vide jusqu'au prochain build().

Exemple d'utilisation

js
// After loading the search module
await GeoLeaf._loadModule("search");

if (GeoLeaf.Search.isReady()) {
    const results = GeoLeaf.Search.query("abbaye");
    console.log("Matching POI IDs:", [...results]);
}

Champs indexés

L'index est construit en concaténant le texte de tous les champs indexés par POI :

poi.title + " " + poi.attributes.commune + " " + poi.attributes.tags[0] + ...

Le moteur FlexSearch utilise :

  • tokenize: "forward" — correspondance préfixe ("rando" → "randonnée")
  • encoder: "advanced" — normalisation complète des diacritiques ("eglise" → "Église")

Gestion incrémentale

Le registry écoute automatiquement les événements DOM GeoLeaf pour maintenir l'index à jour :

ÉvénementAction
geoleaf:poi:loadedReconstruit l'index complet
geoleaf:poi:addedAjoute le nouveau POI (O(1))
geoleaf:poi:removedSupprime le POI (O(1))

Avec FlexSearchEngine, les opérations add/remove sont vraiment incrémentales — pas de rebuild complet.


Benchmark

Mesures effectuées sur Chrome 121, machine de développement (Ryzen 7, 16 GB RAM).

Temps de construction index (build())

VolumeNative (ms)FlexSearch (ms)
500 POI— (pas d'index)~8 ms
1 000 POI~15 ms
5 000 POI~70 ms
10 000 POI~140 ms

Temps de requête (query())

VolumeNative includes() (ms/requête)FlexSearch (ms/requête)
500 POI~0.3~0.05
1 000 POI~0.6~0.05
5 000 POI~3.1~0.05
10 000 POI~6.5~0.05

Conclusion : FlexSearch devient significativement plus rapide dès ~1 000 POI. En dessous de 500 POI, le fallback natif est suffisant.

Taille de l'index en mémoire (approximatif)

VolumeFlexSearch (MB)
1 000 POI~0.8 MB
5 000 POI~3.5 MB
10 000 POI~7.0 MB

Sécurité

  • Sanitisation des requêtes : _sanitizeQueryInput() supprime les caractères de contrôle (\x00–\x1F, \x7F) et tronque à 200 caractères. Aucun risque XSS car le résultat est un Set d'ID (chaînes de caractères), jamais injecté dans le DOM.
  • Protection prototype : les chemins de champs (__proto__, constructor, prototype) sont ignorés pendant la construction du texte indexé.
  • Données POI : sanitisées à l'ingestion via Security.sanitizePoiProperties() avant d'être indexées.
  • IDs : normalisés en String() pour éviter toute injection par coercition de type.

Migration depuis la recherche native

La migration est transparente et non-breaking :

  1. Ajouter "search": { "engine": "flexsearch" } au profil JSON.
  2. GeoLeaf charge automatiquement le chunk search au démarrage.
  3. L'index est construit sur l'événement geoleaf:poi:loaded.
  4. Si le moteur n'est pas encore prêt (ex. premier rendu), le filtre retombe automatiquement sur includes().

Aucune modification du code applicatif n'est requise.


Version : 2.0.0

Dernière mise à jour : mars 2026

Released under the MIT License.