Расчет даты в watcher

Добрый день!

Делаю watcher. В ответ на запрос watcher`a из elastic в таком формате выводятся два поля с датами:

  • формат @timestamp - 2019-10-06T2 0:06:59.140Z
  • формат @buildTimestamp - 2019-10-06T20:06:58.237Z

Мне необходимо вычислить разницу между этими двумя датами в миллисекундах (я так понимаю это делается в поле transform?)

Что для этого необходимо ?

Зависит от того, как вы эти даты получаете в watcher.

отсюда:

  "hits": {
      "hits": [
        {
          "_index": "job",
          "_type": "_doc",
          "_source": {
              "@timestamp": "2019-10-06T20:06:24.580Z",
              "@buildTimestamp": "2019-10-06T20:06:23.479Z",
              "source": "dev",
              "projectName": "dev",
              "type": "job"

.....

То есть как строки? Тогда как-то так:

DELETE test

PUT test/_doc/1
{
  "@timestamp": "2019-10-06T20:06:24.580Z",
  "@buildTimestamp": "2019-10-06T20:06:23.479Z",
  "foo": "bar"
}

PUT _watcher/watch/log_error_watch
{
  "trigger": {
    "schedule": {
      "interval": "2s"
    }
  },
  "input": {
    "search": {
      "request": {
        "indices": [
          "test"
        ],
        "body": {
          "query": {
            "match": {
              "foo": "bar"
            }
          },
          "size": 1
        }
      }
    }
  },
  "transform": {
    "script": {
      "source": """
        Instant timestamp = Instant.parse( ctx.payload.hits.hits[0]['_source']['@timestamp']);
        Instant buildTimestamp = Instant.parse( ctx.payload.hits.hits[0]['_source']['@buildTimestamp']);
        return ['diff': Duration.between(buildTimestamp, timestamp).toMillis()];
      """,
      "lang": "painless"
    }
  },
  "actions": {
    "log_error": {
      "logging": {
        "text": """The diff = {{ctx.payload.diff}}ms"""
      }
    }
  }
}
1 Like

Игорь, спасибо, но есть нюанс:
у нас есть массив и несколько документов (с разными "_source"), которые необходимо обрабатывать и указывать в {{ctx.payload.diff}}, который у каждого будет свой.

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

Скрипты в transform пишутся на языке painless. Там есть доступ к всех хитам через ctx.payload.hits.hits - это список, то есть ctx.payload.hits.hits[0]['_source'] - это все значение первого документа, ctx.payload.hits.hits[1]['_source'] - второго и т.д.

Прошу прощения за неполный вопрос! Вот код ватчера (постарался привести его в более читаемый вид):

{
  "trigger": {....}
  },
  "input": {
    "search": {
      "request": {
        "search_type": "query_then_fetch",
        "indices": [
          "web*"
        ],
        "rest_total_hits_as_int": true,
        "body": {
          "size": 5,
          "query": {
            "bool": {
              "must": [
                {
                  "query_string": {
                    "analyze_wildcard": true,
                    "fields": [
                      "projectName.keyword"
                    ],
                    "query": "domain.ru"
                  }
                }
              ],
              "filter": {
                "range": {
                  "@timestamp": {
                    "gte": "{{ctx.trigger.scheduled_time}}||-30m",
                    "lte": "{{ctx.trigger.scheduled_time}}",
                    "format": "strict_date_optional_time||epoch_millis"
                  }
                }
              }
            }
          }
        }
      }
    }
  },
  "condition": {
    "script": {
      "source": "if (ctx.payload.hits.total >= params.threshold) { return true; } return false;",
      "lang": "painless",
      "params": {
        "threshold": 1
      }
    }
  },
  "actions": {
    "telegram": {.............},
        "body": "chat_id=-1234567890&text=
		
		Дата: {{ctx.payload.human_date}}%0A\n
		{{#ctx.payload.aggregations}}
		Название: {{_source.projectName}}%0A\n
		Время старта: {{ctx.payload.new_date}}%0A\n
		Заняло времени (s): {{ctx.payload.duration}}%0A\n
		{{/ctx.payload.aggregations}}"
      }
    }
  },
  "transform": {
    "script": {
      "source": "DateFormat dateFormat = new SimpleDateFormat(\"yyyy-MM-dd HH:mm:ss z\"); dateFormat.setTimeZone(TimeZone.getTimeZone(\"Europe/Moscow\")); Date date = new Date(); ctx.execution_time = dateFormat.format(date); for (bucket in ctx.payload.hits.hits) {bucket.timestamp = bucket._source['@timestamp']; bucket.buildTimestamp = bucket._source['@buildTimestamp']; def dateString = ctx.payload.hits.hits[0]['_source']['@buildTimestamp']; def inputFormat = new SimpleDateFormat(\"yyyy-MM-dd'T'HH:mm:ss.SSS'Z'\"); def myDate = inputFormat.parse(dateString); def outputFormat = new SimpleDateFormat(\"yyyy-MM-dd HH:mm:ss z\"); ctx.new_date = outputFormat.format(myDate);} Instant timestamp = Instant.parse(ctx.payload.hits.hits[0]['_source']['@timestamp']); Instant buildTimestamp = Instant.parse(ctx.payload.hits.hits[0]['_source']['@buildTimestamp']);  return ['new_date': ctx.new_date, 'human_date': ctx.execution_time, 'aggregations': ctx.payload.hits.hits, 'duration': Duration.between(buildTimestamp, timestamp).toMillis()]",
      "lang": "painless",
      "params": {
        "threshold": 1
      }
    }
  }
}

Есть проблема с выводом {{ctx.payload.new_date}} для нескольких документов.У них new_date разный (т.е. в каждом документе есть @buildTimestamp с разными значениями).

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

Исправил Ваш код:

{
  "trigger": {
    "schedule": {
      "interval": "2s"
    }
  },
  "input": {
    "search": {
      "request": {
        "search_type": "query_then_fetch",
        "indices": [
          "web*"
        ],
        "rest_total_hits_as_int": true,
        "body": {
          "size": 5,
          "query": {
            "bool": {
              "must": [
                {
                  "query_string": {
                    "analyze_wildcard": true,
                    "fields": [
                      "projectName.keyword"
                    ],
                    "query": "domain.ru"
                  }
                }
              ],
              "filter": {
                "range": {
                  "@timestamp": {
                    "gte": "{{ctx.trigger.scheduled_time}}||-30m",
                    "lte": "{{ctx.trigger.scheduled_time}}",
                    "format": "strict_date_optional_time||epoch_millis"
                  }
                }
              }
            }
          }
        }
      },
   "condition": {
    "script": {
      "source": "if (ctx.payload.hits.total >= params.threshold) { return true; } return false;",
      "lang": "painless",
      "params": {
        "threshold": 1
      }
    }
  },
  "transform": {
    "script": {
      "source": "
	  DateFormat dateFormat = new SimpleDateFormat(\"yyyy-MM-dd HH:mm:ss z\"); 
	  dateFormat.setTimeZone(TimeZone.getTimeZone(\"Europe/Moscow\")); 
	  Date date = new Date(); ctx.execution_time = dateFormat.format(date); 
	  for (bucket in ctx.payload.hits.hits) 
	  {
		  bucket.timestamp = bucket._source['@timestamp']; 
		  bucket.buildTimestamp = bucket._source['@buildTimestamp']; 
		  def dateString = ctx.payload.hits.hits[0]['_source']['@buildTimestamp']; 
		  def inputFormat = new SimpleDateFormat(\"yyyy-MM-dd'T'HH:mm:ss.SSS'Z'\"); 
		  def myDate = inputFormat.parse(dateString); 
		  def outputFormat = new SimpleDateFormat(\"yyyy-MM-dd HH:mm:ss z\"); 
		  ctx.new_date = outputFormat.format(myDate);
		} 
		  
	  Instant timestamp = Instant.parse(ctx.payload.hits.hits[0]['_source']['@timestamp']); 
	  Instant buildTimestamp = Instant.parse(ctx.payload.hits.hits[0]['_source']['@buildTimestamp']); 
	  return 
	   [
		 'new_date': ctx.new_date, 
		 'human_date': ctx.execution_time, 
	     'aggregations': ctx.payload.hits.hits, 
	     'diff': Duration.between(buildTimestamp, timestamp).toMillis()
	    ]",
      "lang": "painless",
      "params": {
        "threshold": 1
      }
    }
  },
  "actions": {
    "telegram": {.............},
        "body": "chat_id=-1234567890&text=
		Дата: {{ctx.payload.human_date}}%0A\n
		{{#ctx.payload.aggregations}}
		Название: {{_source.projectName}}%0A\n
		Время старта: {{ctx.payload.new_date}}%0A\n
		Заняло времени (s): {{ctx.payload.duration}}%0A\n
		{{/ctx.payload.aggregations}}"
      }
    }
  }
}

Требуется получить в action в "body " {{ctx.payload.new_date}} для нескольких документов. У них он разный - т.е. в каждом документе есть поле @buildTimestamp с разными датой и временем.

@buildTimestamp по трансформации равен ctx.new_date, который "вытащили" так:

def dateString = ctx.payload.hits.hits[0]['_source']['@buildTimestamp'];
def myDate = inputFormat.parse(dateString);
ctx.new_date = outputFormat.format(myDate)

Исправил Ваш код:

Проблема в том, что у меня нет ваших данных, нет вашего маппинга и нет ключа telegram, чтобы протестировать решение. Поэтому я и привел пример, в котором есть все необходимое - данные и watcher с logging action. Мой пример может запустить кто угодно - он не требует никаких дополнительных установок. Я поэтому и хотел продолжить на моем примере.

В вашем случае, надо все поставить в цикл и возвращать массив дат.

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

"source": 
    "DateFormat dateFormat = new SimpleDateFormat(\"yyyy-MM-dd HH:mm:ss z\"); 
	dateFormat.setTimeZone(TimeZone.getTimeZone(\"Europe/Moscow\")); 
	Date date = new Date(); ctx.execution_time = dateFormat.format(date); 
	for (bucket in ctx.payload.hits.hits) 
	{
		  bucket.timestamp = bucket._source['@timestamp']; 
		  bucket.buildTimestamp = bucket._source['@buildTimestamp']; 
		  def dateString = ctx.payload.hits.hits[0]['_source']['@buildTimestamp']; 
		  def inputFormat = new SimpleDateFormat(\"yyyy-MM-dd'T'HH:mm:ss.SSS'Z'\"); 
		  def myDate = inputFormat.parse(dateString); 
		  def outputFormat = new SimpleDateFormat(\"yyyy-MM-dd HH:mm:ss z\"); 
		  ctx.new_date = outputFormat.format(myDate);
	  } 
	Instant timestamp = Instant.parse(ctx.payload.hits.hits[0]['_source']['@timestamp']); 
	Instant buildTimestamp = Instant.parse(ctx.payload.hits.hits[0]['_source']['@buildTimestamp']); 
	return [
      'new_date': ctx.new_date, 
	  'human_date': ctx.execution_time, 
	  'aggregations': ctx.payload.hits.hits, 
	  'diff': Duration.between(buildTimestamp, timestamp).toMillis() ]",

Плюс к этому, нам надо отформатировать вывод поля buildTimestamp в цикле, так же для каждого документа в массиве по отдельности.
Как это сделать?