概要
regexp_tree Dictionary では、階層的な正規表現パターンに基づいてキーを値にマッピングできます。
完全一致によるキー照合ではなく、パターン一致のルックアップ (たとえば、正規表現パターンとの照合によって user agent 文字列のような文字列を分類するケース) 向けに最適化されています。
YAMLRegExpTree ソースを使用した正規表現ツリー辞書
ClickHouse Open Sourceでは、正規表現ツリーを含む YAML ファイルへのパスを指定した YAMLRegExpTree ソースを使用して、正規表現ツリー辞書を定義します。
CREATE DICTIONARY regexp_dict
(
regexp String,
name String,
version String
)
PRIMARY KEY(regexp)
SOURCE(YAMLRegExpTree(PATH '/var/lib/clickhouse/user_files/regexp_tree.yaml'))
LAYOUT(regexp_tree)
...Dictionary ソース YAMLRegExpTree は、正規表現ツリーの構造を表します。たとえば、次のとおりです。
- regexp: 'Linux/(\d+[\.\d]*).+tlinux'
name: 'TencentOS'
version: '\1'
- regexp: '\d+/tclwebkit(?:\d+[\.\d]*)'
name: 'Android'
versions:
- regexp: '33/tclwebkit'
version: '13'
- regexp: '3[12]/tclwebkit'
version: '12'
- regexp: '30/tclwebkit'
version: '11'
- regexp: '29/tclwebkit'
version: '10'このconfigは、正規表現ツリーノードのリストで構成されています。各ノードは次の構造を持ちます。
- regexp: ノードの正規表現。
- 属性: ユーザー定義のDictionary 属性のリスト。この例では、
nameとversionの2つの属性があります。最初のノードでは両方の属性を定義しています。2番目のノードではname属性のみを定義しています。version属性は、2番目のノードの子ノードによって提供されます。- 属性の値には、マッチした正規表現のcapture groupを参照する後方参照を含めることができます。この例では、最初のノードの
version属性の値は、正規表現内のcapture group(\d+[\.\d]*)への後方参照\1で構成されています。後方参照の番号は1から9までで、$1または\1(1番の場合) のように記述します。後方参照は、クエリの実行時にマッチしたcapture groupで置き換えられます。
- 属性の値には、マッチした正規表現のcapture groupを参照する後方参照を含めることができます。この例では、最初のノードの
- 子ノード: regexp tree nodeの子ノードのリストです。各子ノードは、それぞれ独自の属性と、必要に応じて子ノードを持ちます。文字列のマッチングは深さ優先で行われます。文字列がregexp nodeにマッチした場合、dictionaryはその文字列がそのノードの子ノードにもマッチするかどうかを確認します。その場合、最も深い階層でマッチしたノードの属性が割り当てられます。子ノードの属性は、同じ名前の親ノードの属性を上書きします。YAMLファイル内の子ノードの名前は任意で、たとえば上記の例では
versionsです。
Regexp tree dictionariesでは、dictGet、dictGetOrDefault、dictGetAll の各関数を使ったアクセスのみが可能です。例:
SELECT dictGet('regexp_dict', ('name', 'version'), '31/tclwebkit1024');┌─dictGet('regexp_dict', ('name', 'version'), '31/tclwebkit1024')─┐
│ ('Android','12') │
└─────────────────────────────────────────────────────────────────┘この場合、まず最上位レイヤーの2 番目のノードで正規表現 \d+/tclwebkit(?:\d+[\.\d]*) に一致します。
その後、Dictionary は子ノードの探索を続け、その文字列が 3[12]/tclwebkit にも一致することを見つけます。
その結果、属性 name の値は Android (第1レイヤーで定義) となり、属性 version の値は 12 (子ノードで定義) となります。
高度な YAML 設定ファイルを使うことで、regexp tree dictionaries をユーザーエージェント文字列のパーサーとして使用できます。 ClickHouse は uap-core をサポートしており、その使用方法は functional test 02504_regexp_dictionary_ua_parser で確認できます。
属性値の収集
一致した複数の正規表現から、末端ノードの値だけでなく複数の値を返したい場合があります。そのような場合は、専用の dictGetAll 関数を使用できます。ノードが型 T の attribute 値を持つ場合、dictGetAll は 0 個以上の値を含む Array(T) を返します。
デフォルトでは、キーごとに返される一致数に上限はありません。dictGetAll には、省略可能な第 4 引数として上限を指定できます。配列には トポロジカル順序 で値が格納されます。つまり、子ノードは親ノードより前に配置され、兄弟ノードはソース内の順序に従います。
例:
CREATE DICTIONARY regexp_dict
(
regexp String,
tag String,
topological_index Int64,
captured Nullable(String),
parent String
)
PRIMARY KEY(regexp)
SOURCE(YAMLRegExpTree(PATH '/var/lib/clickhouse/user_files/regexp_tree.yaml'))
LAYOUT(regexp_tree)
LIFETIME(0)# /var/lib/clickhouse/user_files/regexp_tree.yaml
- regexp: 'clickhouse\.com'
tag: 'ClickHouse'
topological_index: 1
paths:
- regexp: 'clickhouse\.com/docs(.*)'
tag: 'ClickHouse Documentation'
topological_index: 0
captured: '\1'
parent: 'ClickHouse'
- regexp: '/docs(/|$)'
tag: 'Documentation'
topological_index: 2
- regexp: 'github.com'
tag: 'GitHub'
topological_index: 3
captured: 'NULL'CREATE TABLE urls (url String) ENGINE=MergeTree ORDER BY url;
INSERT INTO urls VALUES ('clickhouse.com'), ('clickhouse.com/docs/en'), ('github.com/clickhouse/tree/master/docs');
SELECT url, dictGetAll('regexp_dict', ('tag', 'topological_index', 'captured', 'parent'), url, 2) FROM urls;┌─url────────────────────────────────────┬─dictGetAll('regexp_dict', ('tag', 'topological_index', 'captured', 'parent'), url, 2)─┐
│ clickhouse.com │ (['ClickHouse'],[1],[],[]) │
│ clickhouse.com/docs/en │ (['ClickHouse Documentation','ClickHouse'],[0,1],['/en'],['ClickHouse']) │
│ github.com/clickhouse/tree/master/docs │ (['Documentation','GitHub'],[2,3],[NULL],[]) │
└────────────────────────────────────────┴───────────────────────────────────────────────────────────────────────────────────────┘マッチングモード
パターンマッチングの動作は、特定の Dictionary 設定によって変更できます。
regexp_dict_flag_case_insensitive: 大文字と小文字を区別しないマッチングを使用します (デフォルトはfalse) 。個々の式では(?i)と(?-i)を使って上書きできます。regexp_dict_flag_dotall:.が改行文字にも一致するようにします (デフォルトはfalse) 。
ClickHouse Cloudで正規表現ツリーDictionaryを使用する
YAMLRegExpTree ソースはClickHouse Open Sourceでは使用できますが、ClickHouse Cloudでは使用できません。
ClickHouse Cloudで正規表現ツリーDictionaryを使用するには、まずClickHouse Open SourceでYAMLファイルから正規表現ツリーDictionaryをローカルに作成し、次に dictionary テーブル関数と INTO OUTFILE 句を使って、このDictionaryをCSVファイルにダンプします。
SELECT * FROM dictionary(regexp_dict) INTO OUTFILE('regexp_dict.csv')CSVファイルの内容は次のとおりです:
1,0,"Linux/(\d+[\.\d]*).+tlinux","['version','name']","['\\1','TencentOS']"
2,0,"(\d+)/tclwebkit(\d+[\.\d]*)","['comment','version','name']","['test $1 and $2','$1','Android']"
3,2,"33/tclwebkit","['version']","['13']"
4,2,"3[12]/tclwebkit","['version']","['12']"
5,2,"3[12]/tclwebkit","['version']","['11']"
6,2,"3[12]/tclwebkit","['version']","['10']"ダンプファイルのスキーマは次のとおりです。
id UInt64: RegexpTree ノードの ID。parent_id UInt64: ノードの親の ID。regexp String: 正規表現の文字列。keys Array(String): ユーザー定義属性の名前。values Array(String): ユーザー定義属性の値。
ClickHouse Cloud で Dictionary を作成するには、まず次のテーブル構造で regexp_dictionary_source_table テーブルを作成します。
CREATE TABLE regexp_dictionary_source_table
(
id UInt64,
parent_id UInt64,
regexp String,
keys Array(String),
values Array(String)
) ENGINE=Memory;次に、ローカルのCSVを以下のように更新します
clickhouse client \
--host MY_HOST \
--secure \
--password MY_PASSWORD \
--query "
INSERT INTO regexp_dictionary_source_table
SELECT * FROM input ('id UInt64, parent_id UInt64, regexp String, keys Array(String), values Array(String)')
FORMAT CSV" < regexp_dict.csv詳しくは、ローカルファイルの挿入を参照してください。ソーステーブルを初期化した後、テーブルソースから RegexpTree を作成できます。
CREATE DICTIONARY regexp_dict
(
regexp String,
name String,
version String
PRIMARY KEY(regexp)
SOURCE(CLICKHOUSE(TABLE 'regexp_dictionary_source_table'))
LIFETIME(0)
LAYOUT(regexp_tree);