web-dev-qa-db-ja.com

ハイブregexp_extractの奇妙さ

Regexp_extractで問題が発生しています。

タブ区切りファイルでクエリを実行しています。チェックしている列には、次のような文字列があります。

abc.def.ghi

今、私がするなら:

select distinct regexp_extract(name, '[^.]+', 0) from dummy;

MRジョブが実行され、機能し、インデックス0から「abc」を取得します。

しかし今、インデックス1から "def"を取得したい場合は、次のようにします。

select distinct regexp_extract(name, '[^.]+', 1) from dummy;

Hiveが失敗する:

2011-12-13 23:17:08,132 Stage-1 map = 0%,  reduce = 0%
2011-12-13 23:17:28,265 Stage-1 map = 100%,  reduce = 100%
Ended Job = job_201112071152_0071 with errors
FAILED: Execution Error, return code 2 from org.Apache.hadoop.Hive.ql.exec.MapRedTask

ログファイルは言う:

Java.lang.RuntimeException: org.Apache.hadoop.Hive.ql.metadata.HiveException: Hive Runtime Error while processing row

ここで根本的に間違っていることをしていますか?

ありがとう、マリオ

11
Mario

ドキュメントから https://cwiki.Apache.org/confluence/display/Hive/LanguageManual+UDF regexp_extract()は、抽出するデータのレコード/行抽出であるように見えます。

グローバルではなく、最初に見つかった(その後終了した)場合に機能するようです。したがって、インデックスはキャプチャグループを参照します。

0 =完全一致
1 =キャプチャグループ1
2 =キャプチャグループ2など...

マニュアルから言い換えると:

_regexp_extract('foothebar', 'foo(.*?)(bar)', 2)
                                  ^    ^   
               groups             1    2

This returns 'bar'.
_

したがって、あなたの場合、ドットの後にテキストを取得するには、次のようなものが機能する可能性があります。
regexp_extract(name, '\.([^.]+)', 1)
またはこれ
regexp_extract(name, '[.]([^.]+)', 1)

edit

私はこれに再び興味を持ちました、ただのファイです、あなたのためのショートカット/回避策があるかもしれません。

ドットの_._文字で区切られた特定のセグメントが必要であるように見えます。これはほとんど分割のようなものです。
複数回定量化される場合、使用される正規表現エンジンがグループを上書きする可能性が高いです。
次のようなものでそれを利用できます:

最初のセグメントを返します:abc。def.ghi
regexp_extract(name, '^(?:([^.]+)\.?){1}', 1)

2番目のセグメントを返します:abc .def。ghi
regexp_extract(name, '^(?:([^.]+)\.?){2}', 1)

3番目のセグメントを返します:abc.def .ghi
regexp_extract(name, '^(?:([^.]+)\.?){3}', 1)

インデックスは変更されません(インデックスはまだキャプチャグループ1を参照しているため)。正規表現の繰り返しのみが変更されます。

いくつかのメモ:

  • ただし、この正規表現^(?:([^.]+)\.?){n}には問題があります。
    セグメント内のドットの間に何かがあるか、正規表現が_..._と一致しないことが必要です。

  • これは^(?:([^.]*)\.?){n}の可能性がありますが、n-1ドット未満の場合でも一致します。
    空の文字列を含みます。これはおそらく望ましくありません。

ドットの間にテキストを必要としないが、少なくともn-1ドットが必要な場合にそれを行う方法があります。
これは、先読みアサーションとキャプチャバッファ2をフラグとして使用します。

^(?:(?!\2)([^.]*)(?:\.|$())){2}、それ以外はすべて同じです。

したがって、Javaスタイルの正規表現を使用している場合、これは機能するはずです。
regexp_extract(name, '^(?:(?!\2)([^.]*)(?:\.|$())){2}', 1) {2}を必要な「セグメント」に変更します(これによりセグメント2が実行されます)。

また、{N}番目の反復後もキャプチャバッファ1を返します。

ここで分解されます

_^                # Begining of string
 (?:             # Grouping
    (?!\2)            # Assertion: Capture buffer 2 is UNDEFINED
    ( [^.]*)          # Capture buffer 1, optional non-dot chars, many times
    (?:               # Grouping
        \.                # Dot character
      |                 # or,
        $ ()              # End of string, set capture buffer 2 DEFINED (prevents recursion when end of string)
    )                 # End grouping
 ){3}            # End grouping, repeat group exactly 3 (or N) times (overwrites capture buffer 1 each time)
_

アサーションを行わない場合、これは機能しません。

33
user557597

「グループ」を作らないといけないと思いますか?

select distinct regexp_extract(name, '([^.]+)', 1) from dummy;

(未試験)

私はそれがJavaライブラリのように動作すると思います、そしてこれはうまくいくはずですが、私に知らせてください。

1