当前位置 : 首页 » 文章分类 :  开发  »  Elasticsearch-搜索

Elasticsearch-搜索

Elasticsearch 搜索


Search 搜索 API

Search APIs
https://www.elastic.co/guide/en/elasticsearch/reference/current/search.html


GET /index/_search 搜索

Elasticsearch Guide [7.17] » REST APIs » Search APIs » Search API
https://www.elastic.co/guide/en/elasticsearch/reference/7.17/search-search.html

通过 _search 接口可在一个或多个索引或数据流上搜索数据,可通过 q 查询串来指定查询条件,也可以通过 body 参数指定查询条件。

在指定的索引上搜索
GET /<target>/_search
POST /<target>/_search
<target> 路径参数 target 可以是索引名、索引别名、数据流名,还 可以是逗号分割的多个索引/数据流,并且支持通配符 *

如果想在全部索引上搜索,可以使用 *_all,或者忽略 target 参数,例如:
GET /_search
POST /_search

_search 接口的一些参数既可以放在 query string 上,也可以放在 body 中,如果两个地方同时指定了,将以 query string 中的为准

seq_no_primary_term Query 或 Body 参数,boolean 类型,为 true 时返回 _seq_no_primary_term 字段。
version Query 或 Body 参数,boolean 类型,为 true 时返回 _version 字段。

如果不带任何查询参数就会返回 index 下的所有记录。

curl --location --request GET 'http://localhost:9200/article/_doc/_search' \
--header 'Content-Type: application/json' \
--data-raw ''

返回

{
    "took": 102,
    "timed_out": false,
    "_shards": {
        "total": 1,
        "successful": 1,
        "skipped": 0,
        "failed": 0
    },
    "hits": {
        "total": {
            "value": 2,
            "relation": "eq"
        },
        "max_score": 1.0,
        "hits": [
            {
                "_index": "article",
                "_type": "_doc",
                "_id": "uv_ZjXEBrN9oq5tgVMuj",
                "_score": 1.0,
                "_source": {
                    "title": "es的使用",
                    "pathname": "/article/es",
                    "content": "新增记录的时候,也可以不指定 Id,这时要改成 POST 请求。"
                }
            },
            {
                "_index": "article",
                "_type": "_doc",
                "_id": "u__ojXEBrN9oq5tgGcul",
                "_score": 1.0,
                "_source": {
                    "title": "java基础",
                    "pathname": "/article/java-basic",
                    "content": "java基础介绍"
                }
            }
        ]
    }
}

track_total_hits 命中个数统计

track_total_hits query 参数,integer 或 bool 类型。此设置用于确定 Elasticsearch 是否应该精确地追踪与查询匹配的总命中数。

  • 如果设为一个整数,表示精确计算命中个数的最大值,默认值 10000,查询条件命中的文档个数超过此值时不再精确计算命中个数。
  • 如果设为 true 将每次都精确计算命中个数,会比较耗性能。
  • 如果设为 false 将完全不计算命中个数,返回结果无 hits.total 字段。

默认 _search 接口返回的 hits.total.value 值最大为 10000,搜索条件命中的文档个数超过 10000 时就不准了
默认情况下,Elasticsearch 会为最多 10,000 个命中提供精确计数。超出这个数字后,它会返回一个下限估算(例如,“10,000+”)。
命中个数大于 10000 时,hits.total.relation=gte 表示实际命中个数是大于 10000 的。
命中个数小于 10000 时,hits.total.relation=eq value 是准确的命中数。

{
    "hits":{
        "total":{
            "value":10000,
            "relation":"gte"
        }
    }
}

_source 指定返回字段

_source 参数指定返回结果 hits._source 中的字段数,默认值为 true

_source 参数可放在 query string,也可以放在 body 中。

放在 query string 中时,支持以下值:

  • true 返回文档的全部字段
  • false 不返回任何字段
  • <string> 逗号分割的字段名列表,支持 * 通配符

放在 body 中时可内嵌 excludesexcludes 字段

比如只返回文档的 _iduser_id 字段

{
    "_source": ["_id", "user_id"],
  "sort": {
      "user_id": {
          "order": "asc"
      }
  }
}

指定返回的和排除的

GET /_search
{
    "_source": {
        "includes": [ "obj1.*", "obj2.*" ],
        "excludes": [ "*.description" ]
    },
    "query" : {
        "term" : { "user" : "kimchy" }
    }
}

The _source option
https://www.elastic.co/guide/en/elasticsearch/reference/current/search-fields.html#source-filtering


q查询(URI搜索/lucene语法查询)

https://www.elastic.co/guide/en/elasticsearch/reference/7.17/search-search.html#search-api-query-params-q

q 搜索就是 URI 搜索,通过 URI 参数 q 来指定查询相关参数。q 搜索使用 Lucene 语法,不支持完整的 ES DSL 语法,但让我们可以快速做一个查询。
q 搜索会覆盖 body 中的 query 查询参数,如果同时指定,只会使用路径中的 q 参数。

例1、从索引 tweet 里面搜索字段 user 为 kimchy 的记录
GET /tweet/_search?q=user:kimchy

例2、从索引 tweet,user 里面搜索字段 user 为 kimchy的记录
GET /tweet,user/_search?q=user:kimchy
GET /kimchy,elasticsearch/_search?q=tag:wow

例3、从所有索引里面搜索字段 tag 为 wow 的记录
GET /_all/_search?q=tag:wow
GET /_search?q=tag:wow


highlight 高亮搜索

Request Body Search
https://www.elastic.co/guide/en/elasticsearch/reference/7.6/search-request-body.html#request-body-search-highlighting

指定单个高亮字段:

GET /_search
{
    "query" : {
        "match": { "content": "kimchy" }
    },
    "highlight" : {
        "fields" : {
            "content" : {}
        }
    }
}

指定多字段高亮:

{
    "query": {
        "multi_match": {
            "query": "产品开发",
            "type": "cross_fields",
            "fields": ["title", "content", "summary"]
        }
    },
    "_source": ["_id", "title", "fileName"],
    "from": 0,
    "size": 10,
    "highlight": {
        "fields": {
            "title": {},
            "content": {},
            "summary": {}
        }
    }
}

指定高亮标签和高亮片段fragment大小:

GET /_search
{
    "query" : {
        "match": { "content": "kimchy" }
    },
    "highlight" : {
        "pre_tags": [
            "<span style=\"color:red\">"
        ],
        "post_tags": [
            "</span>"
        ],
        "fragment_size": 40,
        "fields" : {
            "content" : {}
        }
    }
}

sort 排序

Elasticsearch Guide [7.17] » Search your data » Sort search results
https://www.elastic.co/guide/en/elasticsearch/reference/7.17/sort-search-results.html

默认按相关性 _score 倒序

在 Elasticsearch 中,相关性得分 由一个浮点数进行表示,并在搜索结果中通过 _score 参数返回, 默认排序是 _score 降序。
指定根据 _score 排序时,默认是倒序 desc,指定其他排序字段时,默认是升序 asc

track_scores 始终计算得分

如果排序字段中同时指定 _score 和其他字段,返回结果有 _score 值,为相关性得分。
如果排序字段中无 _score,只有其他字段,默认不会再计算 _score 分数,返回结果中的 _score 为 null
如果设置 track_scores 为 true,会始终计算 _score 值(无 _score 时值为0)

多字段排序

例1、单字段排序,如按 user_id 升序排序

{
    "sort": {
        "user_id": {
            "order": "asc"
        }
    }
}

例2、多字段排序
结果首先按第一个条件排序,仅当结果集的第一个 sort 值完全相同时才会按照第二个条件进行排序,以此类推。

{
    "sort": [
        { "date":   { "order": "desc" }},
        { "_score": { "order": "desc" }}
    ]
}

或者 先 _score 倒序,_score 相同的按 _id 升序

{
  "sort": [
    { "_score": { "order": "desc" } },
    { "_id": { "order": "asc" } }
  ]
}
多值字段排序(mode:min/max/avg/sum)

一种情形是字段有多个值的排序,这些值并没有固有的顺序;一个多值的字段仅仅是多个值的包装,这时应该选择哪个进行排序呢?
对于数字或日期,你可以将多值字段减为单值,这可以通过使用 min, max, avg, sum, median 等排序模式 。
例如你可以按照每个 date 字段中的最早日期进行排序,通过以下方法:

{
  "sort": {
    "dates": {
        "order": "asc",
        "mode":  "min"
    }
  }
}
值缺失时的处理(missing)

missing 参数自定对于没有排序字段的文档,如何进行排序,可设置为 _last 或 _first 或指定值,例如:

"sort" : [
  { "price" : {"missing" : "_last"} }
]
排序字段的返回值(format)

返回结果的 sort 字段中有每个字段的排序字段值。
例如指定 _score 和 _id 排序,结果中每个文档的 sort 字段是当前文档的 _score 和 _id 的值:

{
  "took": 17,
  "timed_out": false,
  "_shards": { "total": 1, "successful": 1, "skipped": 0, "failed": 0 },
  "hits": {
    "total": { "value": 24, "relation": "eq" },
    "max_score": null,
    "hits": [
      {
        "_index": "index-1",
        "_type": "_doc",
        "_id": "q4a2lI8B3xTCjXouI7Gk",
        "_score": 2.0,
        "_source": { "id": "85cb46be02274ad8ab02b4dfd285a1e3" },
        "sort": [ 2.0, "q4a2lI8B3xTCjXouI7Gk" ]
      },
      {
        "_index": "index-2",
        "_type": "_doc",
        "_id": "rIa2lI8B3xTCjXouI7Gk",
        "_score": 1.9851243,
        "_source": { "id": "f05386cf20b34db8aa5272e8b8e4d2be" },
        "sort": [ 1.9851243, "rIa2lI8B3xTCjXouI7Gk" ]
      }
    ]
  }
}

可通过 format 参数指定比如 date 等字段在返回结果 sort 字段中值的格式,例如:

"sort" : [
  { "post_date" : {"format": "strict_date_optional_time_nanos"}}
]
嵌套字段的排序

分页

Elasticsearch Guide [7.17] » Search your data » Paginate search results
https://www.elastic.co/guide/en/elasticsearch/reference/7.17/paginate-search-results.html

from/size 分页

和 SQL 使用 LIMIT 关键字返回单个 page 结果的方法相同,Elasticsearch 接受 from 和 size 参数:
from 参数,可放在路径中,也可放在 body 中,指定应该跳过的初始结果数量,默认是 0
size 参数,可放在路径中,也可放在 body 中,指定应该返回的结果数量,默认是 10
默认 from + size 的值不可超过 10000,可通过参数 index.max_result_window 增加这个限制值,但这会增加内存消耗,因为Elasticsearch必须为每一个可能的结果保留状态。如果需要对多于 10000 的数据进行分页,可以用 search_after 参数

如果每页展示 5 条结果,下面 3 个请求分别查询第 1 到 3 页的结果:

GET /_search?size=5
GET /_search?size=5&from=5
GET /_search?size=5&from=10
分布式系统中的深度分页问题

假设在一个有 5 个主分片的索引中搜索。 当我们请求结果的第一页(结果从 1 到 10 ),每一个分片产生前 10 的结果,并且返回给协调节点 ,协调节点对 50 个结果排序得到全部结果的前 10 个。

现在假设我们请求第 1000 页—​结果从 10001 到 10010 。所有都以相同的方式工作除了每个分片不得不产生前10010个结果以外。 然后协调节点对全部 50050 个结果排序最后丢弃掉这些结果中的 50040 个结果。

可以看到,在分布式系统中,对结果排序的成本随分页的深度成指数上升。
所以 ES 就默认限制最多只能访问前 1w 个文档。这是通过 index.max_result_window 控制的。

es 目前支持最大的 skip 值是 index.max_result_window ,默认为 10000 。
也就是当 from + size > index.max_result_window 时,es 将返回错误
临时解决方法可以将 index.max_result_window 调高,但不是长久解决方案。

关于分库分表后的分页查询,58 架构师 沈剑 的这篇文章写的非常好
业界难题-“跨库分页”的四种方案
https://cloud.tencent.com/developer/article/1048654


scroll分页(缓存快照,无法跳页,不再推荐)

https://www.elastic.co/guide/en/elasticsearch/reference/7.17/paginate-search-results.html#scroll-search-results

ES 不再推荐 scroll 分页,深度分页应使用 search_after 代替

为了满足深度分页的场景,es 提供了 scroll 的方式进行分页读取。
原理是缓存首次查询的结果快照,之后从每次根据游标 scroll_id 从快照中取数据。
原理上是对某次查询生成一个游标 scroll_id,后续的查询只需要根据这个游标去取数据,直到结果集中返回的 hits 字段为空,就表示遍历结束。scroll_id 的生成可以理解为建立了一个临时的历史快照,在此之后的增删改查等操作不会影响到这个快照的结果。

scroll 分页过程:
1、初始化时将所有符合搜索条件的搜索结果缓存起来,可以想象成快照;

GET fs/_search?scroll=3m
{
  "query": {"match_all": {}}
}

