158 votes

Comment rechercher une partie d'un mot avec ElasticSearch

J'ai récemment commencé à utiliser ElasticSearch et je n'arrive pas à faire en sorte qu'il recherche une partie d'un mot.

Exemple : J'ai trois documents de mon couchdb indexés dans ElasticSearch :

{
  "_id" : "1",
  "name" : "John Doeman",
  "function" : "Janitor"
}
{
  "_id" : "2",
  "name" : "Jane Doewoman",
  "function" : "Teacher"
}
{
  "_id" : "3",
  "name" : "Jimmy Jackal",
  "function" : "Student"
} 

Donc maintenant, je veux rechercher tous les documents contenant "Doe".

curl http://localhost:9200/my_idx/my_type/_search?q=Doe

Cela ne renvoie aucun résultat. Mais si je cherche

curl http://localhost:9200/my_idx/my_type/_search?q=Doeman

Elle renvoie un document (John Doeman).

J'ai essayé de définir différents analyseurs et différents filtres comme propriétés de mon index. J'ai également essayé d'utiliser une requête complète (par exemple :

{
  "query": {
    "term": {
      "name": "Doe"
    }
  }
}

) Mais rien ne semble fonctionner.

Comment faire pour qu'ElasticSearch trouve à la fois John Doeman et Jane Doewoman lorsque je recherche "Doe" ?

UPDATE

J'ai essayé d'utiliser le tokenizer et le filtre nGram, comme Igor l'a proposé, comme ceci :

{
  "index": {
    "index": "my_idx",
    "type": "my_type",
    "bulk_size": "100",
    "bulk_timeout": "10ms",
    "analysis": {
      "analyzer": {
        "my_analyzer": {
          "type": "custom",
          "tokenizer": "my_ngram_tokenizer",
          "filter": [
            "my_ngram_filter"
          ]
        }
      },
      "filter": {
        "my_ngram_filter": {
          "type": "nGram",
          "min_gram": 1,
          "max_gram": 1
        }
      },
      "tokenizer": {
        "my_ngram_tokenizer": {
          "type": "nGram",
          "min_gram": 1,
          "max_gram": 1
        }
      }
    }
  }
}

Le problème que je rencontre actuellement est que chaque requête renvoie TOUS les documents. Des pistes ? La documentation d'ElasticSearch sur l'utilisation de nGram n'est pas très bonne...

10 votes

Pas étonnant, vous habe min/max ngram réglé sur 1, donc 1 lettre :)

3 votes

Je suis en fait surpris que l'ES ne rende pas cela plus facile. C'est ElasticSearch, pas ElasticExactMatchUnlessIDoSomeCeremony.

4voto

Neshta Points 223

Si vous souhaitez mettre en œuvre la fonctionnalité d'autocomplétion, alors Suggestion de complétion est la solution la plus soignée. La prochaine article de blog contient une description très claire de la manière dont cela fonctionne.

En deux mots, il s'agit d'une structure de données en mémoire appelée TSF qui contient des suggestions valides et qui est optimisée pour une récupération rapide et une utilisation de la mémoire. En fait, il s'agit simplement d'un graphe. Par exemple, une TSF contenant les mots hotel , marriot , mercure , munchen y munich ressemblerait à ceci :

enter image description here

4voto

Amit Khandelwal Points 4531

Il existe de nombreuses réponses qui se concentrent sur la résolution du problème mais qui ne parlent pas des différents compromis que l'on doit faire avant de choisir une réponse particulière. Laissez-moi donc essayer d'ajouter quelques détails supplémentaires sur cette perspective.

La recherche partielle est aujourd'hui une fonctionnalité très courante et importante. Si elle n'est pas mise en œuvre correctement, elle peut entraîner une mauvaise expérience pour l'utilisateur et de mauvaises performances. Il faut donc d'abord connaître les exigences fonctionnelles et non fonctionnelles de votre application en ce qui concerne cette fonctionnalité, dont j'ai parlé dans mon article sur le sujet. cette réponse détaillée de SO .

Il existe maintenant plusieurs approches, comme le temps d'interrogation, le temps d'indexation, les suggestions d'achèvement et les types de données de recherche en cours de saisie. ajouté dans une version récente d'elasticsarch.

Les personnes qui souhaitent rapidement mettre en œuvre une solution peuvent désormais utiliser la solution de travail de bout en bout ci-dessous.

Cartographie des indices

{
  "settings": {
    "analysis": {
      "filter": {
        "autocomplete_filter": {
          "type": "ngram",
          "min_gram": 1,
          "max_gram": 10
        }
      },
      "analyzer": {
        "autocomplete": { 
          "type": "custom",
          "tokenizer": "standard",
          "filter": [
            "lowercase",
            "autocomplete_filter"
          ]
        }
      }
    },
    "index.max_ngram_diff" : 10
  },
  "mappings": {
    "properties": {
      "title": {
        "type": "text",
        "analyzer": "autocomplete", 
        "search_analyzer": "standard" 
      }
    }
  }
}

Index des documents types donnés

{
  "title" : "John Doeman"

}

{
  "title" : "Jane Doewoman"

}

{
  "title" : "Jimmy Jackal"

}

Et la requête de recherche

{
    "query": {
        "match": {
            "title": "Doe"
        }
    }
}

qui renvoie les résultats de recherche attendus

 "hits": [
            {
                "_index": "6467067",
                "_type": "_doc",
                "_id": "1",
                "_score": 0.76718915,
                "_source": {
                    "title": "John Doeman"
                }
            },
            {
                "_index": "6467067",
                "_type": "_doc",
                "_id": "2",
                "_score": 0.76718915,
                "_source": {
                    "title": "Jane Doewoman"
                }
            }
        ]

3voto

saravanavelu Points 424

J'utilise ceci et j'ai travaillé

"query": {
        "query_string" : {
            "query" : "*test*",
            "fields" : ["field1","field2"],
            "analyze_wildcard" : true,
            "allow_leading_wildcard": true
        }
    }

2voto

Ali Moshiri Points 37

Vous pouvez utiliser le regexp.

{ "_id" : "1", "name" : "John Doeman" , "function" : "Janitor"}
{ "_id" : "2", "name" : "Jane Doewoman","function" : "Teacher"  }
{ "_id" : "3", "name" : "Jimmy Jackal" ,"function" : "Student"  } 

si vous utilisez cette requête :

{
  "query": {
    "regexp": {
      "name": "J.*"
    }
  }
}

Vous recevrez toutes les données dont le nom commence par "J", mais vous voulez seulement recevoir les deux premiers enregistrements dont le nom se termine par "man", alors vous pouvez utiliser cette requête :

{
  "query": { 
    "regexp": {
      "name": ".*man"
    }
  }
}

et si vous voulez recevoir tous les enregistrements dont le nom contient "m", vous pouvez utiliser cette requête :

{
  "query": { 
    "regexp": {
      "name": ".*m.*"
    }
  }
}

J'espère que ma réponse vous permettra de résoudre votre problème.

0voto

Dardino Points 58

L'utilisation de wilcards (*) empêche le calcul d'un score.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X