Привет, Я DocuDroid!
Оценка ИИ поиска
Спасибо за оценку нашего ИИ поиска!
Мы будем признательны, если вы поделитесь своими впечатлениями, чтобы мы могли улучшить наш ИИ поиск для вас и других читателей.
GitHub

Трансформация внешних данных

Антон Монаков

С помощью трансформаций данных можно читать или записывать данные во внешние файлы в форматах, которые Greengage DB изначально не поддерживает. Трансформация ввода читает файл в стороннем формате данных и передает строки в gpfdist в формате, указанном в выражении FORMAT внешней таблицы (TEXT или CSV). Трансформация вывода получает строки из gpfdist в текстовом формате и преобразует их в сторонний. Трансформации данных поддерживаются при работе с утилитами gpfdist и gpload. Одним из сценариев, требующих использования трансформации, является доступ из БД к данным, хранящимся во внешних структурированных файлах (например, XML или JSON). Более подробную информацию и практические руководства по загрузке и выгрузке данных в формате XML можно получить в разделе Примеры.

Общий принцип настройки проекта трансформации выглядит следующим образом:

  1. Определите схему трансформации: установите цель проекта (например, индексация, анализ или объединение данных). Затем изучите исходные файлы, их структуру и имена элементов. Наконец, выберите элементы для импорта.

  2. Напишите преобразование, которое определит, что именно извлекать из данных. Вы можете использовать любую подходящую для проекта среду разработки и язык программирования. Например, преобразование XML можно выполнить с использованием технологий XSLT, Java или Python в зависимости от целей и масштаба проекта. Преобразование предоставляется в виде исполняемой команды, которую gpfdist вызывает с именем исходного файла данных.

  3. Напишите файл конфигурации трансформации для gpfdist. Файл передается gpfdist через командную строку и содержит правила, которые gpfdist использует для выбора трансформации при загрузке или извлечении данных.

  4. Перенесите данные. Для загрузки внешних данных в таблицу 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

Версия схемы файла конфигурации gpfdist. Текущая версия — 1.0.0.1

Да

TRANSFORMATIONS

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

Да

transformation_name1, transformation_name2, …​

Имя трансформации, используемое впоследствии при конфигурировании передачи данных

Да

TYPE

Указывает направление трансформации и принимает значение input (трансформация ввода) или output (трансформация вывода):

  • input — стандартный вывод процесса трансформации является потоком записей для загрузки в Greengage DB.

  • output — стандартный ввод процесса трансформации является потоком записей из Greengage DB для трансформации данных и их записи в определенный вывод.

Да

COMMAND

Указывает команду, которую gpfdist запускает для выполнения трансформации.

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

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

Да

SAFE

Регулярное выражение POSIX, определяющее шаблон соответствия путей к файлам данных для передачи в трансформацию. Укажите SAFE, если имеется опасность атаки обхода каталога или неправильной интерпретации путей, переданных команде. По умолчанию ограничения на пути не накладываются

Нет

Передача данных

Для загрузки данных в БД или их выгрузки воспользуйтесь одним из следующих методов:

  • gpfdist поддерживает трансформации ввода и вывода, но требует вручную производить действия, которые можно автоматизировать с помощью gpload.

  • gpload поддерживает только трансформации ввода, но может быть проще в настройке.

Загрузка с помощью gpfdist

Трансформация с помощью gpfdist производится следующим образом:

  1. Запустите gpfdist, передав в опции -c путь к файлу конфигурации трансформации:

    $ gpfdist -c config.yaml
  2. Создайте внешнюю таблицу. В выражении 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>'
                   [, ...]
                 )
  3. Используйте команду 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 управляющего файла gpload и в разделе TRANSFORMATIONS файла конфигурации трансформации gpfdist (этот файл указывается в качестве значения ключа TRANSFORM_CONFIG).

Создайте управляющий файл (например, load.yml) и передайте его утилите gpload, используя опцию -f:

$ gpload -f load.yml

Примеры

В этих примерах продемонстрирована загрузка данных XML в таблицу БД и выгрузка данных из таблицы. Трансформация между XML и текстовым форматом реализована с использованием XSLT и Python.

Предварительные требования

Для выполнения примеров из этого раздела подключитесь к мастер-хосту Greengage DB как gpadmin с помощью psql, как описано в статье Подключение к Greengage DB с использованием psql. Затем создайте тестовую базу данных customers и подключитесь к ней:

DROP DATABASE IF EXISTS customers;
CREATE DATABASE customers;
\c customers

Затем создайте таблицу customers:

CREATE TABLE customers
(
    id INTEGER,
    name VARCHAR(50),
    email VARCHAR(100),
    address VARCHAR(255)
);

Чтобы иметь возможность выполнять трансформации на основе XSLT, установите пакет xsltproc:

$ sudo apt-get update && sudo apt-get install xsltproc
$ sudo yum update && sudo yum install libxslt

Загрузка данных

В этом примере продемонстрирована загрузка данных XML в таблицу БД. Трансформация между XML и текстовым форматом реализована с использованием XSLT и Python, а для загрузки используется gpfdist либо gpload.

  1. В каталоге /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>
  2. В каталоге /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>&#xa;</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>&#xa;</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)
  3. Создайте файл конфигурации трансформации (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%
  4. Выполните загрузку. Используйте gpfdist, чтобы выполнить все этапы загрузки вручную, либо gpload, чтобы автоматизировать их с помощью управляющего файла:

    1. В каталоге /tmp мастер-хоста запустите gpfdist, передав файл конфигурации трансформации config_in.yaml:

      $ gpfdist -c config_in.yaml -d /tmp -p 8888 &
    2. Создайте читающую внешнюю таблицу 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);
    3. Вставьте данные в целевую таблицу customers:

      INSERT INTO customers SELECT * FROM customers_r;
    1. Создайте управляющий файл 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
    2. Запустите gpload, передав управляющий файл в опции -f:

      $ gpload -f /tmp/load.yaml
  5. Выполните запрос к таблице 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.

  1. В каталоге /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)
  2. Создайте файл конфигурации трансформации (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%
  3. Выполните загрузку с помощью gpfdist. В каталоге /tmp мастер-хоста запустите gpfdist, передав файл конфигурации трансформации config_out.yaml:

    $ gpfdist -c config_out.yaml -d /tmp -p 8888 &
  4. Создайте пишущую внешнюю таблицу 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 '|');
  5. Вставьте данные в таблицу 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');
  6. Проверьте содержимое созданного файла 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>