初始化的时候就像是普通的 search 一样
其中的 scroll=3m 代表当前查询的数据缓存 3 分钟
结果会返回一个 _scroll_id 用于之后使用

2、遍历时,从这个快照里取数据;
在遍历时候,拿到上一次遍历中的 _scroll_id,然后带 scroll 参数,重复上一次的遍历步骤,直到结果集中返回的 hits 字段为空,表示遍历完成。
每次都要传参数 scroll,刷新搜索结果的缓存时间,另外不需要指定 index 和 type(不要把缓存的时时间设置太长,占用内存)。
请求指定的 scroll_id 时就不需要 /index/_type 等信息了。每读取一页都会重新设置 scroll_id 的生存时间
如果 srcoll_id 的生存期很长,那么每次返回的 scroll_id 都是一样的,直到该 scroll_id 过期,才会返回一个新的 scroll_id。

scroll 快照分页的问题:
1、Search context 开销不小。
2、是一个临时快照,并不是实时的分页结果。

https://www.elastic.co/guide/en/elasticsearch/reference/current/search-request-body.html#request-body-search-scroll


search_after(实时分页,无法跳页,推荐)

https://www.elastic.co/guide/en/elasticsearch/reference/7.17/paginate-search-results.html#search-after

ES 5.0 开始推出了 Search After 分页机制,可提供更实时的分页游标(live cursor),它的思想是利用上一页的分页结果来加速下一页的分页查询。

search_after 分页使用前一页中的一组排序值来检索匹配的下一页数据。
使用 search_after 分页需要多次请求的 query 和 sort 参数完全一致,如果在处理多个请求期间发生了 refresh 操作(也就是有新数据写入),则结果可能不一致。为避免这种情况发生,可以使用 PIT 时间点生成数据快照

第一次查询时指定唯一且稳定的分页方式

GET twitter/tweet/_search
{
    "size": 10,
    "query": {
        "match" : {
            "title" : "elasticsearch"
        }
    },
    "sort": [
        {"date": "asc"},
        {"_id": "desc"}
    ]
}

这里为了避免 sort 字段相同值的导致排序不确定,这里增加了 _id 字段。
返回的结果会包含每个文档的 sort 字段的 sort value。这个就是上面所说的 分页游标(live cursor)

使用最后一个文档的 sort value 作为 search after 请求值,我们就可以这样子请求下一页结果了:

GET twitter/tweet/_search
{
    "size": 10,
    "query": {
        "match" : {
            "title" : "elasticsearch"
        }
    },
    "search_after": [1463538857, "654323"],
    "sort": [
        {"date": "asc"},
        {"_id": "desc"}
    ]
}

search_after 使用方式上跟 scroll 很像,但是相对于 scroll 它是无状态的(stateless),没有 search context 开销;而且它是每次请求都实时计算的,所以也没有一致性问题(相反,有索引变化的话,每次排序顺序会变化呢)。但是比起 from+size 方式,还是有同样的问题没法解决:就是只能顺序的翻页,不能随意跳页。

app 中的信息流翻页很适合这种方式,因为无法跳页。

问题:
1、无法跳页
2、有新的符合查询条件的数据被插入后,会查询到上一页已经返回的数据。
比如用户当前正在看第 2 页的历史数据,如果此时后台数据源新增了一条数据,那么当用户继续上推操作查看第 3 页的历史数据时,就会把第 2 页的最后一条数据获取,并且会把该条数据作为第3页的第一条数据进行展示
解决方法: 查询时参数代入 上次最后一条数据的 create_time ,下一页的数据都必须大于这个 create_time


GET /index/_count 条件计数

Count API
https://www.elastic.co/guide/en/elasticsearch/reference/current/search-count.html

使用和 _search 接口相同的查询条件,可用于查询符合条件的文档个数。

ignore_unavailable=true 索引不存在时不报错

默认情况下,如果索引不存在,返回 index_not_found_exception 错误
如果希望在索引不存在时不报错,可以使用 ignore_unavailable=true 选项。这个选项会让 Elasticsearch 忽略那些在执行操作时不存在的索引。

索引不存在时返回:

POST /index1/_count?ignore_unavailable=true
{
  "count": 0,
  "_shards": {
    "total": 0,
    "successful": 0,
    "skipped": 0,
    "failed": 0
  }
}

PIT 时间点

Elasticsearch Guide [7.17] » REST APIs » Search APIs » Point in time API
https://www.elastic.co/guide/en/elasticsearch/reference/7.17/point-in-time-api.html

在 Elasticsearch 中搜索数据时默认都是在最新实时数据上进行,有些情况下,比如 search_after 分页中需要将 ES 数据固定在一个数据快照上,此时可用 时间点(point in time, PIT) 解决。

POST /index/_pit 时间点

搜索请求前需要先显式地创建时间点,keep_alive 参数指定时间点的有效时间,例如:

POST /my-index-000001/_pit?keep_alive=1m

返回一个 id 字段如下:

{
    "id": "64O1AwESdmVoaWNsZV9mb3VyX3doZWVsFmhMNWNLdnJ0U0NDV2tRUGtWTXdSd1EAFk9LV3JSdzEwUTVPRW1RZksyYjd2bUEAAAAAAAAAAAEWa0p4SnloRFFRTEttVDRmOUJnN0ttZwABFmhMNWNLdnJ0U0NDV2tRUGtWTXdSd1EAAA=="
}

下次请求时将 id 的值放入 pit.id 参数中传过去,例如

POST /_search 
{
    "size": 100,
    "query": {
        "match" : {
            "title" : "elasticsearch"
        }
    },
    "pit": {
        "id":  "46ToAwMDaWR5BXV1aWQyKwZub2RlXzMAAAAAAAAAACoBYwADaWR4BXV1aWQxAgZub2RlXzEAAAAAAAAAAAEBYQADaWR5BXV1aWQyKgZub2RlXzIAAAAAAAAAAAwBYgACBXV1aWQyAAAFdXVpZDEAAQltYXRjaF9hbGw_gAAAAA==", 
        "keep_alive": "1m"  
    }
}

注意:

  • 带有 pit 参数的 search 请求不能指定 index, routing 和 preference 参数,这些参数都从 PIT 时间点中获取。
  • pit.id 告诉 es 在指定的 PIT 时间点上下文中进行查询。
  • keep_alive 参数告诉 es 本次查询需要将 PIT 时间点的有效期延长多长时间。这个参数不需要太大,只需要够本次请求执行即可。

PIT 时间点会消耗磁盘和内存资源

PIT 时间点会消耗磁盘和内存资源:

  • 打开 PIT 时间点会阻止删除不需要的 Lucene Segment,因为这些 segment 还要用到,所以会占用磁盘空间。
  • 打开过多的 PIT 时间点会占用堆内存,尤其在频繁删除、更新的索引上。

DELETE /_pit 删除时间点

超过 keep_alive 时间的 PIT 时间点会自动删除。
为了节省资源,不用的时间点应该及时主动关闭:

DELETE /_pit
{
    "id" : "46ToAwMDaWR5BXV1aWQyKwZub2RlXzMAAAAAAAAAACoBYwADaWR4BXV1aWQxAgZub2RlXzEAAAAAAAAAAAEBYQADaWR5BXV1aWQyKgZub2RlXzIAAAAAAAAAAAwBYgACBXV1aWQyAAAFdXVpZDEAAQltYXRjaF9hbGw_gAAAAA=="
}

返回:

{
   "succeeded": true, 
   "num_freed": 3     
}

“succeeded”: true 表示和此时间点相关的所有搜索上下文都已被清除,num_freed 是删除的搜索上下文的个数。


slice 搜索分片

https://www.elastic.co/guide/en/elasticsearch/reference/7.17/point-in-time-api.html#search-slicing


Query DSL

Elasticsearch Guide [7.17] » Query DSL
https://www.elastic.co/guide/en/elasticsearch/reference/7.17/query-dsl.html

DSL(Domain Specific Language) 领域特定语言,Elasticsearch 里描述检索条件的一种语言,格式为 JSON,包括两种类型的语句:

  • Leaf query clauses 叶子查询,叶子查询匹配具体字段的值,例如 match, term, range,叶子查询可单独使用。
  • Compound query clauses 复合查询,以逻辑方式组合多个叶子、复合查询为一个查询,例如 bool

term 精确词查询
match 匹配查询
multi_match 多条件查询


查询(Query)和过滤(Filter)上下文

Query and filter context
https://www.elastic.co/guide/en/elasticsearch/reference/current/query-filter-context.html

默认情况下,es 会根据相关性对每个匹配结果进行排序,相关性描述每个文档和查询条件的匹配度。
相关性分值是个正的浮点数,在检索结果每个文档的 _score 字段返回,这个值越大,表示此文档和查询条件越相关。
相关性分值计算取决于查询子句是在 query 上下文中还是在 filter 上下文中。

查询(Query)上下文

query 上下文中的查询语句解决 “这个文档有多匹配这个查询条件?” 的问题,所以除了决定文档是否匹配,还会计算一个相关性分值放到 _score 字段返回。
查询条件中有 query 参数时,就是 query 上下文。

过滤(Filter)上下文

filter 上下文中的查询语句解决 “这个文档是否匹配这个查询条件?” 的问题,答案只有“是”或“不是”,不需要评分。filter 上下文经常用于结构化数据的过滤,例如:
timestamp 是否在 2015 ~ 2016 范围内?
status 是否等于 published?

频繁使用的过滤查询会被 es 自动缓存,用于提高查询性能。

查询条件中有 filter 参数时,就是 filter 上下文。例如:

  • bool 查询中的 filter 或 must_not 参数
  • constant_score 查询中的 filter 参数
  • filter 聚合查询

Query和Filter的区别

query 需要计算相关性,按照分数进行排序,而且无法cache结果
filter 不需要计算相关性,且会缓存频繁查询的 filter 结果,所以 filter 会更快

如果是用于搜索,需要将最相关的数据先返回,那么用 query
如果只是要根据一些条件筛选出一部分数据,不关注其相关性排序,那么用 filter

默认情况下,ES 通过一定的算法计算返回的每条数据与查询语句的相关度,并通过 score 字段来表征。
但对于非全文索引的使用场景,用户并不 care 查询结果与查询条件的相关度,只是想精确的查找目标数据。
此时,可以通过 query-bool-filter 组合来让 ES 不计算 score,并且尽可能的缓存 filter 的结果集,供后续包含相同 filter 的查询使用,提高查询效率。


minimum_should_match 参数

minimum_should_match 最小匹配数量。例如,如果设置为 2,那么至少有两个 term 匹配文档才会被认为是匹配的。

  • 正整数,例如3,表示最小匹配词数量
  • 负整数,例如-2,query 分词后的词数量减去2
  • 百分比,例如75%,query 分词后 75% 的 term 匹配
  • 负百分比,例如-25%,query 分词后 (1-25%) 的 term 匹配
  • 3<90%,query 分词数小于等于3则全都需要匹配,分词数大于3则匹配 90%
  • 2<-25% 9<-3,query 分词数小于等于2则全都需要匹配,分词数在3-9之间则需匹配75%,分词数大于9则总数-3个分词需要匹配。

Elastic Docs ›Elasticsearch Guide [7.17] ›Query DSL minimum_should_match parameter
https://www.elastic.co/guide/en/elasticsearch/reference/current/query-dsl-minimum-should-match.html


复合查询(Compound Query)

Compound queries
https://www.elastic.co/guide/en/elasticsearch/reference/current/compound-queries.html

复合查询以逻辑的方式将多个叶子、复合查询组合为一个查询,可能是对查询结果和分数的合并,可能改变其行为,或者从 query 上下文切换为 filter 上下文。


Boolean 组合查询

Boolean query
https://www.elastic.co/guide/en/elasticsearch/reference/current/query-dsl-bool-query.html

bool 查询将多个查询子句组合到一起,多个子查询之间的逻辑关系是与(and),返回的文档必须匹配全部子查询。
支持的查询类型有:

  • must 返回文档必须匹配 must 条件,会影响相关性得分。多个 must 查询的得分相加得到最终得分
  • should 返回文档应当匹配 should 条件,会影响相关性得分。可通过 minimum_should_match 参数指定至少匹配几个 should 条件。
  • filter 返回文档必须严格匹配 filter 条件,在 过滤上下文(Filter Context) 中执行,和 must 唯一的区别是不影响相关性得分。
  • must_not 返回文档必须不匹配 must_not 条件,也在 过滤上下文(Filter Context) 中执行,不影响相关性得分。

查询语句同时包含 must 和 should 时,返回的文档必须满足 must 条件但可以不满足 should 条件,因为 must 条件优先级高于 should,但是如果也满足 should 条件,则会提高相关性得分。
bool 查询中,每个 must 或 should 条件中的得分会相加,成为最终结果的 _score 得分

例1、

POST _search
{
  "query": {
    "bool" : {
      "must" : {
        "term" : { "user.id" : "kimchy" }
      },
      "filter": {
        "term" : { "tags" : "production" }
      },
      "must_not" : {
        "range" : {
          "age" : { "gte" : 10, "lte" : 20 }
        }
      },
      "should" : [
        { "term" : { "tags" : "env1" } },
        { "term" : { "tags" : "deployed" } }
      ],
      "minimum_should_match" : 1,
      "boost" : 1.0
    }
  }
}

