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

Аналитические системы, такие как Google Analytics, Youtube Studio и другие, предоставляют инструменты для сравнения одного показателя за разные периоды времени.

Давайте посмотрим, как можно реализовать такую ​​аналитическую визуализацию с помощью Cube.js.

Здесь вы можете увидеть живую демонстрацию и полный исходный код этого примера.

Мы рассмотрим образец набора данных об электронной торговле и сравним количество заказов за последние три года. Для этого нам нужно запустить сервер Cube.js и простое приложение панели инструментов.

Если вы еще не знакомы с Cube.js, следуйте нашему руководству по настройке базы данных, запуску сервера Cube.js и получению информации о схемах данных и аналитических кубах.

Теперь давайте создадим куб Orders внутри папки schema с помощью следующего кода:

cube(`Orders`, {
  sql: `SELECT * FROM public.orders`,
measures: {
    count: {
      type: `count`,
    },
  },
dimensions: {
    id: {
      sql: `id`,
      type: `number`,
      primaryKey: true,
    },
    createdAt: {
      sql: `created_at`,
      type: `time`,
    },
  },
});

Здесь показатель count используется для расчета количества заказов. Поле createdAt используется для получения метки времени заказа.

В следующих разделах мы реализуем этот пример с React, но вы можете использовать тот же подход с Vue, Angular и vanilla JS.

Несколько запросов для одной диаграммы

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

import React, { useState, useEffect } from 'react';
import { useCubeQuery } from '@cubejs-client/react';
import * as moment from 'moment';

import Line from './Line';

export default () => {
  const [data, setData] = useState([]);

  const { resultSet: result22 } = useCubeQuery({
    measures: ['Orders.count'],
    timeDimensions: [
      {
        dimension: 'Orders.createdAt',
        dateRange: ['2022-01-01', '2022-12-31'],
        granularity: 'month',
      },
    ],
  });

  const { resultSet: result21 } = useCubeQuery({
    measures: ['Orders.count'],
    timeDimensions: [
      {
        dimension: 'Orders.createdAt',
        dateRange: ['2021-01-01', '2021-12-31'],
        granularity: 'month',
      },
    ],
  });

  const { resultSet: result20 } = useCubeQuery({
    measures: ['Orders.count'],
    timeDimensions: [
      {
        dimension: 'Orders.createdAt',
        dateRange: ['2020-01-01', '2020-12-31'],
        granularity: 'month',
      },
    ],
  });

  useEffect(() => {
    const parseResultSet = (resultSet) => {
      return {
        name: moment(
          resultSet.tablePivot()[0]['Orders.createdAt.month']
        ).format('YYYY'),
        data: resultSet
          .tablePivot()
          .map((item) => parseInt(item['Orders.count'])),
      };
    };

    const temp = [
      result22 ? parseResultSet(result22) : [],
      result21 ? parseResultSet(result21) : [],
      result20 ? parseResultSet(result20) : [],
    ];

    setData(temp);
  }, [result22, result21, result20]);

  return <Line data={data} title={'multiple queries'} />;
};

Как только данные подготовлены, мы отправляем их <Line /> компоненту рендеринга и вот, мы создаем информативную диаграмму:

Процедура довольно проста, не правда ли? Однако, как видите, код немного беспорядочный и повторяющийся. Что еще более важно, нелегко адаптировать код к другому промежутку времени или даже динамически корректировать его в ответ на действия пользователей или состояния приложения.

Как решить эту проблему? Попробуем другой подход.

Один запрос для одной диаграммы

В версии 0.20.0 Cube.js можно получать данные за разные периоды времени с помощью одного запроса.

Свойство compareDateRange принимает массив из двух или более разных периодов времени и возвращает данные для всех в одном запросе.

Мы можем переписать наш предыдущий код более простым способом:

import React, { useState, useEffect } from 'react';
import { useCubeQuery } from '@cubejs-client/react';

import Line from './Line';

export default () => {
  const [data, setData] = useState([]);

  const { resultSet: result } = useCubeQuery({
    measures: ['Orders.count'],
    timeDimensions: [
      {
        dimension: 'Orders.createdAt',
        compareDateRange: [
          ['2022-01-01', '2022-12-31'],
          ['2021-01-01', '2021-12-31'],
          ['2020-01-01', '2020-12-31'],
        ],
        granularity: 'month',
      },
    ],
  });

  useEffect(() => {
    if (result) {
      const temp = result.series().map((data) => {
        return {
          name: data.key.substring(0, 4),
          data: data.series.map((item) => item.value),
        };
      });
      setData(temp);
    }
  }, [result]);

  return <Line data={data} title={'the single query'} />;
};

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

Обратите внимание, что мы не вносили никаких изменений в компонент рендеринга <Line />, поэтому диаграмма будет выглядеть практически так же.

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

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

Первоначально опубликовано на https://cube.dev 10 сентября 2020 г.