Чтобы улучшить диаграмму данных временных рядов, мы можем позволить пользователю выбирать временную основу или «разрешение», например. секунды, минуты, часы и т.

Таким образом, они могут видеть микротренды в данных (используя высокое разрешение, например, секунды) или искать более широкие макротренды в тех же данных (используя низкое разрешение, например, часы).

В этой статье мы настроим визуализацию данных временных рядов с помощью Vue и Chart.js. Мы позволим пользователю выбирать различные временные разрешения, которые будут обслуживаться запросами временной выборки GridDB из серверной базы данных.

Эта статья является продолжением серии, в которой мы создавали панель мониторинга сервера с использованием бэкэнда Node Express с GridDB и визуализировали данные в Vue. Рекомендуется сначала прочитать часть первую и часть вторую.

Вы можете получить код этого туториала здесь, на GitHub.

Графики данных с помощью Vue Chartkick

Приложение Vue, которое мы создали в предыдущем руководстве из этой серии, использовало библиотеку VueChartJs для визуализации данных.

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

Библиотекой, обеспечивающей большую гибкость в этом отношении, является Vue Chartkick. Итак, давайте заменим скрипт vue-chartjs, который мы загружаем в заголовок документа, на vue-chartkick.

<head>
  ...
  <script src="<https://cdn.jsdelivr.net/npm/[email protected]/dist/vue-chartkick.min.js>"></script>
</head>

Затем мы можем внести небольшие изменения в наше приложение Vue, чтобы оно использовало компонент линейной диаграммы Chartkick вместо VueChartJs.

<div id="app">
  <line-chart v-if="serverData" :data="serverData"></line-chart>
</div>
<script type="text/javascript">
new Vue({
  el: "#app",
  data: () => ({
    serverData: []
  }),
  async mounted () {
    const res = await fetch("/data");
    this.serverData = await res.json();
  }
})
</script>

На данный момент наше приложение Vue должно выглядеть так же, как и раньше, только теперь мы используем более гибкую библиотеку Chartkick.

Добавление селектора разрешения

В настоящее время наше приложение Vue запрашивает полезную нагрузку данных временного ряда с сервера, которая всегда имеет одну и ту же временную основу: секунды.

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

Обратите внимание, что у нас есть опция для секунд, минут или часов. Кроме того, у нас есть v-model, связанный с переменной resolution, так что мы можем легко получить выбранное значение в нашем приложении Vue.

<div id="app">
  <div>
    <line-chart v-if="serverData" :data="serverData"></line-chart>
    <select name="resolution" class="resolution-select" v-model="resolution">
      <option value="seconds">Seconds</option>
      <option value="minutes">Minutes</option>
            <option value="hours">Hours</option>
    </select>
  </div>
</div>

Давайте теперь добавим новое свойство реактивных данных resolution в экземпляр Vue и присвоим ему значение по умолчанию seconds.

new Vue({
  el: "#app",
  data: () => ({
    serverData: [],
    resolution: 'seconds'
  }),
  ...
})

Мы также можем добавить немного CSS в заголовок документа, чтобы немного улучшить стиль выбора,

<style>
  .resolution-select {
    margin: 2rem 0 0 1rem;
    padding: 0.5rem;
  }
</style>

После этого мы теперь видим селектор под диаграммой.

Перезагрузка данных при выборе изменения

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

Для этого мы создадим метод getServerData, который будет содержать логику вызова сервера, который у нас был раньше.

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

new Vue({
  el: "#app",
  data: () => ({
    serverData: [],
    resolution: 'seconds'
  }),
  methods: {
    async getServerData() {
      const res = await fetch("/data");
      this.serverData = await res.json();
    }
  },
  watch: {
    resolution() {
      this.getServerData();
    }
  },
  mounted () {
    this.getServerData();
  }
})

Передача значения разрешения на сервер

Следующим шагом является передача выбранного пользователем разрешения на сервер, чтобы сервер мог вернуть правильные данные.