例2、3种不同的 filter 条件:时间范围+颜色多选+名字单选

{
  "query": {
    "bool": {
      "filter": [
        {
          "range": {
            "timestamp": {
              "gte": 1645157343381,
              "lte": 1646053743440
            }
          }
        },
        {
          "terms": {
            "color": [ "青色", "黄色" ]
          }
        },
        {
          "term": { "name": "小明" }
        }
      ]
    }
  },
  "sort": {
    "timestamp": {
      "order": "desc"
    }
  },
  "from": 500,
  "size": 20
}

minimum_should_match 最小匹配个数

通常 should 子句是个数组,返回的文档必须匹配 minimum_should_match 个 should 条件。
通过 minimum_should_match 参数指定返回文档需要匹配的 should 子句的个数或百分比。
如果 bool 查询包含至少一个 should 子句且没有 must 和 filter 子句,则 minimum_should_match 默认值为 1,否则默认值为 0


adjust_pure_negative 调整纯否定查询的结果

adjust_pure_negative 参数,用于调整纯否定查询的结果
这个参数的作用是在 must、filter、should 条件都不存在,只有 must_not 条件时,如何处理查询。

  • adjust_pure_negative=true 时,如果所有查询条件都不存在,只有 must_not 条件,则 must_not 会被忽略,ES 内部会加上一个 must match_all 条件,返回所有的文档。
  • adjust_pure_negative=false 时,如果所有查询条件都不存在,只有 must_not 条件,那么 must_not 查询会被独立执行,返回不满足 must_not 条件的所有文档。

默认情况下,adjust_pure_negative 是 true,即如果没有任何其他的查询条件,must_not 查询会被忽略,返回所有的文档。


_name 命名查询

每个查询都可以有个 _name 顶层参数,可以指定查询的名字,指定 _name 后查询结果中的每个文档会有个 matched_queries 字段,指明返回的文档匹配了哪个 query

例如

{
  "query": {
    "bool": {
      "must": {
        "match_all": {
          "_name": "must_match_all"
        }
      },
      "filter": {
        "terms": {
          "_name": "filter_terms_name",
          "name": [
            "xiaoming"
          ]
        }
      }
    }
  }
}

返回结果中每个 hit 都有 matched_queries 字段指明匹配的 query

{
  "hits": {
    "total": {
      "value": 107,
      "relation": "eq"
    },
    "max_score": 1.0,
    "hits": [
      {
        "_index": "student_index",
        "_type": "_doc",
        "_id": "UsVlD5ABC1Bn9HhmZtdb",
        "_score": 1.0,
        "_source": {
          "name": "xiaoming"
        },
        "matched_queries": [
          "filter_terms_intention",
          "must_match_all"
        ]
      }
    ]
  }
}

query.bool.filter 查询的得分

只有 filter 查询的话,只在 过滤上下文(Filter Context) 中执行过滤筛选,结果的相关性得分 _score 固定是 0.0

{
  "query": {
    "bool": {
      "filter": {
        "term": {
          "status": "active"
        }
      }
    }
  }
}

filter 之外增加一个 must match_all 查询,则整个查询会在 查询上下文(Query Context) 中执行,结果的相关性得分 _score 固定是 1.0

{
  "query": {
    "bool": {
      "must": {
        "match_all": {}
      },
      "filter": {
        "term": {
          "status": "active"
        }
      }
    }
  }
}

多个 must/should 子查询得分相加

bool 查询中,每个 must 或 should 条件中的得分会相加,成为最终结果的 _score 得分
下面 must 中有两个 script_score 查询,每个里面是和一个向量字段的 cosineSimilarity 余弦相似度值(0-2),则结果得分是两者相加(0-4)

{
  "from": 0,
  "size": 12,
  "query": {
    "bool": {
      "must": [
        {
          "script_score": {
            "query": {
              "bool": {
                "adjust_pure_negative": true,
                "boost": 1.0
              }
            },
            "script": {
              "source": "cosineSimilarity(params.queryVector, 'title_vec')+1",
              "lang": "painless",
              "params": {
                "queryVector": [
                  -0.0017843246,
                  -0.015037537,
                  ...
                ]
              }
            },
            "boost": 1.0
          }
        },
        {
          "script_score": {
            "query": {
              "bool": {
                "adjust_pure_negative": true,
                "boost": 1.0
              }
            },
            "script": {
              "source": "cosineSimilarity(params.queryVector, 'content_vec')+1",
              "lang": "painless",
              "params": {
                "queryVector": [
                  -0.0017843246,
                  -0.015037537,
                  ...
                ]
              }
            },
            "boost": 1.0
          }
        }
      ],
      "filter": [
        {
          "bool": {
            "should": [
              {
                "terms": {
                  "name": [
                    "xiaoming"
                  ],
                  "boost": 1.0
                }
              }
            ],
            "adjust_pure_negative": true,
            "minimum_should_match": "1",
            "boost": 1.0
          }
        }
      ],
      "adjust_pure_negative": true,
      "boost": 1.0
    }
  },
  "min_score": 1.855
}

直接query 与 query.bool.must

如果只有一个条件,并且没有其他复杂的查询逻辑需要组合,那么直接写 query 和将其放入 query.bool.must 中在功能上是等价的。这两种方式都会对这个单一条件进行评估,并且返回满足该条件的文档。

直接写 query 条件:

{
  "query": {
    "match": {
      "field": "value"
    }
  }
}

将条件放入 query.bool.must 中:

{
  "query": {
    "bool": {
      "must": [
        {
          "match": {
            "field": "value"
          }
        }
      ]
    }
  }
}

在这两种情况下,Elasticsearch 都会查找字段 field 包含 value 的文档。虽然它们在结构上不同,但最终的结果集将是相同的。


Boosting 反向减分查询

Boosting query
https://www.elastic.co/guide/en/elasticsearch/reference/current/query-dsl-boosting-query.html

返回匹配 positive 条件的文档,对于匹配 negative 条件的文档会降低其相关性分数。
boosting 查询不会将匹配 negative 条件的文档从结果中删除,只是降低他们的相关性得分。如果想完全排除这些文档,应该使用 bool 查询的 must_not 子句。

boosting 查询有两个必填参数:positive 和 negative,以及一个可选的 negative_boost 因子:

  • positive:这个参数定义一个查询,所有匹配这个查询的文档都会被包含在结果中。
  • negative:这个参数也定义一个查询,但是所有匹配这个查询的文档的得分都会被降低。
  • negative_boost:这个参数是一个小于1的浮点数,用于降低匹配 negative 查询的文档的得分。

如果一个文档同时满足 positive 和 negative 条件,他的得分计算方式为:
1、计算此文档在 positive 条件下的相关性得分
2、将得分乘以 negative_boost 获得最终得分

例如,所有包含 apple 的文档都会被包含在结果中。然而,如果这些文档同时也包含 pie,那么它们的得分就会被降低 50%

{
  "query": {
    "boosting": {
      "positive": {
        "term": {
          "text": "apple"
        }
      },
      "negative": {
         "term": {
           "text": "pie"
         }
      },
      "negative_boost": 0.5
    }
  }
}

constant_score 常数得分查询

https://www.elastic.co/guide/en/elasticsearch/reference/7.17/query-dsl-constant-score-query.html

constant_score 查询将所有匹配的文档的得分设置为相同的常数值。
这在你想忽略文档与查询的相关性,只关心文档是否满足查询条件的场景中非常有用。

constant_score 查询接收两个参数:filter 和 boost

  • filter:这个参数定义了一个 filter 查询,所有匹配这个查询的文档都会被包含在结果中。注意 filter 查询不计算相关性得分
  • boost:这个参数是一个浮点数,所有匹配的文档的得分都会被设置为这个值,默认是 1.0

例如,所有 category 字段值为 book 的文档都会被包含在结果中,并且它们的得分都会被设置为1.2

{
  "query": {
    "constant_score": {
      "filter": {
        "term": {
          "category": "book"
        }
      },
      "boost": 1.2
    }
  }
}

dis_max 分离最大化查询

https://www.elastic.co/guide/en/elasticsearch/reference/current/query-dsl-dis-max-query.html

分离最大化查询(Disjunction Max Query)将任何与任一查询匹配的文档作为结果返回,但 _score 评分只计算最佳匹配的评分。
dis_max 查询,只取分数最高的那个 query 的分数,但返回结果是全部 query 匹配的结果。

例如在 title 和 body 上进行 term 查询:

GET /_search
{
  "query": {
    "dis_max": {
      "queries": [
        { "term": { "title": "Quick pets" } },
        { "term": { "body": "Quick pets" } }
      ],
      "tie_breaker": 0.7
    }
  }
}

dis_max 查询的得分 = 最佳匹配的得分 + sum(其他query匹配的得分 * tie_breaker)
tie_breaker 参数,默认是 0.0,非最佳匹配 query 的分值权重,取值是 0 到 1.0 之间的小数值。


Match 全文查询

Elasticsearch Guide [7.17] » Query DSL » Full text queries
https://www.elastic.co/guide/en/elasticsearch/reference/7.17/full-text-queries.html


match 分词查询

Elasticsearch Guide [7.17] » Query DSL » Full text queries » Match query
https://www.elastic.co/guide/en/elasticsearch/reference/7.17/query-dsl-match-query.html

match 查询会对入参的 text 进行分词后再和文档匹配。

例如

GET /_search
{
  "query": {
    "match": {
      "message": {
        "query": "this is a test"
      }
    }
  }
}

可以将 match 查询的 <field>query 合并简化,例如:

GET /_search
{
  "query": {
    "match": {
      "message": "this is a test"
    }
  }
}

例如,使用 match 查询 content 字段中的 java 关键词

{
    "query" : {
        "match" : {
            "content" : "java"
        }
    }
}'

结果:

{
    "took": 2,
    "timed_out": false,
    "_shards": {
        "total": 1,
        "successful": 1,
        "skipped": 0,
        "failed": 0
    },
    "hits": {
        "total": {
            "value": 1,
            "relation": "eq"
        },
        "max_score": 0.9336268,
        "hits": [
            {
                "_index": "article",
                "_type": "_doc",
                "_id": "u__ojXEBrN9oq5tgGcul",
                "_score": 0.9336268,
                "_source": {
                    "title": "java基础",
                    "pathname": "/article/java-basic",
                    "content": "java基础介绍"
                }
            }
        ]
    }
}

match 查询的参数

query 输入的查询文本

analyzer 对查询语句进行分词的分词器,默认是对应字段的分词器。

operator 对query进行分词后,多个 term 之间的关系,默认 OR,可选 AND

  • OR 只要文档中包含任何一个查询词,就会被认为是匹配的
  • AND 那么只有文档中包含所有查询词,才会被认为是匹配的

prefix_length 用于模糊查询。它指定了一个词的前缀长度,只有这个长度之后的字符才会被模糊匹配。默认0。

max_expansions 用于模糊查询和前缀查询。它指定了最大的扩展词数量,也就是说,查询词可以被扩展成多少个近义词。默认50。

fuzzy_transpositions 用于模糊查询。如果设置为 true,那么查询词中的字符可以被交换位置。例如,如果查询词是 “abcd”,那么 “abdc” 也会被认为是匹配的。默认true。

fuzziness 这个参数用于模糊查询。它指定了允许的最大编辑距离,也就是说,查询词和文档中的词之间可以有多少个字符的差异。

lenient 如果设置为 true,那么如果字段不存在或者查询词格式错误,Elasticsearch 会忽略这个错误,而不是抛出异常。默认false。

zero_terms_query 当查询词被分析器全部过滤掉时,查询的行为。如果设置为 “NONE”,那么查询不会匹配任何文档。如果设置为 “ALL”,那么查询会匹配所有文档。默认 NONE。

auto_generate_synonyms_phrase_query 如果设置为 true,那么查询词会被当作短语查询,并且会自动生成同义词。默认true

boost 用于调整查询的权重,数值越大,权重越高,匹配的文档排名越靠前。

minimum_should_match 最小匹配数量。例如,如果设置为 2,那么至少有两个 term 匹配文档才会被认为是匹配的。

默认 match 查询参数为:

{
    "query": {
        "match": {
            "content": {
              "query": "查询文本",
              "operator": "OR",
              "minimum_should_match": "2",
              "prefix_length": 0,
              "max_expansions": 50,
              "fuzzy_transpositions": true,
              "lenient": false,
              "zero_terms_query": "NONE",
              "auto_generate_synonyms_phrase_query": true,
              "boost": 1.0
            }
        }
    },
    "from": 0,
    "size": 10
}

https://www.elastic.co/guide/en/elasticsearch/reference/7.17/query-dsl-minimum-should-match.html


match_phrase 短语查询(词序列匹配)

Elasticsearch Guide [7.17] » Query DSL » Full text queries » Match phrase query
https://www.elastic.co/guide/en/elasticsearch/reference/7.17/query-dsl-match-query-phrase.html

