Доброго времени суток!
На проекте для поиска новостей на сайте используется elasticsearch. Для формирования запросов используется библиотека
https://github.com/ongr-io/ElasticsearchDSL .
Индекс следующий
[
'index' => $name,
'body' => [
'settings' => [
'analysis' => [
'analyzer' => [
'ru' => [
'char_filter' => ['html_strip'],
'tokenizer' => 'standard',
'filter' => ['lowercase', 'hunspell_ru', 'stopwords_ru', 'custom_word_delimiter', 'stop'],
],
],
'filter' => [
'hunspell_ru' => [
'type' => 'hunspell',
'locale' => 'ru_RU',
'dedup' => true,
],
'stopwords_ru' => [
'type' => 'stop',
'stopwords' => ['а', 'без', 'более', 'бы', 'был', 'была', 'были', 'было', 'быть', 'в', 'вам', 'вас', 'весь', 'во', 'вот', 'все', 'всего', 'всех', 'вы', 'где', 'да', 'даже', 'для', 'до', 'его', 'ее', 'если', 'есть', 'еще', 'же', 'за', 'здесь', 'и', 'из', 'или', 'им', 'их', 'к', 'как', 'ко', 'когда', 'кто', 'ли', 'либо', 'мне', 'может', 'мы', 'на', 'надо', 'наш', 'не', 'него', 'нее', 'нет', 'ни', 'них', 'но', 'ну', 'о', 'об', 'однако', 'он', 'она', 'они', 'оно', 'от', 'очень', 'по', 'под', 'при', 'с', 'со', 'так', 'также', 'такой', 'там', 'те', 'тем', 'то', 'того', 'тоже', 'той', 'только', 'том', 'ты', 'у', 'уже', 'хотя', 'чего', 'чей', 'чем', 'что', 'чтобы', 'чье', 'чья', 'эта', 'эти', 'это', 'я', 'a', 'an', 'and', 'are', 'as', 'at', 'be', 'but', 'by', 'for', 'if', 'in', 'into', 'is', 'it', 'no', 'not', 'of', 'on', 'or', 'such', 'that', 'the', 'their', 'then', 'there', 'these', 'they', 'this', 'to', 'was', 'will', 'with'],
'ignore_case' => true,
],
'custom_word_delimiter' => [
'type' => 'word_delimiter',
'generate_word_parts' => true,
'generate_number_parts' => true,
'catenate_words' => true,
'catenate_numbers' => false,
'catenate_all' => true,
'split_on_case_change' => true,
'preserve_original' => true,
'split_on_numerics' => false,
],
],
],
],
'mappings' => [
'doc' => [
'properties' => [
'type' => [
'type' => 'keyword',
],
'create_time' => [
'type' => 'text',
],
'alias' => [
'type' => 'keyword',
],
'title' => [
'type' => 'text',
'analyzer' => 'ru',
],
'content' => [
'type' => 'text',
'analyzer' => 'ru',
],
'intro_text' => [
'type' => 'text',
'analyzer' => 'ru',
],
'publish_date' => [
'type' => 'date',
'format' => 'yyyyMMdd',
],
],
],
],
],
]
Необходимо, чтобы результаты поиска сортировались с учётом даты публикации (результаты, которые имеют примерно одинаковый вес сортировались по убыванию.
Пробовал просто указывать `$search->addSort(new FieldSort('publish_date'), 'desc');`, но так предпочтение отдаётся более новым записям, а не более соответствующим поисковому запросу.
В результате чтения документации выявил, что можно использовать FunctionScoreQuery.
Далее уже пробовал читать разные статьи, задавать вопросы ChatGPT.
Первый более-менее рабочий вариант формулы был `Math.log(1 + doc['publish_date'].value.toInstant().toEpochMilli())`, но так довольно старые новости вылезли наверх. В результате я пришёл к следующему коду.
$referenceTimestamp = (new DateTimeImmutable())->getTimestamp() * 1000;
$functionScoreSource = <<<EOL
double t = doc['publish_date'].value.toInstant().toEpochMilli();
double p = params.referenceDate;
double d = Math.abs(7300.0 - ((p - t) / 86400000.0));
double r = Math.log(d);
return Math.max(r, 0);
EOL;
$functionScoreQuery = new FunctionScoreQuery($boolQuery, [
'functions' => [
[
'script_score' => [
'script' => [
'source' => $functionScoreSource,
'params' => [
'referenceDate' => $referenceTimestamp,
],
],
],
'weight' => 2.5,
],
],
'score_mode' => 'sum',
'boost_mode' => 'sum',
]);
$search->addQuery($functionScoreQuery);
7300 - это количество дней в 20 годах. Примерно за такое количество времени новости на сайте.
Но он всё равно ищет неидеально. Возможно ли как-то улучшить скрипт?