Этот гостевой пост написал Осиначи Чуквуджама. Он является веб-разработчиком и техническим писателем. Ему нравится создавать внутренние приложения и использовать облачные вычисления. В свободное от кодинга время он играет на органе и создает случайные анимации. Вы можете связаться с ним через его веб-сайт.
В мире, где каждая организация имеет большие данные, упрощенный подход к анализу данных как никогда востребован. К счастью, библиотеки построения диаграмм с открытым исходным кодом, такие как Chart.js, Recharts и Apache ECharts, достаточно надежны для анализа больших данных. Такие инструменты, как Power BI, Snowflake и Cube, также помогают упростить аналитику, облегчая организациям использование данных для принятия решений.
В этой статье вы узнаете, как использовать Apache ECharts, Cube и React для создания аналитической панели электронной коммерции.
По завершении этого урока вы создадите приложение React, которое отображает графики различных показателей в магазине электронной коммерции.
Результат должен выглядеть следующим образом:
Вы можете найти живую демонстрацию здесь или просмотреть исходный код приложения React в этом репозитории GitHub.
Что такое Apache ECharts?
Apache ECharts — это надежная библиотека построения графиков на JavaScript. Она полностью упакована и предлагает такие распространенные типы диаграмм, как линия, столбец и круговая, а также более сложные типы диаграмм, такие как graph, themeRiver и gauge.
ECharts в первую очередь используется для построения приложений для работы с данными, но она также хорошо подходит для приложений, требующих большого количества визуализированных точек данных. Он бесплатен в использовании, а его исходный код является открытым, что означает его гибкость и долговечность.
Он также невероятно настраивается, позволяя изменять цвет и размер в соответствии с потребностями вашего приложения. Однако рендеринг происходит на стороне клиента. Поэтому если устройство, на котором отображается график, имеет мало памяти, визуализация будет происходить медленнее. Рендеринг происходит быстрее, если вы используете Google Charts, но все ваши данные не хранятся на вашем собственном сервере, как это происходит в ECharts, а значит, их может просматривать Google или любая другая третья сторона.
В отличие от Recharts, ECharts — это в первую очередь библиотека JavaScript. Это означает, что вы не получите компоненты React для осей, легенд и других частей графика. Вместо этого вы будете использовать объект для декларативного определения рендеринга и поведения графика.
Зачем интегрироваться с Cube?
ECharts легко интегрируется с Cube, предлагая превосходные визуализации для данных, возвращаемых API Cube. Все, что вам нужно — это ваши данные, несколько запросов и передача полученных данных API через диаграмму ECharts.
Реализация приборной панели ECharts с помощью React и Cube
Следующий пример проекта состоит из трех основных компонентов:
- Реляционная база данных (PostgresSQL в этом учебнике, но вы можете использовать MySQL, MongoDB или любую другую базу данных, поддерживаемую Cube).
- схема Cube
- Настройка ECharts в React
Чтобы следовать этому примеру, у вас должен быть установлен Docker.
Настройка Cube
Чтобы установить Cube с помощью Docker, измените каталог на нужное место и выполните следующую команду:
docker run -p 4000:4000
-v ${PWD}:/cube/conf
-e CUBEJS_DEV_MODE=true
cubejs/cube
Эта команда загружает образ Cube Docker и открывает порт 4000 для доступа к игровой площадке Cube. Вы можете перейти по адресу http://localhost:4000 в браузере, чтобы увидеть игровую площадку.
Отсюда вы должны выбрать тип базы данных и параметры В этой статье будет использоваться размещенная база данных Postgres, предлагаемая Cube. Выберите Postgres в качестве типа базы данных и используйте приведенные ниже параметры, чтобы завершить настройку экземпляра Cube:
Имя хоста: demo-db.cube.dev
База данных: ecom
Имя пользователя: cube
Пароль: 12345
Генерация схемы данных с помощью Cube
Cube поставляется с конструктором схем, который позволяет создавать желаемые запросы. Эти запросы помогут вам задать аналитические вопросы о ваших данных, например, такие:
- Сколько заказов было сделано в этом месяце?
- Каково общее количество проданных товаров?
Чтобы продолжить, выберите все таблицы в публичной схеме на вкладке Схема на игровой площадке Куб.
После выбора целевых таблиц нажмите кнопку Generate Schema. Появится сообщение о том, что файлы схемы созданы, и можно приступать к построению диаграмм.
Нажмите на кнопку Построить.
Обзор игровой площадки Cube Playground
Cube Playground состоит из трех вкладок.
- Вкладка Build — для построения графиков на основе схемы данных.
- Вкладка Dashboard App для просмотра графиков, созданных на вкладке Build
- Вкладка Schema для выбора таблиц, данные из которых будут использоваться для построения графиков.
Схема, созданная Cube, представляет собой объект JavaScript, состоящий из мер и измерений. Он используется для генерации SQL-кода, который будет запрашиваться в базе данных для аналитики.
В приведенном ниже фрагменте кода показана схема данных для таблицы users. Она содержит меру count
и три измерения, которые соответствуют столбцам таблицы users:
cube(`Users`, {
sql: `SELECT * FROM users`,
measures: {
count: {
sql: `id`,
type: `count`,
},
},
dimensions: {
city: {
sql: `city`,
type: `string`,
},
signedUp: {
sql: `created_at`,
type: `time`,
},
companyName: {
sql: `company_name`,
type: `string`,
},
},
});
Cube позволяет комбинировать меры и измерения, чтобы задавать вопросы типа «На какие компании работают наши пользователи?»:
{
measures: ['Users.count'],
dimensions: ['Users.companyName']
}
Или «Где работают наши пользователи?»:
{
measures: ['Users.count'],
dimensions: ['Users.city']
}
Настройка ECharts в проекте React
Чтобы настроить ECharts в проекте React, создайте новый проект React в нужной вам директории и запустите dev-сервер с помощью команды ниже.
npx create-react-app cube-echarts-app
cd cube-echarts-app
npm start
Теперь установите необходимые зависимости:
npm i --save @cubejs-client/core @cubejs-client/react echarts echarts-for-react react-loader-spinner dayjs react-bootstrap bootstrap
Базовая установка приложения
Теперь, когда зависимости установлены, создайте папку components с помощью этой команды:
mkdir src/components
Замените содержимое файла App.js на следующее:
import React from "react";
import { CubeProvider } from "@cubejs-client/react";
import cubejs from "@cubejs-client/core";
import { Navbar, Container, Row, Col } from "react-bootstrap";
export const cubejsApi = cubejs(process.env.REACT_APP_CUBEJS_TOKEN,
{ apiUrl: "http://localhost:4000/cubejs-api/v1" }
);
const App = () => {
return (
<CubeProvider cubejsApi={cubejsApi}>
<div className="bg-gray">
<Navbar>
<Container>
<Navbar.Brand href="#home">E-Commerce Dashboard</Navbar.Brand>
</Container>
</Navbar>
</div>
</CubeProvider>
);
};
export default App;
Вам не нужно устанавливать REACT_APP_CUBEJS_TOKEN
в вашей среде разработки, так как он строго используется в продакшене. Если вы хотите установить его, вам нужно подписать JWT на https://jwt.io или с помощью вашего любимого инструмента с CUBEJS_API_SECRET
в качестве секретного ключа. Вы можете найти CUBEJS_API_SECRET
в .env файле настройки бэкэнда Cube, который автоматически создается Cube.
Приборная панель будет содержать четыре графика:
- Диаграмма области, содержащая рост выручки за предыдущий год
- Линейная диаграмма, содержащая заказы за последние тридцать дней
- Сложенная гистограмма, содержащая заказы по статусу с течением времени
- гистограмма, содержащая заказы по названию категории продукта
Чтобы приступить к созданию этих графиков, создайте необходимые файлы графиков и загрузчик:
touch src/components/AreaChart.jsx
touch src/components/BarChart.jsx
touch src/components/LineChart.jsx
touch src/components/StackedBarChart.jsx
touch src/components/Loader.jsx
Добавьте следующее в Loader.jsx
:
import { Oval } from "react-loader-spinner";
function Loader() {
return (
<div className="d-flex justify-content-center align-items-center">
<Oval heigth="100" width="100" color="#5470C6" ariaLabel="loading" />
</div>
);
}
export default Loader;
Откройте AreaChart.jsx
и добавьте следующее:
import React from "react";
import ReactECharts from "echarts-for-react";
import { useCubeQuery } from "@cubejs-client/react";
import Loader from "./Loader";
import { Card } from "react-bootstrap";
import dayjs from "dayjs";
function AreaChart() {
const { resultSet, isLoading, error, progress } = useCubeQuery({
measures: ["Users.count"],
timeDimensions: [
{
dimension: "Users.createdAt",
granularity: "year",
},
],
order: {
"Users.createdAt": "asc",
},
});
if (error) {
return <p>{error.toString()}</p>;
}
if (isLoading) {
return (
<div>
{(progress && progress.stage && progress.stage.stage) || <Loader />}
</div>
);
}
if (!resultSet) {
return null;
}
const workingData = resultSet.loadResponse.results[0].data;
const userCount = workingData.map((item) => item["Users.count"]);
const userCreationDate = workingData.map((item) =>
dayjs(item["Users.createdAt.year"]).format("YYYY")
);
const options = {
legend: {
data: ["User count"],
},
tooltip: {
trigger: "axis",
axisPointer: {
type: "shadow",
},
},
xAxis: {
data: userCreationDate,
},
yAxis: {},
series: [
{
name: "User count",
data: userCount,
type: "line",
areaStyle: {},
},
],
};
return (
<Card className="m-4">
<Card.Body>
<Card.Title>User Trend</Card.Title>
<ReactECharts option={options} />
</Card.Body>
</Card>
);
}
export default AreaChart;
Первая часть файла содержит хук React, который получает данные из бэкэнда Cube, как показано ниже:
const { resultSet, isLoading, error, progress } = useCubeQuery({
measures: ["Users.count"],
timeDimensions: [
{
dimension: "Users.createdAt",
granularity: "year",
},
],
order: {
"Users.createdAt": "asc",
},
});
Объект в этом хуке может быть получен с игровой площадки Cube в виде JSON-запроса.
Во второй части AreaChart.jsx
показано, как возвращаемое состояние используется для условного рендеринга:
if (error) {
return <p>{error.toString()}</p>;
}
if (isLoading) {
return (
<div>
{(progress && progress.stage && progress.stage.stage) || <Loader />}
</div>
);
}
if (!resultSet) {
return null;
}
Третья часть AreaChart.jsx
преобразует возвращаемые данные в форму, которую может отобразить график. Диаграмма отвечает на вопрос «Сколько пользователей присоединялось каждый год?», а userCount
и userCreationDate
будут выделены из возвращаемых данных:
const workingData = resultSet.loadResponse.results[0].data;
const userCount = workingData.map((item) => item["Users.count"]);
const userCreationDate = workingData.map((item) =>
dayjs(item["Users.createdAt.year"]).format("YYYY")
);
Наконец, данные графика и метаданные объявляются в объекте options и передаются компоненту ReactECharts:
const options = {
legend: {
data: ["User count"],
},
tooltip: {
trigger: "axis",
axisPointer: {
type: "shadow",
},
},
xAxis: {
data: userCreationDate,
},
yAxis: {},
series: [
{
name: "User count",
data: userCount,
type: "line",
areaStyle: {},
},
],
};
return (
<Card className="m-4">
<Card.Body>
<Card.Title>User Trend</Card.Title>
<ReactECharts option={options} />
</Card.Body>
</Card>
);
Чтобы увидеть график в браузере, обновите содержимое App.js
, чтобы включить его.
+ import AreaChart from "./components/AreaChart";
const App = () => {
...
<div className="bg-gray">
<Navbar>
<Container>
<Navbar.Brand href="#home">E-Commerce Dashboard</Navbar.Brand>
</Container>
</Navbar>
+ <Row>
+ <Col>
+ <AreaChart />
+ </Col>
+ </Row>
</div>
...
Добавьте следующее содержимое в остальные файлы, как указано ниже.
LineChart.jsx
:
import React from "react";
import ReactECharts from "echarts-for-react";
import { useCubeQuery } from "@cubejs-client/react";
import Loader from "./Loader";
import { Card } from "react-bootstrap";
function LineChart() {
const { resultSet, isLoading, error, progress } = useCubeQuery({
measures: ["Products.count"],
order: [["Products.count", "asc"]],
dimensions: ["ProductCategories.name"],
});
if (error) {
return <p>{error.toString()}</p>;
}
if (isLoading) {
return (
<div>
{(progress && progress.stage && progress.stage.stage) || <Loader />}
</div>
);
}
if (!resultSet) {
return null;
}
const workingData = resultSet.loadResponse.results[0].data;
const productCategoryNames = workingData.map(
(item) => item["ProductCategories.name"]
);
const productCategoriesCount = workingData.map(
(item) => item["Products.count"]
);
const options = {
legend: {
data: ["Product Categories count"],
},
tooltip: {
trigger: "axis",
axisPointer: {
type: "shadow",
},
},
xAxis: {
data: productCategoryNames,
},
yAxis: {},
series: [
{
name: "Product Categories count",
data: productCategoriesCount,
type: "line",
},
],
};
return (
<Card className="m-4">
<Card.Body>
<Card.Title>Products by Category</Card.Title>
<ReactECharts option={options} />
</Card.Body>
</Card>
);
}
export default LineChart;
StackedBarChart.jsx
:
import React from "react";
import ReactECharts from "echarts-for-react";
import { useCubeQuery } from "@cubejs-client/react";
import dayjs from "dayjs";
import Loader from "./Loader";
import { Card } from "react-bootstrap";
function StackedBarChart() {
const { resultSet, isLoading, error, progress } = useCubeQuery({
measures: ["Orders.count"],
timeDimensions: [
{
dimension: "Orders.createdAt",
granularity: "month",
},
],
order: [
["Orders.count", "desc"],
["Orders.createdAt", "asc"],
],
dimensions: ["Orders.status"],
filters: [],
});
if (error) {
return <p>{error.toString()}</p>;
}
if (isLoading) {
return (
<div>
{(progress && progress.stage && progress.stage.stage) || <Loader />}
</div>
);
}
if (!resultSet) {
return null;
}
const returnedData = resultSet.loadResponse.results[0].data.sort(
(first, second) =>
dayjs(first["Orders.createdAt.month"]).diff(
dayjs(second["Orders.createdAt.month"])
)
);
const filterOrderStatusBy = (type) =>
returnedData
.filter((order) => order["Orders.status"] === type)
.map((order) => order["Orders.count"]);
const ordersProcessing = filterOrderStatusBy("processing");
const ordersCompleted = filterOrderStatusBy("completed");
const ordersShipped = filterOrderStatusBy("shipped");
const orderMonths = [
...new Set(
returnedData.map((order) => {
return dayjs(order["Orders.createdAt.month"]).format("MMM YYYY");
})
),
];
const options = {
legend: {
data: [
"Processing Orders count",
"Completed Orders count",
"Shipped Orders count",
],
},
tooltip: {
trigger: "axis",
axisPointer: {
type: "shadow",
},
},
xAxis: {
data: orderMonths,
},
yAxis: {},
series: [
{
name: "Processing Orders count",
data: ordersProcessing,
type: "bar",
stack: "x",
},
{
name: "Completed Orders count",
data: ordersCompleted,
type: "bar",
stack: "x",
},
{
name: "Shipped Orders count",
data: ordersShipped,
type: "bar",
stack: "x",
},
],
};
return (
<Card className="m-4">
<Card.Body>
<Card.Title>Orders by Status Over Time</Card.Title>
<ReactECharts option={options} />
</Card.Body>
</Card>
);
}
export default StackedBarChart;
BarChart.jsx
:
import React from "react";
import ReactECharts from "echarts-for-react";
import { useCubeQuery } from "@cubejs-client/react";
import Loader from "./Loader";
import { Card } from "react-bootstrap";
function BarChart() {
const { resultSet, isLoading, error, progress } = useCubeQuery({
measures: ["Orders.count"],
timeDimensions: [],
order: {
"Orders.count": "desc",
},
dimensions: ["ProductCategories.name"],
});
if (error) {
return <p>{error.toString()}</p>;
}
if (isLoading) {
return (
<div>
{(progress && progress.stage && progress.stage.stage) || <Loader />}
</div>
);
}
if (!resultSet) {
return null;
}
const workingData = resultSet.loadResponse.results[0].data;
const productCategoryNames = workingData.map(
(item) => item["ProductCategories.name"]
);
const orderCount = workingData.map((item) => item["Orders.count"]);
const options = {
xAxis: {
type: "category",
data: productCategoryNames,
},
yAxis: {
type: "value",
},
series: [
{
data: orderCount,
type: "bar",
},
],
};
return (
<Card className="m-4">
<Card.Body>
<Card.Title>Orders by Product Category Names</Card.Title>
<ReactECharts option={options} />
</Card.Body>
</Card>
);
}
export default BarChart;
Наконец, обновите App.js
, чтобы включить новые графики:
+ import LineChart from "./components/LineChart";
+ import StackedBarChart from "./components/StackedBarChart";
+ import BarChart from "./components/AreaChart";
const App = () => {
...
<div className="bg-gray">
<Navbar>
<Container>
<Navbar.Brand href="#home">E-Commerce Dashboard</Navbar.Brand>
</Container>
</Navbar>
<Row>
<Col>
<AreaChart />
</Col>
+ <Col>
+ <LineChart />
+ </Col>
+ </Row>
+ <StackedBarChart />
+ <BarChart />
</div>
Добавление интерактивности в приборную панель
Чтобы приборная панель была удобной для пользователя, пользователи должны иметь возможность сортировать, фильтровать и экспортировать данные. В этом разделе вы добавите фильтры дат к вашей сложенной гистограмме, чтобы добавить ей интерактивности.
Перейдите к компоненту StackedBarChart.jsx
и импортируйте следующее:
import { useState } from "react";
import { Card, Form, Button } from "react-bootstrap";
Затем определите начальную дату, конечную дату и крючки запроса JSON:
const [startDate, setStartDate] = useState("");
const [endDate, setEndDate] = useState("");
const [jsonQuery, setJSONQuery] = useState({
measures: ["Orders.count"],
timeDimensions: [
{
dimension: "Orders.createdAt",
granularity: "month",
},
],
order: [
["Orders.count", "desc"],
["Orders.createdAt", "asc"],
],
dimensions: ["Orders.status"],
filters: [],
});
const { resultSet, isLoading, error, progress } = useCubeQuery(jsonQuery);
После этого добавьте функцию, которая будет обрабатывать обновление даты:
const updateDate = (event) => {
event.preventDefault();
setJSONQuery((prevJSONQuery) => {
return {
...prevJSONQuery,
filters: [
{
member: "Orders.createdAt",
operator: "inDateRange",
values: [startDate, endDate],
},
],
};
});
};
Затем обновите содержимое Card.Body
следующим образом:
<Card.Body>
<div className="d-flex align-items-center justify-content-between my-4">
<Card.Title>Orders by Status Over Time</Card.Title>
<Form onSubmit={updateDate} className="d-flex align-items-center gap-4">
<div className="d-flex gap-2 align-items-center">
<div>
<label htmlFor="startDate">Start Date</label>
</div>
<input
id="startDate"
name="start-date"
value={startDate}
onChange={({ target }) => setStartDate(target.value)}
type="date"
/>
</div>
<div className="d-flex gap-2 align-items-center">
<div>
<label htmlFor="endDate">End Date</label>
</div>
<input
id="endDate"
name="end-date"
value={endDate}
onChange={({ target }) => setEndDate(target.value)}
type="date"
/>
</div>
<Button type="submit">Set date</Button>
</Form>
</div>
<ReactECharts option={options} />
</Card.Body>
Если вы посмотрите на свой график в браузере, вы должны увидеть форму даты на графике и иметь возможность самостоятельно обновить дату. На видео ниже показана демонстрация фильтра даты:
Ознакомьтесь с этим руководством по D3 или этим руководством по Material UI, чтобы узнать больше о добавлении интерактивности в ваши графики.
Заключение
Apache ECharts и Cube предлагают надежный способ создания аналитических приложений и информационных панелей. В этом руководстве вы узнали, как создать аналитическую панель с нуля с помощью React, Apache ECharts и Cube. Вы также узнали, как добавить форму диапазона дат к вашим графикам, что дает вам возможность фильтровать данные.
Cube — это безголовый слой API, который соединяет вашу базу данных через любой из трех API, включая REST, GraphQL и SQL, с вашим внешним кодом, чтобы вы могли быстрее создавать приложения для работы с данными. Это упрощает процесс добавления аналитических элементов в существующие приложения. С помощью Cube вы можете создать уровень API, управлять контролем доступа, агрегировать данные, кэшировать запросы для повышения производительности и легко интегрировать Apache ECharts.