7.14. 正規表現

7.14.1. 概要

注釈

正規表現は実験的な機能です。

バージョン 5.0.1 で追加.

Groongaは正規表現を用いたパターンマッチをサポートしています。正規表現はパターンを表現するために広く使われています。正規表現は複雑なパターンを表現するときに便利です。

多くの場合は、正規表現検索は逐次検索で実行します。これはたくさんのレコードがあったり、たくさんのテキストがある場合は遅くなります。

いくつかの場合では、正規表現を使ったパターンマッチをインデックスを使って実現します。これは逐次検索よりも非常に高速です。インデックスを使って評価できるパターンについては後述します。

バージョン 5.0.7 で追加: Groongaは正規表現検索にインデックスを使わないときは、 NormalizerAuto ノーマライザーでマッチ対象のテキストを正規化します。これは、 Groonga というような大文字を使った正規表現は必ずマッチに失敗するということです。なぜなら、 NormalizerAuto ノーマライザーはすべてのアルファベットを小文字に正規化するからです。 groongaGroonga にも groonga にも両方にマッチします。

なぜマッチ対象のテキストを正規化するのでしょうか?それは、インデックスを使って検索できるパターンを増やすためです。もし、Groongaがマッチ対象のテキストを正規化しなかった場合、大文字小文字を区別しないマッチをするために、 [Dd][Ii][Ss][Kk](?i)disk のような複雑な正規表現を書く必要があります。Groongaは複雑な正規表現に対してインデックスを使うことができません。

もし、大文字小文字を区別しないマッチに disk という正規表現を使うなら、Groongaはインデックスを使ってこのパターンを検索できます。これは高速です。

全文検索では通常、Groongaは検索キーワードを語彙表に指定されているノーマライザーを使って正規化します。正規表現を使った検索では、Groongaは、検索キーワードを正規化しません。これは、正規表現は大文字と小文字に意味があるためです。

したがって、インデックスを使えない正規表現検索では、 normalize コマンドで検索前に検索キーワードを正規化することをおすすめします。normalize コマンドを使うことで、検索キーワードをどのように正規化するか考る必要がなくなります。

この挙動を奇妙に思うかもしれません。しかし、この挙動のおかげで高速に検索できることはきっとあなたの役に立つはずです。

正規表現の構文はたくさんあります。GroongaはRubyと同じ構文を使います。なぜなら、Groongaが使っている正規表現エンジンはRubyが使っている正規表現エンジンと同じだからです。この正規表現エンジンは Onigmo といいます。他の正規表現の構文との特徴的な違いは ^$ です。Rubyの正規表現の構文では ^ は行頭を表し、 $ は行末を表します。他の多くの正規表現の構文では、 ^ はテキストの先頭を表し、 $ はテキストの最後を表します。Rubyの正規表現の構文ではテキストの先頭を表す場合は \A を使い、テキストの最後を表す場合は \z を使います。

バージョン 5.0.6 で追加: Groongaは5.0.6からマルチラインモードを有効にしています。これは、 .\n にマッチするということです。

しかし、この挙動は意味がありません。なぜなら、 \nNormalizerAuto ノーマライザーが削除するからです。

select コマンドの query オプションと filter オプションで正規表現を使えます。

7.14.2. 使い方

以下は使い方を説明するためのスキーマ定義とサンプルデータです。このスキーマにはテーブルは1つだけです。 Logs というテーブルです。 Logs テーブルは1つだけカラムを持ちます。 message というカラムです。ログメッセージは message カラムに保存されています。

実行例:

table_create Logs TABLE_NO_KEY
# [[0, 1337566253.89858, 0.000355720520019531], true]
column_create Logs message COLUMN_SCALAR Text
# [[0, 1337566253.89858, 0.000355720520019531], true]
load --table Logs
[
{"message": "host1:[error]: No memory"},
{"message": "host1:[warning]: Remained disk space is less than 30%"},
{"message": "host1:[error]: Disk full"},
{"message": "host2:[error]: No memory"},
{"message": "host2:[info]: Shutdown"}
]
# [[0, 1337566253.89858, 0.000355720520019531], 5]

query で正規表現を使う例です。 ${COLUMN}:~${REGULAR_EXPRESSION} という構文を使います。

実行例:

select Logs --query 'message:~"disk (space|full)"'
# [
#   [
#     0,
#     1337566253.89858,
#     0.000355720520019531
#   ],
#   [
#     [
#       [
#         2
#       ],
#       [
#         [
#           "_id",
#           "UInt32"
#         ],
#         [
#           "message",
#           "Text"
#         ]
#       ],
#       [
#         2,
#         "host1:[warning]: Remained disk space is less than 30%"
#       ],
#       [
#         3,
#         "host1:[error]: Disk full"
#       ]
#     ]
#   ]
# ]

filter で正規表現を使う例です。 ${COLUMN} @~ ${REGULAR_EXPRESSION} という構文を使います。

実行例:

select Logs --filter 'message @~ "disk (space|full)"'
# [
#   [
#     0,
#     1337566253.89858,
#     0.000355720520019531
#   ],
#   [
#     [
#       [
#         2
#       ],
#       [
#         [
#           "_id",
#           "UInt32"
#         ],
#         [
#           "message",
#           "Text"
#         ]
#       ],
#       [
#         2,
#         "host1:[warning]: Remained disk space is less than 30%"
#       ],
#       [
#         3,
#         "host1:[error]: Disk full"
#       ]
#     ]
#   ]
# ]

7.14.3. インデックス

Groongaは正規表現を使った検索をインデックスを使って実現することができます。これは逐次検索より非常に高速です。

しかし、すべての正規表現には対応していません。以下の正規表現にのみ対応しています。対応している正規表現は今後増えていく予定です。

  • disk のようにリテラルしかないパターン

  • \A/disk のようにテキストの最初でのマッチとリテラルのみのケース

  • disk\z のようにテキストの最後でのマッチとリテラルのみのケース

高速に正規表現検索を実現するためにはインデックスを作る必要があります。以下は正規表現用のインデックスが満たさなければいけないことのリストです。

  • 語彙表は TABLE_PAT_KEY テーブルであること。

  • 語彙表は TokenRegexp トークナイザーを使っていること。

  • インデックスカラムは WITH_POSITION フラグ付きであること。

語彙表のノーマライザーといった他の設定は用途に応じて適切なものを設定してください。もし、大文字小文字を区別せずに検索したい場合は NormalizerAuto ノーマライザーを使ってください。

以下はオススメのインデックス定義です。多くの場合はこのインデックス定義が適切です。

実行例:

table_create RegexpLexicon TABLE_PAT_KEY ShortText \
  --default_tokenizer TokenRegexp \
  --normalizer NormalizerAuto
# [[0, 1337566253.89858, 0.000355720520019531], true]
column_create RegexpLexicon logs_message_index \
  COLUMN_INDEX|WITH_POSITION Logs message
# [[0, 1337566253.89858, 0.000355720520019531], true]

これでインデックスを使って正規表現検索をできるようになりました。以下の正規表現は「テキストの先頭」と「リテラル」しか使っていないのでインデックスを使って検索できます。

実行例:

select Logs --query message:~\\\\Ahost1
# [
#   [
#     0,
#     1337566253.89858,
#     0.000355720520019531
#   ],
#   [
#     [
#       [
#         3
#       ],
#       [
#         [
#           "_id",
#           "UInt32"
#         ],
#         [
#           "message",
#           "Text"
#         ]
#       ],
#       [
#         1,
#         "host1:[error]: No memory"
#       ],
#       [
#         2,
#         "host1:[warning]: Remained disk space is less than 30%"
#       ],
#       [
#         3,
#         "host1:[error]: Disk full"
#       ]
#     ]
#   ]
# ]

以下は query の代わりに filter を使った例です。前の例と同じ正規表現を使っています。

実行例:

select Logs --filter 'message @~ "\\\\Ahost1:"'
# [
#   [
#     0,
#     1337566253.89858,
#     0.000355720520019531
#   ],
#   [
#     [
#       [
#         3
#       ],
#       [
#         [
#           "_id",
#           "UInt32"
#         ],
#         [
#           "message",
#           "Text"
#         ]
#       ],
#       [
#         1,
#         "host1:[error]: No memory"
#       ],
#       [
#         2,
#         "host1:[warning]: Remained disk space is less than 30%"
#       ],
#       [
#         3,
#         "host1:[error]: Disk full"
#       ]
#     ]
#   ]
# ]

\ エスケープは紛らわしいかもしれません。あなたが書いたクエリーをGroongaが実行するまでにエスケープが必要なステップがいくつもあるからです。以下は \ エスケープが必要なステップです。

  • シェル。ただし、次のようにGroongaのコマンドをコマンドラインで指定した場合のみ:

    % groonga /tmp/db select Logs --filter '"message @~ \"\\\\Ahost1:"\"'
    

    --filter '"message @~ \"\\\\Ahost1:\""' はシェルが評価して次の2つの引数になります。

    • --filter

    • "message @~ \"\\\\Ahost1:\""

  • Groongaコマンドパーサー。ただし、GroongaのコマンドをHTTPパススタイル ( /d/COMMAND?ARG1_NAME=ARG1_VALUE&ARG2_NAME=ARG3_VALUE ) ではなく、コマンドラインスタイル ( COMMAND ARG1_VALUE ARG2_VALUE ... )で指定した場合のみ。

    "message @~ \"\\\\Ahost1:\"" はGroongaコマンドパーサーが評価して次の値になります。

    • message @~ "\\Ahost1:"

  • grn_expr パーサー。 クエリー構文 でも スクリプト構文 でも \ エスケープが必要です。

    スクリプト構文で "\\Ahost1:" という文字列リテラルを評価すると次の値になります。

    • \Ahost1

    この値が正規表現として評価されます。

7.14.4. 構文

このセクションでは広く使われている構文だけ説明します。他の構文と詳細は Onigmoの構文ドキュメント を参照してください。

7.14.4.1. エスケープ

正規表現で特別な文字は次の通りです。

  • \

  • |

  • (

  • )

  • [

  • ]

  • .

  • *

  • +

  • ?

  • {

  • }

  • ^

  • $

これらの特別な文字そのものにマッチするパターンを書きたいときはこれらの文字をエスケープする必要があります。

特別な文字の前に \ を入れることでエスケープできます。以下は特別な文字そのものにマッチする正規表現です。

  • \\

  • \|

  • \(

  • \)

  • \[

  • \]

  • \.

  • \*

  • \+

  • \?

  • \{

  • \}

  • \^

  • \$

実行例:

select Logs --filter 'message @~ "warning|info"'
# [
#   [
#     0,
#     1337566253.89858,
#     0.000355720520019531
#   ],
#   [
#     [
#       [
#         2
#       ],
#       [
#         [
#           "_id",
#           "UInt32"
#         ],
#         [
#           "message",
#           "Text"
#         ]
#       ],
#       [
#         2,
#         "host1:[warning]: Remained disk space is less than 30%"
#       ],
#       [
#         5,
#         "host2:[info]: Shutdown"
#       ]
#     ]
#   ]
# ]

正規表現が期待した通りに動かないときはエスケープ無しで特別な文字が使われていないか確認してください。

7.14.4.2. 選択

選択の構文は A|B です。 A パターンまたは B パターンにマッチするとこの正規表現にマッチしたことになります。

実行例:

select Logs --filter 'message @~ "warning|info"'
# [
#   [
#     0,
#     1337566253.89858,
#     0.000355720520019531
#   ],
#   [
#     [
#       [
#         2
#       ],
#       [
#         [
#           "_id",
#           "UInt32"
#         ],
#         [
#           "message",
#           "Text"
#         ]
#       ],
#       [
#         2,
#         "host1:[warning]: Remained disk space is less than 30%"
#       ],
#       [
#         5,
#         "host2:[info]: Shutdown"
#       ]
#     ]
#   ]
# ]

ご用心

この構文を使った正規表現はインデックスを使って評価できません。

7.14.4.3. グループ

グループの構文は (...) です。グループは次の機能を提供します。

  • 後方参照

  • スケープの限定

マッチしたグループを \nn はグループの番号とする) という構文で参照できます。例えば、 e(r)\1o\1error にマッチします。なぜなら、 \1 は1番目ののグループ (r) のマッチ結果( r )に置き換えられるからです。

実行例:

select Logs --filter 'message @~ "e(r)\\\\1o\\\\1"'
# [
#   [
#     0,
#     1337566253.89858,
#     0.000355720520019531
#   ],
#   [
#     [
#       [
#         3
#       ],
#       [
#         [
#           "_id",
#           "UInt32"
#         ],
#         [
#           "message",
#           "Text"
#         ]
#       ],
#       [
#         1,
#         "host1:[error]: No memory"
#       ],
#       [
#         3,
#         "host1:[error]: Disk full"
#       ],
#       [
#         4,
#         "host2:[error]: No memory"
#       ]
#     ]
#   ]
# ]

より強力な後方参照機能を使うこともできます。詳細は Onigmoのドキュメントの「8. 後方参照」セクション を参照してください。

グループ構文はスコープを小さくします。例えば、 \[(warning|info)\] は選択構文のスコープを小さくしています。この正規表現は [warning][info] にマッチします。

実行例:

select Logs --filter 'message @~ "\\\\[(warning|info)\\\\]"'
# [
#   [
#     0,
#     1337566253.89858,
#     0.000355720520019531
#   ],
#   [
#     [
#       [
#         2
#       ],
#       [
#         [
#           "_id",
#           "UInt32"
#         ],
#         [
#           "message",
#           "Text"
#         ]
#       ],
#       [
#         2,
#         "host1:[warning]: Remained disk space is less than 30%"
#       ],
#       [
#         5,
#         "host2:[info]: Shutdown"
#       ]
#     ]
#   ]
# ]

より強力なグループ関連の機能を使うこともできます。詳細は Onigmoのドキュメントの「7. 拡張式集合」セクション を参照してください。

ご用心

この構文を使った正規表現はインデックスを使って評価できません。

7.14.4.4. 文字クラス

文字クラスの構文は [...] です。文字クラスはマッチする文字を複数指定するときに便利です。

たとえば、 [12]1 または 2 にマッチします。

実行例:

select Logs --filter 'message @~ "host[12]"'
# [
#   [
#     0,
#     1337566253.89858,
#     0.000355720520019531
#   ],
#   [
#     [
#       [
#         5
#       ],
#       [
#         [
#           "_id",
#           "UInt32"
#         ],
#         [
#           "message",
#           "Text"
#         ]
#       ],
#       [
#         1,
#         "host1:[error]: No memory"
#       ],
#       [
#         2,
#         "host1:[warning]: Remained disk space is less than 30%"
#       ],
#       [
#         3,
#         "host1:[error]: Disk full"
#       ],
#       [
#         4,
#         "host2:[error]: No memory"
#       ],
#       [
#         5,
#         "host2:[info]: Shutdown"
#       ]
#     ]
#   ]
# ]

範囲で文字を指定することもできます。たとえば、 [0-9] は1文字の数字にマッチします。

実行例:

select Logs --filter 'message @~ "[0-9][0-9]%"'
# [
#   [
#     0,
#     1337566253.89858,
#     0.000355720520019531
#   ],
#   [
#     [
#       [
#         1
#       ],
#       [
#         [
#           "_id",
#           "UInt32"
#         ],
#         [
#           "message",
#           "Text"
#         ]
#       ],
#       [
#         2,
#         "host1:[warning]: Remained disk space is less than 30%"
#       ]
#     ]
#   ]
# ]

より強力な文字クラス関連の機能も使うことができます。詳細は Onigmoのドキュメントの「6. 文字集合」セクション を参照してください。

ご用心

この構文を使った正規表現はインデックスを使って評価できません。

7.14.4.5. アンカー

以下はよく使われるアンカーの構文です。いくつかのアンカーはインデックスを使って評価できます。

アンカー

説明

インデックスを使えるか

^

行頭

o

$

行末

x

\A

テキストの先頭

o

\z

テキストの末尾

x

以下は \z を使った例です。

実行例:

select Logs --filter 'message @~ "%\\\\z"'
# [
#   [
#     0,
#     1337566253.89858,
#     0.000355720520019531
#   ],
#   [
#     [
#       [
#         1
#       ],
#       [
#         [
#           "_id",
#           "UInt32"
#         ],
#         [
#           "message",
#           "Text"
#         ]
#       ],
#       [
#         2,
#         "host1:[warning]: Remained disk space is less than 30%"
#       ]
#     ]
#   ]
# ]

他にも使えるアンカーがあります。詳細は Onigmoのドキュメントの「5. 錨」セクション を参照してください。

ご用心

\A\z 以外のアンカーを使った正規表現はインデックスを使って評価できません。

7.14.4.6. 量指定子

以下はよく使われる量指定子の構文です。

量指定子

説明

?

0回か1回

*

0回以上

+

1回以上

例えば、 er+orerrorerrror などにマッチします。

実行例:

select Logs --filter 'message @~ "er+or"'
# [
#   [
#     0,
#     1337566253.89858,
#     0.000355720520019531
#   ],
#   [
#     [
#       [
#         3
#       ],
#       [
#         [
#           "_id",
#           "UInt32"
#         ],
#         [
#           "message",
#           "Text"
#         ]
#       ],
#       [
#         1,
#         "host1:[error]: No memory"
#       ],
#       [
#         3,
#         "host1:[error]: Disk full"
#       ],
#       [
#         4,
#         "host2:[error]: No memory"
#       ]
#     ]
#   ]
# ]

他の量指定子を使うこともできます。詳細は Onigmoのドキュメントの「4. 量指定子」セクション を参照してください。

ご用心

この構文を使った正規表現はインデックスを使って評価できません。

7.14.4.7. その他

他にも構文があります。それらに興味がある場合は Onigmoのドキュメント を参照してください。「文字種」や「文字」の構文に興味があるかもしれません。