Match phrase 查询会先对入参文本进行分词,之后匹配包含全部分词的文档,并且顺序要和入参一致。
Match phrase 用于查找包含精确词序列的文档

注意:Match phrase 也会先对入参分词,并不是有的博客中说的 Match phrase 查询不对入参分词。

slop 参数告诉 match_phrase 分词相隔多远时仍然能将文档视为匹配,默认值是 0。
slop 参数用于放宽对词序的严格要求。Slop 指定了词条之间可以“移动”的次数,以便仍然被认为是匹配的。例如,slop 为 1 允许查询中的词条交换位置一次。
例如入参 I like riding 想匹配文档 I like swimming and riding,需要将 riding 向前移动两个位置,所以 slop 设为 2 时可匹配到。

{
  "query": {
    "match_phrase": {
      "message": {
        "query": "I like riding",
        "slop": 2
      }
    }
  }
}

match_phrase_prefix 短语前缀查询

Elasticsearch Guide [7.17] » Query DSL » Full text queries » Match phrase prefix query
https://www.elastic.co/guide/en/elasticsearch/reference/7.17/query-dsl-match-query-phrase-prefix.html

和 Match phrase 查询类似,Match phrase prefix 查询会先对入参文本进行分词,之后匹配包含全部分词的文档,并且顺序要和入参一致,只不过最后一个单词会当做前缀进行匹配。

例如入参 “quick brown f” 可匹配 “quick brown fox” 或 “two quick brown ferrets”,但不会匹配 “the fox is quick and brown”

GET /_search
{
  "query": {
    "match_phrase_prefix": {
      "message": {
        "query": "quick brown f"
      }
    }
  }
}

intervals 词间隔查询

https://www.elastic.co/guide/en/elasticsearch/reference/7.17/query-dsl-intervals-query.html

intervals 查询提供了一种在文本字段中查找满足特定条件的词组的方式。这些条件可以包括词组中词项的顺序,词项之间的最大距离,以及对词项本身的匹配条件。
相比于 match_phrase 查询,intervals 查询提供了更精细的词语间隔、顺序控制。例如,你可以为每个词项指定不同的最大间隔,你还可以嵌套查询以匹配更复杂的模式。

intervals 查询的顶级参数是一个 <field> 字段名,表示想在哪个字段上进行搜索。
参数的值是一个规则对象,可选的匹配规则有:

  • match 匹配分词文本
  • prefix 匹配前缀
  • wildcard 匹配通配符表达式
  • fuzzy 模糊匹配
  • all_of 内嵌多个其他规则,且所有规则都必须匹配。
  • any_of 内嵌多个其他规则,任意规则匹配即可

match 规则的参数有:

  • query 必填,要查询的文本
  • max_gaps 可选,单词之间的最大间隔,超过最大间隔被认为是不匹配。默认值-1表示不限制间隔,设为0表示 term 之间必须相邻挨着。
  • ordered 可选,是否需要文本中的单词按照给定的顺序出现。默认为 false,表示单词可以以任意顺序出现。
  • filter 一个可选的过滤条件对象,可以是before、after 或 containing 等,可以用于进一步限制匹配的词组。例如,你可以指定匹配的词组必须出现在其他特定词组之前、之后或者包含其他特定词组。
  • analyzer 可以指定 query 使用的分析器,默认使用顶级 <field> 字段的分析器。
  • use_field 可选,可指定一个字段名,之后间隔匹配将在此字段上进行,而不是顶级 <field> 字段,默认分析器也会使用此字段的分析器。

all_of 规则的参数有:

  • intervals 必填,一个数组,定义了一组间隔查询条件,所有这些条件都必须匹配。
  • max_gaps 可选,这组查询条件之间的最大间隔。默认值-1表示不限制间隔宽度,设为0表示 term 之间必须相邻挨着。
  • ordered 可选,是否有序,是否必须按照定义的顺序匹配这些查询条件。默认false,如果设为true,intervals 中的规则条件必须按照定义顺序匹配。
  • filter 一个可选的过滤条件对象,可以是before、after 或 containing 等

filter 过滤器参数:

  • before 指定一个 query 对象,intervals 查询结果必须出现在 before 条件之前才算匹配
  • after 指定一个 query 对象,intervals 查询结果必须出现在 after 条件之后才算匹配
  • containing 指定一个 query 对象,intervals 查询结果必须包含 containing 条件才算匹配
  • not_containing 指定一个 query 对象,intervals 查询结果必须不包含 not_containing 条件才算匹配
  • contained_by 指定一个 query 对象,intervals 查询结果必须被 contained_by 条件包含才算匹配
  • not_contained_by 指定一个 query 对象,intervals 查询结果必须不被 not_contained_by 条件包含才算匹配
  • overlapping 指定一个 query 对象,intervals 查询结果必须和 overlapping 条件重叠才算匹配
  • not_overlapping 指定一个 query 对象,intervals 查询结果必须不和 not_overlapping 条件重叠才算匹配
  • script 返回 boolean 值的一个脚本,intervals 查询结果经 script 脚本计算后必须返回 true 才算匹配

例1、在message字段中查找包含the quick brown词组的文档。
max_gaps参数表示单词之间的最大间隔,值为1表示词组中的单词之间最多可以有一个其他单词。
ordered参数表示词组中的单词必须按照给定的顺序出现。

{
  "query": {
    "intervals" : {
      "message" : {
        "match" : {
          "query" : "the quick brown",
          "max_gaps" : 1,
          "ordered" : true
        }
      }
    }
  }
}

例2、在message字段中查找包含”the quick brown”这个短语的文档,但这个短语必须出现在”fox”之后。

{
  "query": {
    "intervals" : {
      "message" : {
        "match" : {
          "query" : "the quick brown",
          "filter" : {
            "after" : {
              "match" : { "query" : "fox" }
            }
          }
        }
      }
    }
  }
}

例3、在 message 字段上进行匹配
all_of 内有两个match查询条件,它们都必须匹配。第一个match查询查找的是”the quick brown”这个短语,最多允许有一个词的间隔,并且单词必须按顺序出现。第二个match查询查找的是”jumps over”这个短语,最多允许有两个词的间隔,并且单词可以不按顺序出现。
这两个查询条件之间最多可以有5个词的间隔(由max_gaps参数控制),并且必须按照定义的顺序出现(由ordered参数控制)。
此外,还定义了一个filter条件,表示这两个查询条件必须出现在”the lazy dog”这个短语之后。

{
  "query": {
    "intervals" : {
      "message" : {
        "all_of" : {
          "intervals" : [
            { "match" : { "query" : "the quick brown", "max_gaps" : 1, "ordered" : true } },
            { "match" : { "query" : "jumps over", "max_gaps" : 2, "ordered" : false } }
          ],
          "max_gaps" : 5,
          "ordered" : true,
          "filter" : {
            "after" : {
              "match" : { "query" : "the lazy dog" }
            }
          }
        }
      }
    }
  }
}

combined_fields 联合字段查询(7.11+)

https://www.elastic.co/guide/en/elasticsearch/reference/7.17/query-dsl-combined-fields-query.html

combined_fields 查询支持在多个字段上进行匹配,它将多个字段的值合并到一个大的字符串中,然后在这个大的字符串上执行搜索。
combined_fields 是 以词为中心的(term-centric) 查询,首先将 query 查询串分词为多个 term,对于每个term,在多个 fields 中进行匹配。

combined_fields 查询特别适合一个整体被拆分为多个子部分字段,需要在这些子部分字段上匹配的场景
例如文章可拆分为 title, abstract, body,需要在这三个字段上匹配:

GET /_search
{
  "query": {
    "combined_fields" : {
      "query":      "database systems",
      "fields":     [ "title", "abstract", "body"],
      "operator":   "and"
    }
  }
}

combined_fields 和 multi_match 的区别

1、combined_fields 查询要求 fields 中的多个字段的分析器 analyzer 必须相同,如果想在不同 analyzer 的多个字段上匹配,则 multi_match 更合适。multi_match 允许在 text 和 非text 类型字段上一起匹配,也允许 text 类型字段有不同的 analyzer

2、multi_match 的 best_fields 和 most_fields 模式是 以字段为中心的(field-centric)的 查询,相反,combined_fields 是 以词为中心的(term-centric) 查询,operator 和 minimum_should_match 参数都是应用到分词后的每个 term 上的。

举例来说:

GET /_search
{
  "query": {
    "combined_fields" : {
      "query":      "database systems",
      "fields":     [ "title", "abstract"],
      "operator":   "and"
    }
  }
}

的执行模式是:

+(combined("database", fields:["title" "abstract"]))
+(combined("systems", fields:["title", "abstract"]))

也就是说,每个 term(在这里是 database 和 systems)必须在 “title”、”abstract” 中的至少一个字段上出现,文档才能匹配。

3、multi_match 的 cross_fields 模式也是 以词为中心的(term-centric) 查询方式,其中的 operator 和 minimum_should_match 参数也会应用在每个 term 上,而 combined_fields 查询相比 cross_fields multi_match 查询的优势是其基于 BM25F 的更强壮和可解释的相关性算法。
combined_fields 查询中的多个 field 的相关性算法也必须相同。


multi_match 多字段查询

https://www.elastic.co/guide/en/elasticsearch/reference/7.17/query-dsl-multi-match-query.html

multi_match 查询支持在多个字段中搜索单个查询字符串,当希望在两个或多个字段中的任何一个中查找包含给定查询字符串的文档时非常有用。

例如在 subject 或 message 中查询 “this is a test”:

GET /_search
{
  "query": {
    "multi_match" : {
      "query":    "this is a test", 
      "fields": [ "subject", "message" ] 
    }
  }
}

可通过通配符指定字段名,例如:

GET /_search
{
  "query": {
    "multi_match" : {
      "query":    "Will Smith",
      "fields": [ "title", "*_name" ] 
    }
  }
}

指定每个字段的评分权重(boost)

可通过 ^ 单独设置某个字段的权重(boost),比如下面将 subject 字段的得分乘以3倍,默认每个字段的 boost 都是 1.0

GET /_search
{
  "query": {
    "multi_match" : {
      "query" : "this is a test",
      "fields" : [ "subject^3", "message" ] 
    }
  }
}

如果不指定 fields 参数,multi_match 会在索引的默认字段 index.query.default_field 上匹配,默认是 *.* 即全部字段。


multi_match 的类型

multi_match 的 type 参数可指定类型:

  • best_fields 默认,返回和 fields 中任意字段匹配的文档,但 _score 得分只用 best_fields 最佳字段计算(可通过 tie_breaker 参数调)。
  • most_fields 返回 fields 中有任意字段匹配的文档,_score 得分使用每个 filed 上的得分综合计算。
  • cross_fields 将具有相同 analyzer 分词器的字段合并看做一个大字段进行匹配。
  • phrase 在每个 fields 上执行 match_phrase 查询,_score 只计算最佳匹配的得分。
  • phrase_prefix 在每个 fields 上执行 match_phrase_prefix 查询,_score 只计算最佳匹配的得分。
  • bool_prefix 在每个 fields 上执行 match_bool_prefix 查询,_score 只计算最佳匹配的得分。

best_fields 最佳匹配(默认)

best_fields 适合要求 query 的多个词在一个字段中匹配到的情况,即多个词在同一个字段中匹配上才最有意义的
例如搜索 “brown fox”,subject 中只有 brown,或 message 中只有 fox,虽然都能匹配,但可能不是想要的结果,在 subject(或 message) 中同时包含 brown 和 fox 两个字段的才是最佳匹配。

GET /_search
{
  "query": {
    "multi_match" : {
      "query":      "brown fox",
      "type":       "best_fields",
      "fields":     [ "subject", "message" ],
      "tie_breaker": 0.3
    }
  }
}

best_fields 查询对每个 field 生成一个 query 语句,组合为 dis_max 查询。
best_fields 内部会转为 dis_max 分离最大化查询,所以也可以使用 dis_max 查询的 tie_breaker 参数,上面的查询等价于:

GET /_search
{
  "query": {
    "dis_max": {
      "queries": [
        { "match": { "subject": "brown fox" }},
        { "match": { "message": "brown fox" }}
      ],
      "tie_breaker": 0.3
    }
  }
}

和 dis_max 查询一样,默认 best_fields 只用最佳匹配的得分作为结果的 _score,不过可以通过 tie_breaker 参数调整非最佳匹配的得分权重,tie_breaker 默认是 0


most_fields 多字段匹配

most_fields 适合同一份文本在不同字段中是不同分词形式存储的情况。
例如,主字段可能包含同义词、词干和无音调符号的词。第二个字段可能包含原始词,第三个字段可能包含连续词组(shingles)。通过组合所有三个字段的得分,可以实现:通过与主字段匹配可以尽可能多的匹配到文档,然后使用第二和第三个字段将最相似的结果排序到列表开头。
例如 content 是原始文本,content.ik_max 是使用 ik_max 分词器的文本,content.ik_smart, content.jieba 分别是使用其他两个分词器处理的文本,匹配时综合考虑这几个字段上的得分。

