使用ElasticSearch模拟SQL LIKE搜索
我只是从ElasticSearch开始,然后尝试基于它来实现自动完成功能。
我有一个autocomplete
带有city
type 字段的索引string
。这是存储在该索引中的文档的示例:
{ "_index":"autocomplete_1435797593949",
"_type":"listing",
"_id":"40716",
"_source":{
"city":"Rome",
"tags":[
"listings"
]
}
}
分析配置如下所示:
{ "analyzer":{
"autocomplete_term":{
"tokenizer":"autocomplete_edge",
"filter":[
"lowercase"
]
},
"autocomplete_search":{
"tokenizer":"keyword",
"filter":[
"lowercase"
]
}
},
"tokenizer":{
"autocomplete_edge":{
"type":"nGram",
"min_gram":1,
"max_gram":100
}
}
}
映射:
{ "autocomplete_1435795884170":{
"mappings":{
"listing":{
"properties":{
"city":{
"type":"string",
"analyzer":"autocomplete_term"
},
}
}
}
}
}
我将以下查询发送到ES:
{ "query":{
"multi_match":{
"query":"Rio",
"analyzer":"autocomplete_search",
"fields":[
"city"
]
}
}
}
结果,我得到以下信息:
{ "took":2,
"timed_out":false,
"_shards":{
"total":5,
"successful":5,
"failed":0
},
"hits":{
"total":1,
"max_score":2.7742395,
"hits":[
{
"_index":"autocomplete_1435795884170",
"_type":"listing",
"_id":"53581",
"_score":2.7742395,
"_source":{
"city":"Rio",
"tags":[
"listings"
]
}
}
]
}
}
在大多数情况下,它是有效的。city = "Rio"
在用户必须实际键入整个单词之前,它确实找到了带有的文档("Ri"
足够了)。
这就是我的问题。我也希望它返回"Rio de Janeiro"
。要获取"Rio de Janeiro"
,我需要发送以下查询:
{ "query":{
"multi_match":{
"query":"Rio d",
"analyzer":"standard",
"fields":[
"city"
]
}
}
}
注意"<whitespace>d"
那里。
另一个相关问题是,我希望至少所有以开头的城市都"R"
将通过以下查询返回:
{ "query":{
"multi_match":{
"query":"R",
"analyzer":"standard",
"fields":[
"city"
]
}
}
}
我期望"Rome"
,等等…(这是索引中存在的文档),但是,我只能"Rio"
再次得到。我希望它的行为类似于SQL LIKE
条件,即...
LIKE 'CityName%'。
我究竟做错了什么?
回答:
我会这样做:
- 将令牌生成器更改为
edge_nGram
您曾说过的需要LIKE 'CityName%'
(表示前缀匹配):
"tokenizer": { "autocomplete_edge": {
"type": "edge_nGram",
"min_gram": 1,
"max_gram": 100
}
}
- 将字段指定
autocomplete_search
为search_analyzer
。我认为拥有keyword
and 是一个不错的选择lowercase
:
"mappings": { "listing": {
"properties": {
"city": {
"type": "string",
"index_analyzer": "autocomplete_term",
"search_analyzer": "autocomplete_search"
}
}
}
}
- 查询本身很简单:
{ "query": {
"multi_match": {
"query": "R",
"fields": [
"city"
]
}
}
}
详细的解释是这样的:将城市名称分割为ngram。例如,Rio de Janeiro
您将为以下内容编制索引:
"city": [ "r",
"ri",
"rio",
"rio ",
"rio d",
"rio de",
"rio de ",
"rio de j",
"rio de ja",
"rio de jan",
"rio de jane",
"rio de janei",
"rio de janeir",
"rio de janeiro"
]
您会注意到所有内容都是小写的。现在,您希望查询采用任何文本(是否为小写),并将其与索引中的内容匹配。因此,R
应该与上面的列表匹配。
为此,您希望输入文本小写并与用户设置的文本保持一致,这意味着不应对其进行分析。你为什么要这个?因为您已经用ngram分割了城市名称,并且不想为输入文本使用相同的名称。如果用户输入“
RI”,Elasticsearch将小写ri
--并将其与索引中的内容完全匹配。
可能更快的替代方法multi_match
是使用term
,但这要求您的应用程序/网站将文本小写。这样做的原因是term
根本不分析输入文本。
{ "query": {
"filtered": {
"filter": {
"term": {
"city": {
"value": "ri"
}
}
}
}
}
}
以上是 使用ElasticSearch模拟SQL LIKE搜索 的全部内容, 来源链接: utcz.com/qa/422393.html