Работа с JSON в ClickHouse
В этом руководстве рассматриваются типовые подходы к работе с JSON‑данными, реплицируемыми из MongoDB в ClickHouse через ClickPipes.
Предположим, что мы создали коллекцию t1 в MongoDB для отслеживания клиентских заказов:
Коннектор MongoDB CDC реплицирует документы MongoDB в ClickHouse, используя нативный тип данных JSON. Реплицированная таблица t1 в ClickHouse будет содержать следующую строку:
Схема таблицы
Реплицируемые таблицы используют эту стандартную схему:
_id: Первичный ключ из MongoDBdoc: Документ MongoDB, реплицируемый в тип данных JSON_peerdb_synced_at: Фиксирует время последней синхронизации строки_peerdb_version: Отслеживает версию строки; увеличивается при обновлении или удалении строки_peerdb_is_deleted: Показывает, удалена ли строка
Движок таблицы ReplacingMergeTree
ClickPipes сопоставляет коллекции MongoDB с ClickHouse, используя семейство движков таблиц ReplacingMergeTree. В этом движке обновления моделируются как вставки с более новой версией (_peerdb_version) документа для заданного первичного ключа (_id), что обеспечивает эффективную обработку обновлений, замен и удалений как версионных вставок.
ReplacingMergeTree асинхронно удаляет дубликаты в фоновом режиме. Чтобы гарантировать отсутствие дубликатов для одной и той же строки, используйте модификатор FINAL. Например:
Обработка удалений
Удаления из MongoDB реплицируются как новые строки, помеченные как удалённые с помощью столбца _peerdb_is_deleted. Обычно такие строки следует отфильтровывать в запросах:
Вы также можете создать политику на уровне строк, чтобы автоматически отфильтровывать удалённые строки вместо того, чтобы указывать фильтр в каждом запросе:
Выполнение запросов к данным JSON
Вы можете напрямую обращаться к полям JSON, используя точечную нотацию:
При выполнении запросов к вложенным полям объекта с использованием точечной нотации необходимо добавить оператор ^:
Динамический тип
В ClickHouse каждое поле JSON имеет тип Dynamic. Динамический тип позволяет ClickHouse хранить значения любого типа, не зная этот тип заранее. Это можно проверить с помощью функции toTypeName:
Чтобы изучить базовый тип (или типы) данных для поля, вы можете воспользоваться функцией dynamicType. Обратите внимание, что для одного и того же имени поля в разных строках могут быть разные типы данных:
Обычные функции работают с типом Dynamic так же, как и с обычными столбцами:
Пример 1: Разбор дат
Пример 2: Условная логика
Пример 3: Операции с массивами
Приведение типов полей
Агрегатные функции в ClickHouse не работают напрямую с типом dynamic. Например, если вы попытаетесь напрямую использовать функцию sum для типа dynamic, вы получите следующую ошибку:
Чтобы использовать агрегатные функции, приведите поле к соответствующему типу с помощью функции CAST или синтаксиса :::
Приведение значения из динамического типа к его базовому типу данных (определяется dynamicType) очень эффективно с точки зрения производительности, так как ClickHouse уже хранит значение во внутреннем представлении этого типа.
Уплощение JSON
Обычное представление
Вы можете создавать обычные представления поверх JSON-таблицы, чтобы инкапсулировать логику уплощения/приведения типов/преобразования и запрашивать данные в виде, похожем на реляционную таблицу. Обычные представления являются лёгкими, так как они хранят только сам запрос, а не исходные данные. Например:
У этого представления будет следующая схема:
Теперь вы можете выполнять запросы к этому представлению так же, как и к денормализованной («расплющенной») таблице:
Обновляемое материализованное представление
Вы можете создать обновляемые материализованные представления, которые позволяют планировать выполнение запроса для дедупликации строк и сохранения результатов в денормализованной целевой таблице. При каждом запланированном обновлении целевая таблица заменяется последними результатами запроса.
Ключевое преимущество этого подхода заключается в том, что запрос с использованием ключевого слова FINAL выполняется только один раз во время обновления, устраняя необходимость выполнения последующих запросов к целевой таблице с использованием FINAL.
Недостаток заключается в том, что данные в целевой таблице актуальны только на момент последнего обновления. Для многих сценариев интервалы обновления от нескольких минут до нескольких часов обеспечивают хороший баланс между актуальностью данных и производительностью запросов.
Теперь вы можете выполнять запросы к таблице flattened_t1 напрямую, без использования модификатора FINAL:
Incremental materialized view
Если вы хотите получать доступ к развёрнутым столбцам в режиме реального времени, вы можете создать Incremental Materialized Views. Если в вашей таблице часто происходят обновления, не рекомендуется использовать модификатор FINAL в materialized view, так как каждое обновление будет приводить к слиянию. Вместо этого вы можете устранять дубликаты данных на этапе выполнения запроса, построив обычное представление поверх materialized view.
Теперь вы можете выполнить запрос к представлению flattened_t1_final следующим образом: