ヘッダーをスキップ
Oracle® Big Data Connectorsユーザーズ・ガイド
リリース2 (2.3)
E48215-03
  目次へ移動
目次
索引へ移動
索引

前
 
次
 

Avroファイル・アダプタ

Avroファイル・アダプタは、HDFS内のAvroコンテナ・ファイルの読取りおよび書込みを実行する関数を提供します。この項の内容は次のとおりです。


Avroファイルを読み取る組込み関数

問合せで組込み関数を使用するには、次のようにAvroファイル・モジュールをインポートする必要があります。

import module "oxh:avro";

Avroファイル・モジュールには、次の関数が含まれています。

Avroコンテナ・ファイルを書き込む組込み関数はありません。Avroファイルを書き込むには、Avroライター・スキーマを指定するカスタム関数を使用する必要があります。

avro:collection-avroxml

HDFS内のAvroファイルのコレクションにアクセスします。ファイルを分割して、複数のタスクで並列で処理できます。関数は、各オブジェクトのXML要素を返します。「AvroとXML間の値の変換について」を参照してください。

シグネチャ

declare %avro:collection("avroxml") function 
   avro:collection-avroxml($uris as xs:string*) as element()* external;

パラメータ

$uris

AvroファイルのURI

戻り値

各Avroオブジェクトに対して1つのXML要素。

avro:get

XMLとしてモデル化されたAvroマップからエントリを取得します。

$mapが存在しない場合、動作は、2つの引数の関数を呼び出して$mapのコンテキスト項目を使用するのと同じです。

シグネチャ

avro:get($key as xs:string?, $map as node()?) as element(oxh:entry)?
 
avro:get($key as xs:string?) as element(oxh:entry)?

戻り値

このXPath式の値は次のとおりです。

$map/oxh:entry[@key eq $key]

次の関数呼出しは同等です。

$var/avro:get("key")

avro:get("key", $var)
 
$var/oxh:entry[@key eq "key"]

この例では、$varはXMLとしてモデル化されたAvroマップです。「マップの読取り」を参照してください。


Avroコンテナ・ファイルを読み取るカスタム関数

次の注釈を使用して、HDFS内のAvroコンテナ・ファイルのコレクションを読み取る関数を定義できます。これらの注釈によって、組込み関数では使用できない追加機能を使用できます。

シグネチャ

Avroファイルを読み取るカスタム関数には、次のシグネチャが必要です。

declare %avro:collection("avroxml") [additional annotations]
   function local:myFunctionName($uris as xs:string*) as element()* external;

注釈

%avro:collection("avroxml")

avroxml collection関数を宣言します。必須。

collection関数によって、HDFS内のAvroファイルにアクセスします。ファイルを分割して、複数のタスクで並列で処理できます。関数は、各オブジェクトのXML要素を返します。「AvroとXML間の値の変換について」を参照してください。

%avro:schema("avro-schema")

注釈の値としてAvroリーダー・スキーマを指定します。オプション。

指定時に、ファイル内のオブジェクトがリーダー・スキーマにマップされます。次に例を示します。

