一. Filter执行原理

filter底层原理涉及到两块内容: bitset机制和cache机制。


1.1 执行流程

假设现在有一批数据的倒排索引如下:


Word Docs

2016 1,4,5,6

2017 1,2,4,5,6

2018 1,2,3

2019 2,3,4,5,6

2020 5

搜索时,使用的filter条件如下:


GET /search

{

   "query": {

        "constant_score": {

                 "filter": {

                     "term": {

                          "field": 2016

                     }

                 }

        }

    }

}

1

2

3

4

5

6

7

8

9

10

11

12

1.1.1 构建bitset

首先,ES会从index的倒排索引中遍历Word,找到2016对应的符合搜索条件的Docs——“1,4,5,6”。

接着,ES会将"1,4,5,6"构造成一个bitset。bitset是一个二进制数组,数组的下标对应着document数据存储(插入)的顺序,数组的容量由document的数量来决定,在bitset中,0代表不符合搜索条件,1代表符合搜索条件。

比如本例中,ES将会构建一个容量为6的二进制数组,假设document插入的顺序为5,2,4,6,1,3,那么当前filter对应的bitset数组内的组成情况是: [1,0,1,1,1,0]


1.1.2 遍历bitset

一次搜索中,可能会出现多个filter,每一个filter都会对应一个bitset。ES会从最稀疏的bitset开始遍历,以达到最佳的执行效率。

bitset的稀疏性指的是包含符合过滤条件的document的数量,可以从bitset数组内1的数量体现出来。比如[1,0,1,1,1,0] 就比[1,1,1,1,1,1,]要更稀疏。

为什么从最稀疏的bitset开始过滤?因为过滤的目的是为了筛选出符合条件的数据,丢弃无用的数据。bitset越稀疏,代表筛选条件越严格,本次过滤的垃圾数据就越多。

ES5中,query内不能直接连接filter,query和filter之间需要通过bool来间隔。ES7中,and不能写在query中,所以坦白说,目前我没有发现一次搜索中出现多个filter的写法。况且完全可以在一个filter内通过bool条件达到写多个过滤条件的目前,因此没必要写多个filter


1.1.3 缓存bitset

ES会缓存bitset数据到内存中,如果后续的搜索语句中filter过滤条件在之前已经使用过,那么ES会从缓存中直接找到对应的bitset,并根据bitset数组的内容,直接通过_id搜索目标document并返回结果集。ES缓存的是bitset数组,而不是通过filter搜索出的结果。

不是所有的bitset都会被ES缓存到内存中,不进行缓存的bitset大致分为以下两种情况:

情况1: 整个index内存储document的数量不足1000条。

情况2: 索引分段后的数据不足索引中所有数据的3%。

正是因为bitset缓存,使得filter的执行效率比不使用filter时query要高。

注意: ES在缓存bitset的同时,会返回搜索结果。换句话说,"缓存"和"根据bitset的内容查询、返回结果集"这两步操作是并发执行的。


1.2 执行特性

1.2.1 query和filter的执行顺序

一般来说,ES会优先执行filter,,通过filter过滤掉一部分不符合搜索条件的数据,再执行query。况且query需要计算相关度分数来进行排序,因此执行效率低。如果先执行query,那么就会把那些不符合搜索条件的数据也进行了相关度分数计算,浪费性能。


1.2.2 bitset cache auto_update

ES缓存的bitset后,不会置之不理。如果有修改、新增、删除操作,导致原先fitler对应的bitset结构或内容发生变化时,ES会及时的更新内存中缓存的bitset,值得注意的是,这个更新操作由ES自动完成。


1.2.3 bitset cache应用的时机

只要ES中执行的query内包含filter,那么ES首先就会在缓存中搜索该filter条件是否曾经执行过,有没有对应的bitset缓存存在,若有,则使用缓存,若没有,则创建bitset,查询、返回数据,同时缓存bitset。


二. 倒排索引

2.1 倒排索引的结构:

(1)包含这个关键词的document list

(2)包含这个关键词的所有document的数量:IDF(inverse document frequency)

(3)这个关键词在每个document中出现的次数:TF(term frequency)

(4)这个关键词在这个document中的次序

(5)每个document的长度:length norm

(6)包含这个关键词的所有document的平均长度


2.2 倒排索引不可变的好处:

(1)不需要锁,提升并发能力,避免锁的问题

(2)数据不变,一直保存在os cache中,只要cache内存足够

(3)filter cache一直驻留在内存,因为数据不变,同一个过滤条件下得到的bitset一定不会变。

(4)可以压缩,节省cpu和io开销


三. 重建索引

没有哪一个架构师敢说自己设计的index完美无缺,经过一段时间的使用后,往往会根据实际的业务场景,对原有结构进行调整。但遗憾的是,ES不允许对已存在的字段进行修改和删除,比如不能修改字段的类型,字段的分词器,增加新的子字段等操作,原因是这样会倒排索引的结构发生变化。

重建索引的方法如下: 新建一个index,为其设置定制化的mapping,接着将旧数据从旧索引中迁移到新索引中。迁移数据的方法非常多,比如通过scroll滚动读取数据,接着通过bulk POST create操作,将数据批量新增至新索引。

但重建索引不可避免的带来一个问题: 需要重新创建一个新索引。如果程序中操作ES的代码在书写index时使用了硬编码,那么重建索引就会导致整个系统不可用,必须修改代码再重启系统才能恢复功能。为了避免重启系统,实现零停机,我们需要在程序中使用index的别名。


四. Document写入原理

ES为了实现进实时搜索,在写入Document时利用了Buffer(内存),OS Cache(系统缓存 集成于CPU),Disk(磁盘)三种存储方式,尽可能的提升搜索的能力。ES的底层lucene实现的,在 luncene中一个index会被分为若干个数据段segment,每一个segment都会存放index的部分document。从流程上讲,ES会先把一个index中的document分散存储在若干个shard(指的是主分片)上,在shard中,使用若干个segment来存储具体的数据。

ES写入数据的流程大致如下:



客户端发起请求(增、删、改)到ES中。

ES将本次请求要操作的document写入到buffer中。ES为了保证搜索的近实时(Near Real Time 简称 NRT),默认每秒刷新一次buffer,这个刷新时间间隔可以手动修改,也可以通过命令触发buffer的刷新。建议刷新时间间隔设置在1秒左右,好处使在服务器宕机后,只会丢失1秒左右的数据。

POST /index_name/_refresh


PUT index_name

{

  "settings": {

    "number_of_shards": 5,

    "number_of_replicas": 1,

    "refresh_interval": "1s"

  }

}

1

2

3

4

5

6

7

8

9

10

ES在将document写入到缓存的同时,也会将本次操作的具体内容写入到translog文件中,这份文件存在的意义在于即便ES宕机了,也能尽可能的减少丢失的数据(简单来说,就是把translog中的记录重新执行一遍),当然translog也不能保证数据绝对不丢失,其原因在第6点详细的讲出了。由于translog存储在磁盘Disk中,因此为了提高访问效率,ES与translog文件之间会建立并保持一个长连接(不然每次访问都要获取和释放文件流)。

步骤2中提到过ES每隔一段时间就会刷新buffer,这个刷新的动作会在内存中创建一个全新的index segment,并将buffer中的document数据全部到这个新的index segment中。值得注意的是,index segment同样是文件,只不过我们目前访问的是内存中的File(ES底层使用java开发,new File()后文件会被读取到内存当中)。

segment中存储的是buffer指定时间间隔内接收到的document写操作数据(因为Disk与内存有速度差,为了让数据持久化落盘的速度适应数据写入内存的速度,我们使用了buffer,index segment,比如数据写入了10秒,默认每秒刷新一次buffer,则产生10个index segment,而来得及写入Disk的index segment可能只有2个)。

在index segment被创建并写入了来自buffer的数据后,ES会立刻将index segment对应的File写入到系统缓存OS Cache中并打开,这样就可以立刻为客户端提供最新数据的请求服务,而不必等待index semeng写入到磁盘后,再打开index segment。毕竟IO操作是一个重量级的操作,非常费时,一定会影响ES的近实时搜索能力。

