关于Elasticsearch的精确值查找(term)不生效问题

问题

常用的 term 查询, 可以用它处理数字(numbers)、布尔值(Booleans)、日期(dates)以及文本(text)。term查询数字的时候并没有什么问题,但是当我们对字符串类型的字段进行term查询时可能会得到意想不到的情况,可能明明有记录却查询不到,也可能查询出不符合预期的记录。

原因

ES会默认给每个字段进行分词然后建立倒排索引。比如,有两条JSON数据如下:

[
	{
		"id" : 1
		"searchField" : "abc#def"
	},
	{
		"id" : 2
		"searchField" : "abc#xyz"
	}
]

使用ES提供的analyze API 可以看到分词结果如下:
在这里插入图片描述
按照上面的分词结果,那么当我们将这两条数据插入ES的时候,建立的倒排索引如下:

TermCounterDocId
abc21,2
def11
xyz12

注意:如果字段内容是大写的,那么在分词生成索引后,索引的项目会变成小写,比如上面的两条数据是ABC#DEF和ABC#XYZ,那么生成的索引也和上面的一样。此时由于索引项是小写,因此term查询ABC是查不到的,必须要查询abc;match查询ABC是可以查询到的,因为match会进行分词然后再匹配。

①使用term精确查询searchFieldabc#def的记录:

{
  "query": {
    "term": {
      "searchField":"abc#def"
    }
  }
}

此时得到的结果是空,我们无法获得期望的结果,问题不在 term 查询,而在于abc#def并不在我们的倒排索引中。

②使用term精确查询searchFieldabc的记录:

{
  "query": {
    "term": {
      "searchField":"abc"
    }
  }
}

此时得到的结果是两条数据都被检索出来。

根据建立的倒排索引不难发现,当精确匹配abc时,那么会命中如下的索引,它的DocId是1,2,因此会查出两条记录。
在这里插入图片描述

解决方案

①将字段的type设置为keyword

明确字段是否需要分词,不需要分词的字段就将type设置为keyword,可以节省空间和提高写性能。
ElasticSearch 5.0以后,String字段被拆分成两种新的数据类型: text用于全文搜索,会分词,而keyword用于关键词搜索,不进行分词。对于字符串类型的字段,ES默认会再生成一个keyword字段用于精确索引。默认mapping如下:

"mapping": {
    "properties": {
      "id": {
        "type": "long"
      },
      "searchField": {
        "type": "text",
        "fields": {
          "keyword": {
            "type": "keyword",
            "ignore_above": 256
          }
        }
      }
 }

②将该字段设置成 not_analyzed 无需分析的

告诉 Elasticsearch该字段具有精确值,要将index属性设置成 not_analyzed 无需分析的。也是在mapping中进行设置,例如:

"mapping": {
    "properties": {
      "id": {
        "type": "long"
      },
      "searchField": {
        "type": "text",
        "index": "not_analyzed"
      }
 }

如果是使用Java High Level REST Client 操作Elasticsearch的话可以参考官方API进行设置。
例如:

XContentBuilder builder = XContentFactory.jsonBuilder();
builder.startObject();
{
    builder.startObject("properties");
    {
        builder.startObject("message");
        {
            builder.field("type", "text");
        }
        builder.endObject();
    }
    builder.endObject();
}
builder.endObject();
request.source(builder);

index 属性控制怎样索引字符串。它可以是下面三个值:
① analyzed:首先分析字符串,然后索引它。换句话说,以全文索引这个域。
② not_analyzed:索引这个域,所以它能够被搜索,但索引的是精确值。不会对它进行分析。
③ no:不索引这个域。这个域不会被搜索到。
注意:其他简单类型(例如 long , double , date 等)也接受 index 参数,但有意义的值只有 no 和 not_analyzed , 因为它们永远不会被分析。

  • 16
    点赞
  • 33
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值