查询

GET /_search
{
  "query": {
    "multi_match" : {
      "query":      "quick brown fox",
      "type":       "most_fields",
      "fields":     [ "title", "title.original", "title.shingles" ]
    }
  }
}

内部转为 bool 复合查询执行:

GET /_search
{
  "query": {
    "bool": {
      "should": [
        { "match": { "title":          "quick brown fox" }},
        { "match": { "title.original": "quick brown fox" }},
        { "match": { "title.shingles": "quick brown fox" }}
      ]
    }
  }
}

每个 match 语句的得分加到一起,除以 match 语句的个数,就是整体的得分。


cross_fields 跨字段匹配

cross_fields 适合内容被拆分到多个字段存储的情况。
例如,名字被拆分为 first_name 和 last_name 两个字段存储,在这两个字段上进行 cross_fields 类型的 multi_match 匹配时会将 first_name 和 last_name 组合为一个字段匹配,正好能匹配上。

cross_fields 执行时先将 query 分词,然后用每个 term 在全部字段上匹配。

例如

GET /_search
{
  "query": {
    "multi_match" : {
      "query":      "Will Smith",
      "type":       "cross_fields",
      "fields":     [ "first_name", "last_name" ],
      "operator":   "and"
    }
  }
}

执行时,先用 Will 在 first_name 和 last_name 上匹配,再用 Smith 在 first_name 和 last_name 上匹配。
所以,全部 term 必须在至少一个 field 中出现,此 doc 才算匹配。

cross_field 如果遇到 fields 中有不同分词器的情况,会先将 fields 按分词器类型分组 group,然后使用最佳匹配的组来计算得分。
例如

GET /_search
{
  "query": {
    "multi_match" : {
      "query":      "Jon",
      "type":       "cross_fields",
      "fields":     [
        "first", "first.edge",
        "last",  "last.edge"
      ]
    }
  }
}

first 和 last 是同一个分词器,first.edge 和 last.edge 是同一个分词器,则上面的查询会如此执行:

    blended("jon", fields: [first, last])
| (
    blended("j",   fields: [first.edge, last.edge])
    blended("jo",  fields: [first.edge, last.edge])
    blended("jon", fields: [first.edge, last.edge])
)

cross_field 也有 tie_breaker 参数可控制 _score 得分计算,默认只使用 最佳匹配group 的得分(即 tie_breaker=0.0)


field-centric 和 term-centric

field-centric 以字段为中心

multi_match 的 best_fields 和 most_fields 模式是 以字段为中心的(field-centric)的 查询方式,会为每个 field 字段生成一个 query 语句。operator 和 minimum_should_match 参数也是应用到 field 级别的。
例如

GET /_search
{
  "query": {
    "multi_match" : {
      "query":      "Will Smith",
      "type":       "best_fields",
      "fields":     [ "first_name", "last_name" ],
      "operator":   "and" 
    }
  }
}

执行模式为:

  (+first_name:will +first_name:smith)
| (+last_name:will  +last_name:smith)

也就是说,全部 term(will 和 smith)必须在单个 field 中出现,文档才匹配

term-centric 以词为中心

multi_match 的 cross_fields 模式 和 combined_fields 查询是 以词为中心的(term-centric) 查询方式,为 query 分词后的每个 term 生成一个 query 语句,operator 和 minimum_should_match 参数也会应用在每个 term 上。
例如

GET /_search
{
  "query": {
    "multi_match" : {
      "query":      "Will Smith",
      "type":       "cross_fields",
      "fields":     [ "first_name", "last_name" ],
      "operator":   "and"
    }
  }
}

执行模式为:

+(first_name:will  last_name:will)
+(first_name:smith last_name:smith)

也就是说,每个 term(在这里是 will 和 smith)必须在 “first_name”、”last_name” 中的至少一个字段上出现,文档才能匹配


query_string 查询(类Lucene语法查询)

Elasticsearch Guide [7.17] » Query DSL » Full text queries » Query string query
https://www.elastic.co/guide/en/elasticsearch/reference/7.17/query-dsl-query-string-query.html

query_string 查询会严格校验查询串的语法,语法错误时会报错。
由于 query_string 查询语法错误时会报错,不建议在给用户输入的搜索框中使用,可选方案:

  • 如果需要支持 Lucene 语法,使用 simple_query_string 查询
  • 如果不需要支持 Lucene 语法,使用 match 查询
GET /_search
{
    "query": {
        "query_string" : {
            "query" : "(new york city) OR (big apple)",
            "default_field" : "content"
        }
    }
}

simple_query_string 简化lucene查询

Elasticsearch Guide [7.17] » Query DSL » Full text queries » Simple query string query
https://www.elastic.co/guide/en/elasticsearch/reference/7.17/query-dsl-simple-query-string-query.html


Term 级查询

Term-level queries
https://www.elastic.co/guide/en/elasticsearch/reference/current/term-level-queries.html

term 查询用于精确匹配一个字段中的确切值。它不会对查询字符串进行分词,而是将整个字符串作为单个词项进行搜索。
注意字段本身内容是否分词,取决于索引 mapping 中此字段的类型

假设有一个文档,其字段值为 “Quick brown fox”。这个字段值在存入 Elasticsearch 时通常会经过分词处理。标准的分词器会将 “Quick brown fox” 分解为三个词项:[“Quick”, “brown”, “fox”]。
使用 “Quick” 做 term 查询时,可以和分词项 “Quick” 匹配,所以可以查到。
使用 “Quick brown” 做 term 查询时,因为 “Quick brown” 作为整体无法和任何分词项匹配,所以无法查询到。


term 等值匹配

https://www.elastic.co/guide/en/elasticsearch/reference/current/query-dsl-term-query.html


terms In多值匹配

https://www.elastic.co/guide/en/elasticsearch/reference/current/query-dsl-terms-query.html

terms 查询返回包含入参 terms 列表中一个或多个精确匹配的文档。

例如,查询 user.id 包含 kimchy 或 elkbee 的文档,

GET /_search
{
  "query": {
    "terms": {
      "user.id": [ "kimchy", "elkbee" ],
      "boost": 1.0
    }
  }
}

注意:
1、如果字段存储了多个值(array values),其中任意一个在查询 terms 中即可匹配
例如 es 中有文档 "tags": ["tag1", "tag2", "tag3"]
查询 terms 是 {"query": {"terms": {"tag": ["tag3", "tag4"] }}}
也是可以匹配的,因为 tag3 出现在了查询 terms 列表中
也就是说,查询 {"terms": {"tag": ["tag3", "tag4"] }} 会匹配全部 tag 字段(无论tag字段存储了1个值还是多个值)包含 tag3 或 tag4 的文档。

2、在被分词的 text 字段上使用 terms 查询,将匹配任何包含 terms 中任何项的文档,而不仅仅是完全匹配。


terms_set 数组个数匹配

https://www.elastic.co/guide/en/elasticsearch/reference/7.17/query-dsl-terms-set-query.html

terms_set 查询和 terms 查询类似,但是可以指定 term 的匹配个数。

terms_set 查询特别适合做多标签匹配,比如es文档中 tag 是个 keyword 数组,查询参数是 terms 数组,并且需要至少匹配 terms 中的 n 个才召回,此时适合使用 terms_set 查询

参数:
minimum_should_match_field 指定一个 Numeric 类型的字段名,这个字段定义了一个文档需要匹配多少个项才能被认为是匹配的
minimum_should_match_script 脚本指定 terms 需要匹配的个数,source 中可以使用变量 params.num_terms 表示 terms 参数的个数

terms_set 查询有两种用法:

  • 在每个文档上增加一个和 keyword 数组字段搭配使用的 long 类型字段,指定此文档 keyword 数组字段需要匹配的个数,在查询中使用 minimum_should_match_field 参数指定该字段名。
  • 不用提前定义每个文档的匹配个数,只在查询时使用 minimum_should_match_script 脚本指定需要和入参 terms 匹配的个数

minimum_should_match_script

minimum_should_match_script 查询示例

Elasticsearch array property must contain given array items
https://stackoverflow.com/questions/31916523/elasticsearch-array-property-must-contain-given-array-items

1、创建索引,tag 字段存储标签数组

{
  "mappings": {
    "properties": {
      "tag": {
        "type": "keyword"
      }
    }
  }
}

2、写入文档

doc1.tag : ["tag1","tag2"]
doc2.tag : ["tag2","tag3"]
doc3.tag : ["tag3","tag2","tag1"]

3、查询
(1)查询 tags 字段包含全部 ["tag1", "tag2", "tag3"] 的文档,少1个都不行,只召回 doc3

{
  "query": {
    "terms_set": {
      "tags": {
        "terms": [ "tag1", "tag2", "tag3" ],
        "minimum_should_match_script": {
          "source": "3"
        }
      }
    }
  }
}

source 中可以使用变量 params.num_terms 表示 terms 参数的个数,下面的查询中,由于 terms 数组长度为3,所以 params.num_terms=3

{
  "query": {
    "terms_set": {
      "tags": {
        "terms": [ "tag1", "tag2", "tag3" ],
        "minimum_should_match_script": {
          "source": "params.num_terms"
        }
      }
    }
  }
}

(2)tags 字段包含 ["tag1", "tag2", "tag3"] 中任意2个即可匹配,并且 返回结果中doc3相关性得分最高,排在第一位,因为doc3与terms匹配个数最多

{
  "query": {
    "terms_set": {
      "tags": {
        "terms": [ "tag1", "tag2", "tag3" ],
        "minimum_should_match_script": {
          "source": "2"
        }
      }
    }
  }
}

minimum_should_match_field

minimum_should_match_field 查询示例
1、创建索引,required_matches 指定 keyword 数组字段 programming_languages 需要匹配的个数

{
  "mappings": {
    "properties": {
      "name": {
        "type": "keyword"
      },
      "programming_languages": {
        "type": "keyword"
      },
      "required_matches": {
        "type": "long"
      }
    }
  }
}

2、写入数据

PUT /job-candidates/_doc/1?refresh
{
  "name": "Jane Smith",
  "programming_languages": [ "c++", "java" ],
  "required_matches": 2
}

3、查询
minimum_should_match_field 指定为 required_matches,也就是根据每个文档的 required_matches 字段决定 programming_languages 需要匹配的 terms 个数

GET /job-candidates/_search
{
  "query": {
    "terms_set": {
      "programming_languages": {
        "terms": [ "c++", "java", "php" ],
        "minimum_should_match_field": "required_matches"
      }
    }
  }
}

prefix 前缀匹配

https://www.elastic.co/guide/en/elasticsearch/reference/7.17/query-dsl-prefix-query.html

{
  "query": {
    "prefix": {
      "name": "张"
    }
  },
  "_source": ["_id", "name"]
}

range 范围查询

https://www.elastic.co/guide/en/elasticsearch/reference/current/query-dsl-range-query.html


exists 字段是否存在


wildcard 通配符查询

Elasticsearch Guide [7.17] » Query DSL » Term-level queries » Wildcard query
https://www.elastic.co/guide/en/elasticsearch/reference/7.17/query-dsl-wildcard-query.html

* 匹配任何字符序列(包括空字符)
? 匹配任何单个字符

注意:避免使用以 *? 开头的查询串,速度会很慢

例如查询姓张的

GET /_search
{
    "query": {
        "wildcard" : { "name": "张*" }
    }
}

regexp 正则匹配


join 关联查询

https://www.elastic.co/guide/en/elasticsearch/reference/7.17/joining-queries.html

nested 嵌套查询

Nested query
https://www.elastic.co/guide/en/elasticsearch/reference/current/query-dsl-nested-query.html


脚本查询

Elastic Docs ›Elasticsearch Guide [8.8] ›Query DSL ›Specialized queries
https://www.elastic.co/guide/en/elasticsearch/reference/current/query-dsl-script-query.html

脚本查询通常用在 filter 过滤上下文中,脚本过滤查询的功能也可通过 7.11+ 版本提供的 runtime fields 运行时字段 功能来实现。


特殊查询-script score 脚本评分查询

Elasticsearch Guide [7.17] » Query DSL » Specialized queries » Script score query
https://www.elastic.co/guide/en/elasticsearch/reference/current/query-dsl-script-score-query.html

脚本评分查询(script_score) 可通过入参脚本定制文档的 相关性评分 函数。

例如下面的查询用 my-int 字段除以 10 作为每个文档的相关性得分

GET /_search
{
  "query": {
    "script_score": {
      "query": {
        "match": { "message": "elasticsearch" }
      },
      "script": {
        "source": "doc['my-int'].value / 10 "
      }
    }
  }
}

脚本评分查询的参数:

  • query (必填)查询对象,用于过滤
  • script (必填)脚本对象
  • min_score (可选)最小分数阈值,分数小于此阈值的文档不会返回。
  • boost (可选)如果指定了 boost 参数,最终的得分是脚本计算出的分值乘以 boost,默认值是 1.0

脚本对象

https://www.elastic.co/guide/en/elasticsearch/reference/current/modules-scripting-using.html

