Elasticsearch 2.0: ce qui change

Une première bêta d’Elasticsearch 2.0 vient de sortir, c’est l’occasion de faire un premier bilan des changements.

Il s’agit en effet d’une véritable V2 qui, sur certains aspects, rompt clairement avec le passé.
Cet article n’a pas vocation a énumérer de manière exhaustive les changements, la documentation fait cela bien mieux que moi, mais plutôt synthétiser les principaux changements.

Installation

Un premier changement se situe au niveau de la configuration réseau. Jusqu’ici, lorsqu’on démarrait des nœuds Elasticsearch, il se regroupaient d’eux mêmes en cluster, c’était épatant mais dangereux. Elasticsearch 2 fait un pas la sagesse avec une configuration par défaut plus raisonnable. Elle évitera de former des clusters inopinément :

  • Elasticsearch écoute sur l’adresse loopback (127.0.0.1), comme MySQL par exemple
  • Il ne découvre plus les autres membres de cluster par multicast. Le multicast qui était activé par défaut jusqu’ici, n’était de toutes façons pas recommandé en production.

La nouvelle configuration par défaut est parfaite pour le développement, elle l’est probablement moins pour la production. Il faudra donc, en plus du cluster.name et node.name positionner
- network.host pour préciser sur quelle interface réseau écouter.
- discovery.zen.ping.unicast.hosts pour donner une liste de nœuds à contacter pour rejoindre le cluster
Plus d’info

Indexation

Elastic l’avait annoncé depuis la V1.5, les Rivers allaient disparaître. C’est chose faite dans Elasticsearch 2, les Rivers ne sont plus. Pourtant, certaines rivières comme JDBC ou RabbitMQ étaient utiles et utilisées. Quelles solutions avons-nous pour remplacer les rivières?

  • Logstash, c’est la solution préconisée par Elastic. Il existe déjà de nombreux inputs (Twitter, RabbitMQ par exemple) et d’autres naissent régulièrement (JDBC par exemple)
  • Des utilitaires dédiés: la JDBC River est devenue le JDBC Importer et la river Wikipedia est dans Stream2Es
  • Une application spécifique, avec des frameworks comme Spring Batch, Spring Intégration ou Apache Camel qui disposent d’adaptateurs pour écrire dans Elasticsearch, il n’est pas très compliqué de transférer de la donnée dans Elasticsearch.
  • Une application distribuée spécifique, avec des frameworks comme Hadoop ou Spark et la librairie Elasticsearch Hadoop.

Lorsqu’on indexe un document, on est à présent prévenu du nombre de shards qui ont acquitté l’écriture:

{
    "_id": "9781617291623",
    "_index": "bibliotheque",
    "_shards": {
        "failed": 0,
        "successful": 1,
        "total": 2
    },
    "_type": "livre",
    "_version": 1,
    "created": true
}

Mappings et analyse

C’était une mauvaise pratique dans Elasticsearch 1, c’est interdit dans Elasticsearch 2: Appliquer un mapping différent pour un même champ dans 2 types de documents d’un même index n’est plus permis. Par exemple, le mapping suivant n’est plus valide:

  "mappings": {
    "fr": {
      "properties": {
        "titre": {
          "type": "string",
          "analyzer": "french"
        }
      }
    },
    "en": {
      "properties": {
        "titre": {
          "type": "string",
          "analyzer": "english"
        }
      }
    }
  }

Autre mauvaise pratique, utiliser des points dans les noms de champs est aussi interdit dans cette nouvelle version.
Les pseudo-champs comme _id, _timestamp, _routing ne permettent plus de référencer un champ du document avec l’attribut path car celui-ci a disparu. Quant aux pseudo-champs _analyzer (analyseur par défaut) et _boost (document boost), que l’on positionnait aussi à la racine des mappings, ils ont aussi disparu. On les remplacera respectivement avec des dynamic_templates et des function_score queries.
La gestion des relations parent-enfants a été refondue. Pour cette raison, il est conseillé de ré-indexer les données pour tirer partie de ces nouvelles optimisations.
Dans Elasticsearch 1, supprimer le mapping pour un type de document entraînait la perte de tous les documents de ce type. Elasticsearch 2 ne permet plus la suppression de mapping, comme par exemple:

DELETE bibliotheque/_mapping/dvd

Enfin, les mappings et les templates d’index ne pourront plus être configurés sous forme de fichiers JSON placés dans les dossiers config/mappings ou config/templates Ils devront donc être déployés avec l’API REST.

Recherche

Dans le Query DSL d’Elasticsearch 1, on avait deux grands types d’opérateurs: les queries et les filters. Ces derniers étant particulièrement intéressants pour des raisons de performances (mise en cache, combinaisons booléennes efficaces…). On avait de ce fait un term query et term filter, une range query et un range filter… Avec Elasticsearch 2, et Lucene 5 en particulier, la distinction s’estompe.
Queries & Filters
Il n’y a plus qu’un seul type d’opérateur, mais il subsiste une signification différente en fonction du contexte d’utilisation. Pour mettre en œuvre les filtres, dans Elasticsearch 1, on utilisait généralement une query de type filtered. Dans Elasticsearch 2, ce type de query est toujours là mais devient obsolète. On utilisera à la place la nouvelle clause filter de la query bool, qui se comporte comme la clause must sauf que les opérateurs seront traités comme des filtres: Ils n’interviendront pas dans le calcul du score par exemple. Ainsi, la requête Elasticsearch 1 …

{
  "query": {
    "filtered": {
      "query": {
        "match": {
          "titre": "spring"
        }
      },
      "filter": {
        "range": {
          "prix": {
            "gte": 50
          }
        }
      }
    }
  }
}

… deviendra avec Elasticsearch 2:

{
  "query": {
    "bool": {
      "must": {
        "match": {
          "titre": "spring"
        }
      },
      "filter": {
        "range": {
          "prix": {
            "gte": 50
          }
        }
      }
    }
  }
}

Les filtres and et or pouvaient être utilisés pour combiner des filtres. Ils deviennent obsolètes dans la V2, on utilisera à la place l’opérateur bool en toutes circonstances.
En ce qui concerne les recherches de type fuzzy, Elasticsearch 2 passe de la distance de Levenshtein à Damerau-Levenshtein. En résumé, les mots zenika et zeinka seront distants de 1 car il y a une permutation de 2 caractères adjacents, alors qu’ils étaient distants de 2 jusqu’ici. De plus, la fuzziness ne pourra plus être renseignée sous forme de pourcentage, les seules valeurs autorisées seront donc 0,1,2 et AUTO
Dans Elasticsearch 1, on pouvait référencer un champ en omettant son préfixe: par exemple le champ editeur.nom pouvait être abrégé nom dans les recherches. Pour éviter les amalgames, ce ne sera plus le cas avec Elasticsearch 2.
Plus d’info:

Agrégations

Les facettes qui ont été rendues obsolètes par l’avènement des agrégations ont été supprimées dans cette V2.
En plus des deux types d’agrégations déjà existants, metrics et bucket, un troisiéme type nommé pipeline fait son apparition. Son objectif est de calculer une agrégation, non pas sur des documents, mais sur des résultats d’agrégation. L’exemple suivant calcule l’amplitude de prix par éditeur:

{
  "aggregations": {
    "editeur": {
      "terms": { "field": "editeur.code" },
      "aggs": {
        "maxprix": {
          "max": { "field": "prix" }
        },
        "minprix": {
          "min": { "field": "prix" }
        },
        "ecartprix": {
          "bucket_script": {
            "buckets_path": {
              "minprix": "minprix",
              "maxprix": "maxprix"
            },
            "script": "maxprix - minprix"
          }
        }
      }
    }
  }
}

Les agrégations pipeline sont néanmoins au stade expérimental pour l’instant.
Les Field Datas sont une structure de données utilisée pour les agrégations, les tris et les scripts. Depuis Elastic 1, il était possible stocker ces field datas sur le disque au lieu d’occuper de la mémoire Java: on parle de Doc Values. Les Doc Values seront dorénavant activées par défaut pour presque tous les champs sauf ceux de type string analysée.
Plus d’info:

Messages d’erreur

Dans Elasticsearch 1, en cas d’erreur de syntaxe dans une requête, on obtenait un message d’erreur peu lisible:

{
  "error" : "SearchPhaseExecutionException[Failed to execute phase [query], all shards failed; shardFailures {[k2my_gmKTIS8kQ8bsVgHQA][bibliotheque][0]: SearchParseException[[bibliotheque][0]: from[-1],size[-1]: Parse Failure [Failed to parse source [{\"query\":{\"macht\":{\"titre\":\"spring\"}}}]]]; nested: QueryParsingException[[bibliotheque] No query registered for [macht]]; }{[k2my_gmKTIS8kQ8bsVgHQA][bibliotheque][1]: SearchParseException[[bibliotheque][1]: from[-1],size[-1]: Parse Failure [Failed to parse source [{\"query\":{\"macht\":{\"titre\":\"spring\"}}}]]]; nested: QueryParsingException[[bibliotheque] No query registered for [macht]]; }{[k2my_gmKTIS8kQ8bsVgHQA][bibliotheque][2]: SearchParseException[[bibliotheque][2]: from[-1],size[-1]: Parse Failure [Failed to parse source [{\"query\":{\"macht\":{\"titre\":\"spring\"}}}]]]; nested: QueryParsingException[[bibliotheque] No query registered for [macht]]; }{[k2my_gmKTIS8kQ8bsVgHQA][bibliotheque][3]: SearchParseException[[bibliotheque][3]: from[-1],size[-1]: Parse Failure [Failed to parse source [{\"query\":{\"macht\":{\"titre\":\"spring\"}}}]]]; nested: QueryParsingException[[bibliotheque] No query registered for [macht]]; }{[k2my_gmKTIS8kQ8bsVgHQA][bibliotheque][4]: SearchParseException[[bibliotheque][4]: from[-1],size[-1]: Parse Failure [Failed to parse source [{\"query\":{\"macht\":{\"titre\":\"spring\"}}}]]]; nested: QueryParsingException[[bibliotheque] No query registered for [macht]]; }]",
  "status" : 400
}

Dans Elasticsearch 2, l’erreur est beaucoup plus lisible car la chaîne d’exceptions est représentée en JSON:

{
  "error" : {
    "root_cause" : [ {
      "type" : "query_parsing_exception",
      "reason" : "No query registered for [macht]",
      "index" : "bibliotheque",
      "line" : 1,
      "col" : 11
    } ],
    "type" : "search_phase_execution_exception",
    "reason" : "all shards failed",
    "phase" : "query",
    "grouped" : true,
    "failed_shards" : [ {
      "shard" : 0,
      "index" : "bibliotheque",
      "node" : "L6GCrNrYTgWDurzr-05bEA",
      "reason" : {
        "type" : "query_parsing_exception",
        "reason" : "No query registered for [macht]",
        "index" : "bibliotheque",
        "line" : 1,
        "col" : 11
      }
    } ]
  },
  "status" : 400
}

Upgrade

La migration Elasticsearch 1 vers Elasticesearch 2 nécessite un redémarrage complet du cluster et donc une période d’indisponibilité.
Un plugin Elasticsearch Migration pourra être installé sur Elasticsearch 1 ou 0.90 pour évaluer les incompatibilités. Le plugin vérifie un certains nombre de règles sur la configuration, les mappings…
Avec le passage à Lucene 5.2, le format de fichier au niveau disque va changer. Cela signifie que d’une part un retour arrière nécessitera la restauration d’un snapshot. Et d’autre part, les fichiers au format Lucene 3.x (produits par les versions d’Elasticsearch antérieures à la 0.90) devront subir une transformation pour pouvoir être relus.
Plus d’info:

Conclusion

Les principales nouveautés d’Elasticsearch 2 sont le passage à Lucene 5, le rapprochement des queries et des filters, les agrégations de type pipeline et la suppression des rivers. D’autres changements rendent Elasticsearch moins permissif et obligent à plus de rigueur, ce qui permettra d’éviter les erreurs de configuration, les mauvaises pratiques et les problèmes.

Auteur/Autrice

Laisser un commentaire

Ce site utilise Akismet pour réduire les indésirables. En savoir plus sur comment les données de vos commentaires sont utilisées.

%d blogueurs aiment cette page :