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>