#elasticsearch #kibana #elasticsearch-painless
#elasticsearch #kibana #elasticsearch -безболезненно
Вопрос:
Например, у меня есть поле даты delivery_datetime
в индексе, я должен показать пользователю на текущий день, должна ли конкретная посылка быть оплачена сегодня или просрочена или нет
Я не могу создать отдельное поле и выполнить переиндексацию, потому что оно основано на текущей дате и меняется каждый день, например, если мне нужно вычислять во время индексации, я должен переиндексировать каждый день, а это невозможно, потому что у меня много данных.
Я могу использовать обновление по запросу, но мой индекс часто обновляется с помощью скрипта Python, думал, что у нас здесь нет свойства ACID, у нас будет конфликт версий.
Насколько мне известно, я думаю, что мой единственный вариант — использовать скриптовое поле.
Если мне нужно написать логику в псевдокоде:
Due - delivery_datetime.dateOnly == now.dateOnly
Over Due - delivery_datetime.dateOnly < now.dateOnly
Not Due - delivery_datetime.dateOnly > now.dateOnly
Думал, что у меня много данных, если я создаю CSV, я не хочу, чтобы скриптовое поле сильно влияло на производительность кластера.
Поэтому мне нужна помощь, чтобы сделать это эффективно в скриптовом поле, или, если бы было какое-либо совершенно другое решение, также было бы очень полезно.
Ожидание помощи путем предоставления безболезненного скрипта, если скриптовое поле является единственным решением.
Ответ №1:
После того, как мы исключили обновления / обновления документов, по сути, существует 2 подхода к этому: script_fields
или filter aggregations
.
Давайте сначала предположим, что ваше отображение выглядит так:
{
"mappings": {
"properties": {
"delivery_datetime": {
"type": "object",
"properties": {
"dateOnly": {
"type": "date",
"format": "dd.MM.yyyy"
}
}
}
}
}
}
Теперь, если мы отфильтруем все наши пакеты, скажем, по его идентификатору и захотим узнать, в каком состоянии он находится, мы можем создать 3 поля сценария следующим образом:
GET parcels/_search
{
"_source": "timeframe_*",
"script_fields": {
"timeframe_due": {
"script": {
"source": "doc['delivery_datetime.dateOnly'].value.dayOfMonth == params.nowDayOfMonth",
"params": {
"nowDayOfMonth": 8
}
}
},
"timeframe_overdue": {
"script": {
"source": "doc['delivery_datetime.dateOnly'].value.dayOfMonth < params.nowDayOfMonth",
"params": {
"nowDayOfMonth": 8
}
}
},
"timeframe_not_due": {
"script": {
"source": "doc['delivery_datetime.dateOnly'].value.dayOfMonth > params.nowDayOfMonth",
"params": {
"nowDayOfMonth": 8
}
}
}
}
}
который вернет что-то вроде:
...
"fields" : {
"timeframe_due" : [
true
],
"timeframe_not_due" : [
false
],
"timeframe_overdue" : [
false
]
}
Это тривиально, и математика даты имеет существенный недостаток, который будет рассмотрен ниже.
В качестве альтернативы мы можем использовать 3 совокупности фильтров и аналогичным образом отфильтровать только 1 рассматриваемый документ следующим образом:
GET parcels/_search
{
"size": 0,
"query": {
"ids": {
"values": [
"my_id_thats_due_today"
]
}
},
"aggs": {
"due": {
"filter": {
"range": {
"delivery_datetime.dateOnly": {
"gte": "now/d",
"lte": "now/d"
}
}
}
},
"overdue": {
"filter": {
"range": {
"delivery_datetime.dateOnly": {
"lt": "now/d"
}
}
}
},
"not_due": {
"filter": {
"range": {
"delivery_datetime.dateOnly": {
"gt": "now/d"
}
}
}
}
}
}
выдача
...
"aggregations" : {
"overdue" : {
"doc_count" : 0
},
"due" : {
"doc_count" : 1
},
"not_due" : {
"doc_count" : 0
}
}
Теперь преимущества 2-го подхода заключаются в следующем:
-
Никаких задействованных скриптов -> более быстрое выполнение.
-
Что еще более важно, вам не нужно беспокоиться о том, что математика по дням месяца, например, 15 декабря, будет позже, чем 20 ноября, но тривиальное сравнение по дням месяца приведет к обратному результату. Вы можете реализовать нечто подобное в своих сценариях, но большая сложность означает худшую скорость выполнения.
-
Вы можете отказаться от фильтрации идентификаторов и использовать эти агрегированные показатели на внутренней панели мониторинга. Возможно, даже панель управления клиентами, но у постоянных клиентов редко бывает много посылок, которые было бы разумно объединить.
Комментарии:
1. Спасибо за ваш ответ, я действительно использую агрегацию фильтров в таблице данных Kibana, чтобы показать количество просроченных / просроченных / не просроченных, и, как вы упомянули, его производительность также очень хорошая, но она полезна только для подсчета. Я должен показывать в однострочном поле со значением
Due
илиOver Due
илиNot Due
в детализации (обнаружение сохраненного поиска). Итак, похоже, что скриптовое поле — единственный способ, я реализовал его сейчас, я опубликую его в ответе для сообщества.
Ответ №2:
Отвечая на мой собственный вопрос, вот что сработало для меня.
Сценарий сценария поля:
def DiffMillis = 0;
if(!doc['delivery_datetime'].empty) {
// Converting each to days, 1000*60*60*24 = 86400000
DiffMillis = (new Date().getTime() / 86400000) - (doc['delivery_datetime'].value.getMillis() / 86400000);
}
doc['delivery_datetime'].empty ? "No Due Date": (DiffMillis==0?"Due": (DiffMillis>0?"Over Due":"Not Due") )
Я специально использовал тернарный оператор, потому что, если я использую if else
, я должен использовать return
, если я использую return
, я столкнулся search_phase_execution_exception
при добавлении фильтров для поля сценария.