Estratégia best_fields no Elasticsearch: dis_max, tie_breaker e minimum_should_match

Quando fazemos uma busca full-text sobre vários campos, o comportamento padrão do bool com should pode devolver documentos que combinam com muitos campos, mas com poucas palavras-chave em cada um, à frente de documentos onde um único campo concentra a maior parte dos termos. Para priorizar o docuemnto em que um campo específico acumula mais termos, usamos a estratégia best fields.

Preparando os documentos

O exemplo a seguir adiciona um campo de conteúdo a posts já existentes em um índice chamado blog:

POST /blog/posts/_bulk
{ "update": { "_id": "1" } }
{ "doc": { "conteudo": "i like to write best elasticsearch article" } }
{ "update": { "_id": "2" } }
{ "doc": { "conteudo": "i think java is the best programming language" } }
{ "update": { "_id": "3" } }
{ "doc": { "conteudo": "i am only an elasticsearch beginner" } }
{ "update": { "_id": "4" } }
{ "doc": { "conteudo": "elasticsearch and hadoop are all very good solution, i am a beginner" } }
{ "update": { "_id": "5" } }
{ "doc": { "conteudo": "spark is best big data solution based on scala, a programming language similar to java" } }

Problema do bool/should

Imagine buscar posts cujo titulo ou conteudo contenham java ou solution:

GET /blog/posts/_search
{
  "query": {
    "bool": {
      "should": [
        { "match": { "titulo": "java solution" } },
        { "match": { "conteudo": "java solution" } }
      ]
    }
  }
}

Como o bool should soma os scores e aplica um fator de coordenação, um documento que casa nos dois campos pode superar outro que casa de forma mais concentrada em apenas um campo. O cálculo é:

score = (soma dos scores) × (queries que casaram / total de queries)

Suponha que o doc4 tenha score 1,1 no título e 1,2 no conteúdo: (1,1 + 1,2) × 2/2 = 2,3. O doc5, com score 0 no título e 2,3 no conteúdo, ficaria com 2,3 × 1/2 = 1,15. Assim, o doc4 aparece antes do doc5, mesmo não sendo o resultado mais relevante.

Usando dis_max para best_fields

A consulta dis_max retorna o maior score entre as cláusulas, sem somá-las:

GET /blog/posts/_search
{
  "query": {
    "dis_max": {
      "queries": [
        { "match": { "titulo": "java solution" } },
        { "match": { "conteudo": "java solution" } }
      ]
    }
  }
}

Agora, o score do doc4 é max(1,1; 1,2) = 1,2, e o do doc5 é max(0; 2,3) = 2,3. O doc5 sobe no ranking, o que corresponde ao objetivo de best fields.

Limitação: campos ignorados

Com a consulta java beginner, o dis_max pode deixar de considerar a contribuição dos demais campos. Um documento com java apenas no título e outro com beginner apenas no conteúdo podem superar um documento com java no título e beginner no conteúdo, porque o maior score individual de cada um pode ser semelhante.

tie_breaker

Para não ignorar totalmente as outras cláusulas, usamos tie_breaker:

GET /blog/posts/_search
{
  "query": {
    "dis_max": {
      "queries": [
        { "match": { "titulo": "java beginner" } },
        { "match": { "conteudo": "java beginner" } }
      ],
      "tie_breaker": 0.3
    }
  }
}

O valor, geralmente entre 0 e 1, multiplica os scores das demais cláusulas e adiciona ao maior score. Dessa forma, o documento que casa em mais de um campo recebe um reforço adicional.

minimum_should_match

Esse parâmetro ajuda a eliminar a cauda longa de resultados, exigindo que um percentual mínimo dos termos seja atendido:

GET /blog/posts/_search
{
  "query": {
    "dis_max": {
      "queries": [
        {
          "match": {
            "titulo": {
              "query": "java beginner",
              "minimum_should_match": "50%",
              "boost": 2
            }
          }
        },
        {
          "match": {
            "conteudo": {
              "query": "java beginner",
              "minimum_should_match": "30%"
            }
          }
        }
      ],
      "tie_breaker": 0.3
    }
  }
}

best_fields versus most_fields

As duas estratégias resolvem problemas diferentes.

best_fields seleciona o campo com maior score e, com tie_breaker, considera levemente os demais. É ideal quando queremos que um campo concentre a maior parte dos termos. A vantagem é a precisão dos primeiros resultados; a desvantagem é que o restante do ranking pode ficar pouco diferenciado, como acontece em mecanismos de busca gerais.

most_fields, por outro lado, premia documentos em que muitos campos casam com pelo menos um termo. A vantagem é um ranking mais uniforme, mas a desvantagem é que resultados altamente precisos podem não ficar no topo, situação comum em wikis, onde precisamos navegar por várias páginas para encontrar o melhor resultado.

Tags: elasticsearch dis_max tie_breaker minimum_should_match best_fields

Publicado em 7-6 21:36