前面说了,translog中记录的是ES操作的过程,万一遇到系统宕机,在系统重启后,ES会重新读取磁盘Disk中保存的数据(一份份的 index segment文件)至系统缓存,接着读取translog中的操作日志,并逐条执行,以此来达到恢复数据的目的。ES默认每隔5秒执行一次translog文件的持久化。如果在持久化的过程中恰好ES在做document写操作,那么本次持久化操作将暂停,直到写操作彻底完成后,才继续执行。translog的持久化方式默认同步,如果修改成异步,那么在translog持久化的过程中新执行的写操作对应的日志就会丢失,如果恰好此时ES所在的服务器宕机,那么这段时间还没来得及持久化到Disk,仅位于内存中index segment或OS Cache缓存中的数据便无法恢复,永久丢失。

PUT index_name

{

  "settings": {

    "number_of_shards": 5,

    "number_of_replicas": 1,

    "index.translog.durability" : "async",

    "index.translog.sync_interval" : "5s"

  }

}

1

2

3

4

5

6

7

8

9

随着时间的推移,translog文件会不断的增大,在内存中积压的数量众多的index segment file的文件流也在不断的增大,当translog文件大到一定程度或默认30分钟执行一次,ES会自动触发commit操作。commit操作的具体内容有:

将buffer中的数据刷新到一个新的index segment中;

将index segment写入到OS Cache并打开index segment为搜索提供服务;

执行一个commit point操作,将OS Cache中所有的index segment标识记录在这个commit point中,并持久化到系统磁盘Disk;

commit point操作会触发fsync操作(file sync),将内存中已经写入数据的index segment落盘到Disk,持久化成文件。

清空本次持久化的index segment对应在translog中的日志。

按照上述的流程来看,每1秒会生成一个index segment文件,每30分钟会将index segment文件流持久化到磁盘,照这样来看,磁盘中的index segment文件会非常多,从而需要处于开启状态的index segment也非常多,在执行搜索操作时,找到数据对应的index segment就会比较费时了。为了解决这个问题,ES会自动的执行segment merge操作,merge时,被标记为deleted状态的document会被物理删除。

merge的大致流程如下:

ES会选取一些大小相近的segment文件流,合并成一个大的segment文件流(注意: segment可能是尚未持久化到磁盘的segment file,也可能是已经持久化到磁盘的segment file)。

执行commit操作,在Disk中记录commit point,这个commit point不仅包含新增的segment,还包含合并后,需要被删除的segment源文件的标识。

commit操作结束后,ES会将merge后的segment文件重新打开,为搜索提供服务,而那些旧的需要被删除的segment文件则进行关闭并物理删除。

此外,ES在执行search搜索时,目标数据可能位于不同的index segment上,因此ES会扫描所有已经开打的index segment文件并找到目标数据。文件打开指的是将数据读到了OS Cache系统缓存,而不是内存中。


对index执行document的删除和更新操作都不会立刻生效,而是标记成deleted状态。当ES空闲(比如每隔30分钟执行一次commit)或Disk存储空间不足,ES就会物理删除这些待删除的数据。(猜测在内存不足时,也会执行删除操作,因为总不能让尚未落盘的index segment大量的占用内存吧)


在buffer数据写入到segment的同时,会生成一个.del文件专门记录哪一个index segment中哪一条document是deleted状态(在merge后,这个.del文件会被更新)。因此ES搜索时,如果在多个index segment中查到了不同版本(version)的相同id值的document时,会根据.del文件中的记录来继续过滤,保证搜索结果的唯一性和正确性(比如segment1中包含一条document version=1,对应新增状态;而segment2中包含相同id的document version=2,对应更新状态。由于后者的版本号更新,因此在.del中,version1被视作旧document,会被标记成deleted状态,从而在搜索时就会得到segment2中包含的version=2的数据了)。


五. 缓存原理

Elasticsearch的缓存主要分成三类: Node Query Cache, Shard Query Cache, Fielddata Cache


5.1 Node Query Cache

5.2 Shard Query Cahce

5.3 Fielddata Cache

六. 相关度评分算法

6.1 概念

relevance score算法用于计算document与搜索条件的相关匹配度。

ES使用了term frequency / inverse document frequency 算法,简称TF/IDF算法,它是ES相关度评分算法的一部分,也是 最重要的一部分。


TF 对搜索条件进行分词后,各词条在document中指定的field内出现的次数越多,则相关度越高。

eg: 搜索条件为hello es,document1对应 {“field_test”: “hello world, I am learning es”},document2对应{“field_test”: “hello Wuhan”}。

