Spec-Zone.ru › Elasticsearch 8
›Elasticsearch Guide [8.17] ›Поиск данных ›API поиска

Фильтрация результатов поиска

Вы можете использовать два метода для фильтрации результатов поиска:

  • Используйте булеву запросу с клаузой filter. Запросы поиска применяют булевы фильтры к результатам поиска и агрегациям.
  • Используйте параметр post_filter API поиска. Запросы поиска применяют постовые фильтры только к результатам поиска, а не к агрегациям. Вы можете использовать постовой фильтр для расчета агрегаций на основе более широкого набора результатов и затем сузить результаты.

    Вы также можете переоценить результаты после применения постового фильтра, чтобы улучшить релевантность и изменить порядок вывода.

Постовой фильтр

Когда вы используете параметр post_filter для фильтрации результатов поиска, результаты поиска фильтруются после расчета агрегаций. Постовой фильтр не оказывает никакого влияния на результаты агрегации.

Например, вы продаёте рубашки с следующими свойствами:

resp = client.indices.create(
    index="shirts",
    mappings={
        "properties": {
            "brand": {
                "type": "keyword"
            },
            "color": {
                "type": "keyword"
            },
            "model": {
                "type": "keyword"
            }
        }
    },
)
print(resp)

resp1 = client.index(
    index="shirts",
    id="1",
    refresh=True,
    document={
        "brand": "gucci",
        "color": "red",
        "model": "slim"
    },
)
print(resp1)
response = client.indices.create(
  index: 'shirts',
  body: {
    mappings: {
      properties: {
        brand: {
          type: 'keyword'
        },
        color: {
          type: 'keyword'
        },
        model: {
          type: 'keyword'
        }
      }
    }
  }
)
puts response

response = client.index(
  index: 'shirts',
  id: 1,
  refresh: true,
  body: {
    brand: 'gucci',
    color: 'red',
    model: 'slim'
  }
)
puts response
const response = await client.indices.create({
  index: "shirts",
  mappings: {
    properties: {
      brand: {
        type: "keyword",
      },
      color: {
        type: "keyword",
      },
      model: {
        type: "keyword",
      },
    },
  },
});
console.log(response);

const response1 = await client.index({
  index: "shirts",
  id: 1,
  refresh: "true",
  document: {
    brand: "gucci",
    color: "red",
    model: "slim",
  },
});
console.log(response1);
PUT /shirts
{
  "mappings": {
    "properties": {
      "brand": { "type": "keyword"},
      "color": { "type": "keyword"},
      "model": { "type": "keyword"}
    }
  }
}

PUT /shirts/_doc/1?refresh
{
  "brand": "gucci",
  "color": "red",
  "model": "slim"
}

Представьте, что пользователь задал два фильтра:

color:red и brand:gucci. Вы хотите показать только красные рубашки от Gucci в результатах поиска. Обычно это делается с помощью bool запроса:

resp = client.search(
    index="shirts",
    query={
        "bool": {
            "filter": [
                {
                    "term": {
                        "color": "red"
                    }
                },
                {
                    "term": {
                        "brand": "gucci"
                    }
                }
            ]
        }
    },
)
print(resp)
response = client.search(
  index: 'shirts',
  body: {
    query: {
      bool: {
        filter: [
          {
            term: {
              color: 'red'
            }
          },
          {
            term: {
              brand: 'gucci'
            }
          }
        ]
      }
    }
  }
)
puts response
const response = await client.search({
  index: "shirts",
  query: {
    bool: {
      filter: [
        {
          term: {
            color: "red",
          },
        },
        {
          term: {
            brand: "gucci",
          },
        },
      ],
    },
  },
});
console.log(response);
GET /shirts/_search
{
  "query": {
    "bool": {
      "filter": [
        { "term": { "color": "red"   }},
        { "term": { "brand": "gucci" }}
      ]
    }
  }
}