Elasticsearch 中任何使用脚本的地方都用下面的格式来定义脚本:

"script": {
  "lang":   "...",
  "source" | "id": "...",
  "params": { ... }
}
  • lang 脚本语言,默认是 painless
  • source, id 脚本自身,可以通过 source 参数直接内联写入脚本内容,也可以通过 id 指定提前创建的脚本(通过脚本存储 API 创建脚本)
  • params 指定传入脚本的参数,参数名可自定义,和脚本中对应上即可。

例如:

GET my-index-000001/_search
{
  "script_fields": {
    "my_doubled_field": {
      "script": { 
        "source": "doc['my_field'].value * params['multiplier']", 
        "params": {
          "multiplier": 2
        }
      }
    }
  }
}

Elasticsearch 首次遇到新脚本会编译并缓存脚本,编译脚本是个比较耗时的动作,所以尽量保持 source 中的脚本内容保持不变,将变量放到 param 参数中。
比如上面将倍数 multiplier 改为参数可保证编译的脚本不变,变的只是 multiplier 的值,如果 source 直接写成 "source": "return doc['my_field'].value * 2" 每次改变倍数都会导致重新编译脚本。

创建和存储脚本

https://www.elastic.co/guide/en/elasticsearch/reference/current/create-stored-script-api.html


向量函数-cosineSimilarity 余弦相似度

向量函数都需要 O(n) 线性扫描匹配的文档,文档量较大时会很慢,建议先通过 query 对文档进行过滤。

向量字段的类型需设置为 dense_vector,其中 dims 是向量维度

PUT my-index-000001
{
  "mappings": {
    "properties": {
      "my_dense_vector": {
        "type": "dense_vector",
        "dims": 3
      },
      "status" : {
        "type" : "keyword"
      }
    }
  }
}

插入数据:

PUT my-index-000001/_doc/1
{
  "my_dense_vector": [0.5, 10, 6],
  "status" : "published"
}

检索,用 status 字段做了筛选,用入参的 query_vector 向量和文档的 my_dense_vector 向量做相似度计算,结果再加 1.0 避免余弦负值,余弦相似度最小值 0.74,返回 top 10

GET my-index-000001/_search
{
  "from": 0,
  "size": 10,
  "query": {
    "script_score": {
      "query" : {
        "bool" : {
          "filter" : {
            "term" : {
              "status" : "published" 
            }
          }
        }
      },
      "min_score": 0.74,
      "boost": 1.0,
      "script": {
        "source": "cosineSimilarity(params.query_vector, 'my_dense_vector') + 1.0", 
        "params": {
          "query_vector": [4, 3.4, -0.2]  
        }
      }
    }
  }
}


多向量余弦加权

入参向量 query_vector 同时和 title_vector 及 centent_vector 做余弦,分数取两者加权平均。

{
  "size": 2,
  "query": {
    "script_score": {
      "script": {
        "source": "0.5 * cosineSimilarity(params.query_vector, 'centent_vector') + 0.5 * cosineSimilarity(params.query_vector, 'title_vector')",
        "lang": "painless",
        "params": {
          "query_vector": [
            0.0072784424,
            -0.0020256042,
            -0.023422241
          ]
        }
      }
    }
  }
}

多向量余弦加权+判空

如果某些文档没有向量字段,cosineSimilarity 查询会报错,可在脚本中加入 if 条件判断文档的向量字段是否存在,避免查询报错。
入参向量 query_vector 同时和 title_vector, centent_vector, summary_vector 做余弦,有些文档可能没有 summary_vector 或 title_vector,使用 if 语句检查。

{
  "size": 3,
  "query": {
    "script_score": {
      "script": {
        "source": "if (doc['summary_vector'].size() == 0 && doc['title_vector'].size() == 0) {\n    return cosineSimilarity(params.query_vector, 'centent_vector')\n} else if (doc['summary_vector'].size() == 0) {\n    return 0.5 * cosineSimilarity(params.query_vector, 'centent_vector') +\n           0.5 * cosineSimilarity(params.query_vector, 'title_vector')\n} else {\n    return 0.33 * cosineSimilarity(params.query_vector, 'centent_vector') +\n           0.33 * cosineSimilarity(params.query_vector, 'title_vector') +\n           0.33 * cosineSimilarity(params.query_vector, 'summary_vector')\n}\n",
        "lang": "painless",
        "params": {
          "query_vector": [
            0.0072784424,
            -0.0020256042,
            -0.023422241
          ]
        }
      }
    }
  }
}

脚本 source 格式化后是:

if (doc['summary_vector'].size() == 0 && doc['title_vector'].size() == 0) {
    return cosineSimilarity(params.query_vector, 'centent_vector')
} else if (doc['summary_vector'].size() == 0) {
    return 0.5 * cosineSimilarity(params.query_vector, 'centent_vector') +
           0.5 * cosineSimilarity(params.query_vector, 'title_vector')
} else {
    return 0.33 * cosineSimilarity(params.query_vector, 'centent_vector') +
           0.33 * cosineSimilarity(params.query_vector, 'title_vector') +
           0.33 * cosineSimilarity(params.query_vector, 'summary_vector')
}

向量函数-dotProduct 点积

向量函数-l1norm L1距离(曼哈顿距离)

向量函数-l2norm L2距离(欧氏距离)


Aggregation 聚合查询

统计多个字段的字符数总和

计算索引内全部文档的 title.keyword 和 content.keyword 字符数总和

GET /your_index/_search
{
  "size": 0,
  "aggs": {
    "total_chars": {
      "sum": {
        "script": {
          "lang": "painless",
          "source": "int totalLength = 0; if (doc['title.keyword'].size() != 0) { totalLength += doc['title.keyword'].value.length(); } if (doc['content.keyword'].size() != 0) { totalLength += doc['content.keyword'].value.length(); } return totalLength;"
        }
      }
    }
  }
}

指标聚合

value_count 计数

{
    "size":0,
    "aggs": {
        "length_count": {
            "value_count": {
                "field": "length"
            }
        }
    }
}

结果:

{
    "aggregations": {
        "length_count": {
            "value": 2639
        }
    }
}

avg 平均值

{
    "size":0,
    "aggs": {
        "avg_length": {
            "avg": {
                "field": "length"
            }
        }
    }
}

结果

{
    "aggregations": {
        "avg_length": {
            "value": 1774.666161424782
        }
    }
}

sum 求和

{
    "size":0,
    "aggs": {
        "sum_length": {
            "sum": {
                "field": "length"
            }
        }
    }
}

结果:

{
    "aggregations": {
        "sum_length": {
            "value": 4683344.0
        }
    }
}

桶聚合

在 ES 中如果没有明确指定指标聚合,默认使用 Value Count 指标聚合,统计桶内文档总数。
默认情况,ES会根据 doc_count 文档总数,降序排序。

https://www.tizi365.com/archives/646.html

terms 分组聚合

{
    "size": 0,  // size=0代表不需要返回query查询结果,仅仅返回aggs统计结果
    "aggs": {
        "agg_groupby": { // 自定义的聚合名字
            "terms": {
                "field": "title.keyword",  // 聚合字段
                "size": 10,
                "order": { "_count": "desc" }
            }
        }
    }
}

结果

{
    "aggregations": {
        "agg_groupby": {  // 和请求中对应的自定义聚合名字
            "doc_count_error_upper_bound": 0,
            "sum_other_doc_count": 2342,
            "buckets": [    // 桶
                {
                    "key": "算法1",  // 桶的标识
                    "doc_count": 112   // 不指定metric指标,默认使用 value_count 计数
                },
                {
                    "key": "数据1",
                    "doc_count": 108
                },
                {
                    "key": "备份1",
                    "doc_count": 77
                }
            ]
        }
    }
}

histogram 直方图

根据数值间隔分组,适合绘制柱状图

{
    "size": 0,
    "aggs": {
        "length_hist": {
            "histogram": {
                "field": "length",
                "interval" : 300   // 分桶间隔
            }
        }
    }
}

结果:

{
    "aggregations": {
        "length_hist": {
            "buckets": [
                {
                    "key": 3300.0,   // 桶标识
                    "doc_count": 31  // 不指定metric指标,默认使用 value_count 计数
                },
                {
                    "key": 3600.0,
                    "doc_count": 51
                },
                {
                    "key": 3900.0,
                    "doc_count": 728
                }
            ]
        }
    }
}

date_histogram 日期直方图

用于根据时间、日期分桶的场景

calendar_interval:month代表每月、支持minute(每分钟)、hour(每小时)、day(每天)、week(每周)、year(每年)

{
    "size": 0,
    "aggs": {
        "date_hist": {
            "date_histogram": {
                "field": "date",
                "calendar_interval": "year",  // 间隔
                "format" : "yyyy"  // 返回结果中桶key的时间格式
            }
        }
    }
}

结果:

{
    "aggregations": {
        "date_hist": {
            "buckets": [
                {
                    "key_as_string": "2021",  // 桶标识,字符串类型
                    "key": 1609459200000,   // 桶标识,毫秒时间戳
                    "doc_count": 35    // 不指定metric指标,默认使用 value_count 计数
                },
                {
                    "key_as_string": "2022",
                    "key": 1640995200000,
                    "doc_count": 18
                },
                {
                    "key_as_string": "2023",
                    "key": 1672531200000,
                    "doc_count": 9
                }
            ]
        }
    }
}

range 范围聚合

{
    "size": 0,
    "aggs": {
        "length_range": {
            "range": {
                "field": "length",
                "ranges" : [
                    {"to": 1000},  // <= 1000 的在一个桶
                    {"from": 1000, "to": 3000},  // 1000 到 3000 的在一个桶
                    {"from": 3000} // 3000+ 的在一个桶
                ]
            }
        }
    }
}

结果:

{
    "aggregations": {
        "length_range": {
            "buckets": [
                {
                    "key": "*-1000.0",   // 桶标识
                    "to": 1000.0,
                    "doc_count": 1313   // 不指定metric指标,默认使用 value_count 计数
                },
                {
                    "key": "1000.0-3000.0",
                    "from": 1000.0,
                    "to": 3000.0,
                    "doc_count": 481
                },
                {
                    "key": "3000.0-*",
                    "from": 3000.0,
                    "doc_count": 845
                }
            ]
        }
    }
}

为了方便解析返回结果,可以通过 key 指定每个桶的标识

{
    "size": 0,
    "aggs": {
        "length_range": {
            "range": {
                "field": "length",
                "ranges" : [
                    {"key": "short", "to": 1000},
                    {"key": "medium", "from": 1000, "to": 3000},
                    {"key": "long", "from": 3000}
                ]
            }
        }
    }
}

结果:

{
    "aggregations": {
        "length_range": {
            "buckets": [
                {
                    "key": "short",
                    "to": 1000.0,
                    "doc_count": 1313
                },
                {
                    "key": "medium",
                    "from": 1000.0,
                    "to": 3000.0,
                    "doc_count": 481
                },
                {
                    "key": "long",
                    "from": 3000.0,
                    "doc_count": 845
                }
            ]
        }
    }
}

桶聚合+指标查询

桶聚合一般不单独使用,都是配合指标聚合一起使用,对数据分组之后肯定要统计桶内数据。

内置排序参数:

  • _count - 按文档数排序。对 terms, histogram, date_histogram 有效
  • _term - 按词项的字符串值的字母顺序排序。只在 terms 内使用
  • _key - 按每个桶的键值数值排序, 仅对 histogram 和 date_histogram 有效

或者可以按指标排序,order 的 key 是 聚合指标的自定义名字。

terms分组+sum总和倒序

先 terms 按 title.keyword 分桶,计算每个桶内的 length 字段 sum 总和,结果按 sum 倒序

{
  "size": 0,
  "aggs": {
    "group_by_title": {   // 按 title 分桶
      "terms": {
        "field": "title.keyword",
        "size": 3,
        "order": {
          "sum_length": "desc"   // 按嵌套聚合查询 sum_length 倒序排序
        }
      },
      "aggs": {  // 嵌套聚合查询,桶内按 length 求和
        "sum_length": {
          "sum": {
            "field": "length"
          }
        }
      }
    }
  }
}
{
    "aggregations": {
        "group_by_title": {
            "doc_count_error_upper_bound": -1,
            "sum_other_doc_count": 1125,
            "buckets": [
                {
                    "key": "算法1",
                    "doc_count": 3,
                    "sum_length": {
                        "value": 10073.0
                    }
                },
                {
                    "key": "数据1",
                    "doc_count": 3,
                    "sum_length": {
                        "value": 9331.0
                    }
                },
                {
                    "key": "算力1",
                    "doc_count": 2,
                    "sum_length": {
                        "value": 6665.0
                    }
                }
            ]
        }
    }
}

SQL查询

POST _sql?format=txt
{
  "query": "SELECT title, count(*) FROM my_index group by title order by count(*) desc limit 10"
}

sql转dsl

POST /_sql/translate
{
  "query": "sql"
}

https://segmentfault.com/a/1190000038394618


Elasticsearch SQL 客户端

