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.
Благодарю за внимание.
