elasticsearch - 使用 ElasticSearch 模拟 SQL LIKE 搜索

标签 elasticsearch sql-like

我刚开始使用 ElasticSearch 并尝试基于它实现自动完成功能。

我有一个 autocomplete带有字段的索引 city类型 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。我认为有一个 keywordlowercase 是个不错的选择:
  "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 应该与上面的列表匹配。

要实现这一点,您需要将输入文本小写并保持与用户设置的完全相同,这意味着不应对其进行分析。你为什么要这个?因为您已经在 ngrams 中拆分了城市名称,并且您不希望输入文本使用相同的名称。如果用户输入“RI”,Elasticsearch 会将其小写 - ri - 并将其与索引中的内容完全匹配。

可能比 multi_match 更快的替代方法是使用 term,但这需要您的应用程序/网站将文本小写。原因是 term 根本不分析输入文本。

{
  "query": {
    "filtered": {
      "filter": {
        "term": {
          "city": {
            "value": "ri"
          }
        }
      }
    }
  }
}

关于elasticsearch - 使用 ElasticSearch 模拟 SQL LIKE 搜索,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/31173988/

相关文章:

elasticsearch - 适用于具有大量聚合的大型集群的ElasticSearch设置

php - 如何使用 LIKE 编写查询来接受特殊字符?

sql - 使用LIKE选择所有可能重复的相似行?

elasticsearch - Elasticsearch:如果存在其他记录,则排除结果

mysql 查询使用多个 LIKE 表达式返回不正确的结果

mysql 针对 concat 查询 LIKE 条件

php - 带有 LIKE 通配符的 SQL 代码无法正常工作

performance - ElasticSearch 预期性能

elasticsearch - 如何验证Elasticsearch无痛脚本?

elasticsearch - Elasticsearch服务器超时