SQL CLI
https://www.elastic.co/guide/en/elasticsearch/reference/7.17/sql-cli.html

/usr/share/elasticsearch/bin 目录中有个jar包 elasticsearch-sql-cli-7.6.1.jar
可直接 java -jar 执行,连接指定的 Elasticsearch,连接后执行 sql,例如:

java -jar elasticsearch-sql-cli-7.6.1.jar http://10.153.106.19:8200
sql> select title, count(*) from my_index group by title order by count(*) desc limit 10;
     title     |   count(*)    
---------------+------------
title1         |7              
title2         |6              

搜索你的数据

Elasticsearch Guide [7.17] » Search your data
https://www.elastic.co/guide/en/elasticsearch/reference/7.17/search-your-data.html


高亮

Elastic Docs ›Elasticsearch Guide [8.6] ›Search your data > Highlightingedit
https://www.elastic.co/guide/en/elasticsearch/reference/8.6/highlighting.html

fields 指定高亮字段
pre_tags, post_tags 包围高亮的 HTML 标签,默认 pre_tags 是 <em>,默认 post_tags 是 </em>
fragment_size 指定单个高亮片段(fragment)的长度

例1,指定高亮字段 body

GET /_search
{
  "query" : {
    "match": { "user.id": "kimchy" }
  },
  "highlight" : {
    "fields" : {
      "body" : {}
    }
  }
}

例2,指定高亮字段 body 和 前后标签

GET /_search
{
  "query" : {
    "match": { "user.id": "kimchy" }
  },
  "highlight" : {
    "pre_tags" : ["<tag1>"],
    "post_tags" : ["</tag1>"],
    "fields" : {
      "body" : {}
    }
  }
}

准实时(near-realtime)搜索

Elasticsearch Guide [7.17] » Search your data » Near real-time search
https://www.elastic.co/guide/en/elasticsearch/reference/current/near-real-time.html

elastic 底层采用的是 lucene 这个库来实现倒排索引的功能,在 lucene 的概念里每一条记录称为 document(文档),lucene 使用 segment(分段) 来存储数据。Segment 是最小的数据存储单元。其中包含了文档中的词汇字典、词汇字典的倒排索引以及 Document 的字段数据。因此 Segment 直接提供了搜索功能。但是 Segment 能提供搜索的前提是数据必须被提交(Lucene commit),即文档经过一系列的处理之后生成倒排索引等一系列数据。

由于 Lucene commit 非常耗时,es 并不会每收到一条数据就提交一次,而是维护一个 Lucene 内部的 in-memory buffer(内存缓存区),新增一条记录时,es 只是将数据写入内存缓存区,每隔一段时间(默认为1秒),Elasticsearch 会执行一次 refresh 操作:lucene中所有的缓存数据都被写入到一个新的Segment,清空缓存数据。此时数据就可以被搜索。当然,每次执行refresh操作都会生成一个新的Segment文件,这样一来Segment文件有大有小,相当碎片化。Elasticsearch内部会开启一个线程将小的Segment合并(Merge)成大的Segment,减少碎片化,降低文件打开数,提升IO性能。

新增一条记录时,es 会把数据写到 translog 和 in-memory buffer(内存缓存区) 中,数据写入内存缓冲区后并不能立即被搜索到,需要等 refresh 操作(默认1秒一次)执行后刷入 segment 才能被搜索到,所以 es 搜索是 near-realtime 准实时的,而不是实时的。


ES 查询优化

和美大家说 | Elasticsearch之相关性调整实践
https://mp.weixin.qq.com/s/kcCfP8zyGyKiOQdCOvzs_Q


文本分析

POST /_analyze 分析文本

Elasticsearch Guide [7.17] » REST APIs » Index APIs » Analyze API
https://www.elastic.co/guide/en/elasticsearch/reference/7.17/indices-analyze.html

对一段文本进行分词

GET /_analyze
POST /_analyze
GET /<index>/_analyze
POST /<index>/_analyze

参数
text 要分析的文本。必填。
field 有 field 时会使用 field 的默认分析器。
analyzer 分析器,可以是内置分析器,也可以是自定义分析器。如果不设置,使用 field 的分析器。如果不指定 field,使用 index 的默认分析器。如果也不指定 index,或者 index 没有默认分析器,使用 standard 分析器。
tokenizer 分词器。将 text 转换为 token 的分词器。

不指定额外分词器时,es 使用一个名为 standard 的默认分析器,对于中文,standard 分词器只是将汉字拆分成一个个的汉字。

例1,测试 ik_smart 分析器:

GET /_analyze
{
    "text": "中国的",
    "analyzer": "ik_smart"
}

返回分词结果

{
  "tokens": [
    {
      "token": "中国",
      "start_offset": 0,
      "end_offset": 2,
      "type": "CN_WORD",
      "position": 0
    },
    {
      "token": "的",
      "start_offset": 2,
      "end_offset": 3,
      "type": "CN_CHAR",
      "position": 1
    }
  ]
}

例2,测试 分词器 和 词过滤器

POST _analyze
{
  "tokenizer": "standard",
  "filter":  [ "lowercase", "asciifolding" ],
  "text":      "Is this déja vu?"
}

ik_max_word/ik_smart 最佳实践

ik_max_word 和 ik_smart

  • ik_max_word: 会将文本做最细粒度的拆分,会尽可能多地切分词语,比如会将“中华人民共和国国歌”拆分为“中华人民共和国,中华人民,中华,华人,人民共和国,人民,人,民,共和国,共和,和,国国,国歌”,会穷尽各种可能的组合,适合 Term Query;
  • ik_smart: 会做最粗粒度的拆分,比如会将“中华人民共和国国歌”拆分为“中华人民共和国,国歌”,适合 Phrase 查询。

两种分词器使用的最佳实践是:索引时用ik_max_word,在搜索时用ik_smart

GET /_analyze
{
    "analyzer": "ik_smart",
  "text" : "怪兽小当家,我是masikkk"
}

分词结果

{
    "tokens": [
        {
            "token": "怪兽",
            "start_offset": 0,
            "end_offset": 2,
            "type": "CN_WORD",
            "position": 0
        },
        {
            "token": "小",
            "start_offset": 2,
            "end_offset": 3,
            "type": "CN_CHAR",
            "position": 1
        },
        {
            "token": "当家",
            "start_offset": 3,
            "end_offset": 5,
            "type": "CN_WORD",
            "position": 2
        },
        {
            "token": "我",
            "start_offset": 6,
            "end_offset": 7,
            "type": "CN_CHAR",
            "position": 3
        },
        {
            "token": "是",
            "start_offset": 7,
            "end_offset": 8,
            "type": "CN_CHAR",
            "position": 4
        },
        {
            "token": "masikkk",
            "start_offset": 8,
            "end_offset": 15,
            "type": "ENGLISH",
            "position": 5
        }
    ]
}

里面有 ik 分词演示
SpringBoot集成Elasticsearch7.4 实战(一)
https://www.jianshu.com/p/1fbfde2aefa5


Elasticsearch 内置分析器

https://www.elastic.co/guide/en/elasticsearch/reference/7.17/analysis-analyzers.html

Elasticsearch 内置分析器

  • Standard Analyzer 这是默认的analyzer。使用 standard tokenizer 将文本分成单词,然后使用 lowercase token filter将单词转换为小写。
  • Simple Analyzer 使用非单词字符将文本分成单词,然后使用 lowercase token filter将单词转换为小写。
  • Whitespace Analyzer 使用空白字符将文本分成单词。不执行小写转换或其他标记过滤。
  • Stop Analyzersimple analyzer 类似,但会删除停用词(如“and”、“the”等)。停用词列表是内置的,但也可以自定义。
  • Keyword Analyzer 将整个字段值作为单个单词输出。不进行分词、小写转换或其他标记过滤。
  • Pattern Analyzer 使用正则表达式来匹配并生成标记。默认情况下,它使用\W+(非单词字符)来分割文本,但你可以指定自己的模式。
  • Language Analyzers Elasticsearch 提供了许多针对特定语言的analyzer,如englishfrenchgerman等。这些analyzer通常包括针对该语言的特定分词和标记过滤规则,如词干提取(stemming)和词形还原(lemmatization)。
  • Fingerprint Analyzer 特殊用途的分析器,用于计算文本的指纹,用于重复检测。

要在 Elasticsearch 中使用这些analyzer,你可以在索引映射(index mapping)中指定它们。例如,在创建索引时,设置字段的 analyzer 参数,指定分析器。


创建自定义分析器

https://www.elastic.co/guide/en/elasticsearch/reference/7.17/analysis-custom-analyzer.html

一个分析器 analyzer 包括:

  • 0个或多个字符过滤器(character filters)
  • 一个分词器(tokenizer)
  • 0个或多个词过滤器(token filters)

参数:
type 对于自定义分析器,类型为 custom
tokenizer (必须)分词器,可使用内置分词器,或三方插件分词器(例如ik)
char_filter (可选)字符过滤器数组,内置字符过滤器或自定义字符过滤器
filter (可选)词过滤器数组,内置token过滤器或自定义token过滤器


基于ik分词器+同义词+停用词创建自定义分析器

1、关闭索引
2、创建自定义分析器
3、打开索引

如果 ik 的词库发生改变,实测基于 ik 自定义的分析器必须关闭索引、设置分析器、打开索引后,新加的词才能生效

POST /my_index/_close

PUT /my_index/_settings
{
  "analysis": {
    "filter": {
      "my_synonyms": {
        "type": "synonym_graph",
        "synonyms": [
          "项目,案例"
        ]
      },
      "my_stop": {
        "type": "stop",
        "ignore_case": true,
        "stopwords": ["的", "一", "不", "在", "人", "有", "是", "为", "以", "于", "上", "他", "而", "后", "之", "来", "及", "了", "因", "下", "可", "到", "由", "这", "与", "也", "此", "但", "并", "个", "其", "已", "无", "小", "我", "们", "起", "最", "再", "今", "去", "好", "只", "又", "或", "很", "亦", "某", "把", "那", "你", "乃", "它", "吧", "被", "比", "别", "趁", "当", "从", "到", "得", "打", "凡", "儿", "尔", "该", "各", "给", "跟", "和", "何", "还", "即", "几", "既", "看", "据", "距", "靠", "啦", "了", "另", "么", "每", "们", "嘛", "拿", "哪", "那", "您", "凭", "且", "却", "让", "仍", "啥", "如", "若", "使", "谁", "虽", "随", "同", "所", "她", "哇", "嗡", "往", "哪", "些", "向", "沿", "哟", "用", "于", "咱", "则", "怎", "曾", "至", "致", "着", "诸", "自"]
      }
    },
    "analyzer": {
      "ik_max_custom": {
        "type": "custom",
        "tokenizer": "ik_max_word",
        "filter": [
          "my_synonyms", "my_stop"
        ]
      },
      "ik_smart_custom": {
        "type": "custom",
        "tokenizer": "ik_smart",
        "filter": [
          "my_synonyms", "my_stop"
        ]
      }
    }
  }
}

POST /my_index/_open

Character Filter 字符过滤器

https://www.elastic.co/guide/en/elasticsearch/reference/7.17/analysis-charfilters.html

Elasticsearch 中的 Character Filters(字符过滤器)是用于在将字符流传递给分词器(Tokenizer)之前对其进行预处理的工具。这些过滤器可以接收原始文本作为字符流,并通过添加、删除或更改字符来转换流。

内置字符过滤器类型:

  • html_strip HTML Strip Character Filter,去除 html 中的标签,或者将 html 中的标签转换为对应的 html 编码,例如将 <b> 转换为其对应的编码 &amp;
  • mapping 字符映射,使用指定的替换字符替换文本中任何出现的指定字符串。例如,将所有出现的“foo”替换为“bar”
  • pattern_replace 使用正则表达式匹配文本中的字符,并将其替换为指定的内容。

例如,定义一个 mapping 字符映射过滤器

PUT /my-index-000001
{
  "settings": {
    "analysis": {
      "analyzer": {
        "my_analyzer": {
          "tokenizer": "standard",
          "char_filter": [
            "my_mappings_char_filter"
          ]
        }
      },
      "char_filter": {
        "my_mappings_char_filter": {
          "type": "mapping",
          "mappings": [
            ":) => _happy_",
            ":( => _sad_"
          ]
        }
      }
    }
  }
}

Tokenizer 分词器

Elasticsearch Guide [7.17] ›Text analysis ›Tokenizer reference
https://www.elastic.co/guide/en/elasticsearch/reference/7.17/analysis-tokenizers.html


Token Filter 词过滤器

Elasticsearch Guide [7.17] ›Text analysis ›Token filter reference
https://www.elastic.co/guide/en/elasticsearch/reference/7.17/analysis-tokenfilters.html

词过滤器(Token Filter) 对 分词器(tokenizer) 产生的 token流 进行修改(例如变小写 lowercasing)、删除(例如删除停用词 stopwords)、添加(例如同义词 synonyms)。
Elasticsearch 内置了多个词过滤器。


stop 停用词过滤器

https://www.elastic.co/guide/en/elasticsearch/reference/7.17/analysis-stop-tokenfilter.html

