web-dev-qa-db-fra.com

Outil de suggestion d'achèvement orienté Word (ElasticSearch 5.x)

ElasticSearch 5.x a introduit quelques changements (de rupture) dans l'API Suggester ( Documentation ). Le changement le plus notable est le suivant:

L'outil de suggestion d'achèvement est orienté document

Les suggestions connaissent le document auquel elles appartiennent. Maintenant, les documents associés (_source) sont retournés dans le cadre des suggestions d'achèvement.

En bref, toutes les requêtes d'achèvement renvoient tous les documents correspondants au lieu des mots juste correspondants . Et c'est là que réside le problème - la duplication des mots remplis automatiquement s'ils se produisent dans plus d'un document.

Disons que nous avons ce mappage simple:

{
   "my-index": {
      "mappings": {
         "users": {
            "properties": {
               "firstName": {
                  "type": "text"
               },
               "lastName": {
                  "type": "text"
               },
               "suggest": {
                  "type": "completion",
                  "analyzer": "simple"
               }
            }
         }
      }
   }
}

Avec quelques documents de test:

{
   "_index": "my-index",
   "_type": "users",
   "_id": "1",
   "_source": {
      "firstName": "John",
      "lastName": "Doe",
      "suggest": [
         {
            "input": [
               "John",
               "Doe"
            ]
         }
      ]
   }
},
{
   "_index": "my-index",
   "_type": "users",
   "_id": "2",
   "_source": {
      "firstName": "John",
      "lastName": "Smith",
      "suggest": [
         {
            "input": [
               "John",
               "Smith"
            ]
         }
      ]
   }
}

Et une requête par le livre:

POST /my-index/_suggest?pretty
{
    "my-suggest" : {
        "text" : "joh",
        "completion" : {
            "field" : "suggest"
        }
    }
}

Ce qui donne les résultats suivants:

{
   "_shards": {
      "total": 5,
      "successful": 5,
      "failed": 0
   },
   "my-suggest": [
      {
         "text": "joh",
         "offset": 0,
         "length": 3,
         "options": [
            {
               "text": "John",
               "_index": "my-index",
               "_type": "users",
               "_id": "1",
               "_score": 1,
               "_source": {
                 "firstName": "John",
                 "lastName": "Doe",
                 "suggest": [
                    {
                       "input": [
                          "John",
                          "Doe"
                       ]
                    }
                 ]
               }
            },
            {
               "text": "John",
               "_index": "my-index",
               "_type": "users",
               "_id": "2",
               "_score": 1,
               "_source": {
                 "firstName": "John",
                 "lastName": "Smith",
                 "suggest": [
                    {
                       "input": [
                          "John",
                          "Smith"
                       ]
                    }
                 ]
               }
            }
         ]
      }
   ]
}

En bref, pour une suggestion de complétion pour le texte "joh", deux (2) documents ont été retournés - John et les deux avaient la même valeur de text propriété.

Cependant, j'aimerais recevoir un (1) mot . Quelque chose de simple comme ça:

{
   "_shards": {
      "total": 5,
      "successful": 5,
      "failed": 0
   },
   "my-suggest": [
      {
         "text": "joh",
         "offset": 0,
         "length": 3,
         "options": [
          "John"
         ]
      }
   ]
}

Question : comment implémenter un outil de suggestion d'achèvement basé sur Word. Il n'est pas nécessaire de renvoyer de données liées au document, car je n'en ai pas besoin à ce stade.

Le "Suggestion d'achèvement" est-il même approprié pour mon scénario? Ou devrais-je utiliser une approche complètement différente?


