Что такое маппинг в программировании
Что такое маппинг в программировании
На сегодняшний момент существует несколько ORM для Python, среди них:
Наиболее популярной является 1 и 2, однако 3 и 4 являются хорошей альтернативой.
Системы 3 и 4 позиционируются как легковесные, не обладают полным функционалом 1 и 2, но его достаточно для выполнения большинства сложных запросов. Стоит отметить, что их производительность по сравнению с 1 или 2 выше.
Для простоты рассмотрим особенности работы с PeeWee.
Определение модели
Классы модели, поля и экземпляры сущности все отображаются на концепцию базы данных.
Термин | Соответствие |
---|---|
Класс модели | таблица в БД |
Экземпляр поля | столбец в таблице |
Экземпляр сущности | строка в таблице |
В PeeWee определено много типов столбцов:
Field Type | Sqlite | Postgresql | MySQL |
CharField | varchar | varchar | varchar |
FixedCharField | char | char | char |
TextField | text | text | longtext |
DateTimeField | datetime | timestamp | datetime |
IntegerField | integer | integer | integer |
BooleanField | integer | boolean | bool |
FloatField | real | real | real |
DoubleField | real | double precision | double precision |
BigIntegerField | integer | bigint | bigint |
SmallIntegerField | integer | smallint | smallint |
DecimalField | decimal | numeric | numeric |
PrimaryKeyField | integer | serial | integer |
ForeignKeyField | integer | integer | integer |
DateField | date | date | date |
TimeField | time | time | time |
TimestampField | integer | integer | integer |
BlobField | blob | bytea | blob |
UUIDField | text | uuid | varchar(40) |
BareField | untyped | not supported | not supported |
Чтобы открыть соединение с БД нужно написать
и хотя в мануале пишут, что это не обязательно, тем не менее это хорошая практика, так как позволяет обнаружить ошибки при соединении.
Определение связей происходит при помощи внешнего ключа
создание таблиц в БД происходит следующим образом:
сохранение экземпляра класса в БД происходит по команде:
Подробная документация находится здесь
Прежде чем бросаться и писать ORM обертку для таблиц сущностей, сначала надо спроектировать логическую структуру БД или ХД. Установить все зависимости и способы взаимодействия между сущностями.
ORM SQLAlchemy
Работа с этой ORM начинается с импорта элементов из пакета sqlalchemy.
Для подключения к БД мы создаем объект Engine, который создает связку пула подключения и диалекта конкретной БД.
Далее нужно создать базовый класс для объявления сущностей наших данных
Указывать параметры при вызове конструктора не обязательно, присвоить значения полям экземпляра сущности можно и после.
Для того, чтобы связаться с базой данных и начать с ней диалог, создадим класс сессия.
теперь мы можем сохранять наши экземпляры сущностей в базу.
Чернобровов Алексей Аналитик
Big Data Mapping: что такое маппирование больших данных
В этой статье рассмотрено, что такое маппирование больших данных, как это связано с Data Science, когда и как часто выполняется этот процесс, а также, какие программные инструменты позволяют автоматизировать Big Data mapping.
Что такое маппирование данных и где это используется
Представим, что в одной из корпоративных систем сведения о семейном положении сотрудника хранятся так, что «1» в поле «дети» означает их наличие. В другой системе эти же данные записаны с помощью значения «True», а в третьей – словом «да». Таким образом, разные системы для обозначения одних и тех же данных используют разные отображения. Чтобы привести информацию к единообразию, следует сопоставить обозначения одной системы обозначениям в других источниках, т.е. выполнить процедуру мэппинга данных (от английского map – сопоставление). В широком смысле маппирование – это определение соответствия данных между разными семантиками или представлениями одного объекта в разных источниках. На практике этот термин чаще всего используется для перевода или перекодировки значений [1].
Дисциплина управления данными, Data Management, трактует маппинг как процесс создания отображений элементов данных между двумя различными моделями, который выполняется в начале следующих интеграционных задач [2]:
Таким образом, маппирование данных представляет собой процесс генерации инструкций по объединению информации из нескольких наборов данных в единую схему, например, конфигурацию таблицы. Поскольку схемы данных в разных источниках обычно отличаются друг от друга, информацию из них следует сопоставить, выявив пересечение, дублирование и противоречия [3].
С прикладной точки зрения можно следующие приложения маппинга данных [4]:
В Big Data мэппинг выполняется при загрузке информации в озеро данных (Data Lake) и корпоративное хранилище (DWH, Data Warehouse). Чем Data Lake отличается от DWH, рассмотрено здесь. В этом случае маппинг реализуется в рамках ETL-процесса (Extract, Transform, Load) на этапе преобразования. При этом настраивается соответствие исходных данных с целевой моделью (рис. 1). В случае реляционных СУБД для идентификации одной сущности в разных представлениях нужно с ключами таблиц и настройкой отношений (1:1, *:1, 1:* или *:*) [5].
Рис.1. Маппирование данных при консолидации таблиц
В Data Science маппирование данных входит в этап их подготовки к ML-моделированию, когда выполняется формирование датасета в виде матрицы значений для обработки соответствующими алгоритмами. В частности, когда Data Scientist обогащает исходный датасет данными из сторонних источников, он занимается маппингом данных. Проводить процедуру дата мэппинга можно вручную или автоматически с помощью соответствующих подходов и инструментов, которые рассмотрены далее.
Особенности процесса дата мэппинга
На практике трудоемкость мэппинга зависит от следующих факторов [3]:
Облегчить процесс маппирования можно за счет метаданных – сведениях о признаках и свойствах объектов, которые позволяют автоматически искать и управлять ими в больших информационных потоках. В частности, если каждое приложение будет выполнять публикацию метаданных, что позволит создать их стандартизированный реестр, то маппинг будет полностью автоматизированным [2]. Однако в большинстве случаев процесс мапирования данных не полностью автоматизирован и состоит из следующих этапов [4]:
При работе с большими объемами данных выделяют 3 основных подхода к маппированию [2]:
Также стоит упомянуть полуавтоматическое маппирование в виде конвертирования схем данных, когда специализированная программа сравнивает источники данных и целевую схему для консолидации. Затем разработчик проверяет схему маппирования и вносит исправления, где это необходимо. Далее программа конвертирования схем данных автоматически генерирует код на C++, C # или Java для загрузки данных в систему приемник (рис. 3) [3].
Рис. 3. Конвертирование схем данных в процессе мэппинга
Далее рассмотрим, какие инструментальные средства реализуют вышеперечисленные подходы.
Инструменты маппирования больших данных
Как и большинство прикладных решений, все средства для маппинга данных можно разделить на 3 категории [6]:
Большинство перечисленных продуктов поддерживают все 3 подхода к маппированию: ручной (GUI и кодирование), data-driven и семантический. Однако, семантический мэппинг требует наличия реестров метаданных, что имеется далеко не в каждом предприятии. А публичные реестры метаданных, такие как национальные, отраслевые или городские репозитории [7] не всегда напрямую коррелируют, например, с задачами построения локального DWH. Но, наряду с открытыми государственными данными и другими публичными датасетами, их можно использовать в исследовательских DS-задачах.
При выборе конкретного инструмента для маппинга больших данных стоит учитывать следующие факторы:
Резюме
Итак, маппирование данных – это важная часть процесса работы с данными, в том числе и для Data Scientist’а. Эта процедура выполняется в рамках подготовки к ML-моделированию, в частности, при обогащении датасетов. В случае одноразового формирования датасета из нескольких разных источников сопоставление данных можно выполнить вручную или с помощью самописного Python-скрипта. Однако, такой подход не применим в промышленной интеграции нескольких информационных систем или построении корпоративных хранилищ и озер данных. Поэтому знание инструментов дата мэппинга пригодится как Data Scientist’у, так и Data Engineer’у. Наконец, сопоставление данных с целью избавления от дублирующихся и противоречивых значений входит в задачи обеспечения качества данных (Data Quality) [4]. В свою очередь, Data Quality относится к области ответственности стратега по данным и инженера по качеству данных. Таким образом, понимание процесса маппирования необходимо каждому Data-специалисту.
Elasticsearch — Урок 3.1 Mapping: схема документов
Маппинг (сопоставление) — это процесс определения схемы или структуры документов. Он описывает свойства полей в документе. Свойства поля включают тип данных (например, string, integer и т.д.) и метаданные.
Подобно тому, как вы определяете схему таблицы в SQL, важно рассказать про схему документов, прежде чем индексировать любые данные. Как мы обсуждали ранее, тип в Elasticsearch похож на таблицу SQL, который группирует документы аналогичного характера (например тип для пользователей, тип для заказов). Каждый тип имеет схему. Наличие разных схем данных также может быть мотивацией для определения нового типа.
Apache Lucene, которая хранит ваши данные под капотом, не имеет понятия типов. Информация о типе хранится в метаданных и обрабатывается Elasticsearch.
Прежде чем говорить о различных типах данных, поддерживаемых Elasticsearch, давайте взглянем на документы и как определим их схему. Предположим, мы хотим индексировать документы, показанные ниже:
Динамическое отображение
Когда вы индексируете документ без указания сопоставления, Elasticsearch автоматически определяет типы данных для полей в документе. Чтобы понять, как работает динамическое сопоставление, давайте попробуем проиндексировать документ человека, как показано ниже:
Теперь давайте проверим сопоставление типа человека, которое устанавливается автоматически:
Вы можете видеть из предыдущего ответа, что Elasticsearch определил тип данных age как числовое, date_of_birth как дату и name как строковое. Когда встречается новое поле, он пытается определить, является ли поле логическим, числовым, текстовым или датой.
Сопоставление числовых и логических полей данных является простым, но для сопоставления полей даты сначала проверяется значение строки, чтобы увидеть, соответствует ли оно любым известным шаблонам дат.
По умолчанию строковое значение проверяется в отношении трех форматов дат, показанных ниже:
Хотя Elasticsearch может определить тип данных, вы должны установить отображение во все известные поля, чтобы избежать любых неожиданностей. По умолчанию для любых полей, не входящих в сопоставление, тип данных определяется на основе первого найденного значения поля. Чтобы избежать неожиданного сопоставления, вы можете отключить динамическое сопоставление, как показано ниже:
Динамическая настройка принимает три разных значения:
Иногда автоматическое определение даты может вызвать проблемы. Если начало строкового поля случайно совпадает с форматом даты по умолчанию, тип данных поля может быть установлен на дата вместо текста. Чтобы этого избежать, вы можете отключить автоматическое определение даты, как показано ниже:
Создать индекс с отображением
Чтобы определить или добавить схему, мы должны использовать API маппинга.
API маппинга позволит вам сделать следующее:
Обратите внимание, что метод HTTP, используемый для запроса — PUT.
Если все прошло успешно, вы должны увидеть ответ, как показано ниже:
Добавление нового типа / поля
В предыдущем разделе мы обсудили, как создать индекс со схемой маппинга. В этом разделе мы обсудим, как добавить новый тип и новые поля. Давайте добавим новый тип, назовем его history с целью хранить там историю заходов пользователя.
Обратите внимание на _mapping в конце URL-адреса. Вы можете добавить имя типа history в example3 индекс, как показано ниже:
Установив ip_address поле как тип ip, мы можем выполнить запросы диапазона и агрегации по IP-адресу. Мы обсудим тип данных ip в скором будущем.
Получение существующей схемы
API маппинга также используется для извлечения существующей схемы. Вы можете проверить схему существующего индекса или типа, как показано ниже:
Обратите внимание, что метод HTTP, используемый для запроса — GET.
Следующий ответ содержит отображение всех типов в example3 :
Вы также можете получить сопоставление одного типа, как показано ниже:
Изменение существующей схемы
Схема существующих полей не может быть изменена. Когда документы индексируются, они сохраняются в инвертированном индексе в соответствии с типом данных. Если вы попытаетесь обновить тип данных существующего поля, вы получите исключение, как показано ниже:
Вы всегда можете добавлять новые поля или использовать несколько полей для индексации одного и того же поля с использованием нескольких типов данных, но вы не можете обновлять существующее сопоставление. Если вы хотите изменить отображение, вам нужно заново создать индекс или использовать Reindex API. Мы обсудим переиндексацию в 5 уроке. Если вам не нужны уже индексированные данные, вы можете добавить новое поле с правильным типом данных.
Тип данных
В традиционном мире SQL тип данных может быть только простым, таким как integer, boolean и т. д. Поскольку данные в Elasticsearch представлены как JSON, он поддерживает типы данных, которые являются более сложными объекты, массивы и т. д.
Поддерживаются разные типы данных:
Основные типы данных:
Сложные типы данных:
Типы данных Geo:
Специализированные типы данных:
Прежде чем мы перейдем к каждому типу данных, давайте поговорим о мета-полях, которые содержатся в каждом документе.
Мета-поля
Каждый индекс, который мы индексируем, имеет следующие мета-поля, также известные как мета-поля идентификации, поскольку они используются для однозначного определения документа:
Как обрабатывать нулевые значения
Когда Elasticsearch встречает нулевое значение JSON в документе, оно пропускает это поле, поскольку оно не может быть проиндексировано. Но если вы хотите искать все документы, содержащие нулевое значение, вы можете сказать Elasticsearch заменить значение null значением по умолчанию.
Обратите внимание, что он null_value должен иметь тот же тип данных, что и поле. Целочисленное поле не может иметь нулевое значение, которое является строкой.
Хранение оригинального документа
Поиск по всем полем в документе
Когда предыдущий документ индексируется вместе с полями документа, Elasticsearch индексирует все значения полей в одну большую строку ( «john jake iphone 2017-02-08» ) в качестве _all поля. Запрос для всех заказов, которые содержат, item_title как iphone показано ниже:
По умолчанию поле _all включено. Если вы не планируете использовать его можно отключить, как показано ниже. Отключение его приведет к уменьшению размера индекса на диске:
В следующем части урока мы обсудим анализаторы и почему отображение необходимо для получения правильных результатов поиска.
Практичные способы маппинга данных в Kotlin
Маппинг данных – один из способов для разделения кода приложения на слои. Маппинг широко используется в Android приложениях. Популярный пример архитектуры мобильного приложения Android-CleanArchitecture использует маппинг как в оригинальной версии (пример маппера из CleanArchitecture), так и в новой Kotlin версии (пример маппера).
Маппинг позволяет развязать слои приложения (например, отвязаться от API), упростить и сделать код более наглядным.
Пример полезного маппинга изображен на схеме:
Для примера модели упрощены. Person содержит Salary в обоих слоях приложения.
В настоящем коде, если у вас одинаковые модели, возможно, стоит пересмотреть слои приложения и не использовать маппинг.
Метод №1: Методы-мапперы
Самый быстрый и простой метод. Именно он используется в CleanArchitecture Kotlin (пример маппинга).
Такой код быстрее писать и проще модифицировать – объявления полей и их использование находятся в одном месте. Не надо бегать по проекту и модифицировать разные файлы при изменении полей класса.
Еще проблема может возникнуть если по требованиям архитектуры слои приложения не могут знать друг о друге: т.е. в классе Src слоя нельзя работать со слоем Dst и наоборот. В этом случае такой вариант маппинга использовать не получится.
В рассмотренном примере слой Src зависим от слоя Dst и может создавать классы этого слоя. Для обратной ситуации (когда Dst зависим от Src ) подойдет вариант со статическими методами-фабриками:
Маппинг находится внутри классов Dst слоя, значит эти классы не раскрывают все свои свойства и структуру использующему их коду.
Если в приложении один слой зависим от другого и осуществляется передача данных между слоями приложения в обоих направлениях, статические методы-фабрики логично использовать вместе с методами-мапперами.
Резюме метода маппинга:
+ Быстро писать код, маппинг всегда под рукой
+ Легкая модификация
+ Низкая связность кода
— Затруднено Unit-тестирование (нужны моки)
— Не всегда позволено архитектурой
Метод №2: функции-мапперы
Размещение маппера и классов, с которыми он работает, в разных местах проекта не всегда удобно. При частой модификации класса придётся искать и изменять разные файлы в разных местах.
Резюме метода маппинга:
+ Простое Unit-тестирование
— Затруднена модификация
— Требуются открытые поля у классов с данными
Метод № 3: Функции-расширения
При этом стоит учесть, что функции расширения могут приводить к неожиданному поведению из-за своей статической природы: https://kotlinlang.org/docs/reference/extensions.html#extensions-are-resolved-statically
Резюме метода маппинга:
+ Простое Unit-тестирование
— Затруднена модификация
— Требуются открытые поля у классов с данными
Метод №4: Классы-мапперы с интерфейсом
Относительно маппинга в функции у этого примера только один недостаток – необходимость писать немного больше кода.
Резюме метода маппинга:
+ Лучше типизация
— Больше кода
Как и функции-мапперы:
+ Простое Unit-тестирование
— Затруднена модификация
— требует открытые поля у классов с данными
Метод 5: Рефлексия
Метод черной магии. Рассмотрим этот метод на других моделях.
В данном примере EmployeeSrc и EmployeeDst хранят имя в разных форматах. Мапперу нужно только составить имя для новой модели. Остальные поля обработаются автоматически, без написания кода (вариант else в when ).
Метод может быть полезен, например, если у вас большие модели с кучей полей и поля в основном совпадают у одних и тех же моделей из разных слоев.
Большая проблема возникнет, например, если вы добавите обязательные поля в Dst и его случайно не окажется в Src или в маппере: cлучится IllegalArgumentException в runtime. Также рефлексия имеет проблемы с производительностью.
Резюме метода маппинга:
+ меньше кода
+ простое Unit-тестирование
— опасен
— может негативно сказаться на производительности
Выводы
Такие выводы можно сделать из нашего рассмотрения:
Методы-мапперы — наглядный код, быстрее писать и поддерживать
Функции-мапперы и функции расширения – просто тестировать маппинг.
Классы мапперы с интерфейсом — просто тестировать маппинг и яснее код.
Рефлексия – подходит для нестандартных ситуаций.
Маппинг данных из реляционной БД
Иногда возникают ситуации, когда решение задачи выборки данных из реляционной БД не укладывается в возможности используемой в проекте ОРМ, например, либо из-за недостаточной скорости работы самой ОРМ, либо не совсем оптимальных SQL запросов генерируемых ею. В таком случае обычно приходится писать запросы вручную.
Проблема в том, что данные из БД (в т.ч. в ответ на JOIN запрос) возвращаются в виде “плоского” двухмерного массива никак не отражающего сложную “древовидную” структуру данных приложения. Работать с таким массивом дальше крайне неудобно, поэтому требуется более-менее универсальное решение, позволяющее привести этот массив в более подходящий вид по заданному шаблону.
Решение было найдено, удобное и достаточно быстрое.
На сколько быстрое
Для оценки скорости работы библиотеки я собрал небольшой испытательный стенд на котором скорость работы моей библиотеки сравнивается со скоростью работы Eloquent. Для замеров использовался пакет phpbench.
Для того чтобы развернуть стенд у себя:
Здесь я использовал инструмент описанный в моей предыдущей статье.
Затем в меню выбираем: 1 Develop, затем: 1 Build, затем 2 Deploy and Up;
Затем запускаем тесты 5. Run tests
В базе 3000 книг. Результаты получились следующие:
benchEloquent — вытаскивает все книги с авторами с использованием Eloquent
benchEloquentId — вытаскивает определенную книгу с авторами с использованием Eloquent (10 раз)
benchProc — вытаскивает все книги с авторами с использованием библиотеки
benchProcId — вытаскивает определенную книгу с авторами с использованием библиотеки (10 раз)
Возможно приведенные тесты недостаточно репрезентативны, но разница заметна, как по времени выполнения, так и по расходованию памяти.
Как это работает
Далее, для примера (крайне простого), представим, что у нас имеется БД книг и авторов со следующей структурой.
Задача — вытащить все книги с их авторами.
Запрос будет выглядеть как-то так:
В ответ мы получим примерно такой массив данных.
book.id | book.name | author.id | author.name |
1 | book1 | 2 | author2 |
1 | book1 | 4 | author4 |
1 | book1 | 6 | author6 |
2 | book2 | 2 | author2 |
2 | book2 | 3 | author3 |
2 | book2 | 6 | author6 |
2 | book2 | 7 | author7 |
Для этого немного изменим наш запрос:
Здесь мы в секции SELECT задали алиасы: для полей с данными о книгах алиасы с префиксом ‘book_’, а для полей с информацией об авторах с префиксом ‘author’.
Далее преобразуем ответ БД
$rows — ответ БД в виде массива объектов /stdClass()
$config — ассоциативный массив отражающий структуру данных итогового массива