Elasticsearch 6.2 中没有规范化的精确匹配、不区分大小写的匹配
Exact-match, case-insensitive match without normalization in Elasticsearch 6.2
我查看了每篇文章,post我可以找到关于执行完全匹配、不区分大小写的查询的信息,但在实施时,它们并没有执行我正在寻找的内容。
在将此问题标记为重复之前,请阅读整个 post。
给定一个用户名,我想查询我的 Elasticsearch 数据库以仅 return 一个与用户名完全匹配但不区分大小写的文档。
我已经尝试为我的 username
属性 指定一个 lowercase
分析器并使用 match
查询来实现此行为。虽然这解决了不区分大小写匹配的问题,但它在完全匹配时失败了。
我研究过使用 lowercase
规范化器,但这会使我的所有用户名在索引之前都变成小写,所以当我聚合用户名时,它们会 return 以小写形式出现,这不是我想要的是。我需要保留用户名中每个字母的原始大小写。
我想要的是以下行为:
正在插入用户
POST {elastic}/users/_doc
{
"email": "random@email.com",
"username": "UsErNaMe",
"password": "1234567"
}
此文档将按原样存储在名为 users
的索引中。
通过用户名获取用户
GET {frontend}/user/UsErNaMe
应该return
{
"email": "random@email.com",
"username": "UsErNaMe",
"password": "1234567"
}
和
GET {frontend}/user/username
应该return
{
"email": "random@email.com",
"username": "UsErNaMe",
"password": "1234567"
}
和
GET {frontend}/user/USERNAME
应该return
{
"email": "random@email.com",
"username": "UsErNaMe",
"password": "1234567"
}
和
GET {frontend}/user/UsErNaMe $RaNdoM LeTteRs
应该不 return任何东西。
谢谢。
要实现不区分大小写的精确匹配,您需要定义自己的分析器。分析器需要执行两个操作:
- 小写输入值。 (不区分大小写)
- 不允许在小写操作后对输入进行任何修改。 (精确搜索)
以上两个可以通过以下方式实现:
- 在定义自定义分析器时使用
lowercase
过滤器。
- 将
tokenizer
设置为 keyword
,这将确保在应用小写过滤器后生成输入值的单个标记。
现在可以将此自定义分析器应用于需要不区分大小写的精确搜索的文本字段。
所以要创建索引,您可以使用以下内容:
PUT test
{
"settings": {
"analysis": {
"analyzer": {
"case_insensitive_analyzer": {
"type": "custom",
"filter": [
"lowercase"
],
"tokenizer": "keyword"
}
}
}
},
"mappings": {
"_doc": {
"properties": {
"email": {
"type": "text",
"fields": {
"keyword": {
"type": "keyword"
}
}
},
"username": {
"type": "text",
"analyzer": "case_insensitive_analyzer"
},
"password": {
"type": "keyword"
}
}
}
}
}
上面的 case_insensitive_analyzer
是必需的分析器,如您所见,它已应用于 username
字段。
因此,当您按如下方式索引文档时:
PUT test/_doc/1
{
"email": "random@email.com",
"username": "UsErNaMe",
"password": "1234567"
}
对于字段 username
,输入是 UsErNaMe
。分析器首先对输入 UsErNaMe
应用 lowercase
过滤器,得到值 username
。现在在这个值 username
上它应用 keyword
分词器,它除了输出应用过滤器后获得的值外什么都不做,作为一个单一的分词,即 username
.
现在您可以使用如下匹配查询来搜索用户名字段:
GET test/_doc/_search
{
"query": {
"match": {
"username": "USERNAME"
}
}
}
使用上面的方法会给你想要的输出。将上述查询中的 USERNAME
替换为 username
或 UsErNaMe
或 USERname
都将匹配文档。这样做的原因是,如果没有明确指定分析器,则在搜索时,elasticsearch 在索引时使用应用于该字段的分析器。在上述情况下,当搜索字段 username
时,case_insensitive_analyzer
将应用于输入值,即 USERNAME
,这将导致标记 username
,从而匹配。
我查看了每篇文章,post我可以找到关于执行完全匹配、不区分大小写的查询的信息,但在实施时,它们并没有执行我正在寻找的内容。
在将此问题标记为重复之前,请阅读整个 post。
给定一个用户名,我想查询我的 Elasticsearch 数据库以仅 return 一个与用户名完全匹配但不区分大小写的文档。
我已经尝试为我的 username
属性 指定一个 lowercase
分析器并使用 match
查询来实现此行为。虽然这解决了不区分大小写匹配的问题,但它在完全匹配时失败了。
我研究过使用 lowercase
规范化器,但这会使我的所有用户名在索引之前都变成小写,所以当我聚合用户名时,它们会 return 以小写形式出现,这不是我想要的是。我需要保留用户名中每个字母的原始大小写。
我想要的是以下行为:
正在插入用户
POST {elastic}/users/_doc
{
"email": "random@email.com",
"username": "UsErNaMe",
"password": "1234567"
}
此文档将按原样存储在名为 users
的索引中。
通过用户名获取用户
GET {frontend}/user/UsErNaMe
应该return
{
"email": "random@email.com",
"username": "UsErNaMe",
"password": "1234567"
}
和
GET {frontend}/user/username
应该return
{
"email": "random@email.com",
"username": "UsErNaMe",
"password": "1234567"
}
和
GET {frontend}/user/USERNAME
应该return
{
"email": "random@email.com",
"username": "UsErNaMe",
"password": "1234567"
}
和
GET {frontend}/user/UsErNaMe $RaNdoM LeTteRs
应该不 return任何东西。
谢谢。
要实现不区分大小写的精确匹配,您需要定义自己的分析器。分析器需要执行两个操作:
- 小写输入值。 (不区分大小写)
- 不允许在小写操作后对输入进行任何修改。 (精确搜索)
以上两个可以通过以下方式实现:
- 在定义自定义分析器时使用
lowercase
过滤器。 - 将
tokenizer
设置为keyword
,这将确保在应用小写过滤器后生成输入值的单个标记。
现在可以将此自定义分析器应用于需要不区分大小写的精确搜索的文本字段。
所以要创建索引,您可以使用以下内容:
PUT test
{
"settings": {
"analysis": {
"analyzer": {
"case_insensitive_analyzer": {
"type": "custom",
"filter": [
"lowercase"
],
"tokenizer": "keyword"
}
}
}
},
"mappings": {
"_doc": {
"properties": {
"email": {
"type": "text",
"fields": {
"keyword": {
"type": "keyword"
}
}
},
"username": {
"type": "text",
"analyzer": "case_insensitive_analyzer"
},
"password": {
"type": "keyword"
}
}
}
}
}
上面的 case_insensitive_analyzer
是必需的分析器,如您所见,它已应用于 username
字段。
因此,当您按如下方式索引文档时:
PUT test/_doc/1
{
"email": "random@email.com",
"username": "UsErNaMe",
"password": "1234567"
}
对于字段 username
,输入是 UsErNaMe
。分析器首先对输入 UsErNaMe
应用 lowercase
过滤器,得到值 username
。现在在这个值 username
上它应用 keyword
分词器,它除了输出应用过滤器后获得的值外什么都不做,作为一个单一的分词,即 username
.
现在您可以使用如下匹配查询来搜索用户名字段:
GET test/_doc/_search
{
"query": {
"match": {
"username": "USERNAME"
}
}
}
使用上面的方法会给你想要的输出。将上述查询中的 USERNAME
替换为 username
或 UsErNaMe
或 USERname
都将匹配文档。这样做的原因是,如果没有明确指定分析器,则在搜索时,elasticsearch 在索引时使用应用于该字段的分析器。在上述情况下,当搜索字段 username
时,case_insensitive_analyzer
将应用于输入值,即 USERNAME
,这将导致标记 username
,从而匹配。