XMLファイル・アダプタは、HDFSに格納されたXMLファイルへのアクセスを提供します。アダプタは必要に応じて個々のXMLファイルを分割するため、1つのファイルを複数のタスクで並列で処理できます。
次のトピックでは、このアダプタについて説明します。
問合せで組込み関数を使用するには、次のようにXMLファイル・モジュールをインポートする必要があります。
import module "oxh:xmlf";
XMLファイル・モジュールには、次の関数が含まれています。
「XMLファイル・アダプタの関数の例」を参照してください。
HDFS内のXMLドキュメントのコレクションにアクセスします。複数のファイルを同時に処理できますが、個々のファイルは1つのタスクによって解析されます。
Hadoopでサポートされているコーデックを使用して圧縮されたファイルは、この関数によって自動的に解凍されます。
|
注意: データが多数の小さいファイルに格納されている場合、HDFSは適切に実行されません。多数の小さいXMLドキュメントを含む大規模なデータセットの場合は、Hadoop順序ファイルおよび順序ファイル・アダプタを使用します。 |
シグネチャ
declare %xmlf:collection function xmlf:collection($uris as xs:string*) as document-node()* external;
パラメータ
$uris: XMLファイルのURI
戻り値
各ファイルに対して1つのXMLドキュメント
HDFS内のXMLドキュメントのコレクションにアクセスします。ファイルを分割して、複数のタスクで同時に処理できるため、非常に大きなXMLファイルを効率的に処理できます。この関数は、指定した名前に一致する要素のみを返します。
この関数によってファイルは自動的に解凍されません。特定の要件を満たすXMLファイルのみをサポートしています。「XMLファイルを分割する際の制限」を参照してください。
シグネチャ
declare %xmlf:collection function xmlf:collection($uris as xs:string*, $names as xs:anyAtomicType+) as element()* external;
次の注釈を使用して、HDFS内のXMLファイルのコレクションを読み取る関数を定義できます。これらの注釈によって、組込み関数では使用できない追加機能を使用できます。
シグネチャ
XMLファイルを読み取るカスタム関数には、次のいずれかのシグネチャが必要です。
declare %xmlf:collection [additional annotations] function local:myFunctionName($uris as xs:string*) as node()* external; declare %xmlf:collection [additional annotations] function local:myFunctionName($uris as xs:string*, $names as xs:anyAtomicType+) as element()* external;
注釈
collection関数を宣言します。この注釈はパラメータを取りません。必須。
並列のXML解析で使用する要素名を指定します。この注釈は、$names引数のかわりに使用できます。
この注釈を指定すると、1つの引数を取る関数のみ使用できます。この制限によって、要素名を静的に指定できるため、関数の呼出し時に要素名を指定する必要がありません。
入力ドキュメントのテキスト・エンコードを識別します。
このエンコードを%xmlf:split注釈または$names引数とともに使用する場合、有効なエンコードはISO-8859-1、US-ASCIIおよびUTF-8のみです。そうでない場合、有効なエンコードは、JVMでサポートされているエンコードです。この注釈を省略すると、UTF-8とみなされます。
|
関連項目: 次のサイトにあるOracle Java SEドキュメントのサポートされているエンコードに関する項
|
この注釈は、追加のネームスペース宣言をパーサーに提供します。これを複数回指定して、1つ以上のネームスペースを宣言できます。
この注釈を使用して、祖先要素のネームスペースを宣言します。XMLが並列で処理される場合は、指定した名前に一致する要素のみがXMLパーサーによって処理されます。一致する要素が祖先要素の1つのネームスペース宣言に依存する場合、パーサーは宣言を識別できず、エラーが発生する場合があります。
これらのネームスペース宣言は、分割名を指定するときに要素名でも使用できます。次に例を示します。
declare
%xmlf:collection
%xmlf:split("eg:foo")
%xmlf:split-namespace("eg", "http://example.org")
function local:myFunction($uris as xs:string*) as document-node() external;
エンティティ定義をXMLパーサーに提供します。XMLが並列で処理される場合は、指定した分割名に一致する要素のみがXMLパーサーによって処理されます。分割されて並列で処理される入力ドキュメントのDTDは処理されません。
この例では、XMLパーサーが&foo;エンティティ参照を"Hello World"として拡張します。
%xmlf:split-entity("foo","Hello World")
最大分割サイズを整数または文字列値で指定します。分割サイズによって、入力ファイルがタスクに分割される方法を制御します。Hadoopは、分割サイズをmax($split-min, min($split-max, $block-size))で計算します。オプション。
文字列値の場合、バイト(デフォルトの単位)のかわりに、K、k、M、m、Gまたはgを値に追加してキロバイト、メガバイトまたはギガバイトを表すことができます。これらの修飾子は大文字と小文字を区別しません。次の例は同等です。
%xmlf:split-max(1024)
%xmlf:split-max("1024")
%xmlf:split-max("1K")
最小分割サイズを整数または文字列値で指定します。分割サイズによって、入力ファイルがタスクに分割される方法を制御します。Hadoopは、分割サイズをmax($split-min, min($split-max, $block-size))で計算します。オプション。
文字列値の場合、バイト(デフォルトの単位)のかわりに、K、k、M、m、Gまたはgを値に追加してキロバイト、メガバイトまたはギガバイトを表すことができます。これらの修飾子は大文字と小文字を区別しません。次の例は同等です。
%xmlf:split-min(1024)
%xmlf:split-min("1024")
%xmlf:split-min("1K")
注意
$names引数または$xmlf:split注釈を使用して要素名が指定されている場合は、各XMLドキュメントを並列で処理できます。
並列で処理するには、入力ドキュメントが次の制約を満たしている必要があります。
XMLには、コメント、CDATAセクション、または指定した要素名(つまり、<文字の後にQNameに拡張する名前が続く)の1つに一致するテキストを含む処理手順を含めることができません。そうすると、内容は要素として正しく解析されません。
指定した要素名に一致するファイル内の要素には、指定した名前に一致する子孫要素を含めることができません。そうすると、複数のプロセッサが一致する子孫を選択でき、関数は正しい結果を生成できません。
指定した要素名(およびその子孫すべて)の1つに一致する要素は、そのいずれかの祖先のネームスペース宣言に依存できません。一致する要素の祖先は解析されないため、これらの要素のネームスペース宣言は処理されません。
この制限を回避するには、%xmlf:split-namespace注釈を使用してネームスペース宣言を手動で指定します。
指定した要素名は、分割サイズより大きいファイル内の要素と照合しないことをお薦めします。それを行うと、アダプタは正しく機能しますが、効率が悪くなります。
解析はXMLファイルの途中から開始できないため、XMLを並列で処理するのは困難です。XML構造体(CDATAセクション、コメント、ネームスペース宣言など)によって、この制限が発生します。XMLドキュメントの途中から開始するパーサーは、ドキュメントの先頭に向かって逆方向検索してCDATAセクションまたはコメント内にいないことを確認せずに、たとえば、文字列<foo>が開始要素タグであるとみなすことはできません。ただし、通常、大規模なXMLドキュメントには同様に構造化された一連の要素が含まれるため、並列処理の対象になります。要素名を指定すると、各タスクは、指定した名前の1つに一致する要素についてドキュメントの一部をスキャンして実行されます。指定した名前に一致する要素のみ、実際のXMLパーサーに渡されます。したがって、並列プロセッサでは、ドキュメント全体の真の意味での解析は実行されません。
この例は、次の内容でHDFS内の3つのXMLファイルを問い合せます。各XMLファイルには、特定の日にユーザーが記入したコメントが含まれています。各コメントには、他のユーザーからのlikeが1個以上あるか、likeがない場合もあります。
mydata/comments1.xml
<comments date="2013-12-30">
<comment id="12345" user="john" text="It is raining :( "/>
<comment id="56789" user="kelly" text="I won the lottery!">
<like user="john"/>
<like user="mike"/>
</comment>
</comments>
mydata/comments2.xml
<comments date="2013-12-31">
<comment id="54321" user="mike" text="Happy New Year!">
<like user="laura"/>
</comment>
</comments>
mydata/comments3.xml
<comments date="2014-01-01">
<comment id="87654" user="mike" text="I don't feel so good."/>
<comment id="23456" user="john" text="What a beautiful day!">
<like user="kelly"/>
<like user="phil"/>
</comment>
</comments>
この問合せは、各年に記入されたコメントの数をテキスト・ファイルに書き込みます。xmlf:collectionに渡される要素名はないため、3つのドキュメント(各ファイルに1つ)を返します。各ファイルは、単一のタスクによって順次処理されます。
import module "oxh:xmlf";
import module "oxh:text";
for $comments in xmlf:collection("mydata/comments*.xml")/comments
let $date := xs:date($comments/@date)
group by $year := fn:year-from-date($date)
return
text:put($year || ", " || fn:count($comments/comment))
この問合せは、次の行を含むテキスト・ファイルを作成します。
2013, 3 2014, 2
次の問合せでは、ユーザーごとのコメント数およびlikeの平均数を書き込みます。各入力ファイルを分割して、複数のタスクで並列で処理できます。xmlf:collection関数は、5つの要素(各コメントに1つ)を返します。
import module "oxh:xmlf";
import module "oxh:text";
for $comment in xmlf:collection("mydata/comments*.xml", "comment")
let $likeCt := fn:count($comment/like)
group by $user := $comment/@user
return
text:put($user || ", " || fn:count($comment) || ", " || fn:avg($likeCt))
この問合せは、次の行を含むテキスト・ファイルを作成します。
john, 2, 1 kelly, 1, 2 mike, 2, 0.5
次の例は、XMLファイルにアクセスするカスタム関数を宣言します。
import module "oxh:text";
declare
%xmlf:collection
%xmlf:split("comment")
%xmlf:split-max("32M")
function local:comments($uris as xs:string*) as element()* external;
for $c in local:comments("mydata/comment*.xml")
where $c/@user eq "mike"
return text:put($c/@id)
この問合せは、次の行を含むテキスト・ファイルを作成します。
54321 87654