Elasticsearch - найти ближайший номер при оценке результатов - PullRequest
1 голос
/ 22 сентября 2019

Мне нужен способ сопоставления с ближайшим номером документа эластичного поиска.

Я хочу использовать упругий поиск для фильтрации количественных атрибутов и смог достичь жестких ограничений, используя диапазон запросы принимают, что результаты, которые находятся за пределами этого набора результатов, пропускаются.Я бы предпочел, чтобы результаты поиска нескольких фильтров были наиболее близки.

const query = {
  query: {
    bool: {
      should: [
        {
          range: {
            gte: 5,
            lte: 15
          }
        },
        {
          range: {
            gte: 1979,
            lte: 1989
          }
        }
      ]
    }
  }
}
const results = await client.search({
  index: 'test',
  body: query
})

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

const data = [
  { "item": "one", "year": 1980, "sales": 20 },
  { "item": "two", "year": 1982, "sales": 12 },
  { "item": "three", "year": 1986, "sales": 6 },
  { "item": "four", "year": 1989, "sales": 4 },
  { "item": "five", "year": 1991, "sales": 6 }
]

const add = (a, b) => a + b


const findClosestMatch = (filters, data) => {
 const scored = data.map(item => ({
    ...item,
    // add the score to a copy of the data
    _score: calculateDifferenceScore(filters, item)
  }))
  // mutate the scored array by sorting it
  scored.sort((a, b) => a._score.total - b._score.total)
  return scored
}

const calculateDifferenceScore = (filters, item) => {
  const result = Object.keys(filters).reduce((acc, x) => ({
    ...acc,
    // calculate the absolute difference between the filter and data point
    [x]: Math.abs(filters[x] - item[x])
  }), {})
  // sum the total diffences
  result.total = Object.values(result).reduce(add)
  return result
}

console.log(
  findClosestMatch({ sales: 10, year: 1984 }, data)
)
<script src="https://codepen.io/synthet1c/pen/KyQQmL.js"></script>

Я пытаюсь добиться того же в эластичном поиске, но безуспешно при использовании запроса function_score .например,

const query = {
  query: {
    function_score: {
      functions: [
        {
          linear: {
            "year": {
              origin: 1984,
            },
            "sales": {
              origin: 10,
            }
          }
        }
      ]
    }
  }
}
const results = await client.search({
  index: 'test',
  body: query
})

Нет текста для поиска, я использую его для фильтрации только по числам, я делаю что-то не так или это не тот упругий поиск, и есть ли лучшие альтернативы?

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

Спасибо за любую помощь, я новичок вasticsearchссылки на статьи или области документации приветствуются!

1 Ответ

1 голос
/ 22 сентября 2019

У вас была правильная идея, вам просто не хватает нескольких полей в вашем запросе, чтобы заставить его работать.

Это должно выглядеть так:

{
      "query": {
        function_score: {
            functions: [
                {
                    linear: {
                        "year": {
                            origin: 1984,
                            scale: 1,
                            decay: 0.999
                        },
                        "sales": {
                            origin: 10,
                            scale: 1,
                            decay: 0.999
                        }
                    }
                },
            ]
        }
    }
}

The scaleполе является обязательным, так как оно говорит эластичному, как затухать счет, без него запрос просто не выполняется.

Поле decay не является обязательным, однако без него эластичный не знает, как рассчитать новый счет длядокументы, так что в итоге он будет давать оценку по умолчанию только для документов в диапазоне происхождения + шкалы, что нам не полезно.

исходные документы .

  • Я также рекомендую ограничить размер результата до 1, если вы хотите получить документ с наивысшей оценкой, в противном случае вам придется добавить фазу сортировки (в эластичном или в коде).

РЕДАКТИРОВАТЬ: (ИЗБЕГАТЬ)NULLS)

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

{
    "query": {
        "function_score": {
            "query": {
                "bool": {
                    "must": [
                        {
                            "bool": {
                                "filter": [
                                    {
                                        "bool": {
                                            "must": [
                                                {
                                                    "exists": {
                                                        "field": "year"
                                                    }
                                                },
                                                {
                                                    "exists": {
                                                        "field": "sales"
                                                    }
                                                },
                                            ]
                                        }
                                    }
                                ]
                            }
                        },
                        {
                            "match_all": {}
                        }
                    ]
                }
            },
            "functions": [
                {
                    "linear": {
                        "year": {
                            "origin": 1999,
                            "scale": 1,
                            "decay": 0.999
                        },
                        "sales": {
                            "origin": 50,
                            "scale": 1,
                            "decay": 0.999
                        }
                    }
                }
            ]
        }
    }
}

Обратите внимание, что у меня небольшой взлом с использованием запроса match_all, это связано с установкой запроса фильтрасчет до 0, поэтому с помощью запроса match all я сбросил его обратно1 для всех сопоставленных документов.

Этого также можно добиться более «правильным» способом, изменив функции - путь, который я предпочитаю не выбирать.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...