分析:由于搜索条件分词后,在document1中出现了2次,在document2中出现了1次,因此document1的相关度分数比document2高。


IDF 对搜索条件进行分词后,统计各词条在所有document中出现的次数,出现的次数越多,则该词条在后续用于评定相关度分数时,起到的作用越低。

eg: 搜索条件为hello es,document1对应 {“field_test”: “hello world, I am learning something new”},document2对应{“field_test”: “hello es”} 其中,hello在index中出现了1000次,es出现了100次。

分析:由于es比hello的相关度评定价值更高,因此document2比document1的相关度分数要高。


Field-length norm: 在匹配成功后,计算field字段的数据长度,长度越长,则相关度分数越低。Field-length-norm是TF的一部分。

eg: 搜索条件为hello es,document1对应 {“field_test”: “hello world, I am learning something new”}, document2对应{“field_test”: “heelo es”}

分析:document1和document2都包含了相同数量的搜索词条,但是前者除了目标词条外,还包含了许多无用的数据信息,因此document1的相关度分数比document2要低。


七. Doc values

ES存储document时,会根据数据对应的field类型建立对应的索引。通常来说只创建倒排索引,倒排索引是为了搜索而存在的,但如果对数据进行排序、聚合、过滤等操作时,再使用倒排索引就明显不适合了。这个时候就需要在ES中创建正排索引(doc values)。doc values保存在磁盘中,如果OS Cache系统缓存的空间足够大,ES会缓存doc values,因此性能还是很不错的。

问: 为什么说倒排索引不适合做聚合、排序等操作?

答: 比如有数据如下:


{

  “name” : “张三”,

  “remark” : “java开发工程师”,

  "_id": 1

}

{

  “name” : “李四”,

  “remark” : “java架构工程师”,

  "_id": 2

}

1

2

3

4

5

6

7

8

9

10

为remark字段创建倒排索引:


Term Doc

java 1,2

开发 1

架构 2

工程师 1,2

如果在倒排索引的基础上进行聚合,那么到底是根据java进行聚合呢、还是根据开发或者架构来聚合呢?不难发现,用哪一个都不合适。排序也是如此。倒排索引因分词得到了许多好处,但也因此留下了弊端。

所以,为了应对这种不需要分词的需求和场景,ES设计了正排索引Doc values。Doc values不会对字段作任何分词处理,皆保留原值。正排索引的大致结构如下:


Doc values terms

Doc1 java开发工程师

Doc2 java架构工程师

ES会根据document中每个字段是否分词,有选择性的实现字段对应的倒排索引或正排索引(Doc values)。

比如,如果字段类型为keyword,long,date,那么这些类型修饰的字段一定会有正排索引(Doc values)。

比如,如果字段类型为text,则只会有倒排索引,不会主动创建正排索引。


如果想在同一个字段中,既实现正排索引,又实现倒排索引,只需要使用fielddata即可,实现方式如下:

若遇到聚合、排序等需求时,ES使用test_field.keyword,若遇到搜索需求时,ES使用test_field。


{

"mappings": {

"type": {

"properties": {

"test_field": {

"type": "text",

"analyzer": "standard",

"fields": {

"keyword": {

"type": "keyword"

}

}

}

}

}

}

}

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

八. 搜索技术深入

8.1 手工控制搜索结果的精准度

8.1.1 搜索包含查询条件分词后所有词条的数据

搜索包含first或content词条的document:


GET /test_sort/_search

{

  "query": {

    "content": {

      "remark": "first content"

    }

  }

}

1

2

3

4

5

6

7

8

升级: 搜索包含first和content词条的document:


GET /test_sort/_search

{

  "query": {

    "match": {

      "content": {

        "query": "first content",

        "operator": "and"

      }

    }

  }

}

1

2

3

4

5

6

7

8

9

10

11

上述搜索方式中,如果把operator设置成or,则查询的接口与不使用operator没有区别。and表明查询的document中必须既包含first,又包含content。(注意: 并不强制要求first和content必须紧挨着出现)


8.1.2 搜索包含查询条件分词后一定比例数量的词条的数据

在搜索document时,希望目标数据包含一定比例的查询条件分词后的词条个数,可以使用minimum_should_match,填入百分比或固定数字来实现。百分比代表需要包含搜索条件中词条的个数的百分比,如果无法整除,则向下匹配。固定数字代表至少需要包含多少个词条。


