我遇到了一些我似乎无法克服的 ElasticSearch 问题,特别是正则表达式查询类型。我有一个名为
test-index
的索引。其中包含带有 url 字段的文档。一个特定文档的 url 字段设置为以下内容:
https://hello-world.com/help/me
我希望能够运行一个查询,该查询将使用正则表达式将其返回到我的结果集中。这是我的查询:
"query": {
"bool": {
"must": [
{
"bool": {
"should": [
{
"regexp": {
"url": "*.hello-world.com/help/me/?"
}
}
]
}
}
]
}
}
}
基本上,我希望任何与子域、二级域、顶级域和子目录(有或没有最后的
/
)匹配的 url 都能匹配。但是,当我运行上述查询时,结果集中未返回预期的文档。根据我对 ElasticSearch 正则表达式的了解,我还尝试了以下查询更新,但无济于事:
*.hello-world\.com\/help\/me\/?
*.hello-world\\.com\\/help\\/me\\/?
*.hello-world\\.com/help/me/?
(https?:\\/\\/)?(www\\.)?hello-world\\.com\\/help\\/me\\/?
(https?:\/\/)?(www\.)?hello-world\.com\/help\/me\/?
我真的不明白为什么这个文档没有吸引人。我已经对索引运行了精确匹配查询,并且文档以这种方式被捕获。关于 ElasticSearch 中的正则表达式查询,有什么我不明白的地方吗?我还可以采取另一条路吗?再次提前致谢!
GET test-index/_search
{
"query": {
"regexp": {
"url":"https?://(?:[a-zA-Z0-9-]+.)?hello-world.com\/help/me/?"
}
}
}
GET test-index/_search
{
"query": {
"wildcard": {
"url": "*hello-world.com/help/me*"
}
}
}
两者都可能很慢,因为这会增加查找匹配项所需的迭代并降低搜索性能。
尝试使用 UAX URL 电子邮件标记器对 URL 执行搜索。
最快的解决方案是没有
regexp
和 wildcard
的解决方案
URL 不是原子数据。所以应该分成几部分
将 URL 的每个部分映射为
parsed_url
对象的字段
PUT /test-index
{
"mappings": {
"properties": {
"url": {
"type": "keyword"
},
"parsed_url": {
"properties": {
"is_parsed": {
"type": "boolean"
},
"protocol": {
"type": "keyword"
},
"host": {
"type": "keyword"
},
"path": {
"type": "keyword"
},
"query": {
"type": "keyword"
},
"fragment": {
"type": "keyword"
}
}
}
}
}
}
is_parsed
是解析结果。所有字段都是关键字(除了is_parsed
)
文件
PUT /test-index/_bulk
{"create":{"_id":1}}
{"url":"https://sub.hello-world.com/help/me/index.php?param=value#label"}
{"create":{"_id":2}}
{"url":"https://hello-world.com/help/me"}
URL 由
update_by_query
解析
POST /test-index/_update_by_query
{
"script": {
"source": """
String[] urlItems = new String[]{'protocol', 'host', 'path', 'query', 'fragment'};
int[] urlItemIndices = new int[]{2, 4, 5, 7, 9};
String url = ctx['_source']['url'];
Pattern regexp = /^(([^:\/?#]+):)?(\/\/([^\/?#]*))?([^?#]*)(\?([^#]*))?(#(.*))?$/;
Matcher m = regexp.matcher(url);
ctx['_source']['parsed_url.is_parsed'] = m.matches();
if (m.matches()) {
for (int i = 0; i < urlItems.length; i++) {
ctx['_source']['parsed_url.' + urlItems[i]] = m.group(urlItemIndices[i]);
}
}
"""
}
}
在这个answer
中找到了正则表达式是时候去寻找了。每个部分都可以通过单独的
term
查询找到。解析后的 URL 通过 filter
选择
GET /test-index/_search?filter_path=hits.hits
{
"query": {
"bool": {
"must": [
{
"term": {
"parsed_url.host": "hello-world.com"
}
},
{
"term": {
"parsed_url.path": "/help/me"
}
}
],
"filter": [
{
"term": {
"parsed_url.is_parsed": "true"
}
}
]
}
}
}
回应
{
"hits" : {
"hits" : [
{
"_index" : "test-index",
"_type" : "_doc",
"_id" : "2",
"_score" : 1.3862942,
"_source" : {
"parsed_url.host" : "hello-world.com",
"parsed_url.path" : "/help/me",
"parsed_url.query" : null,
"parsed_url.is_parsed" : true,
"url" : "https://hello-world.com/help/me",
"parsed_url.protocol" : "https",
"parsed_url.fragment" : null
}
}
]
}
}
可能的改进是提取子域、域和域区域
java.net.URL
类来解析 URL,但 Painless 无法为我的 Elasticsearch 识别它