Busca por palavras incompletas

Bom dia pessoal.

Preciso de ajuda.

Sou novo no ElasticSearch e estou com um problema.

Supondo que eu tenha um índice de filmes, e um dos meus campos é a Sinopse do Filme, por exemplo, a Capitã Marvel:

"Carol Danvers (Brie Larson) é uma ex-agente da Força Aérea norte-americana, que, sem se lembrar de sua vida na Terra, é recrutada pelos Kree para fazer parte de seu exército de elite. Inimiga declarada dos Skrull, ela acaba voltando ao seu planeta de origem para impedir uma invasão dos metaformos, e assim vai acabar descobrindo a verdade sobre si, com a ajuda do agente Nick Fury (Samuel L. Jackson) e da gata Goose."

Se eu precisar buscar nesse campo com palavras incompletas, por exemplo, que o usuário se lembre que há algo com "america" (que na verdade é americana) e cita algo como "formo" (que são metaformos).

Qual a melhor de maneira de fazer essa busca com mais de uma palavra incompleta?

Agradeço a quem puder ajudar.

Oi @Jonathan_Marfil,

Uma forma de conseguir esse resultado é utilizando NGrams. Para isso, você precisa criar um analyzer customizado e mapear o campo para utilizar esse analyzer. Um detalhe importante é que para melhorar a precisão dos resultados vamos utilizar um analyzer de indexação diferente do analyzer de consulta. Veja a seguir:

PUT /ngram-test
{
  "settings": {
    "index.max_ngram_diff": 15,
    "analysis": {
      "filter": {
        "like_filter": {
          "type": "ngram",
          "min_gram": 3,
          "max_gram": 18,
          "token_chars": [
            "letter",
            "digit"
          ]
        }
      },
      "analyzer": {
        "like_analyzer": {
          "type": "custom",
          "tokenizer": "keyword",
          "filter": [
            "lowercase",
            "like_filter"
          ]
        }
      }
    }
  },
  "mappings": {
    "properties": {
      "sinopse": {
        "analyzer": "like_analyzer",
        "search_analyzer": "standard", 
        "type": "text"
      }
    }
  }
}

Agora basta indexar o documento e fazer a busca:

PUT ngram-test/_doc/1
{
  "sinopse": "Carol Danvers (Brie Larson) é uma ex-agente da Força Aérea norte-americana, que, sem se lembrar de sua vida na Terra, é recrutada pelos Kree para fazer parte de seu exército de elite. Inimiga declarada dos Skrull, ela acaba voltando ao seu planeta de origem para impedir uma invasão dos metaformos, e assim vai acabar descobrindo a verdade sobre si, com a ajuda do agente Nick Fury (Samuel L. Jackson) e da gata Goose."
}

GET ngram-test/_search
{
  "query": {
    "match": {
      "sinopse": {
        "query": "formos"
      }
    }
  }
}

Resultado:

{
  "took" : 0,
  "timed_out" : false,
  "_shards" : {
    "total" : 1,
    "successful" : 1,
    "skipped" : 0,
    "failed" : 0
  },
  "hits" : {
    "total" : {
      "value" : 1,
      "relation" : "eq"
    },
    "max_score" : 0.48679504,
    "hits" : [
      {
        "_index" : "ngram-test",
        "_type" : "_doc",
        "_id" : "1",
        "_score" : 0.48679504,
        "_source" : {
          "sinopse" : "Carol Danvers (Brie Larson) é uma ex-agente da Força Aérea norte-americana, que, sem se lembrar de sua vida na Terra, é recrutada pelos Kree para fazer parte de seu exército de elite. Inimiga declarada dos Skrull, ela acaba voltando ao seu planeta de origem para impedir uma invasão dos metaformos, e assim vai acabar descobrindo a verdade sobre si, com a ajuda do agente Nick Fury (Samuel L. Jackson) e da gata Goose."
        }
      }
    ]
  }
}

Alguns ressalvas importantes:

  • Ao utilizar o filter ngram é esperado que o índice ocupe muito espaço em disco uma vez que a quantidade de tokens produzido é muito maior;
  • Quanto maior a diferença entre min_gram e max_gram, maior será o espaço em disco utilizado;
  • Ao mesmo tempo se o max_gram for 18 e o termo digitado pelo usuário for maior que 18 o documento não será achado, é um trade-off;
  • Dependendo do tamanho da sua base de documentos essa estratégia pode ser inviável;

Recomendo a leitura de um post com algumas discussões interessantes sobre o tema.

Abraço,
Luiz Guilherme