GET /test_sort/_search

{

  "query": {

    "match": {

      "content": {

        "query": "first content",

        "minimum_should_match": "50%"

      }

    }

  }

}

1

2

3

4

5

6

7

8

9

10

11

minimum_should_match可以和bool should搭配使用,should本身代表着多个条件只需要满足一个即可,如果使用了minimum_should_match=2,则代表至少满足2个条件才能被视为目标数据。


GET /test_sort/_search

{

  "query": {

    "bool": {

      "should": [

        {

          "match": {

            "content": "first"

          }

        },

        {

          "match": {

            "content": "C++"

          }

        },

        {

          "match": {

            "content": "second"

          }

        }

      ],

      "minimum_should_match": 2

    }

  }

}

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

8.1.3 match的底层转换

官方建议搜索时,尽量使用转换后的形式,执行效率更高(不需要ES自行转换了)。 在条件较少时转换请求的数据结构不会带来太明显的性能提升,但如果条件非常多,那么节约的时间就很可观了。

转换前1:


GET /test_sort/_search

{

  "query": {

    "content": {

      "content": "first content"

    }

  }

}

1

2

3

4

5

6

7

8

转换后1:


GET /test_sort/_search

{

  "query": {

    "bool": {

      "should": [

        {

          "term": {

            "content": {

              "value": "first"

            }

          }

        },

        {

          "term": {

            "content": {

              "value": "content"

            }

          }

        }

      ]

    }

  }

}

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

转换前2:


GET /test_sort/_search

{

  "query": {

    "match": {

      "content": {

        "query": "first content",

        "operator": "and"

      }

    }

  }

}

1

2

3

4

5

6

7

8

9

10

11

转换后2:


GET /test_sort/_search

{

  "query": {

    "bool": {

      "must": [

        {

          "term": {

            "content": {

              "value": "first"

            }

          }

        },

        {

          "term": {

            "content": {

              "value": "content"

            }

          }

        }

      ]

    }

  }

}

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

8.2 boost权重控制

人为的控制搜索条件在进行相关度分数计算时的权重大小。

比如搜索document中content字段内包含first的数据,如果content包含java或C++,则优先显示包含它们的数据,并且java的权重是C++的三倍。

boost权重控制一般用于搜索时搭配相关度排序使用。比如: 电商平台对商品进行综合排序。将一个商品的销量、广告费、评价值、库存、单价等信息进行比较并综合排序。排序时,库存的权重最低,广告费的权重最高。


GET /test_sort/_search

{

  "query": {

    "bool": {

      "must": [

        {

          "match": {

            "content": "first"

          }

        }

      ],

      "should": [

        {

          "match": {

             "content": {

               "query": "java",

               "boost": 3

             }

          }

        },

        {

          "match": {

             "content": {

               "query": "C++",

               "boost": 1

             }

          }

        }

      ]

    }

  }

}

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

搜索结果为:


"hits" : [

      {

        "_index" : "test_sort",

        "_type" : "sort_type",

        "_id" : "4",

        "_score" : 3.2636642,

        "_source" : {

          "content" : "first content java"

        }

      },

      {

        "_index" : "test_sort",

        "_type" : "sort_type",

        "_id" : "5",

        "_score" : 1.8323578,

        "_source" : {

          "content" : "first content C++"

        }

      },

      {

        "_index" : "test_sort",

        "_type" : "sort_type",

        "_id" : "1",

        "_score" : 0.48120394,

        "_source" : {

          "content" : "first content",

          "order" : 1

        }

      }

    ]

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

8.2.1 多shard环境中相关度分数不准确问题

在 ES的搜索结果中,相关度分数不是一定准确的。在多个shard环境中,使用相同的搜索条件得到的相关度分数可能会有误差(如果index只有一个主分片,则不会出现误差)。只要数据量达到一定的程度,那么相关度分数的误差就会逐渐趋近于0。


请看以下场景:

现在有两个主分片: shard0和shard1,它们分别持有10000条document数据,其中,shard0含有100个包含java的document,shard1含有10个包含java的document。ES在shard本地计算相关度分数(不是把数据统一上报到coordinate协调节点后,才进行相关度分数的计算),当以java作为条件进行搜索时,虽然经过TF计算出的值相同,但使用IDF算法计算相关度分数时,shard0内的得分比shard1低。


