Avroファイル・アダプタは、HDFS内のAvroコンテナ・ファイルの読取りおよび書込みを実行する関数を提供します。この項の内容は次のとおりです。
問合せで組込み関数を使用するには、次のようにAvroファイル・モジュールをインポートする必要があります。
import module "oxh:avro";
Avroファイル・モジュールには、次の関数が含まれています。
Avroコンテナ・ファイルを書き込む組込み関数はありません。Avroファイルを書き込むには、Avroライター・スキーマを指定するカスタム関数を使用する必要があります。
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要素。
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マップです。「マップの読取り」を参照してください。
次の注釈を使用して、HDFS内のAvroコンテナ・ファイルのコレクションを読み取る関数を定義できます。これらの注釈によって、組込み関数では使用できない追加機能を使用できます。
シグネチャ
Avroファイルを読み取るカスタム関数には、次のシグネチャが必要です。
declare %avro:collection("avroxml") [additional annotations]
function local:myFunctionName($uris as xs:string*) as element()* external;
avroxml collection関数を宣言します。必須。
collection関数によって、HDFS内のAvroファイルにアクセスします。ファイルを分割して、複数のタスクで並列で処理できます。関数は、各オブジェクトのXML要素を返します。「AvroとXML間の値の変換について」を参照してください。
注釈の値として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』のスキーマ解決に関する項
|
%avro:schemaと似ていますが、注釈値はAvroリーダー・スキーマを含むファイルURIです。相対URIは、クライアントのローカル・ファイル・システムの現行作業ディレクトリに対して解決されます。オプション。
例: %avro:schema-file("schemas/person.avsc")。
この注釈は、%avro:schemaまたは%avro:schema-kvと組み合せることはできません。
%avro:schemaと似ていますが、注釈値は完全修飾されたレコード名です。レコード・スキーマは、Oracle NoSQL Databaseカタログから取得されます。オプション。
例: %avro:schema-kv("org.example.PersonRecord")。
この注釈を使用する場合は、Oracle NoSQL Databaseへの接続パラメータを指定する必要があります。「Oracle NoSQL Databaseアダプタの構成プロパティ」を参照してください。
この注釈は、%avro:schemaまたは%avro:schema-fileと組み合せることはできません。
最大分割サイズを整数または文字列値で指定します。分割サイズによって、入力ファイルがタスクに分割される方法を制御します。Hadoopは、分割サイズをmax($split-min, min($split-max, $block-size))で計算します。オプション。
文字列値の場合、バイト(デフォルトの単位)のかわりに、K、k、M、m、Gまたはgを値に追加してキロバイト、メガバイトまたはギガバイトを表すことができます。これらの修飾子は大文字と小文字を区別しません。次の例は同等です。
%avro:split-max(1024)
%avro:split-max("1024")
%avro:split-max("1K")
最小分割サイズを整数または文字列値で指定します。分割サイズによって、入力ファイルがタスクに分割される方法を制御します。Hadoopは、分割サイズをmax($split-min, min($split-max, $block-size))で計算します。オプション。
文字列値の場合、バイト(デフォルトの単位)のかわりに、K、k、M、m、Gまたはgを値に追加してキロバイト、メガバイトまたはギガバイトを表すことができます。これらの修飾子は大文字と小文字を区別しません。次の例は同等です。
%avro:split-min(1024)
%avro:split-min("1024")
%avro:split-min("1K")
次の注釈を使用して、Avroファイルを書き込む関数を定義できます。
シグネチャ
Avroファイルを書き込むカスタム関数には、次のシグネチャが必要です。
declare %avro:put("avroxml") [additional annotations]
local:myFunctionName($value as item()) external;
注釈
avroxml put関数を宣言します。必須。
Avroスキーマは、次のいずれかの注釈を使用して指定する必要があります。
%avro:schema
%avro:schema-file
%avro:schema-kv
入力XML値はスキーマのインスタンスに変換されます。「AvroとしてのXMLの書込み」を参照してください。
ファイルのスキーマを指定します。次に例を示します。
%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と似ていますが、注釈値はAvroリーダー・スキーマを含むファイルURIです。相対URIは、クライアントのローカル・ファイル・システムの現行作業ディレクトリに対して解決されます。
例: %avro:schema-file("schemas/person.avsc")
この注釈は、%avro:schemaまたは%avro:schema-kvと組み合せることはできません。
%avro:schemaと似ていますが、注釈値は完全修飾されたレコード名です。レコード・スキーマは、Oracle NoSQL Databaseカタログから取得されます。
例: %avro:schema-kv("org.example.PersonRecord")
この注釈を使用する場合は、Oracle NoSQL Databaseへの接続パラメータを指定する必要があります。「Oracle NoSQL Databaseアダプタの構成プロパティ」を参照してください。
この注釈は、%avro:schemaまたは%avro:schema-fileと組み合せることはできません。
codecは、次のいずれかの文字列リテラル値です。
deflate: levelは、速度と圧縮の兼合いを制御します。有効な値は1から9で、1は最速で、9は最も圧縮されます。
snappy: このアルゴリズムは、高速で中程度の圧縮に設計されています。
デフォルトは圧縮なしです。
levelは整数値です。これはオプションで、codecがdeflateの場合のみサポートされています。
次に例を示します。
%avro:compress("snappy")
%avro:compress("deflate")
%avro:compress("deflate", 3)
出力ファイル名の接頭辞を指定します。デフォルトの接頭辞はpartです。
次の各例では、HDFS内の次のテキスト・ファイルを使用します。
mydata/ages.txt john,45 kelly,36 laura, mike,27
次の問合せは、ファイルを圧縮済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}}
次の問合せは、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
この項では、Oracle XQuery for HadoopがデータをAvroとXML間で変換する方法について説明します。
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">Example Inc</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という名前のファイルを問い合せて、Example Inc.社に雇用された人の名前を書き込みます。この問合せは、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 "Example Inc"
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 "Example Inc"
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 |
|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Avro null値は空のnilled要素にマップされます。nullの文字列値と空の文字列値を識別するには、XQueryのnilled関数を使用します。このパス式は、フィールド値がnullの場合のみtrueを返します。
$record/field/fn:nilled()
Avro固定値はxs:hexBinaryにマップされ、enumsはxs:stringにマップされます。
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">Example Inc</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"] }
]
}
string値appleをintに変換できないため、次のXMLをこのスキーマのインスタンスにマップしようとするとエラーが発生します。
<person> <full_name>John Doe</full_name> <age>apple</age> </person>