Для этого мы изменим наш запрос на выборку на тип POST и отправим значение разрешения как часть полезной нагрузки JSON в теле запроса. Мы можем сделать это, передав объект конфигурации в fetch API. Этот объект будет иметь method из POST, подобъект headers с Content-Type из application/json и свойство body, для которого мы создаем строку JSON, содержащую значение разрешения.

async getServerData() {
  const res = await fetch("/data", {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json'
    },
    body: JSON.stringify({ resolution: this.resolution })
  });
  this.serverData = await res.json();
}

Обработка POST с помощью Express

Раньше наш сервер Express возвращал данные временных рядов при получении запроса GET к /data.

Теперь мы хотим использовать команду POST, чтобы мы могли получить параметр resolution в полезной нагрузке JSON. Чтобы обеспечить это, мы сначала добавим промежуточное ПО JSON на сервер Express, чтобы он имел эту возможность.

app.use(express.json());

Далее мы изменим маршрут GET /data на POST /data. Затем мы сможем передать параметр разрешения, который будет свойством объекта req.body, в getLatestRows. Это метод, который мы создали для запроса данных из GridDB.

/* Before */
app.get('/data', async (req, res) => {
  const rows = await container.getLatestRows();
});
/* After */
app.post('/data', async (req, res) => {
  const rows = await container.getLatestRows(req.body.resolution);
  res.send(rows);
});

Запросы выборки времени

В предыдущем уроке мы сделали так, чтобы снимок памяти сервера делался каждую секунду работы сервера.

Концепция функции getLatestRows заключается в том, что мы используем запрос для получения данных за последние 5 минут и возвращаем их в виде массива.

const getLatestRows = (container) => async () => {
  try {
    const query = container.query("select * where timestamp > TIMESTAMPADD(MINUTE, NOW(), -5)");
    const rowset = await query.fetch();
    const data = [];
    while (rowset.hasNext()) {
      data.push(rowset.next());
    }
    return data;
  } catch(err) {
    console.log(err);
  }
}

Теперь мы передаем этой функции параметр resolution. Мы можем использовать это, чтобы указать GridDB получать выборки данных за минуту или час, а не только за секунду.

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

Итак, давайте используем TIME_SAMPLE в операторе select в нашей базе данных. Первый аргумент — это данные, которые мы хотим отобрать. В данном случае это будет freeMemPercentage.

Второй аргумент — это начало диапазона. Третий аргумент — это конец диапазона. В этом случае мы хотим начать за час до запроса, что мы можем сделать, вычтя 60 минут из текущего времени, TIMESTAMPADD(MINUTE, NOW(), -60), и закончить в текущем времени, NOW().

Четвертый аргумент — это интервал, на котором вы хотите произвести выборку, а пятый аргумент — это временная основа. В этом случае мы берем пробы каждую минуту. выберите TIME_SAMPLING (freeMemPercentage, TIMESTAMPADD (MINUTE, NOW(), -60), NOW(), 1, MINUTE)

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

const getLatestRows = (container) => async (resolution) => {
  try {
    let tql;
    switch(resolution) {
      case 'hours':
        tql = "select TIME_SAMPLING(freeMemPercentage, TIMESTAMPADD(HOUR, NOW(), -24), NOW(), 1, HOUR)"
        break;
      case 'minutes':
        tql = "select TIME_SAMPLING(freeMemPercentage, TIMESTAMPADD(MINUTE, NOW(), -60), NOW(), 1, MINUTE)"
        break;
      default:
        tql = "select TIME_SAMPLING(freeMemPercentage, TIMESTAMPADD(MINUTE, NOW(), -1), NOW(), 1, SECOND)"
    }
    const query = container.query(tql);
    ...
  }
  ...
}

После этого GridDB будет возвращать данные с правильным разрешением. Теперь мы можем пойти и протестировать это в браузере.

Вот что мы видим, выбирая «секунды» в качестве основы времени:

Вот что мы видим при выборе «минут»:

Первоначально опубликовано на https://griddb.net 3 декабря 2021 г.