Elasticsearch 通过kibana查询的结果与sdk查询的结果不同

项目场景:

提示:这里简述项目相关背景:

最近在解决一个工作中关于elasticsearch查询的一个问题,改造点是由原来的十几个字段条件的模糊查询改为7个字段的精确查找,在进行自测的时候发现一个问题如下:


问题描述

当我设置两个查询条件,字段A和字段B,然后编写DSL语句用于查询脚本如下:

GET test_index/_search
{
  "from":0,
  "size":30,
  "query": {
    "bool": {
      "should": [
        {
          "bool": {
            "should": [
              {
                "term": {
                  "name.keyword": {
                    "value": "测试名称01",
                    "boost": 1
                  }
                }
              },
              {
                "nested": {
                  "query": {
                    "term": {
                      "prins.hone": {
                        "value": "13000000001",
                        "boost": 1
                      }
                    }
                  },
                  "path": "prins",
                  "ignore_unmapped": false,
                  "score_mode": "sum",
                  "boost": 1
                }
              }
            ],
            "adjust_pure_negative": true,
            "boost": 1
          }
        }
      ],
      "adjust_pure_negative": true,
      "boost": 1
    }
  }
}

但是我使用java 的sdk使用相同的查询条件后得到的结果和kibana查询的数据有一些差别(查询出来的30条数据中有一些数据是相同的,但是有一些是不同),为什么会得到这样的结果呢,很奇怪。


原因分析:

在网上也找了一些资料,然后发现没什么有价值的东西,于是就在想,怎么能将sdk产生的dsl打印出来进行比较,于是配置了spring data elasticsearch的日志级别,如下:

# 日志配置
logging:
  level: 
    #es日志
    org.springframework.data.elasticsearch.client.WIRE:trace

打印出来的DSL如下:

经过逐个排查发现在增加了search_type=dfs_query_then_fetch 后和sdk查询的一致了,那么这个参数是干什么用的呢,源码中有SearchType这么一个枚举,就简单百度了一下:

1、query and fetch

向索引的所有分片(shard)都发出查询请求,各分片返回的时候把元素文档(document)和计算后的排名信息一起返回。这种搜索方式是最快的。因为相比下面的几种搜索方式,这种查询方法只需要去shard查询一次。但是各个shard返回的结果的数量之和可能是用户要求的size的n倍。

2、query then fetch(默认的搜索方式)

如果你搜索时,没有指定搜索方式,就是使用的这种搜索方式。这种搜索方式,大概分两个步骤,第一步,先向所有的shard发出请求,各分片只返回排序和排名相关的信息(注意,不包括文档document),然后按照各分片返回的分数进行重新排序和排名,取前size个文档。然后进行第二步,去相关的shard取document。这种方式返回的document与用户要求的size是相等的。

3、DFS query and fetch

这种方式比第一种方式多了一个初始化散发(initial scatter)计算全局词频(term frequencies)步骤,有这一步,据说可以更精确控制搜索打分和排名。先对所有分片发送请求, 把所有分片中的词频和文档频率等打分依据全部汇总到一块, 再执行后面的操作。优点很明显,数据量是准确并且排名也准确,但性能是最差的。

4、DFS query then fetch

比第2种方式多了一个初始化散发(initial scatter)计算全局词频(term frequencies)步骤,过程与上一种类似,优点是排名准确,但返回的数据量不准确,可能返回(N*分片数量)的数据。
 

有一点疑惑的是,那么默认是query then fetch,但是我用api确为什么是DFS query then fetch呢?看了一下代码没有地方设置这玩意

经过查看源码发现在NativeSearchQueryBuilder中会判断是否有SearchType参数,如果没有就不设置,不设置则实际使用了NativeSearchQuery父类AbstractQuery中的SearchType的默认值:DFS_QUERY_THEN_FETCH


解决方案:

在查询语句中添加参数search_type=dfs_query_then_fetch, 最后语句如下:

GET test_index/_search?search_type=dfs_query_then_fetch
{
  "from":0,
  "size":30,
  "query": {
    "bool": {
      "should": [
        {
          "bool": {
            "should": [
              {
                "term": {
                  "name.keyword": {
                    "value": "测试名称01",
                    "boost": 1
                  }
                }
              },
              {
                "nested": {
                  "query": {
                    "term": {
                      "prins.hone": {
                        "value": "13000000001",
                        "boost": 1
                      }
                    }
                  },
                  "path": "prins",
                  "ignore_unmapped": false,
                  "score_mode": "sum",
                  "boost": 1
                }
              }
            ],
            "adjust_pure_negative": true,
            "boost": 1
          }
        }
      ],
      "adjust_pure_negative": true,
      "boost": 1
    }
  }
}

你可能感兴趣的:(elasticsearch,大数据,搜索引擎)