Однако, вы также хотели бы использовать навигацию по аспектам для отображения списка других параметров, которые пользователь мог бы выбрать. Возможно, у вас есть поле model, которое позволило бы пользователю ограничить результаты поиска красными рубашками Gucci t-shirts или dress-shirts.

Это можно сделать с помощью terms агрегации:

resp = client.search(
    index="shirts",
    query={
        "bool": {
            "filter": [
                {
                    "term": {
                        "color": "red"
                    }
                },
                {
                    "term": {
                        "brand": "gucci"
                    }
                }
            ]
        }
    },
    aggs={
        "models": {
            "terms": {
                "field": "model"
            }
        }
    },
)
print(resp)
response = client.search(
  index: 'shirts',
  body: {
    query: {
      bool: {
        filter: [
          {
            term: {
              color: 'red'
            }
          },
          {
            term: {
              brand: 'gucci'
            }
          }
        ]
      }
    },
    aggregations: {
      models: {
        terms: {
          field: 'model'
        }
      }
    }
  }
)
puts response
const response = await client.search({
  index: "shirts",
  query: {
    bool: {
      filter: [
        {
          term: {
            color: "red",
          },
        },
        {
          term: {
            brand: "gucci",
          },
        },
      ],
    },
  },
  aggs: {
    models: {
      terms: {
        field: "model",
      },
    },
  },
});
console.log(response);
GET /shirts/_search
{
  "query": {
    "bool": {
      "filter": [
        { "term": { "color": "red"   }},
        { "term": { "brand": "gucci" }}
      ]
    }
  },
  "aggs": {
    "models": {
      "terms": { "field": "model" } 
    }
  }
}

Возвращает самые популярные модели красных рубашек от Gucci.

Но, возможно, вы также хотели бы сказать пользователю, сколько рубашек Gucci доступно в других цветах. Если вы просто добавите terms агрегацию по полю color, вы получите только цвет red, потому что ваш запрос возвращает только красные рубашки от Gucci.

Вместо этого вы хотите включить рубашки всех цветов во время агрегации, а затем применить colors фильтр только к результатам поиска. В этом заключается цель post_filter:

resp = client.search(
    index="shirts",
    query={
        "bool": {
            "filter": {
                "term": {
                    "brand": "gucci"
                }
            }
        }
    },
    aggs={
        "colors": {
            "terms": {
                "field": "color"
            }
        },
        "color_red": {
            "filter": {
                "term": {
                    "color": "red"
                }
            },
            "aggs": {
                "models": {
                    "terms": {
                        "field": "model"
                    }
                }
            }
        }
    },
    post_filter={
        "term": {
            "color": "red"
        }
    },
)
print(resp)
response = client.search(
  index: 'shirts',
  body: {
    query: {
      bool: {
        filter: {
          term: {
            brand: 'gucci'
          }
        }
      }
    },
    aggregations: {
      colors: {
        terms: {
          field: 'color'
        }
      },
      color_red: {
        filter: {
          term: {
            color: 'red'
          }
        },
        aggregations: {
          models: {
            terms: {
              field: 'model'
            }
          }
        }
      }
    },
    post_filter: {
      term: {
        color: 'red'
      }
    }
  }
)
puts response
const response = await client.search({
  index: "shirts",
  query: {
    bool: {
      filter: {
        term: {
          brand: "gucci",
        },
      },
    },
  },
  aggs: {
    colors: {
      terms: {
        field: "color",
      },
    },
    color_red: {
      filter: {
        term: {
          color: "red",
        },
      },
      aggs: {
        models: {
          terms: {
            field: "model",
          },
        },
      },
    },
  },
  post_filter: {
    term: {
      color: "red",
    },
  },
});
console.log(response);
GET /shirts/_search
{
  "query": {
    "bool": {
      "filter": {
        "term": { "brand": "gucci" } 
      }
    }
  },
  "aggs": {
    "colors": {
      "terms": { "field": "color" } 
    },
    "color_red": {
      "filter": {
        "term": { "color": "red" } 
      },
      "aggs": {
        "models": {
          "terms": { "field": "model" } 
        }
      }
    }
  },
  "post_filter": { 
    "term": { "color": "red" }
  }
}

