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

#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-го подхода заключаются в следующем:

  1. Никаких задействованных скриптов -> более быстрое выполнение.

  2. Что еще более важно, вам не нужно беспокоиться о том, что математика по дням месяца, например, 15 декабря, будет позже, чем 20 ноября, но тривиальное сравнение по дням месяца приведет к обратному результату. Вы можете реализовать нечто подобное в своих сценариях, но большая сложность означает худшую скорость выполнения.

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

Комментарии:

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 при добавлении фильтров для поля сценария.