如何使用 include 和 regex 在 elasticsearch 中正确查询术语内部聚合值?
How to correctly query inside of terms aggregate values in elasticsearch, using include and regex?
如何在聚合结果中有效过滤 out/search?
假设您在 Elastic Search 中有 100 万个文档。在那些文档中,你有一个 multi_field (keyword, text) tags
:
{
...
tags: ['Race', 'Racing', 'Mountain Bike', 'Horizontal'],
...
},
{
...
tags: ['Tracey Chapman', 'Silverfish', 'Blue'],
...
},
{
...
tags: ['Surfing', 'Race', 'Disgrace'],
...
},
您可以将这些值用作过滤器(构面),以针对查询仅提取包含此标记的文档:
...
"filter": [
{
"terms": {
"tags": [
"Race"
]
}
},
...
]
但是您希望用户能够查询可能的标签过滤器。因此,如果用户键入 race
,return 应该显示(来自前面的示例)['Race', 'Tracey Chapman', 'Disgrace']
。这样,用户就可以查询要使用的过滤器。为了做到这一点,我不得不使用聚合:
{
"aggs": {
"topics": {
"terms": {
"field": "tags",
"include": ".*[Rr][Aa][Cc][Ee].*", // I have to dynamically form this
"size": 6
}
}
},
"size": 0
}
这正是我所需要的!但是它很慢,非常慢。我试过添加 execution_hint,但对我没有帮助。
您可能会想,“只需在聚合之前使用查询!”但问题是它会提取该查询中所有文档的所有值。意思是,您可以显示完全不相关的标签。如果我在聚合之前查询 race
,并且没有使用 include 正则表达式,我最终会得到所有其他值,比如 'Horizontal', etc...
如何重写此聚合以更快地工作?有没有更好的方法来写这个?我真的必须为值创建一个单独的索引吗? (悲伤的脸)这似乎是一个常见问题,但通过文档和谷歌搜索没有找到答案。
您当然不需要仅针对值的单独索引...
这是我的看法:
- 你用正则表达式做的基本上是 tokenizer -- i.e. constructing substrings (or N-grams) 应该做的,这样他们以后就可以成为目标。
这意味着关键字 Race
需要标记为 n-grams ["rac", "race", "ace"]
。 (少于 3 个字符真的没有意义 -- 大多数自动完成库选择忽略少于 3 个字符,因为可能的匹配气球太快了。)
Elasticsearch 提供从 1 到(任意)10 的 N-gram tokenizer but we'll need to increase the default index-level setting called max_ngram_diff
,因为我们希望捕获尽可能多的 ngrams 是合理的:
PUT tagindex
{
"settings": {
"index": {
"max_ngram_diff": 10
},
"analysis": {
"analyzer": {
"my_ngrams_analyzer": {
"tokenizer": "my_ngrams",
"filter": [ "lowercase" ]
}
},
"tokenizer": {
"my_ngrams": {
"type": "ngram",
"min_gram": 3,
"max_gram": 10,
"token_chars": [ "letter", "digit" ]
}
}
}
},
{ "mappings": ... } --> see below
}
- 当您的
tags
字段是关键字列表时,如果不使用 include
选项 则根本不可能在该字段上进行聚合是完全匹配或正则表达式(您已经在使用)。现在,我们不能保证完全匹配,但我们也不想正则表达式!所以这就是我们需要使用 nested list 的原因,它将 分别处理每个标签 .
现在,嵌套列表应包含对象,因此
{
"tags": ["Race", "Racing", "Mountain Bike", "Horizontal"]
}
需要转换为
{
"tags": [
{ "tag": "Race" },
{ "tag": "Racing" },
{ "tag": "Mountain Bike" },
{ "tag": "Horizontal" }
]
}
之后,我们将继续进行 multi field 映射,保持原始标签不变,同时添加一个 .tokenized
字段进行搜索,并添加一个 .keyword
字段进行聚合:
"index": { ... },
"analysis": { ... },
"mappings": {
"properties": {
"tags": {
"type": "nested",
"properties": {
"tag": {
"type": "text",
"fields": {
"tokenized": {
"type": "text",
"analyzer": "my_ngrams_analyzer"
},
"keyword": {
"type": "keyword"
}
}
}
}
}
}
}
然后我们将添加调整后的标签文档:
POST tagindex/_doc
{"tags":[{"tag":"Race"},{"tag":"Racing"},{"tag":"Mountain Bike"},{"tag":"Horizontal"}]}
POST tagindex/_doc
{"tags":[{"tag":"Tracey Chapman"},{"tag":"Silverfish"},{"tag":"Blue"}]}
POST tagindex/_doc
{"tags":[{"tag":"Surfing"},{"tag":"Race"},{"tag":"Disgrace"}]}
GET tagindex/_search
{
"aggs": {
"topics_parent": {
"nested": {
"path": "tags"
},
"aggs": {
"topics": {
"filter": {
"term": {
"tags.tag.tokenized": "race"
}
},
"aggs": {
"topics": {
"terms": {
"field": "tags.tag.keyword",
"size": 100
}
}
}
}
}
}
},
"size": 0
}
屈服
{
...
"topics_parent" : {
...
"topics" : {
...
"topics" : {
...
"buckets" : [
{
"key" : "Race",
"doc_count" : 2
},
{
"key" : "Disgrace",
"doc_count" : 1
},
{
"key" : "Tracey Chapman",
"doc_count" : 1
}
]
}
}
}
}
注意事项
- 为了使其正常工作,您必须重新索引
- ngrams 会增加存储空间——这取决于每个文档有多少个标签,这可能会成为一个问题
- 嵌套字段在内部被视为“单独的文档”,因此这也会影响磁盘 space
P.S.: 这是一个有趣的用例。让我知道实施情况如何!
如何在聚合结果中有效过滤 out/search?
假设您在 Elastic Search 中有 100 万个文档。在那些文档中,你有一个 multi_field (keyword, text) tags
:
{
...
tags: ['Race', 'Racing', 'Mountain Bike', 'Horizontal'],
...
},
{
...
tags: ['Tracey Chapman', 'Silverfish', 'Blue'],
...
},
{
...
tags: ['Surfing', 'Race', 'Disgrace'],
...
},
您可以将这些值用作过滤器(构面),以针对查询仅提取包含此标记的文档:
...
"filter": [
{
"terms": {
"tags": [
"Race"
]
}
},
...
]
但是您希望用户能够查询可能的标签过滤器。因此,如果用户键入 race
,return 应该显示(来自前面的示例)['Race', 'Tracey Chapman', 'Disgrace']
。这样,用户就可以查询要使用的过滤器。为了做到这一点,我不得不使用聚合:
{
"aggs": {
"topics": {
"terms": {
"field": "tags",
"include": ".*[Rr][Aa][Cc][Ee].*", // I have to dynamically form this
"size": 6
}
}
},
"size": 0
}
这正是我所需要的!但是它很慢,非常慢。我试过添加 execution_hint,但对我没有帮助。
您可能会想,“只需在聚合之前使用查询!”但问题是它会提取该查询中所有文档的所有值。意思是,您可以显示完全不相关的标签。如果我在聚合之前查询 race
,并且没有使用 include 正则表达式,我最终会得到所有其他值,比如 'Horizontal', etc...
如何重写此聚合以更快地工作?有没有更好的方法来写这个?我真的必须为值创建一个单独的索引吗? (悲伤的脸)这似乎是一个常见问题,但通过文档和谷歌搜索没有找到答案。
您当然不需要仅针对值的单独索引...
这是我的看法:
- 你用正则表达式做的基本上是 tokenizer -- i.e. constructing substrings (or N-grams) 应该做的,这样他们以后就可以成为目标。
这意味着关键字Race
需要标记为 n-grams["rac", "race", "ace"]
。 (少于 3 个字符真的没有意义 -- 大多数自动完成库选择忽略少于 3 个字符,因为可能的匹配气球太快了。)
Elasticsearch 提供从 1 到(任意)10 的 N-gram tokenizer but we'll need to increase the default index-level setting called max_ngram_diff
,因为我们希望捕获尽可能多的 ngrams 是合理的:
PUT tagindex
{
"settings": {
"index": {
"max_ngram_diff": 10
},
"analysis": {
"analyzer": {
"my_ngrams_analyzer": {
"tokenizer": "my_ngrams",
"filter": [ "lowercase" ]
}
},
"tokenizer": {
"my_ngrams": {
"type": "ngram",
"min_gram": 3,
"max_gram": 10,
"token_chars": [ "letter", "digit" ]
}
}
}
},
{ "mappings": ... } --> see below
}
- 当您的
tags
字段是关键字列表时,如果不使用include
选项 则根本不可能在该字段上进行聚合是完全匹配或正则表达式(您已经在使用)。现在,我们不能保证完全匹配,但我们也不想正则表达式!所以这就是我们需要使用 nested list 的原因,它将 分别处理每个标签 .
现在,嵌套列表应包含对象,因此
{
"tags": ["Race", "Racing", "Mountain Bike", "Horizontal"]
}
需要转换为
{
"tags": [
{ "tag": "Race" },
{ "tag": "Racing" },
{ "tag": "Mountain Bike" },
{ "tag": "Horizontal" }
]
}
之后,我们将继续进行 multi field 映射,保持原始标签不变,同时添加一个 .tokenized
字段进行搜索,并添加一个 .keyword
字段进行聚合:
"index": { ... },
"analysis": { ... },
"mappings": {
"properties": {
"tags": {
"type": "nested",
"properties": {
"tag": {
"type": "text",
"fields": {
"tokenized": {
"type": "text",
"analyzer": "my_ngrams_analyzer"
},
"keyword": {
"type": "keyword"
}
}
}
}
}
}
}
然后我们将添加调整后的标签文档:
POST tagindex/_doc
{"tags":[{"tag":"Race"},{"tag":"Racing"},{"tag":"Mountain Bike"},{"tag":"Horizontal"}]}
POST tagindex/_doc
{"tags":[{"tag":"Tracey Chapman"},{"tag":"Silverfish"},{"tag":"Blue"}]}
POST tagindex/_doc
{"tags":[{"tag":"Surfing"},{"tag":"Race"},{"tag":"Disgrace"}]}
GET tagindex/_search
{
"aggs": {
"topics_parent": {
"nested": {
"path": "tags"
},
"aggs": {
"topics": {
"filter": {
"term": {
"tags.tag.tokenized": "race"
}
},
"aggs": {
"topics": {
"terms": {
"field": "tags.tag.keyword",
"size": 100
}
}
}
}
}
}
},
"size": 0
}
屈服
{
...
"topics_parent" : {
...
"topics" : {
...
"topics" : {
...
"buckets" : [
{
"key" : "Race",
"doc_count" : 2
},
{
"key" : "Disgrace",
"doc_count" : 1
},
{
"key" : "Tracey Chapman",
"doc_count" : 1
}
]
}
}
}
}
注意事项
- 为了使其正常工作,您必须重新索引
- ngrams 会增加存储空间——这取决于每个文档有多少个标签,这可能会成为一个问题
- 嵌套字段在内部被视为“单独的文档”,因此这也会影响磁盘 space
P.S.: 这是一个有趣的用例。让我知道实施情况如何!