Подтвердить что ты не робот

Elasticsearch сортирует по детям

Два объекта: сбор и продукт. Коллекция является родителем продукта.

Мне нужно искать по условиям продукта и показывать коллекции по 4 товарам каждый.

Коллекции и продукты могут быть частично сопоставлены, но лучше всего соответствовать первым. Если совпадение не заполнено, некоторые термины имеют приоритет.

Пример: поиск "цвет: красный" и "материал: камень" должен сначала отображать красные камни, а другой красный - следующий (это касается соответствия коллекции и соответствия продукта).

Итак, все это разрешено ниже:

{
  "query": {
    "has_child": {
      "type": "products",
      "query": {
        "bool": {
          "should": [
            {
              "constant_score": {
                "filter": {
                  "match_all": {}
                },
                "boost": 1
              }
            },
            {
              "constant_score": {
                "filter": {
                  "terms": { "_name": "colors", "colors": [5] }
                },
                "boost": 1.2
              }
            },
            {
              "constant_score": {
                "filter": {
                  "terms": { "_name": "materials", "productTypes": [6] }
                },
                "boost": 1
              }
            }
          ]
        }
      },
      "score_mode": "max",
      "inner_hits": {
        "size": 4,
        "sort": [
          "_score"
        ]
      }
    }
  },
  "sort": [
    "_score"
  ]
}

Хорошо, теперь проблема.

Нужно сортировать по цене. Как ASC, как DESC. Цена - это свойство продукта.

Нужно сортировать по цене совпадающих продуктов, поэтому не может переместить цену в коллекцию. Необходимо сортировать по цене как коллекцию как продукты. Коллекции отсортированы по минимальной (или максимальной) цене совпадающих продуктов.

Нужно сортировать по цене только 100% сопоставимых продуктов (ну, частично совпадающие могут быть отсортированы тоже, но после). Я имею в виду, сортировка должна быть как ORDER BY _score, цена

Пример, который я хочу получить, сортировка по цене asc, [nn] означает частично согласованный продукт:

Collection1
100 - 200 - 800 - [99]
Collection2
300 - 500 - [10] - [20]
Collection3
400 - 450 - 500 - [100]

Я обнаружил, что сортировка по дочернему не поддерживается. И предложение пересчитать счет. Но я использую счет для сортировки по совпадению. Моя попытка была

{
  "query": {
    "has_child": {
      "type": "products",
      "query": {
        "function_score": {
          "query": {
            "bool": {
              "should": [
                ... same query as above ...
              ]
            }
          },
          "functions": [
            {
              "script_score": {
                "script": "ceil(_score * 100) * 100000 + (99999 - doc['price'].value/100)",
                "lang": "expression"
              }
            }
          ]
        }
      },
      "score_mode": "max",
      "inner_hits": {
        "size": 4,
        "sort": [
          "_score",
          {
            "price": {
              "order": "desc"
            }
          }
        ]
      }
    }
  },
  "sort": [
    "_score"
  ]
}

Но я действительно сбиваю с толку результаты, которые я вижу в ответ. Запрос на помощь:) Или, может быть, отбросьте это и создайте вложенный индекс?

UPD: найдено, что было ошибкой со счетом. По умолчанию эластичный комбинированный балл и результат script_score. Так что оценка была ceil(_score * 100) * 100000 + (99999 - doc['price'].value/100) * _score - это может сломать идею, но легко исправить с помощью параметра boost_mode function_score. Запрос результата:

{
  "query": {
    "has_child": {
      "type": "products",
      "query": {
        "function_score": {
          "query": {
            "bool": {
              "should": [
                ... same query as above ...
              ]
            }
          },
          "functions": [
            {
              "script_score": {
                "script": "ceil((log10(_score)+10) * 100) * 100000 + (99999 - doc['price'].value)",
                "lang": "expression"
              }
            }
          ],
          "boost_mode": "replace"
        }
      },
      "score_mode": "max",
      "inner_hits": {
        "size": 4,
        "sort": [
          "_score",
          {
            "price": {
              "order": "desc"
            }
          }
        ]
      }
    }
  },
  "sort": [
    "_score"
  ]
}

boost_mode == 'replace означает "результат использования функции как результат". Кроме того, используется log10, чтобы узнать, сколько цифр в _score. Для сортировки по цене DESC необходимо изменить формулу на ceil((log10(_score)+10) * 100) * 100000 + (doc['price'].value)

UPD2

Формула ceil((log10(_score)+10) * 100) * 100000 + (99999 - doc['price'].value) возвращает 100099952 для цены 48 и для цены 50 (boost == 1, queryNorm == 1), поскольку ограничение одиночной точности.

Новая формула ceil((log10(_score)+5) * 100) * 10000 + (9999 - ceil(log10(doc['price'].value) * 1000)) - уменьшено количество цифр для оценки и переключено с цены на lg цены и уменьшено количество цифр. Обратная связь приветствуется.

4b9b3361