停用词也叫停止词,是指文本在被分词之后的词语中包含的没有搜索意义的词,例如 ”的地得“。

停用词过滤器 从分词器产生的 token流 中删除停用词,不指定时,默认会删除下面这些英文停用词:

a, an, and, are, as, at, be, but, by, for, if, in, into, is, it, no, not, of, on, or, such, that, the, their, then, there, these, they, this, to, was, will, with

中文停用词
https://www.ranks.nl/stopwords/chinese-stopwords

参数
stopwords 停用词数组
ignore_case 默认false,是否忽略大小写


synonym 同义词过滤器

https://www.elastic.co/guide/en/elasticsearch/reference/7.17/analysis-synonym-tokenfilter.html


synonym_graph 多同义词过滤器

https://www.elastic.co/guide/en/elasticsearch/reference/7.17/analysis-synonym-graph-tokenfilter.html

synonym_graph 可处理多单词同义词,且仅可在搜索分析器中使用。

创建 synonym_graph 过滤器,指定同义词配置文件 analysis/synonym.txt,然后创建自定义分词器 search_synonyms 指定使用 graph_synonyms 同义词过滤器:

PUT /test_index
{
  "settings": {
    "index": {
      "analysis": {
        "analyzer": {
          "search_synonyms": {
            "tokenizer": "whitespace",
            "filter": [ "graph_synonyms" ]
          }
        },
        "filter": {
          "graph_synonyms": {
            "type": "synonym_graph",
            "synonyms_path": "analysis/synonym.txt"
          }
        }
      }
    }
  }
}

analysis/synonym.txt 是相对于 config 目录的文件。

参数:
expand 是否进行同义词扩展,默认 true
lenient 默认false,设为true时可忽略同义词配置文件中的错误

也可以不使用配置文件,直接在请求参数中配置同义词

PUT /test_index
{
  "settings": {
    "index": {
      "analysis": {
        "filter": {
          "synonym": {
            "type": "synonym_graph",
            "synonyms": [
              "lol, laughing out loud",
              "universe, cosmos"
            ]
          }
        }
      }
    }
  }
}
Elasticsearch 同义词配置格式

https://www.elastic.co/guide/en/elasticsearch/reference/7.17/analysis-synonym-graph-tokenfilter.html#_solr_synonyms_2

# => 左边的词替换为所有右边的词
i-pod, i pod => ipod
sea biscuit, sea biscit => seabiscuit

# 逗号分割同义词,expand=true 时逗号分割的词可任意替换
ipod, i-pod, i pod
foozball , foosball
universe , cosmos
lol, laughing out loud

# expand=true 时,等同于 ipod, i-pod, i pod
ipod, i-pod, i pod => ipod, i-pod, i pod

# expand=false 时,等同于 ipod, i-pod, i pod
ipod, i-pod, i pod => ipod

# 相同的左侧 key 映射会自动合并
foo => foo bar
foo => baz
# 等同于:
foo => foo bar, baz

synonym 同义词

一样,却又不同:借助同义词让 Elasticsearch 更加强大
https://www.elastic.co/cn/blog/boosting-the-power-of-elasticsearch-with-synonyms

Elasticsearch:使用同义词 synonyms 来提高搜索效率
https://blog.csdn.net/UbuntuTouch/article/details/108003222

索引时同义词 VS 搜索时同义词

可在索引时使用同义词,也可在搜索时使用。

1、在索引时应用同义词
索引文档时进行一次性同义词替换或扩展,结果将一直保存在搜索索引中。
优势是性能好,因为在索引文档时已经做了同义词扩展,无需再在每次查询时完成一遍扩展过程。
索引时使用同义词有几个劣势:

  • 由于必须对所有同义词进行索引,所以索引规模会变大。
  • 搜索得分(依赖于字词统计数据)可能会受影响,因为同义词也会计算在内,所以不常见单词的统计数据会存在偏差。
  • 除非进行重新索引,否则无法针对既有文档更改同义词规则。

后2点劣势较严重,不建议使用。

2、搜索时使用同义词
劣势是每次查询时都必须执行同义词扩展操作
优势:

  • 索引规模不受影响。
  • 语料库中的字词统计数据保持不变。
  • 如需变更同义词规则,无需对文档进行重新索引。

此外,搜索时扩展同义词还能够允许使用更加复杂的 synonym_graph 词过滤器。synonym_graph 可处理多单词同义词,且仅可在搜索分析器中使用。

创建/更新同义词必须关闭索引

尽管更改同义词规则不需要对文档进行重新索引,但是如要更改的话,必须暂时关闭再重新打开索引。
因为分析器在下列时候才会创建实例:创建索引时,重启节点时,以及重新打开已关闭的索引时。


Token Graph 词图

https://www.elastic.co/guide/en/elasticsearch/reference/7.17/token-graphs.html

分词器将文本转换为 token 流,同时也会记录每个 token 的位置。
position 0, 1, 2 作为顶点,token 作为边(弧),可以创建一个 DAG 图。
例如 quick brown fox:
1 -quick-> 1 -brown-> 2 -fox-> 3


检索性能调优

Elasticsearch Guide [7.17] » How to » Tune for search speed
https://www.elastic.co/guide/en/elasticsearch/reference/current/tune-for-search-speed.html

增加系统缓存

Elasticsearch 重度依赖操作系统的文件系统缓存,为了加快检索速度,要保证至少一半可用内存用于系统缓存,以保证 ES 可将热点索引放到物理内存中。
这也是为什么官方建议堆内存不要超过可用内存的一半。

使用更快的硬件

1、保证文件系统缓存足够
2、使用 SSD 磁盘

优化文档模型

避免 nested 嵌套字段,避免 join 关系字段。
nested 字段有几倍的性能损失,join 关系字段会带来几百倍的性能损失。

使用copy_to合并字段

将多个字段的内容 copy_to 到一个字段中,方便搜索

提前计算中间结果

比如经常需要对 price 字段进行聚合查询,聚合为 0-200,200-500,500+,可以在插入文档时就计算好这个聚合分类

使用keyword代替数字类型

ES 对 integer, long 等数字类型做了优化更适合 range 查询,但 keyword 类型适合 term 级别查询。
并不是所有的数字类型数据都要存储为数字类型(integer, long等),例如数字类型的 ISBN 号、产品ID等,很少会用做范围 range 查询,更多的是 term 查询,所以更适合保存为 keyword 类型。

避免脚本搜索

强制合并只读索引

将只读索引强制合并为单个 Segment 可加快查询。对于 log 等基于时间写入的索引尤其适用,只保留当天的日志可写,将之前的日志合并为一个索引。

预热全局序号

全局序号用于加速聚合查询,是 fielddata 缓存结构的一部分,位于 JVM 堆内存中,在查询时触发。
对于常用的聚合字段,告诉 ES 提前预热该字段的全局序号缓存,可加快聚合查询速度。

预热文件系统缓存

机器重启后操作系统文件缓存会丢失,随着查询请求才会逐渐将索引中的热区域加载到系统缓存中。
对于常用的索引,配置到 "index.store.preload": ["index1", "index2"],以便启动时自动预热数据

冷->热->缓存

冷数据:查询条件term的索引文件还未加载系统缓存,读磁盘
热数据:查询条件term的索引文件已加载到系统缓存,使用同条件查询一次后触发,读内存
缓存后:用完全相同的查询条件再次查询,结果是完全缓存在内存的

索引数据排序

Elasticsearch 从 6.0 版本开始引入了一个新的特征,叫 Index Sorting(索引排序)。用户可以将索引数据按照指定的顺序存储在硬盘上,这样在搜索排序取前 N 条时,不需要访问所有的匹配中的记录再进行排序,只需要访问前 N 条记录即可。


上一篇 Elasticsearch-集群及运维

下一篇 Elasticsearch-索引

阅读
评论
22.8k
阅读预计101分钟
创建日期 2024-01-02
修改日期 2024-09-25
类别
目录
  1. Search 搜索 API
    1. GET /index/_search 搜索
      1. track_total_hits 命中个数统计
      2. _source 指定返回字段
      3. q查询(URI搜索/lucene语法查询)
      4. highlight 高亮搜索
      5. sort 排序
        1. 默认按相关性 _score 倒序
        2. track_scores 始终计算得分
        3. 多字段排序
        4. 多值字段排序(mode:min/max/avg/sum)
        5. 值缺失时的处理(missing)
        6. 排序字段的返回值(format)
        7. 嵌套字段的排序
      6. 分页
        1. from/size 分页
        2. 分布式系统中的深度分页问题
        3. scroll分页(缓存快照,无法跳页,不再推荐)
        4. search_after(实时分页,无法跳页,推荐)
    2. GET /index/_count 条件计数
      1. ignore_unavailable=true 索引不存在时不报错
    3. PIT 时间点
      1. POST /index/_pit 时间点
      2. PIT 时间点会消耗磁盘和内存资源
      3. DELETE /_pit 删除时间点
      4. slice 搜索分片
  2. Query DSL
    1. 查询(Query)和过滤(Filter)上下文
      1. 查询(Query)上下文
      2. 过滤(Filter)上下文
      3. Query和Filter的区别
    2. minimum_should_match 参数
  3. 复合查询(Compound Query)
    1. Boolean 组合查询
      1. minimum_should_match 最小匹配个数
      2. adjust_pure_negative 调整纯否定查询的结果
      3. _name 命名查询
      4. query.bool.filter 查询的得分
      5. 多个 must/should 子查询得分相加
      6. 直接query 与 query.bool.must
    2. Boosting 反向减分查询
    3. constant_score 常数得分查询
    4. dis_max 分离最大化查询
  4. Match 全文查询
    1. match 分词查询
      1. match 查询的参数
    2. match_phrase 短语查询(词序列匹配)
    3. match_phrase_prefix 短语前缀查询
    4. intervals 词间隔查询
    5. combined_fields 联合字段查询(7.11+)
      1. combined_fields 和 multi_match 的区别
    6. multi_match 多字段查询
      1. 指定每个字段的评分权重(boost)
      2. multi_match 的类型
        1. best_fields 最佳匹配(默认)
        2. most_fields 多字段匹配
        3. cross_fields 跨字段匹配
      3. field-centric 和 term-centric
        1. field-centric 以字段为中心
        2. term-centric 以词为中心
    7. query_string 查询(类Lucene语法查询)
    8. simple_query_string 简化lucene查询
  5. Term 级查询
    1. term 等值匹配
    2. terms In多值匹配
    3. terms_set 数组个数匹配
      1. minimum_should_match_script
      2. minimum_should_match_field
    4. prefix 前缀匹配
    5. range 范围查询
    6. exists 字段是否存在
    7. wildcard 通配符查询
    8. regexp 正则匹配
  6. join 关联查询
    1. nested 嵌套查询
    2. 脚本查询
  7. 特殊查询-script score 脚本评分查询
    1. 脚本对象
    2. 创建和存储脚本
    3. 向量函数-cosineSimilarity 余弦相似度
      1. 多向量余弦加权
      2. 多向量余弦加权+判空
    4. 向量函数-dotProduct 点积
    5. 向量函数-l1norm L1距离(曼哈顿距离)
    6. 向量函数-l2norm L2距离(欧氏距离)
  8. Aggregation 聚合查询
    1. 统计多个字段的字符数总和
    2. 指标聚合
      1. value_count 计数
      2. avg 平均值
      3. sum 求和
    3. 桶聚合
      1. terms 分组聚合
      2. histogram 直方图
      3. date_histogram 日期直方图
      4. range 范围聚合
    4. 桶聚合+指标查询
      1. terms分组+sum总和倒序
  9. SQL查询
    1. Elasticsearch SQL 客户端
  10. 搜索你的数据
    1. 高亮
    2. 准实时(near-realtime)搜索
    3. ES 查询优化
  11. 文本分析
    1. POST /_analyze 分析文本
    2. ik_max_word/ik_smart 最佳实践
    3. Elasticsearch 内置分析器
    4. 创建自定义分析器
      1. 基于ik分词器+同义词+停用词创建自定义分析器
    5. Character Filter 字符过滤器
    6. Tokenizer 分词器
    7. Token Filter 词过滤器
      1. stop 停用词过滤器
      2. synonym 同义词过滤器
      3. synonym_graph 多同义词过滤器
        1. Elasticsearch 同义词配置格式
    8. synonym 同义词
      1. 索引时同义词 VS 搜索时同义词
      2. 创建/更新同义词必须关闭索引
    9. Token Graph 词图
  12. 检索性能调优
    1. 增加系统缓存
    2. 使用更快的硬件
    3. 优化文档模型
    4. 使用copy_to合并字段
    5. 提前计算中间结果
    6. 使用keyword代替数字类型
    7. 避免脚本搜索
    8. 强制合并只读索引
    9. 预热全局序号
    10. 预热文件系统缓存
    11. 冷->热->缓存
    12. 索引数据排序

页面信息

location:
protocol:
host:
hostname:
origin:
pathname:
href:
document:
referrer:
navigator:
platform:
userAgent:

评论