Основной запрос теперь находит все рубашки от Gucci, независимо от цвета.

Агрегация colors возвращает популярные цвета рубашек от Gucci.

Агрегация color_red ограничивает под-агрегацию models красными рубашками Gucci.

Наконец, post_filter удаляет цвета, отличные от красного, из поиска hits.

Переоценка результатов отфильтрованного поиска

Переоценка может помочь улучшить точность, переупорядочив только верхние (например, 100 - 500) документов, возвращенные фазами query и post_filter, используя вторичный (обычно более затратный) алгоритм, вместо применения дорогостоящего алгоритма ко всем документам в индексе.

Запрос rescore выполняется на каждом фрагменте перед возвратом результатов для сортировки узлом, обрабатывающим общий запрос поиска.

В настоящее время API переоценки имеет только одну реализацию: переоценка запросом, которая использует запрос для настройки оценки. В будущем могут быть доступны альтернативные переоценки, например, парная переоценка.

Будет выброшено исключение, если явный sort (кроме _score в порядке убывания) указан с запросом rescore.

При экспонировании постраничности пользователям не следует изменять window_size при прохождении каждой страницы (передавая различные значения from), так как это может изменить верхние результаты, вызывая непоследовательное смещение результатов при переходе пользователя по страницам.

Переоценка запросом

Переоценка запросом выполняет второй запрос только для верхних результатов, возвращаемых фазами query и post_filter. Количество документов, которые будут проверены на каждом фрагменте, можно контролировать с помощью параметра window_size, который по умолчанию равен 10.

По умолчанию оценки от исходного запроса и запроса переоценки комбинируются линейно, чтобы получить итоговую _score для каждого документа. Относительная важность исходного запроса и запроса переоценки можно контролировать с помощью параметров query_weight и rescore_query_weight соответственно. Оба по умолчанию равны 1.

Например:

resp = client.search(
    query={
        "match": {
            "message": {
                "operator": "or",
                "query": "the quick brown"
            }
        }
    },
    rescore={
        "window_size": 50,
        "query": {
            "rescore_query": {
                "match_phrase": {
                    "message": {
                        "query": "the quick brown",
                        "slop": 2
                    }
                }
            },
            "query_weight": 0.7,
            "rescore_query_weight": 1.2
        }
    },
)
print(resp)
response = client.search(
  body: {
    query: {
      match: {
        message: {
          operator: 'or',
          query: 'the quick brown'
        }
      }
    },
    rescore: {
      window_size: 50,
      query: {
        rescore_query: {
          match_phrase: {
            message: {
              query: 'the quick brown',
              slop: 2
            }
          }
        },
        query_weight: 0.7,
        rescore_query_weight: 1.2
      }
    }
  }
)
puts response
const response = await client.search({
  query: {
    match: {
      message: {
        operator: "or",
        query: "the quick brown",
      },
    },
  },
  rescore: {
    window_size: 50,
    query: {
      rescore_query: {
        match_phrase: {
          message: {
            query: "the quick brown",
            slop: 2,
          },
        },
      },
      query_weight: 0.7,
      rescore_query_weight: 1.2,
    },
  },
});
console.log(response);
POST /_search
{
   "query" : {
      "match" : {
         "message" : {
            "operator" : "or",
            "query" : "the quick brown"
         }
      }
   },
   "rescore" : {
      "window_size" : 50,
      "query" : {
         "rescore_query" : {
            "match_phrase" : {
               "message" : {
                  "query" : "the quick brown",
                  "slop" : 2
               }
            }
         },
         "query_weight" : 0.7,
         "rescore_query_weight" : 1.2
      }
   }
}

Способ объединения оценок можно контролировать с помощью score_mode:

Режим оценки Описание

total

Сложить исходную оценку и оценку запроса переоценки. По умолчанию.

multiply

