Трансформация внешних данных
С помощью трансформаций данных можно читать или записывать данные во внешние файлы в форматах, которые Greengage DB изначально не поддерживает.
Трансформация ввода читает файл в стороннем формате данных и передает строки в gpfdist
в формате, указанном в выражении FORMAT
внешней таблицы (TEXT
или CSV
).
Трансформация вывода получает строки из gpfdist
в текстовом формате и преобразует их в сторонний.
Трансформации данных поддерживаются при работе с утилитами gpfdist и gpload.
Одним из сценариев, требующих использования трансформации, является доступ из БД к данным, хранящимся во внешних структурированных файлах (например, XML или JSON).
Более подробную информацию и практические руководства по загрузке и выгрузке данных в формате XML можно получить в разделе Примеры.
Общий принцип настройки проекта трансформации выглядит следующим образом:
-
Определите схему трансформации: установите цель проекта (например, индексация, анализ или объединение данных). Затем изучите исходные файлы, их структуру и имена элементов. Наконец, выберите элементы для импорта.
-
Напишите преобразование, которое определит, что именно извлекать из данных. Вы можете использовать любую подходящую для проекта среду разработки и язык программирования. Например, преобразование XML можно выполнить с использованием технологий XSLT, Java или Python в зависимости от целей и масштаба проекта. Преобразование предоставляется в виде исполняемой команды, которую
gpfdist
вызывает с именем исходного файла данных. -
Напишите файл конфигурации трансформации для
gpfdist
. Файл передаетсяgpfdist
через командную строку и содержит правила, которыеgpfdist
использует для выбора трансформации при загрузке или извлечении данных. -
Перенесите данные. Для загрузки внешних данных в таблицу Greengage DB используйте либо
gpfdist
, либоgpload
, автоматизирующий некоторые задачи по загрузке. Более подробную информацию можно получить в разделе Передача данных.
Файл конфигурации трансформации
Файл конфигурации gpfdist
определяет параметры трансформации и использует формат YAML 1.1.
Содержимое документа обрабатывается в заданном порядке, а отступы (пробелы) используются для определения иерархии и взаимосвязей между разделами.
Пробелы в YAML-документах являются важной частью разметки. Не следует использовать пробелы с целью форматирования, а символы табуляции не следует использовать вообще.
Структура конфигурационного файла в общем виде:
---
VERSION: 1.0.0.1
TRANSFORMATIONS:
transformation_name1:
TYPE: input | output
COMMAND: command
SAFE: posix-regex
transformation_name2:
TYPE: input | output
COMMAND: command
...
Ключ | Описание | Обязательность |
---|---|---|
VERSION |
Версия схемы файла конфигурации |
Да |
TRANSFORMATIONS |
Открывает раздел спецификации преобразований.
В файле конфигурации должно быть определено хотя бы одно преобразование.
При получении запроса на преобразование |
Да |
transformation_name1, transformation_name2, … |
Имя трансформации, используемое впоследствии при конфигурировании передачи данных |
Да |
TYPE |
Указывает направление трансформации и принимает значение
|
Да |
COMMAND |
Указывает команду, которую
|
Да |
SAFE |
Регулярное выражение POSIX, определяющее шаблон соответствия путей к файлам данных для передачи в трансформацию.
Укажите |
Нет |
Передача данных
Для загрузки данных в БД или их выгрузки воспользуйтесь одним из следующих методов:
Загрузка с помощью gpfdist
Трансформация с помощью gpfdist
производится следующим образом:
-
Запустите
gpfdist
, передав в опции-c
путь к файлу конфигурации трансформации:$ gpfdist -c config.yaml
-
Создайте внешнюю таблицу. В выражении
LOCATION
командыCREATE EXTERNAL TABLE
укажите имя хоста, на котором запущенgpfdist
(<hostname>
), прослушиваемый порт (<port>
) и имя файла данных (<filename>
). С помощью параметра#transform
укажите имя трансформации (<transformation_name>
). При использовании нескольких серверовgpfdist
перечислите их все, указав для каждого имя требуемой трансформации:CREATE READABLE EXTERNAL TABLE <external_table_name> LOCATION ('gpfdist://<hostname>:<port>/<filename>#transform=<transformation_name>' [, ...] )
-
Используйте команду
INSERT
для вставки данных в целевую таблицу БД (<table_name>
):INSERT INTO <table_name> SELECT * FROM <external_table_name>;
Утилиту gpfdist
можно запустить, передав в опциях -I
или -O
имя трансформации ввода или вывода по умолчанию, например:
$ gpfdist -c config.yaml -I <input_transformation_name>
Выбранная трансформация будет применяться ко всем задействованным файлам.
Использование параметра #transform
на уровне таблицы переопределяет значение опций -I
и -O
.
Загрузка с помощью gpload
Утилита gpload
использует файловый сервер gpfdist
и управляющий файл в формате YAML для организации процесса загрузки и автоматизации следующих задач:
-
Создание читающей внешней таблицы БД.
-
Запуск экземпляров
gpfdist
с файлом конфигурации, в котором описана трансформация. -
Запуск команды
INSERT INTO <table_name> SELECT FROM <external_table_name>
для загрузки данных. -
Удаление внешней таблицы после загрузки данных.
gpload
поддерживает только трансформации ввода.
Для настройки трансформации укажите значения ключей TRANSFORM
и TRANSFORM_CONFIG
в разделе INPUT
управляющего файла gpload
:
---
VERSION: 1.0.0.1
GPLOAD:
INPUT:
- TRANSFORM_CONFIG: <gpfdist_configuration_file>
- TRANSFORM: <transformation_name>
- SOURCE:
FILE: <filename>
где:
-
TRANSFORM_CONFIG
указывает имя файла конфигурации трансформацииgpfdist
. -
TRANSFORM
указывает имя трансформации из файла конфигурации трансформацииgpfdist
.
Имя трансформации должно быть указано в двух местах: в значении ключа TRANSFORM
управляющего файла gpload
и в разделе TRANSFORMATIONS
файла конфигурации трансформации gpfdist
(этот файл указывается в качестве значения ключа TRANSFORM_CONFIG
).
Создайте управляющий файл (например, load.yml) и передайте его утилите gpload
, используя опцию -f
:
$ gpload -f load.yml
Примеры
В этих примерах продемонстрирована загрузка данных XML в таблицу БД и выгрузка данных из таблицы. Трансформация между XML и текстовым форматом реализована с использованием XSLT и Python.
Загрузка данных
В этом примере продемонстрирована загрузка данных XML в таблицу БД.
Трансформация между XML и текстовым форматом реализована с использованием XSLT и Python, а для загрузки используется gpfdist
либо gpload
.
-
В каталоге /tmp мастер-хоста создайте файл customers.xml c данными для загрузки:
<customers> <customer id="1"> <first_name>John</first_name> <last_name>Doe</last_name> <email>john.doe@example.com</email> <address>123 Elm Street</address> </customer> <customer id="2"> <first_name>Jane</first_name> <last_name>Smith</last_name> <email>jane.smith@example.com</email> <address>456 Oak Street</address> </customer> <customer id="3"> <first_name>Bob</first_name> <last_name>Brown</last_name> <email>bob.brown@example.com</email> <address>789 Pine Street</address> </customer> <customer id="4"> <first_name>Rob</first_name> <last_name>Stuart</last_name> <email>rob.stuart@example.com</email> <address>119 Willow Street</address> </customer> </customers>
-
В каталоге /tmp мастер-хоста создайте файл transform_customers_in.xslt или файл transform_customers_in.py, описывающий логику трансформации. XML-документ преобразуется в CSV-файл путем итерации по каждому элементу
customer
внутри корневого элементаcustomers
c извлечением идентификатора, полного имени (объединение имени и фамилии), адреса электронной почты и адреса:<?xml version="1.0" encoding="UTF-8"?> <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <xsl:output method="text"/> <xsl:template match="/"> <!-- Header Row --> <xsl:text>id,name,email,address</xsl:text> <xsl:text>
</xsl:text> <xsl:for-each select="//customers/customer"> <!-- Data Row --> <xsl:value-of select="@id"/> <xsl:text>,</xsl:text> <xsl:value-of select="concat(first_name/text(), ' ', last_name/text())"/> <xsl:text>,</xsl:text> <xsl:value-of select="email/text()"/> <xsl:text>,</xsl:text> <xsl:value-of select="address/text()"/> <xsl:text>
</xsl:text> </xsl:for-each> </xsl:template> </xsl:stylesheet>
import xml.etree.ElementTree as ET import csv import sys def xml_to_csv(xml_data): root = ET.fromstring(xml_data) header = ['id', 'name', 'email', 'address'] writer = csv.writer(sys.stdout) writer.writerow(header) for customer in root.findall('customer'): customer_id = customer.get('id') first_name = customer.find('first_name').text last_name = customer.find('last_name').text name = "{0} {1}".format(first_name, last_name) email = customer.find('email').text address = customer.find('address').text writer.writerow([customer_id, name, email, address]) if __name__ == "__main__": xml_data = sys.stdin.read() xml_to_csv(xml_data)
-
Создайте файл конфигурации трансформации (config_in.yaml). В разделе
TRANSFORMATIONS
укажите имя трансформации (transform_customers
) и ее тип (input
). Обратите внимание, что в ключеCOMMAND
указывается команда для запуска XSLT- или Python-трансформации с подстановочным выражением%filename%
. Утилитаgpfdist
выполняет трансформациюtransform_customers
путем запуска команды с помощью/bin/bash
, заменяя выражение%filename%
на путь к файлу данных:--- VERSION: 1.0.0.1 TRANSFORMATIONS: transform_customers: TYPE: input COMMAND: /usr/bin/xsltproc /tmp/transform_customers_in.xslt %filename%
--- VERSION: 1.0.0.1 TRANSFORMATIONS: transform_customers: TYPE: input COMMAND: /usr/bin/env bash -c "(/usr/bin/python /tmp/transform_customers_in.py < $0)" %filename%
-
Выполните загрузку. Используйте
gpfdist
, чтобы выполнить все этапы загрузки вручную, либоgpload
, чтобы автоматизировать их с помощью управляющего файла:-
В каталоге /tmp мастер-хоста запустите
gpfdist
, передав файл конфигурации трансформации config_in.yaml:$ gpfdist -c config_in.yaml -d /tmp -p 8888 &
-
Создайте читающую внешнюю таблицу
customers_r
. В выраженииLOCATION
используйте параметр#transform
, указав имя трансформации (transform_customers
):CREATE EXTERNAL TABLE customers_r (LIKE customers) LOCATION ('gpfdist://mdw:8888/customers.xml#transform=transform_customers') FORMAT 'CSV' (HEADER);
-
Вставьте данные в целевую таблицу
customers
:INSERT INTO customers SELECT * FROM customers_r;
-
Создайте управляющий файл
gpload
(load.yaml). В качестве значения ключаTRANSFORM_CONFIG
укажите местоположение файла конфигурации трансформацииgpfdist
(config_in.yaml), а в качестве значенияTRANSFORM_CONFIG
— имя трансформации (transform_customers
):--- VERSION: 1.0.0.1 DATABASE: customers USER: gpadmin HOST: mdw PORT: 5432 GPLOAD: INPUT: - SOURCE: FILE: - /tmp/customers.xml - COLUMNS: - id: integer - name: text - email: text - address: text - TRANSFORM_CONFIG: /tmp/config_in.yaml - TRANSFORM: transform_customers - FORMAT: CSV - HEADER: true OUTPUT: - TABLE: customers - MODE: INSERT
-
Запустите
gpload
, передав управляющий файл в опции-f
:$ gpload -f /tmp/load.yaml
-
-
Выполните запрос к таблице
customers
:TABLE customers;
Вывод должен выглядеть следующим образом:
id | name | email | address ----+------------+------------------------+------------------- 1 | John Doe | john.doe@example.com | 123 Elm Street 2 | Jane Smith | jane.smith@example.com | 456 Oak Street 3 | Bob Brown | bob.brown@example.com | 789 Pine Street 4 | Rob Stuart | rob.stuart@example.com | 119 Willow Street
Выгрузка данных
В этом примере продемонстрирована выгрузка данных из таблицы БД с помощью gpfdist
и их трансформация в формат XML с использованием Python.
-
В каталоге /tmp мастер-хоста создайте файл transform_customers_out.py, описывающий логику трансформации. CSV-файл преобразуется в XML-документ путем чтения каждой строки c извлечением идентификатора, полного имени (с последующим разделением на имя и фамилию), адреса электронной почты и адреса. Соответствующие элементы
<customer>
создаются внутри корневого элемента<customers>
:import csv import xml.etree.ElementTree as ET import sys def csv_to_xml(csv_data): root = ET.Element('customers') reader = csv.reader(csv_data.splitlines(), delimiter='|') header = next(reader) for row in reader: if len(row) != 4: print "Skipping invalid row: %s" % row continue customer_id = row[0] name = row[1] email = row[2] address = row[3] customer = ET.SubElement(root, 'customer', {'id': customer_id}) ET.SubElement(customer, 'first_name').text = name.split(' ')[0] ET.SubElement(customer, 'last_name').text = name.split(' ')[-1] ET.SubElement(customer, 'email').text = email ET.SubElement(customer, 'address').text = address return ET.tostring(root, encoding='utf-8') if __name__ == "__main__": csv_data = sys.stdin.read() try: xml_output = csv_to_xml(csv_data) print xml_output except ValueError as e: print "Error: %s" % e sys.exit(1)
-
Создайте файл конфигурации трансформации (config_out.yaml). В разделе
TRANSFORMATIONS
укажите имя трансформации (transform_customers
) и ее тип (output
). Обратите внимание, что в ключеCOMMAND
указывается команда для запуска трансформации с подстановочным выражением%filename%
. Утилитаgpfdist
выполняет трансформациюtransform_customers
путем запуска команды с помощью/bin/bash
, заменяя выражение%filename%
на путь к выходному файлу:--- VERSION: 1.0.0.1 TRANSFORMATIONS: transform_customers: TYPE: output COMMAND: /usr/bin/env bash -c "(/usr/bin/python /tmp/transform_customers_out.py | cat - >> $0)" %filename%
-
Выполните загрузку с помощью
gpfdist
. В каталоге /tmp мастер-хоста запуститеgpfdist
, передав файл конфигурации трансформации config_out.yaml:$ gpfdist -c config_out.yaml -d /tmp -p 8888 &
-
Создайте пишущую внешнюю таблицу
customers_w
. В выраженииLOCATION
используйте параметр#transform
, указав имя трансформации (transform_customers
). В выраженииFORMAT
укажите формат выходных данных (TEXT
) и используемый символ разделителя (DELIMITER
) — вертикальную косую черту (|
):CREATE WRITABLE EXTERNAL TABLE customers_w (LIKE customers) LOCATION ('gpfdist://mdw:8888/customers_out.xml#transform=transform_customers') FORMAT 'TEXT' (DELIMITER '|');
-
Вставьте данные в таблицу
customers_w
:INSERT INTO customers_w (id, name, email, address) VALUES (1,'John Doe','john.doe@example.com','123 Elm Street'), (2,'Jane Smith','jane.smith@example.com','456 Oak Street'), (3,'Bob Brown','bob.brown@example.com','789 Pine Street'), (4,'Rob Stuart','rob.stuart@example.com','119 Willow Street');
-
Проверьте содержимое созданного файла customers_out.xml:
$ cat /tmp/customers_out.xml
Содержимое должно выглядеть следующим образом:
<customers> <customer id="1"> <first_name>John</first_name> <last_name>Doe</last_name> <email>john.doe@example.com</email> <address>123 Elm Street</address> </customer> <customer id="2"> <first_name>Jane</first_name> <last_name>Smith</last_name> <email>jane.smith@example.com</email> <address>456 Oak Street</address> </customer> <customer id="3"> <first_name>Bob</first_name> <last_name>Brown</last_name> <email>bob.brown@example.com</email> <address>789 Pine Street</address> </customer> <customer id="4"> <first_name>Rob</first_name> <last_name>Stuart</last_name> <email>rob.stuart@example.com</email> <address>119 Willow Street</address> </customer> </customers>