%avro:schema('
   {
      "type": "record",
      "name": "Person",
      "fields" : [
         {"name": "full_name", "type": "string"},
         {"name": "age", "type": ["int", "null"] }
       ]
   }
')

この注釈は、%avro:schema-fileまたは%avro:schema-kvと組み合せることはできません。


関連項目:

次のサイトにある『Apache Avro Specification』のスキーマ解決に関する項

http://avro.apache.org/docs/current/spec.html#Schema+Resolution


%avro:schema-file("avro-schema-uri")

%avro:schemaと似ていますが、注釈値はAvroリーダー・スキーマを含むファイルURIです。相対URIは、クライアントのローカル・ファイル・システムの現行作業ディレクトリに対して解決されます。オプション。

例: %avro:schema-file("schemas/person.avsc")

この注釈は、%avro:schemaまたは%avro:schema-kvと組み合せることはできません。

%avro:schema-kv("schema-name")

%avro:schemaと似ていますが、注釈値は完全修飾されたレコード名です。レコード・スキーマは、Oracle NoSQL Databaseカタログから取得されます。オプション。

例: %avro:schema-kv("org.example.PersonRecord")

この注釈を使用する場合は、Oracle NoSQL Databaseへの接続パラメータを指定する必要があります。「Oracle NoSQL Databaseアダプタの構成プロパティ」を参照してください。

この注釈は、%avro:schemaまたは%avro:schema-fileと組み合せることはできません。

%avro:split-max("split-size")

最大分割サイズを整数または文字列値で指定します。分割サイズによって、入力ファイルがタスクに分割される方法を制御します。Hadoopは、分割サイズをmax($split-min, min($split-max, $block-size))で計算します。オプション。

文字列値の場合、バイト(デフォルトの単位)のかわりに、KkMmGまたはgを値に追加してキロバイト、メガバイトまたはギガバイトを表すことができます。これらの修飾子は大文字と小文字を区別しません。次の例は同等です。

%avro:split-max(1024)
%avro:split-max("1024")
%avro:split-max("1K")
%avro:split-min("split-size")

最小分割サイズを整数または文字列値で指定します。分割サイズによって、入力ファイルがタスクに分割される方法を制御します。Hadoopは、分割サイズをmax($split-min, min($split-max, $block-size))で計算します。オプション。

文字列値の場合、バイト(デフォルトの単位)のかわりに、KkMmGまたはgを値に追加してキロバイト、メガバイトまたはギガバイトを表すことができます。これらの修飾子は大文字と小文字を区別しません。次の例は同等です。

%avro:split-min(1024)
%avro:split-min("1024")
%avro:split-min("1K")

Avroファイルを書き込むカスタム関数

次の注釈を使用して、Avroファイルを書き込む関数を定義できます。

シグネチャ

Avroファイルを書き込むカスタム関数には、次のシグネチャが必要です。

declare %avro:put("avroxml") [additional annotations]
   local:myFunctionName($value as item()) external;

注釈

%avro:put("avroxml")

avroxml put関数を宣言します。必須。

Avroスキーマは、次のいずれかの注釈を使用して指定する必要があります。

  • %avro:schema

  • %avro:schema-file

  • %avro:schema-kv

入力XML値はスキーマのインスタンスに変換されます。「AvroとしてのXMLの書込み」を参照してください。

%avro:schema("avro-schema")

ファイルのスキーマを指定します。次に例を示します。

%avro:schema('
   {
      "type": "record",
      "name": "Person",
      "fields" : [
         {"name": "full_name", "type": "string"},
         {"name": "age", "type": ["int", "null"] }
      ]
   }
')

この注釈は、%avro:schema-fileまたは%avro:schema-kvと組み合せることはできません。

%avro:schema-file("avro-schema-uri")

%avro:schemaと似ていますが、注釈値はAvroリーダー・スキーマを含むファイルURIです。相対URIは、クライアントのローカル・ファイル・システムの現行作業ディレクトリに対して解決されます。

例: %avro:schema-file("schemas/person.avsc")

この注釈は、%avro:schemaまたは%avro:schema-kvと組み合せることはできません。

%avro:schema-kv("schema-name")

%avro:schemaと似ていますが、注釈値は完全修飾されたレコード名です。レコード・スキーマは、Oracle NoSQL Databaseカタログから取得されます。

例: %avro:schema-kv("org.example.PersonRecord")

この注釈を使用する場合は、Oracle NoSQL Databaseへの接続パラメータを指定する必要があります。「Oracle NoSQL Databaseアダプタの構成プロパティ」を参照してください。

この注釈は、%avro:schemaまたは%avro:schema-fileと組み合せることはできません。

%avro:compress("method", [level]?)

出力で使用する圧縮形式を指定します。

codecは、次のいずれかの文字列リテラル値です。

  • deflate: levelは、速度と圧縮の兼合いを制御します。有効な値は1から9で、1は最速で、9は最も圧縮されます。

  • snappy: このアルゴリズムは、高速で中程度の圧縮に設計されています。

デフォルトは圧縮なしです。

levelは整数値です。これはオプションで、codecdeflateの場合のみサポートされています。

次に例を示します。

%avro:compress("snappy")
%avro:compress("deflate")
%avro:compress("deflate", 3)
%avro:file("name")

出力ファイル名の接頭辞を指定します。デフォルトの接頭辞はpartです。

Avroファイル・アダプタの関数の例

この例では、HDFSに次のテキスト・ファイルがあります。

mydata/ages.txt

john,45
kelly,36
laura,
mike,27
例1   テキスト・ファイルからAvroへの変換

次の問合せは、ファイルを圧縮済Avroコンテナ・ファイルに変換します。

import module "oxh:text";
 
declare 
   %avro:put("avroxml")
   %avro:compress("snappy")
   %avro:schema('
      {
         "type": "record",
         "name": "AgeRec",
         "fields" : [
            {"name": "user", "type": "string"},
            {"name": "age", "type": ["int", "null"] }
         ]
      }
   ')
function local:put($arg as item()) external;

for $line in text:collection("mydata/ages.txt")
let $split := fn:tokenize($line, ",")
return
   local:put(
      <rec>
         <user>{$split[1]}</user>
         {
            if ($split[2] castable as xs:int) then
               <age>{$split[2]}</age>
            else 
               ()
         }
      </rec>
   )

この問合せは、次のレコードを含むAvroファイルを生成します(ここではJSONと表されています)。

{"user":"john","age":{"int":45}}
{"user":"kelly","age":{"int":36}}
{"user":"laura","age":null}
{"user":"mike","age":{"int":27}}
例2   Avroコンテナ・ファイル内のレコードの問合せ

次の問合せは、myoutputディレクトリから、ageがnullまたは30を超えるレコードを選択します。レコードは、例1の問合せで生成されています。

import module "oxh:text";
import module "oxh:avro";

for $rec in avro:collection-avroxml("myoutput/part*.avro")
where $rec/age/nilled() or $rec/age gt 30
return
   text:put($rec/user)

この問合せは、次の行を含むファイルを作成します。

john
kelly
laura

AvroとXML間の値の変換について

この項では、Oracle XQuery for HadoopがデータをAvroとXML間で変換する方法について説明します。

XMLとしてのAvroの読取り

Avroファイル・アダプタとOracle NoSQL Databaseアダプタの両方にavroxmlメソッドがあり、このメソッドとcollection関数を使用して、AvroレコードをXMLとして読み取ることができます。AvroをXMLに変換した後、XQueryを使用してデータを問い合せて変換できます。

次のトピックでは、Oracle XQuery for HadoopがAvroを読み取る方法について説明します。

レコードの読取り

Avroレコードは、レコード内のフィールドごとに1つの子要素を持つ<oxh:item>要素に変換されます。

たとえば、次のAvroスキーマを考えてみます。

{
   "type": "record",
   "name": "Person",
   "fields" : [
      {"name": "full_name", "type": "string"},
      {"name": "age", "type": ["int", "null"] }
   ]
}

これは、XMLとしてモデル化されたレコードのインスタンスです。

<oxh:item>
   <full_name>John Doe</full_name>
   <age>46</age>
</oxh:item>

AvroレコードをXMLに変換することによって、XQueryを使用してレコードを問い合せることができます。次の例は、Personレコードを含むperson.avroというAvroコンテナ・ファイルを問い合せます。この問合せは、各行にfull_name値とage値を含むCSVテキスト・ファイルにレコードを変換します。

import module "oxh:avro";
import module "oxh:text";
 
for $x in avro:collection-avroxml("person.avro")
return
   text:put($x/full_name || "," || $x/age)
 

null値はnilled要素に変換されます。nilled要素によってxsi:nil属性がtrueに設定されるため、常に空になります。XQueryのfn:nilled関数を使用して、レコード・フィールドがnullかどうかをテストできます。たとえば、次の問合せは、ageがnull値のPersonレコードの名前を書き込みます。

import module "oxh:avro";
import module "oxh:text";
 
for $x in avro:collection-avroxml("person.avro")
where $x/age/nilled()
return
   text:put($x/full_name)
 

ネストされたレコードの場合、内部スキーマのフィールドは、外部スキーマのフィールドに対応する要素の子要素になります。たとえば、このスキーマにはネストされたレコードがあります。

{
   "type": "record", 
   "name": "PersonAddress",
   "fields" : [
      {"name": "full_name", "type": "string"},
      {"name": "address", "type": 
         { "type" : "record",
           "name" : "Address",
           "fields" : [
              { "name" : "street", "type" : "string" },
              { "name" : "city", "type" : "string" }
            ]
         }
      }
   ]
}

これは、XMLとしてのレコードのインスタンスです。

<oxh:item>
   <full_name>John Doe</full_name>
   <address>
      <street>123 First St.</street>
      <city>New York</city>
   </address>
</oxh:item>

次の例は、PersonAddressレコードを含むpeople-address.avroという名前のAvroコンテナ・ファイルを問い合せて、ニューヨーク在住の個人の名前をテキスト・ファイルに書き込みます。

import module "oxh:avro";
import module "oxh:text";

for $person in avro:collection-avroxml("examples/person-address.avro")
where $person/address/city eq "New York"
return
   text:put($person/full_name)

マップの読取り

Avroマップ値は、マップ内のエントリごとに1つの子<oxh:entry>要素を含む要素に変換されます。たとえば、次のスキーマを考えてみます。

{
   "type": "record", 
   "name": "PersonProperties",
   "fields" : [
      {"name": "full_name", "type": "string"},
      {"name": "properties", "type": 
         {"type": "map", "values": "string"}
      }
   ]
}

これは、XMLとしてのスキーマのインスタンスです。

<oxh:item>
   <full_name>John Doe</full_name>
   <properties>
      <oxh:entry key="employer">Oracle</oxh:entry>
      <oxh:entry key="hair color">brown</oxh:entry>
      <oxh:entry key="favorite author">George RR Martin</oxh:entry>
   </properties>
</oxh:item>

次の例は、PersonAddressレコードを含むperson-properties.avroという名前のファイルを問い合せて、Oracleに雇用されている個人の名前を書き込みます。この問合せは、正規のXPath式によってマップ・エントリを取得する方法を示しています。さらに、avro:get関数をショートカットとして使用して、マップ・エントリを取得することもできます。

import module "oxh:avro";
import module "oxh:text";
 
for $person in avro:collection-avroxml("person-properties.avro")
where $person/properties/oxh:entry[@key eq "employer"] eq "Oracle"
return
   text:put($person/full_name)
 

次の問合せは、avro:get関数を使用して、employerエントリを取得します。これは、前の問合せと同等です。

import module "oxh:avro";
import module "oxh:text";
 
for $person in avro:collection-avroxml("person-properties.avro")
where $person/properties/avro:get("employer") eq "Oracle"
return
   text:put($person/full_name)

XQueryのfn:nilled関数を使用して、null値をテストできます。次の例は、マップ・エントリがnullの場合にtrueを返します。

$var/avro:get("key")/nilled()

配列の読取り

Oracle XQuery for Hadoopは、Avro配列値を、配列内の項目ごとに子<oxh:item>要素を含む要素に変換します。たとえば、次のスキーマを考えてみます。

{
   "type": "record", 
   "name": "PersonScores",
   "fields" : [
      {"name": "full_name", "type": "string"},
      {"name": "scores", "type": 
         {"type": "array", "items": "int"}
      }
   ]
}

これは、XMLとしてのスキーマのインスタンスです。

<oxh:item>
   <full_name>John Doe</full_name>
   <scores>
      <oxh:item>128</oxh:item>
      <oxh:item>151</oxh:item>
      <oxh:item>110</oxh:item>
   </scores>
</oxh:item>

次の例は、PersonScoresレコードを含むperson-scores.avroという名前のファイルを問い合せて、個人ごとのスコアの合計と件数を書き込みます。

import module "oxh:avro";
import module "oxh:text";
 
for $person in avro:collection-avroxml("person-scores.avro")
let $scores := $person/scores/*
return
   text:put($person/full_name || "," || sum($scores) || "," || count($scores))

数値のXPath述語を使用すると、配列の特定の要素にアクセスできます。たとえば、このパス式は2番目のスコアを選択します。XPath索引は(0ではなく)1から始まります。

$person/scores/oxh:item[2]

共用体の読取り

Oracle XQuery for Hadoopは、値の実際のメンバー・タイプに基づいて、Avro共用体タイプのインスタンスを変換します。メンバー・タイプの名前はXML avro:type属性として包含する要素に追加されるため、問合せでは異なるメンバー・タイプのインスタンスを識別できます。ただし、メンバー・タイプが2つのみで、そのいずれかがnullの単純な共用体の場合、属性は追加されません。

たとえば、2つのレコードの次の共用体を考えてみます。

[
   {
      "type": "record", 
      "name": "Person1",
      "fields" : [
         {"name": "full_name", "type": "string"}
      ]
   }
   ,
   {
      "type": "record", 
      "name": "Person2",
      "fields" : [
         {"name": "fname", "type": "string"}
      ]
   }
]
 

これは、XMLとしてのスキーマのインスタンスです。

<oxh:item avro:type="Person2">
   <fname>John Doe</fname>
</oxh:item>

次の例は、前の共用体スキーマのインスタンスを含むperson-union.avroという名前のファイルを問い合せて、両方のレコード・タイプから個人の名前をテキスト・ファイルに書き込みます。

import module "oxh:avro";
import module "oxh:text";

for $person in avro:collection-avroxml("examples/person-union.avro")
return
   if ($person/@avro:type eq "Person1") then
      text:put($person/full_name)
   else if ($person/@avro:type eq "Person2") then
      text:put($person/fname)
   else
      error(xs:QName("UNEXPECTED"), "Unexpected record type:" || $person/@avro:type)

プリミティブの読取り

表6-1に、Oracle XQuery for HadoopがAvroプリミティブ・タイプをXQueryアトミック・タイプにマップする方法を示します。

表6-1 Avroプリミティブ・タイプからXQueryアトミック・タイプへのマッピング

Avro XQuery

boolean

xs:boolean

int

xs:int

long

xs:long

float

xs:float

double

xs:double

bytes

xs:hexBinary

string

xs:string


Avro null値は空のnilled要素にマップされます。nullの文字列値と空の文字列値を識別するには、XQueryのnilled関数を使用します。このパス式は、フィールド値がnullの場合のみtrueを返します。

$record/field/nilled()

Avro固定値はxs:hexBinaryにマップされ、enumsxs:stringにマップされます。


AvroとしてのXMLの書込み

Avroファイル・アダプタとOracle NoSQL Databaseアダプタの両方にavroxmlメソッドがあり、このメソッドとput関数を使用してXMLをAvroとして書き込むことができます。次のトピックでは、XMLをAvroインスタンスに変換する方法について説明します。

レコードの書込み

Oracle XQuery for Hadoopは、子要素名とレコードのフィールド名を照合してXMLをAvroレコード・スキーマにマップします。たとえば、次のAvroスキーマを考えてみます。

{
   "type": "record",
   "name": "Person",
   "fields" : [
      {"name": "full_name", "type": "string"},
      {"name": "age", "type": ["int", "null"] }
   ]
}

次のXML要素を使用して、full_nameフィールドがJohn Doeでageフィールドが46のレコードのインスタンスを書き込むことができます。ルート要素の名前(Person)は重要ではありません。子要素の名前のみを使用して、Avroレコード・フィールド(full_nameおよびage)にマップされます。

<person>
   <full_name>John Doe</full_name>
   <age>46</age>
</person>

次の例では、people.csvという名前の次のCSVファイルを使用します。

John Doe,46
Jane Doe,37
     .
     .
     .
 

この問合せは、CSVファイルからAvro Personレコードに値を変換します。

import module "oxh:avro";
import module "oxh:text";
 
declare 
   %avro:put("avroxml")
   %avro:schema('
      {
         "type": "record", 
         "name": "Person",
         "fields" : [
            {"name": "full_name", "type": "string"},
            {"name": "age", "type": ["int", "null"] }
         ]
      }
   ')
function local:put-person($person as element()) external;

for $line in text:collection("people.csv")
let $split := tokenize($line, ",")
return
   local:put-person(
      <person>
         <full_name>{$split[1]}</full_name>
         <age>{$split[2]}</age>
      </person>
   )
 

null値の場合は、要素を省略するか、xsi:nil="true"属性を設定できます。たとえば、この変更した問合せは、値が数値でない場合にageをnullに設定します。

   .
   .
   .
for $line in text:collection("people.csv")
let $split := tokenize($line, ",")
return
   local:put-person(
      <person>
         <full_name>{$split[1]}</full_name>
         {
         if ($split[2] castable as xs:int) then
            <age>{$split[2]}</age>
         else 
            ()
         }
      </person>
   )

ネストされたレコードの場合、値はネストされた要素から取得されます。次の例では、次のスキーマを使用します。

{
"type": "record", 
"name": "PersonAddress",
"fields" : [
   {"name": "full_name", "type": "string"},
   {"name": "address", "type": 
      { "type" : "record",
        "name" : "Address",
        "fields" : [
           { "name" : "street", "type" : "string" },
           { "name" : "city", "type" : "string" }
        ]
      }
   }
]
}

次のXMLを使用して、このレコードのインスタンスを書き込むことができます。

<person>
   <full_name>John Doe</full_name>
   <address>
      <street>123 First St.</street>
      <city>New York</city>
   </address>
</person>

マップの書込み

Oracle XQuery for Hadoopは、XMLを、<oxh:entry>子要素ごとに1つのマップ・エントリを持つAvroマップに変換します。たとえば、次のスキーマを考えてみます。

{
   "type": "record", 
   "name": "PersonProperties",
   "fields" : [
      {"name": "full_name", "type": "string"},
      {"name": "properties", "type": 
         {"type": "map", "values": "string"}
      }
   ]
}

次のXML要素を使用して、full_nameフィールドがJohn Doeでpropertiesフィールドが3つのエントリを持つマップに設定されたスキーマのインスタンスを書き込むことができます。

<person>
   <full_name>John Doe</full_name>
   <properties>
      <oxh:entry key="hair color">brown</oxh:entry>
      <oxh:entry key="favorite author">George RR Martin</oxh:entry>
      <oxh:entry key="employer">Oracle</oxh:entry>
   </properties>
</person>

配列の書込み

Oracle XQuery for Hadoopは、XMLを、<oxh:item>子要素ごとに1つの項目を持つAvro配列に変換します。たとえば、次のスキーマを考えてみます。

{
   "type": "record", 
   "name": "PersonScores",
   "fields" : [
      {"name": "full_name", "type": "string"},
      {"name": "scores", "type": 
         {"type": "array", "items": "int"}
      }
   ]
}

次のXML要素を使用して、full_nameフィールドがJohn Doeでscoresフィールドが[128、151、110]に設定されたスキーマのインスタンスを書き込むことができます。

<person>
   <full_name>John Doe</full_name>
   <scores>
      <oxh:item>128</oxh:item>
      <oxh:item>151</oxh:item>
      <oxh:item>110</oxh:item>
   </scores>
</person>

共用体の書込み

Avro共用体タイプを書き込むとき、Oracle XQuery for Hadoopはavro:type属性の値に基づいてメンバー・タイプを選択します。

この例では、次のスキーマを使用します。

[
   {
      "type": "record", 
      "name": "Person1",
      "fields" : [
         {"name": "full_name", "type": "string"}
      ]
   }
   ,
   {
      "type": "record", 
      "name": "Person2",
      "fields" : [
         {"name": "fname", "type": "string"}
      ]
   }
]

次のXMLは、Person1レコードのインスタンスにマップされます。

<person avro:type="Person1">
   <full_name>John Doe</full_name>
</person>

このXMLは、Person2レコードのインスタンスにマップされます。

<person avro:type="Person2">
   <fname>John Doe</fname>
</person>

avro:type属性では、共用体のメンバー・タイプを選択します。nullと他のタイプを1つずつ含む単純な共用体の場合、avro:type属性は不要です。メンバー・タイプが判別できない場合は、エラーが発生します。


プリミティブの書込み

プリミティブ値をマップするために、Oracle XQuery for Hadoopは表6-1に示す同等のデータ型を使用して、XML値を対応するAvroタイプにキャストします。値をAvroタイプに変換できない場合は、エラーが発生します。

この例では、次のスキーマを使用します。

{
   "type": "record",
   "name": "Person",
   "fields" : [
      {"name": "full_name", "type": "string"},
      {"name": "age", "type": ["int", "null"] }
   ]
}

stringappleintに変換できないため、次のXMLをこのスキーマのインスタンスにマップしようとするとエラーが発生します。

<person>
   <full_name>John Doe</full_name>
   <age>apple</age>
</person>