Умножить исходную оценку на оценку запроса переоценки. Полезно для переоценок с помощью function query.

avg

Среднее значение исходной оценки и оценки запроса переоценки.

max

Максимальное значение исходной оценки и оценки запроса переоценки.

min

Минимальное значение исходной оценки и оценки запроса переоценки.

Несколько переоценок

Также возможно выполнить несколько переоценок последовательно:

resp = client.search(
    query={
        "match": {
            "message": {
                "operator": "or",
                "query": "the quick brown"
            }
        }
    },
    rescore=[
        {
            "window_size": 100,
            "query": {
                "rescore_query": {
                    "match_phrase": {
                        "message": {
                            "query": "the quick brown",
                            "slop": 2
                        }
                    }
                },
                "query_weight": 0.7,
                "rescore_query_weight": 1.2
            }
        },
        {
            "window_size": 10,
            "query": {
                "score_mode": "multiply",
                "rescore_query": {
                    "function_score": {
                        "script_score": {
                            "script": {
                                "source": "Math.log10(doc.count.value + 2)"
                            }
                        }
                    }
                }
            }
        }
    ],
)
print(resp)
response = client.search(
  body: {
    query: {
      match: {
        message: {
          operator: 'or',
          query: 'the quick brown'
        }
      }
    },
    rescore: [
      {
        window_size: 100,
        query: {
          rescore_query: {
            match_phrase: {
              message: {
                query: 'the quick brown',
                slop: 2
              }
            }
          },
          query_weight: 0.7,
          rescore_query_weight: 1.2
        }
      },
      {
        window_size: 10,
        query: {
          score_mode: 'multiply',
          rescore_query: {
            function_score: {
              script_score: {
                script: {
                  source: 'Math.log10(doc.count.value + 2)'
                }
              }
            }
          }
        }
      }
    ]
  }
)
puts response
const response = await client.search({
  query: {
    match: {
      message: {
        operator: "or",
        query: "the quick brown",
      },
    },
  },
  rescore: [
    {
      window_size: 100,
      query: {
        rescore_query: {
          match_phrase: {
            message: {
              query: "the quick brown",
              slop: 2,
            },
          },
        },
        query_weight: 0.7,
        rescore_query_weight: 1.2,
      },
    },
    {
      window_size: 10,
      query: {
        score_mode: "multiply",
        rescore_query: {
          function_score: {
            script_score: {
              script: {
                source: "Math.log10(doc.count.value + 2)",
              },
            },
          },
        },
      },
    },
  ],
});
console.log(response);
POST /_search
{
   "query" : {
      "match" : {
         "message" : {
            "operator" : "or",
            "query" : "the quick brown"
         }
      }
   },
   "rescore" : [ {
      "window_size" : 100,
      "query" : {
         "rescore_query" : {
            "match_phrase" : {
               "message" : {
                  "query" : "the quick brown",
                  "slop" : 2
               }
            }
         },
         "query_weight" : 0.7,
         "rescore_query_weight" : 1.2
      }
   }, {
      "window_size" : 10,
      "query" : {
         "score_mode": "multiply",
         "rescore_query" : {
            "function_score" : {
               "script_score": {
                  "script": {
                    "source": "Math.log10(doc.count.value + 2)"
                  }
               }
            }
         }
      }
   } ]
}

Первая получает результаты запроса, а затем вторая получает результаты первой и так далее. Вторая переоценка «увидит» сортировку, выполненную первой переоценкой, поэтому можно использовать большой диапазон в первой переоценке, чтобы вытащить документы в меньший диапазон для второй переоценки.

© 2023-2025 Elasticsearch
As of September 2024, Elasticsearch is available under a choice of three licenses: the Server Side Public License (SSPL), the Elastic License, or the AGPLv3 (OSI approved).
Elasticsearch and the Elasticsearch logo are trademarks of Elasticsearch B.V., registered in the U.S. and in other countries.
https://www.elastic.co/guide/en/elasticsearch/reference/8.17/filter-search-results.html

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API