Пользовательские источники, приёмники и преобразователи в задаче ETL

Андрей Алифанов

Задача ETL позволяет работать со множеством источников, приемников и преобразователей. Здесь Excel, dBase, текстовые файлы с xml и разнообразные объекты репозитория.

Но что делать, если нам нужно работать с данными в неподдерживаемых платформой форматах? Или преобразовывать данные каким-то специфическим образом? Здесь на помощь приходят пользовательские источники, приёмники и преобразователи.

Для работы с ETL в целом не обязательно быть программистом – всю работу можно сделать через пользовательский интерфейс платформы. Но для работы с пользовательскими объектами необходимо иметь навыки программирования на Fore / Fore.NET.

Пользовательские источники

Давайте попробуем создать пользовательский источник. Мастер создания нового объекта задачи ETL представлен ниже:

Если мы посмотрим на комментарий в верхней части окна, то увидим, что от нас требуется реализация одного из двух интерфейсов: IDtRecordsetProvider или IDtCustomProvider.

Реализация интерфейса IDtRecordsetProvider – наиболее простой путь. Интерфейс содержит всего один метод, не принимающий никаких параметров и возвращающий двумерный массив.

Но на этом достоинства такой реализации и заканчиваются. А вот недостатков здесь чуть менее, чем много. 🙂

Во-первых, код может быть написан только на Fore. Никакого Fore.NET. 🙁

Во-вторых, нет возможности управлять типами и именами полей в источнике данных. Имена полям назначаются ядром платформы (и имеют вид FIELD1, FIELD2, …), типы данных автоматически определяются ядром с помощью анализа содержимого первых нескольких записей источника.

В-третьих, нет возможности управлять алгоритмом получения данных и управлять памятью – все данные возвращаются за один вызов, целым массивом. И т.д., и т.п.

Ниже дана полная реализация IDtRecordsetProvider. Как можно увидеть – реализация действительно очень проста. В примере возвращается двумерный массив. В первом столбце массива возвращаются строки, во втором – вещественные числа.

Public Class UserProvider : Object, IDtRecordsetProvider Public Function Fetch : Array Of Variant; Var Data: Array[0..1, 0..1] Of Variant; Begin Data[0, 0] := "Строка 1"; Data[0, 1] := "Строка 2"; Data[1, 0] := 1.0; Data[1, 1] := 2.0; Return Data; End Function Fetch; End Class UserProvider;

Если нас все эти ограничения не устраивают, мы должны использовать IDtCustomProvider. Этот способ гораздо сложнее в реализации, чем первый. Так как методов в интерфейсе гораздо больше. Зато при использовании данного интерфейса появляется возможность писать на Fore.NET (а значит, задействовать всю мощь платформы Microsoft.NET), задавать свои имена и типы данных полям, управлять процессом выдачи данных клиенту, потреблением памяти и т.д.

Реализацию IDtCustomProvider я здесь приводить не буду – она занимает довольно много места. Минимальный код дан в тестовом репозитории (информация о том, как работать с тестовым репозиторием, в конце статьи).

Пользовательские приёмники

Итак, с источниками данных разобрались. Давайте посмотрим, что же нужно для реализации пользовательских приёмников. Как вы уже догадались, для них нужно написать реализацию интерфейсов IDtRecordsetConsumer или IDtCustomConsumer. 🙂

Достоинства и недостатки обеих реализаций приёмника аналогичны описанным ранее реализациям источника. Различается только набор методов в интерфейсах.

Ниже дана реализация пользовательского приёмника. Как видите, она ненамного сложнее источника.

Public Class UserConsumer : Object, IDtRecordsetConsumer Public Sub Put(Data: Array Of Variant); Var i,j : Integer; Begin For i := Data.GetLowerBound(2) To Data.GetUpperBound(2) Do For j := Data.GetLowerBound(1) To Data.GetUpperBound(1) Do Debug.WriteLine(Data[j, i]); End For; End For; End Sub Put; Public Sub Clear; Begin End Sub Clear; End Class UserConsumer;

Пример реализации IDtCustomConsumer занимает намного больше места и также есть в тестовом репозитории.

Пользовательские преобразователи

С источниками и приёмниками данных разобрались. Давайте теперь посмотрим, а как же реализовать свой механизм преобразования данных? Что для этого нужно? Давайте снова посмотрим на окно мастера: там есть подсказка.

Подсказка говорит, что нужно указать некий макрос из некоего модуля. Давайте разберемся, что же это за макрос такой и как его написать? Сделать это мы можем тремя способами:

    1. Написав статическую процедуру-член какого-либо класса (Shared в терминологии языков Fore/Fore.NET).

    2. Написав глобальную процедуру (это справедливо только для Fore, так как в Fore.NET нет глобальных процедур).

    3. Реализовав интерфейс IEtlCustomUser.

В любом случае процедура должна принимать два параметра: входной и выходной наборы данных типа IEtlPlainRecordSets.

В данном примере выбрана глобальная процедура UserTransform.

Пример реализации своего преобразователя данных вы можете увидеть ниже. В примере преобразователь складывает данные в первых полях двух источников данных. Остальные поля первого источника передаются на выход без изменений, поля второго источника не задействуются.

Чтобы не усложнять логику, в примере предполагается, что количество записей в обоих источниках одинаковое. В реальном примере, конечно, все будет не так просто.

Public Sub UserTransform(Input, Output: IEtlPlainRecordSets); Var RecordSetIn1, RecordSetIn2, RecordSetOut: IEtlPlainRecordSet; RecordOut: IEtlPlainRecord; RecordIn1, RecordIn2: IEtlPlainRecord; i, j: Integer; Begin RecordSetIn1 := Input.Item(0); // Вход для 1-го источника RecordSetIn2 := Input.Item(1); // Вход для 2-го источника RecordSetOut := Output.Item(0); // Выход на приёмник For i := 0 To RecordSetIn1.Count - 1 Do RecordSetOut.Add; RecordOut := RecordSetOut.Item(i); RecordIn1 := RecordSetIn1.Item(i); RecordIn2 := RecordSetIn2.Item(i); For j := 0 To RecordIn1.Count - 1 Do If j = 0 Then RecordOut.Value(j) := "S1:" + RecordIn1.Value(j) + " S2:" + RecordIn2.Value(j); Else RecordOut.Value(j) := RecordIn1.Value(j); End If; End For; End For; End Sub UserTransform;

Так как преобразователь может иметь несколько входов и несколько выходов, появляется возможность разными способами комбинировать данные между источниками и приёмниками.

Реальное применение ограничивается только фантазией разработчика и требованиями задачи.

Итак, если собрать все наши реализованные объекты вместе, получится вот такая задача ETL.

В задаче есть две одинаковых цепочки, с абсолютно одинаковыми источниками. А вот преобразователи и приёмники написаны по-разному, и даже на разных языках.

Задача наглядно показывает, как можно достаточно просто реализовать объекты для работы с пользовательскими данными и алгоритмами в Prognoz Platform.

Читайте также

Комментарии

Подробнее о политике использования персональных данных