[~ # ~] modifier [~ # ~] : Comme beaucoup d'entre vous l'ont souligné, un index supplémentaire uniquement pour l'achèvement serait une solution viable. Cependant, je peux voir plusieurs problèmes avec cette approche:

  1. Garder le nouvel index synchronisé.
  2. La saisie automatique des mots suivants serait probablement globale, au lieu d'être restreinte. Par exemple, supposons que vous avez les mots suivants dans l'index supplémentaire: "John", "Doe", "David", "Smith". Lors de la requête pour "John D", le résultat pour le mot incomplet doit être "Doe" et pas "Doe", "David".

Pour surmonter le deuxième point, l'indexation de mots simples ne serait pas suffisante, car vous auriez également besoin de mapper tous les mots aux documents afin d'affiner correctement les mots suivants à auto-complétion. Et avec cela, vous avez en fait le même problème que l'interrogation de l'index d'origine. Par conséquent, l'index supplémentaire n'a plus de sens.

21
alesc

Comme indiqué dans le commentaire, une autre façon d'y parvenir sans obtenir les documents en double consiste à créer un sous-champ pour le champ firstname contenant les ngrammes du champ. Vous définissez d'abord votre mappage comme ceci:

PUT my-index
{
  "settings": {
    "analysis": {
      "analyzer": {
        "completion_analyzer": {
          "type": "custom",
          "filter": [
            "lowercase",
            "completion_filter"
          ],
          "tokenizer": "keyword"
        }
      },
      "filter": {
        "completion_filter": {
          "type": "Edge_ngram",
          "min_gram": 1,
          "max_gram": 24
        }
      }
    }
  },
  "mappings": {
    "users": {
      "properties": {
        "autocomplete": {
          "type": "text",
          "fields": {
            "raw": {
              "type": "keyword"
            },
            "completion": {
              "type": "text",
              "analyzer": "completion_analyzer",
              "search_analyzer": "standard"
            }
          }
        },
        "firstName": {
          "type": "text"
        },
        "lastName": {
          "type": "text"
        }
      }
    }
  }
}

Ensuite, vous indexez quelques documents:

POST my-index/users/_bulk
{"index":{}}
{ "firstName": "John", "lastName": "Doe", "autocomplete": "John Doe"}
{"index":{}}
{ "firstName": "John", "lastName": "Deere", "autocomplete": "John Deere" }
{"index":{}}
{ "firstName": "Johnny", "lastName": "Cash", "autocomplete": "Johnny Cash" }

Ensuite, vous pouvez rechercher joh et obtenir un résultat pour John et un autre pour Johnny

{
  "size": 0,
  "query": {
    "term": {
      "autocomplete.completion": "john d"
    }
  },
  "aggs": {
    "suggestions": {
      "terms": {
        "field": "autocomplete.raw"
      }
    }
  }
}

Résultats:

{
  "aggregations": {
    "suggestions": {
      "doc_count_error_upper_bound": 0,
      "sum_other_doc_count": 0,
      "buckets": [
        {
          "key": "John Doe",
          "doc_count": 1
        },
        {
          "key": "John Deere",
          "doc_count": 1
        }
      ]
    }
  }
}

MISE À JOUR (25 juin 2019):

ES 7.2 a introduit un nouveau type de données appelé search_as_you_type qui permet ce type de comportement de manière native. En savoir plus sur: https://www.elastic.co/guide/en/elasticsearch/reference/7.2/search-as-you-type.html

19
Val

Un champ supplémentaire skip_duplicates sera ajouté dans la prochaine version 6.x.

Extrait des documents sur https://www.elastic.co/guide/en/elasticsearch/reference/master/search-suggesters-completion.html#skip_duplicates :

POST music/_search?pretty
{
    "suggest": {
        "song-suggest" : {
            "prefix" : "nor",
            "completion" : {
                "field" : "suggest",
                "skip_duplicates": true
            }
        }
    }
}
2
Dries Cleymans

Nous sommes confrontés exactement au même problème. Dans Elasticsearch 2.4, l'approche que vous décrivez fonctionnait bien pour nous, mais maintenant, comme vous le dites, le suggérant est devenu basé sur des documents alors que, comme vous, nous ne nous intéressons qu'aux mots uniques, pas aux documents.

La seule `` solution '' à laquelle nous pourrions penser jusqu'à présent est de créer un index séparé juste pour les mots sur lesquels nous voulons effectuer les requêtes de suggestion et dans cet index séparé, assurez-vous que les mots identiques ne sont indexés qu'une seule fois. Ensuite, vous pouvez effectuer les requêtes de suggestion sur cet index séparé. C'est loin d'être idéal, ne serait-ce que parce que nous devrons alors nous assurer que cet index reste synchronisé avec l'autre index dont nous avons besoin pour nos autres requêtes.

1
Edgar Vonk