MongoDB – одна из самых популярных документ-ориентированных баз данных класса NoSQL с большим сообществом пользователей. Ее основными преимуществами являются гибкость схемы хранения, иерархическая структура документов, поддержка расширенного набора типов данных. Сегодня MongoDB чаще всего используется как бэкенд веб- и мобильных приложений.
Казалось бы, зачем может потребоваться извлекать схему данных в schemaless database? Однако это может быть крайне полезно и в некоторых ситуациях абсолютно необходимо:
Репликация данных в аналитическое хранилище
Интерактивная аналитика из BI-инструментов (SQL)
Аудит имеющейся структуры БД
В этой публикации я хотел бы показать простой и удобный способ получения схемы хранения данных, даже при наличии сотен коллекций и миллионов документов в MongoDB.
Причины для получения схемы всех коллекций
Репликация данных в аналитическое хранилище
Экспорт из MongoDB в реляционные аналитические СУБД сопряжен с рядом сложностей:
Отсутствие единой заранее заданной схемы документов
Наличие вложенных документов и массивов динамической структуры
Разные документы одной коллекции могут иметь несопоставимые типы данных
В документ (запись) могут быть добавлены новые атрибуты в любой момент времени
Различные допустимые значения длины текстовых атрибутов (необходимость в column resize)
Интерактивная аналитика из BI-инструментов
Современные BI-решения выполняют роль визуального конструктора и генератора запросов, прежде всего на языке SQL. И, в подавляющем большинстве случаев, работают они с двумерными табличными структурами данных.
Однако MongoDB не поддерживает SQL в чистом виде, но свой диалект языка работы с данными – MongoDB Query Language (MQL). Таким образом, для поддержки работы из популярных BI-решений, таких как Metabase, Tableau, Power BI необходим медиаторный слой в виде транслятора запросов на SQL в запросы на языке MQL.
Этот слой должен обладать достаточной информацией для формирования плана запросов, в том числе для выполнения операций:
Проекция (projection)
Соединение таблиц (join)
Выполнение подзапроса (subquery)
Агрегирующие функции (aggregate functions)
И в первую очередь это схема хранения данных и ее маппинг на реляционную модель.
Аудит имеющейся структуры БД
На каком-то этапе команды сталкиваются с необходимостью верхнеуровневого взгляда на работу с данными:
Наглядно представить набор коллекций, атрибутов, типов, наличие вложенных структур и массивов для всего проекта
Идентифицировать коллекции и атрибуты, содержащие пользовательские данные (PII)
Обнаружить избыточное и дублирующее хранение
Разделить зоны ответственности команд
findOne() – самый примитивный подход
В случае самого простого и быстрого решения, наша задача сводится к следующим действиям:
Вывести список БД
Отобразить список коллекций
Для каждой коллекции найти документ и показать его схему
Выглядеть это может следующим образом:
show dbs // show list of databases
use mydb // switch to the database you want to query
show collections // list all collections in the database
db.collectionName.findOne().pretty() // show a single document in the database in a readable format
{
_id: ObjectId("558448a20294464239ae9f8a"),
name: 'Goroka',
city: 'Goroka',
country: 'Papua New Guinea',
iatacode: 'GKA',
icaocode: 'AYGA',
latlon: [ -6.081689, 145.391881 ]
}
А теперь еще чуть более наглядно – вывести список атрибутов и их типы:
var schematodo = db.collection_name.findOne()
for(var key in schematodo) { print (key, typeof schematodo[key]); }
_id object
name string
city string
country string
iatacode string
icaocode string
latlon object
Однако у этого подхода есть недостатки, и весьма значительные:
Результат мы получаем на основании всего одного документа, и это отнюдь не значит, что все остальные документы обладают такой же схемой
Все заботы об автоматизации и парсинге результатов ложатся на плечи инженера
Использование специализированных приложений
Конечно, рассмотренный выше подход “в лоб” совсем прост, и различными энтузиастами был выработан ряд решений, которые предлагали чуть больше, чем простой анализ схемы, и пытались сделать это максимально удобно. Среди ключевых преимуществ можно отметить:
Использование парадигмы Map Reduce для анализа схемы данных
Гибкие настройки: вы решаете, какие документы и в каком количестве необходимо проанализировать
Возможность построения гистограмм частотности значений в коллекциях
Простой и удобный cli-интерфейс
Выбор формата экспорта данных: json, yaml, table
Лучшим из приложений для анализа схемы MongoDB сегодня является проект Mongoeye (Go) – последнее обновление в марте 2021.
Простое использование, конфигурирование с помощью флагов:
mongoeye [host] database collection [flags]
Экспорт доступен в таблицу окна терминала, и во внешний файл json/yaml.
Помимо этого mongoeye поможет построить гистограмму распределения значений атрибута:
valueHistogram:
start: 2.5
end: 12
range: 9.5
step: 0.5
numOfSteps: 19
intervals: [36, 25, 14, 81, 95, 86, 59, 6, 82, 84, 62, 33, 19, 9, 1, 14, 67, 2, 45]
Еще один заметный проект – Variety (JS). Последнее обновление – 2018 год.
Этот инструмент позволит максимально гибко подойти к набору анализируемых документов, в случае огромных коллекций.
# Analyze Only Recent Documents
$ mongo test --eval "var collection = 'users', limit = 1" variety.js
# Analyze Documents to a Maximum Depth
$ mongo test --eval "var collection = 'users', maxDepth = 3" variety.js
# Analyze a Subset of Documents
$ mongo test --eval "var collection = 'users', query = {'caredAbout':true}" variety.js
# Analyze Documents Sorted In a Particular Order
$ mongo test --eval "var collection = 'users', sort = { updated_at : -1 }" variety.js
# Include Last Value
$ mongo test --eval "var collection = 'orders', lastValue = true" variety.js
Утилита mongodrdl от MongoDB
Эта утилита входит в состав компонентов MongoDB Connector for BI и предназначена она для управления маппингом схемы MongoDB на реляционную. Сегодня mongodrdl интересна нам как отдельная утилита, способная:
Формировать схемы на основе содержимого документов ряда коллекций
Выгружать схемы в файлы .drdl (yaml-like)
Управлять файлами схем .drdl – просмотр, присваивание имени, версионирование, обновление, удаление
Базовый скрипт для формирования и выгрузки схем всех коллекций ряда баз данных может выглядеть следующим образом:
# prepare list of databases
declare -a DATABASES=(
"documents"
"communication"
"flights"
)
# loop through databases and generate schemas to yaml files
for db in "${DATABASES[@]}"
do
mongodrdl \
--host=<host_address> \
--port=<port> \
--db=$db \
--username=<username> \
--password=<password> \
--out=./$db.yml \
--sampleSize=10000 # choose number of documents to be sampled
done
Формат Document Relational Definition Language
Файлы DRDL определяют реляционное представление схемы MongoDB.
В предыдущем абзаце мы получили ряд таких файлов. Общая структура файла выглядит следующим образом:
schema:
- db: <database name>
tables:
- table: <SQL table name>
collection: <MongoDB collection name>
pipeline:
- <optional pipeline elements>
columns:
- Name: <MongoDB field name>
MongoType: <MongoDB field type>
SqlName: <mapped SQL column name>
SqlType: <mapped SQL column type>
Впоследствии эти файлы могут быть использованы утилитой mongosqld для ответов на запросы языка SQL (из BI-инструментов, например).
Анализ полученных метаданных
Для целей аудита и обзора данных, хранящихся в MongoDB можно сформировать единый документ, например с помощью pandas.
import yaml
import pandas as pd
import glob
yml = glob.glob('*.yml')
s = []
for y in yml:
with open(y, 'r') as f:
s += yaml.load(f, Loader=yaml.FullLoader)['schema']
df = pd.io.json.json_normalize(s,
record_path=['tables', 'columns'],
meta=['db', ['tables', 'table'], ['tables', 'collection']],
record_prefix='column.',
meta_prefix='database.')
Полная версия ipython notebook доступна на Github Gist – Export and analyse MongoDB schema to relational view.
Давайте разберем один из самых важных моментов – то, как отображатся массивы и вложенные документы. Для примера возьмем документ:
{
_id: ObjectId("5df740b7823773cd52a3d137"),
localized_names: [ { locale: 'en', name: 'Bagotville' } ],
iata_code: 'YBG'
}
Обратите внимание на ключ localized_names. В качестве значения он содержит массив элементов. А теперь посмотрим, как это отражается в сгенерированной mongodrdl схеме:
Для каждого массива создается поле с префиксом _idx содержащее индекс (порядковый номер) элемента массива. Ключи вложенных документов перечисляются с помощью dot notation (через точку).
Сформированную таблицу можно использовать как справочник полей, с быстрым поиском, фильтрацией, версионированием, указанием контактов ответственных лиц, примечаниями по статусу атрибутов.
NoSQL базы данных – часть стека Инженера Данных
Работа с данными – одно из наиболее востребованных и бурно развивающихся направлений. Каждый день я нахожу новые интересные задачи и придумываю решения для них. Это захватывающий и интересный путь, расширяющий горизонты.
Приглашаю вас присоединиться к обучению самым интересным и актуальным технологиям в переработанной версию курса Data Engineer на платформе OTUS:
Data Architecture
Data Lake
DWH
NoSQL/NewSQL
MLOps
В рамках курса студентам предлагаются практические навыки не только по document-oriented базам данных (MongoDB, CouchDB), но и key-value (Redis, Aerospike), full-text search (ELK).
Подписывайтесь и следите за моими вебинарами и публикациями в телеграм-канале Technology Enthusiast.
Благодарю за внимание.