Обзор
Словарь regexp_tree позволяет сопоставлять ключи со значениями на основе иерархии шаблонов регулярных выражений.
Он оптимизирован для поиска по совпадению с шаблоном (например, для классификации строк, таких как user agent, по шаблонам регулярных выражений), а не для точного сопоставления ключей.
Использование словаря на основе дерева регулярных выражений с источником YAMLRegExpTree
В ClickHouse open-source словари на основе дерева регулярных выражений определяются с помощью источника YAMLRegExpTree, которому передаётся путь к YAML-файлу, содержащему дерево регулярных выражений.
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)
...Источник словаря 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'Эта конфигурация состоит из списка узлов дерева регулярных выражений. Каждый узел имеет следующую структуру:
- regexp: регулярное выражение узла.
- attributes: список пользовательских атрибутов словаря. В этом примере есть два атрибута:
nameиversion. Первый узел определяет оба атрибута. Второй узел определяет только атрибутname. Атрибутversionзадаётся дочерними узлами второго узла.- Значение атрибута может содержать обратные ссылки, указывающие на группы захвата в совпавшем регулярном выражении. В примере значение атрибута
versionв первом узле состоит из обратной ссылки\1на группу захвата(\d+[\.\d]*)в регулярном выражении. Номера обратных ссылок находятся в диапазоне от 1 до 9 и записываются как$1или\1(для номера 1). При выполнении запроса обратная ссылка заменяется соответствующей совпавшей группой захвата.
- Значение атрибута может содержать обратные ссылки, указывающие на группы захвата в совпавшем регулярном выражении. В примере значение атрибута
- child nodes: список дочерних узлов узла дерева регулярных выражений, каждый из которых имеет собственные атрибуты и (потенциально) дочерние узлы. Сопоставление строк выполняется в порядке обхода в глубину. Если строка соответствует узлу regexp, словарь проверяет, соответствует ли она также его дочерним узлам. В этом случае присваиваются атрибуты самого глубокого совпавшего узла. Атрибуты дочернего узла перезаписывают одноимённые атрибуты родительских узлов. Имена дочерних узлов в YAML-файлах могут быть произвольными, например
versionsв приведённом выше примере.
Словарь дерева регулярных выражений поддерживает доступ только с использованием функций dictGet, dictGetOrDefault и dictGetAll. Например:
SELECT dictGet('regexp_dict', ('name', 'version'), '31/tclwebkit1024');┌─dictGet('regexp_dict', ('name', 'version'), '31/tclwebkit1024')─┐
│ ('Android','12') │
└─────────────────────────────────────────────────────────────────┘В этом случае мы сначала сопоставляем регулярное выражение \d+/tclwebkit(?:\d+[\.\d]*) со вторым узлом второго уровня.
Затем словарь продолжает поиск в дочерних узлах и обнаруживает, что строка также соответствует 3[12]/tclwebkit.
В результате значение атрибута name равно Android (определено на первом уровне), а значение атрибута version равно 12 (определено в дочернем узле).
С помощью сложного YAML-файла конфигурации вы можете использовать словарь дерева регулярных выражений как парсер строки user agent. ClickHouse поддерживает uap-core, и вы можете посмотреть, как его использовать, в функциональном тесте 02504_regexp_dictionary_ua_parser
Сбор значений атрибутов
Иногда полезно возвращать значения всех совпавших регулярных выражений, а не только значение листового узла. В таких случаях можно использовать специализированную функцию dictGetAll. Если у узла есть значение атрибута типа T, dictGetAll вернёт Array(T), содержащий ноль или более значений.
По умолчанию число совпадений, возвращаемых для каждого ключа, не ограничено. Ограничение можно передать в dictGetAll как необязательный четвёртый аргумент. Массив заполняется в топологическом порядке: дочерние узлы идут перед родительскими, а узлы одного уровня следуют порядку, заданному в исходном описании.
Пример:
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],[]) │
└────────────────────────────────────────┴───────────────────────────────────────────────────────────────────────────────────────┘Режимы сопоставления
Поведение сопоставления с шаблоном можно изменять с помощью некоторых настроек словаря:
regexp_dict_flag_case_insensitive: использовать регистронезависимое сопоставление (по умолчаниюfalse). Можно переопределить в отдельных выражениях с помощью(?i)и(?-i).regexp_dict_flag_dotall: разрешить символу.соответствовать символам новой строки (по умолчаниюfalse).
Использование словаря на основе дерева регулярных выражений в ClickHouse Cloud
Источник YAMLRegExpTree поддерживается в ClickHouse Open Source, но не в ClickHouse Cloud.
Чтобы использовать словари на основе дерева регулярных выражений в ClickHouse Cloud, сначала создайте словарь на основе дерева регулярных выражений из YAML-файла в локальной установке ClickHouse Open Source, затем выгрузите этот словарь в CSV-файл с помощью табличной функции dictionary и оператора INTO OUTFILE.
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.parent_id UInt64: идентификатор родительского узла.regexp String: строка регулярного выражения.keys Array(String): имена пользовательских атрибутов.values Array(String): значения пользовательских атрибутов.
Чтобы создать словарь в ClickHouse Cloud, сначала создайте таблицу 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);