使用ES当数据库进行查询的时候模糊查询都是会进行分词的,这边记录一下等价于关系型数据库的like查询

1、ES中的索引mapping字符串有Text和Keyword两种格式

  • Text格式的查询会进行先分词在查询
  • keyword则不会分词

使用模糊查询可以使用QueryBuilders.wildcardQuery*通配符进行查询,对于text格式的数据可以指定字段名.keyword进行模糊查询,如下所示

boolQuery.filter(QueryBuilders.wildcardQuery("deviceIp.keyword",  deviceIp + "*"));

2、注意

但是需要注意的是wildcard query应杜绝使用通配符打头,实在不得已要这么做,就一定需要限制用户输入的字符串长度。

使用ElasticSearch的时候,很容易就想到使用(Wildcard Query)来实现模糊查询,因为这是和SQL里like操作最相似的查询方式。

这边再搜索实现上测试,根据用户输入的关键词,在首尾加上通配符,使用wildcard query来实现模糊搜索,例如使用*云计算*来搜索含有“云计算”关键字的产品。 然而用户输入的字符串长度没有做限制,导致首尾通配符中间可能是很长的一个字符串。 后果就是对应的wildcard Query执行非常慢,非常消耗CPU。

1、测试

1、创建一个只有一条文档的索引

POST test_index/doc/?refresh=true
{
  "foo": "bar"
}

2、 使用wildcard query执行一个首尾带有通配符*的长字符串查询

POST /test_index/_search
{
  "query": {
    "wildcard": {
      "foo": {
        "value": "*云计算在基础设施应用上的重要组成部分,位于云计算产业链金字塔底层,产品源自云计算平台。该平台整合了互联网应用三大核心要素:计算、存储、网络,面向用户提供公用化的互联网基础设施服务。云主机是一种类似VPS主机的虚拟化技术, VPS是采用虚拟软件,VZ或VM在一台主机上虚拟出多个类似独立主机的部分,能够实现单机多用户,每个部分都可以做单独的操作系统,管理方法同主机一样*"
      }
    }
  }
}

3、查看结果

{
  "took": 3346,
  "timed_out": false,
  "_shards": {
    "total": 5,
    "successful": 5,
    "skipped": 0,
    "failed": 0
  },
  "hits": {
    "total": 0,
    "max_score": null,
    "hits": []
  }
}

结果发现模糊查询中间此长的时候只有一条文档的索引查询不到结果也消费了3秒多(3346),而且越长消耗时间越多,超过一定长度是es还会报错误亦或是超时

nested: TooComplexToDeterminizeException[Determinizing automaton with 13742 states and 21252 transitions would result in more than 10000 states.]; }] with root cause:175{
  "statusCode": 504,
  "error": "Gateway Time-out",
  "message": "Client request timeout"
}

不加前缀的*则效率非常高

4、只使用后缀*查询

POST /test_index/_search
{
  "query": {
    "wildcard": {
      "foo": {
        "value": "云计算在基础设施应用上的重要组成部分,位于云计算产业链金字塔底层,产品源自云计算平台。该平台整合了互联网应用三大核心要素:计算、存储、网络,面向用户提供公用化的互联网基础设施服务。云主机是一种类似VPS主机的虚拟化技术, VPS是采用虚拟软件,VZ或VM在一台主机上虚拟出多个类似独立主机的部分,能够实现单机多用户,每个部分都可以做单独的操作系统,管理方法同主机一样*"
      }
    }
  }
}
{
  "took": 8,
  "timed_out": false,
  "_shards": {
    "total": 5,
    "successful": 5,
    "skipped": 0,
    "failed": 0
  },
  "hits": {
    "total": 0,
    "max_score": null,
    "hits": []
  }
}

结果只有0.008秒

5、总结一下

wildcard query应杜绝使用通配符打头,实在不得已要这么做,就一定需要限制用户输入的字符串长度。 最好换一种实现方式,通过在index time做文章,选用合适的分词器,比如nGram tokenizer预处理数据,然后使用更廉价的term query来实现同等的模糊搜索功能。 对于部分输入即提示的应用场景,可以考虑优先使用completion suggester, phrase/term suggeter一类性能更好,模糊程度略差的方式查询,待suggester没有匹配结果的时候,再fall back到更模糊但性能较差的wildcard, regex, fuzzy一类的查询。

6、TIP

是底层lucene中WildcardQuery的toAutomaton方法所影响的,有时间的可以去研究一下