Elasticsearch | Filtrer via des scripts

By | 17 février 2015

L’objectif de ce mini blog-post est de donner un exemple de recherche de documents sur Elasticsearch en s’appuyant sur 2 valeurs d’un objet contenu dans un tableau. Ayant trouvé très peu de documentation sur ce type de requête, je me suis dit que ça serait une bonne idée de le partager.

Structure des documents

Les documents indexés seront donc constitué de 2 champs à la racine, le premier de type string et le seconde de type array où chaque entrée correspondra à un objet de type candidat. J’ai volontairement simplifié les documents.

{
    "concours": "php",
    "candidats": [
        {
            "name": "paul",
            "rank": 0
        },
        {
            "name": "jacques",
            "rank": 1
        },
        {
            "name": "pierre",
            "rank": 2
        }
    ]
}

La structure est simple, mais nous sommes pourtant incapable d’extraire les documents contenant le candidat « paul » dont le rank  est égale à « 0 » sans utiliser un filtre script ou sans avoir a reindexer en utilisant un type nested ce qui est conseillé de faire, si vous le pouvez.

 Utilisation d’un filtre script

Un filtre de type script, s’utilise de la même façon qu’un filtre term ou range.

{
    "query": {
        "filtered": {
            "filter": {
                "script": {
                    "script": "foreach (candidat : _source.candidats)  { if (candidat['rank'] == 0 && candidat['name'] == candidat_name) { return true; } } return false;",
                    "params": {
                        "candidat_name": "paul"
                    }
                }
            }
        }
    }
}

Le script extrait de la requête ES:

foreach (candidat : _source.candidats)  
{ 
    if (candidat['rank'] == 0 && candidat['name'] == candidat_name) 
    { 
        return true; 
    } 
} 
return false;

Si le champ candidat n’existe pas de manière systématique, il vous suffit d’ajouter une condition avant le foreach afin de vous s’assurer de son existence.

Pensez à désactiver la désactivation (ou à activer) d’utilisation des scripts:

$ echo "script.disable_dynamic: false" >> /etc/elasticsearch/elasticsearch.yml
$ service elasticsearch restart

 

En espérant que ça puisse aider.

  • Hi,

    This approach is going to be *very* slow.

    What you want is to use *nested* documents.

    Here’s a runnable example: https://found.no/play/gist/82c6d577a44b54b2ba87

  • David Pilato

    Merci pour l’article. C’est toujours bien de partager ses découvertes.

    Tu devrais regarder du côté des nested documents qui permettent de résoudre ce problème que tu évoques.
    Sans doute aussi plus rapide à l’exécution.

    David

  • Merci David et Alex pour cette précision. Il est vrai que je ne me suis même pas posé la question d’utiliser un type nested. Il s’agit d’une problématique que nous avons rencontré en production, et cette requête ne sera executée que de manière exceptionnelle et je ne souhaite pas réindexer.

    Cette réflexion m’amène donc à une autre question, quel est l’impact sur l’indexation d’utiliser un type nested plutôt qu’un type array, en prenant en considération que l’exemple de requête que je viens de donner sera joué de manière très exceptionnelle ?

    Merci.