如果数据量足够大,使得含有java的document数据均匀的分布在所有分片上时,那么无论在哪个shard上计算相关度分数,得到的结果就都是相同的了。


在开发测试阶段,我们可以通过在设置settings时,将number_of_shards的值设置为1来解决问题,也可以通过增加dfs_query_then_fetch请求参数来解决问题:


GET /test_sort/_search?search_type=dfs_query_then_fetch

{

  "query": {

    "bool": {

      "must": [

        {

          "match": {

            "content": "first"

          }

        }

      ]

    }

  }

}

1

2

3

4

5

6

7

8

9

10

11

12

13

14

search_type=dfs_query_then_fetch不建议在生产环境中使用,因为它会使相关度分数的计算时机从shard本地搜索目标数据,挪到每个shard反馈到协调节点后,由协调节点统一做相关度分数的计算。由于所有的目标结果都会在协调节点汇总,因此协调节点的数据量非常大,而为了计算相关度分数又不得不把数据全部读取到内存中,因此这种方式不仅对内存的压力非常大,还会增加额外的IO开销。这就是ES官网明明知道多shard时相关度分数不准确的问题,却不得不在shard本地计算相关度分数的原因。


8.3 基于dis_max实现多字段搜索

8.3.1 dis_max的基本用法

概念


best fields策略: 搜索的document中的某一个field字段,尽可能多的匹配搜索搜索条件。

most fields策略: 尽可能多的让document中多个field参与到计算相关度分数中。显然,这种策略下搜索的精准度没有best field高。比如某个document中,虽然有一个字段包含了非常多的关键字(由搜索条件提供),但其它字段没有包含关键字。而其它document与之对应的字段中虽然没有包含这么多关键字,但胜在包含关键字的字段数目多,因此在搜索结果返回时,会被排列到前一个document之前。百度就搜索利用了most fields策略,因为往往匹配维度多的document比只匹配了寥寥几个维度(哪怕这些维度匹配的非常精准)的document,对用户更有用。

best field的实现手段: dis_max 对每个搜索条件进行评估,以单独作为条件进行搜索时,得到的最高相关度分数进行排序。

例子 单独使用条件1和条件2进行搜索时,各document计算出的相关度分数如下:


document1 document2

条件1 0.5 1.2

条件2 1.1 0.3

最大值 1.1 1.2

现在想把条件1和条件2结合在一起使用,共同进行搜索,但是希望document1得到的相关度分数为1.1,document2得到的相关度分数为1.2,各个条件互不影响,最终得到的排序结果为: document2 -> document1


做法是在搜索时使用dis_max。 可以注意到,使用dis_max时,不再配合使用should或must,而是使用queries。


GET test_dis_max/_search

{

  "query": {

    "dis_max": {

      "queries": [

        {

           "条件1"

        },

        {

          "条件2"

        }

      ]

    }

  }

1

2

3

4

5

6

7

8

9

10

11

12

13

14

8.3.2 使用tie_breaker参数优化dis_max的搜索效果

dis_max是只取多个query条件中相关度分数最高的用于排序,忽略其它的query条件对应的分数。在某些情况下,我们还需将其它query条件对应的分数加入到最后相关度分数的计算上,这个时候就可以使用tie_breaker参数来优化dis_max的功能。郑州不孕不育医院:http://www.xasgnk.com/

tie_breaker参数的含义是:将其它所有搜索条件对应的相关度分数乘以一个比例,再参与到结果的排序中。默认参数值为0,因此不定义此参数,会导致其它搜索条件不参与到结果的排序中。


GET test_dis_max/_search

{

  "query": {

    "dis_max": {

      "queries": [

        {

           "条件1"

        },

        {

          "条件2"

        }

      ],

      "tie_breaker" : 3

    }

  }

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

此时,相关度分数的计算如下:


document1 document2

条件1 0.5 1.2

条件2 1.1 0.3

最大值 1.1 1.2

最终结果 1.1 + 0.5*3 = 2.6 1.2 + 0.3*3 = 2.1

最终得到的排序结果为: document1 -> document2


8.3.3 使用multi_match简化dis_max+tie_breaker

可以通过multi_match整合boost, query, minimum_should_match, tie_breaker以及fields。使用方式如下: