1. 映射高级

1.1 地理坐标点数据类型

  • 地理坐标点

地理坐标点是指地球表面可以用经纬度描述的一个点。 地理坐标点可以用来计算两个坐标间的距离,还可以判断一个坐标是否在一个区域中。地理坐标点需要显式声明对应字段类型为 geo_point :

  1. PUT /company-locations
  2. {
  3. "mappings": {
  4. "properties": {
  5. "name": {
  6. "type": "text"
  7. },
  8. "location": {
  9. "type": "geo_point"
  10. }
  11. }
  12. }
  13. }
  • 经纬度坐标格式

如上例, location 字段被声明为 geo_point 后,我们就可以索引包含了经纬度信息的文档了。 经纬度信息的形式可以是字符串、数组或者对象

  1. # 字符串形式
  2. PUT /company-locations/_doc/1
  3. {
  4. "name":"NetEase",
  5. "location":"40.715,74.011"
  6. }
  7. # 对象形式
  8. PUT /company-locations/_doc/2
  9. {
  10. "name":"Sina",
  11. "location":{
  12. "lat":40.722,
  13. "lon":73.989
  14. }
  15. }
  16. # 数组形式
  17. PUT /company-locations/_doc/3
  18. {
  19. "name":"Baidu",
  20. "location":[73.983,40.719]
  21. }

注意
字符串形式以半角逗号分割,如 “lat,lon”
对象形式显式命名为 lat 和 lon
数组形式表示为 [lon,lat]

  • 通过地理坐标点过滤

有四种地理坐标点相关的过滤器 可以用来选中或者排除文档
image.png

  • geo_bounding_box查询

这是目前为止最有效的地理坐标过滤器了,因为它计算起来非常简单。 你指定一个矩形的顶部 ,底部 , 左边界和右边界,然后过滤器只需判断坐标的经度是否在左右边界之间,纬度是否在上下边界之间
然后可以使用 geo_bounding_box 过滤器执行以下查询

  1. GET /company-locations/_search
  2. {
  3. "query": {
  4. "bool" : {
  5. "must" : {
  6. "match_all" : {}
  7. },
  8. "filter" : {
  9. "geo_bounding_box" : {
  10. "location" : {
  11. "top_left" : {
  12. "lat" : 40.73,
  13. "lon" : 71.12
  14. },
  15. "bottom_right" : {
  16. "lat" : 40.01,
  17. "lon" : 74.1
  18. }
  19. }
  20. }
  21. }
  22. }
  23. }
  24. }

location这些坐标也可以用 bottom_left 和 top_right 来表示

  • geo_distance

过滤仅包含与地理位置相距特定距离内的匹配的文档。假设以下映射和索引文档然后可以使用 geo_distance 过滤器执行以下查询

  1. GET /company-locations/_search
  2. {
  3. "query": {
  4. "bool" : {
  5. "must" : {
  6. "match_all" : {}
  7. },
  8. "filter" : {
  9. "geo_distance" : {
  10. "distance" : "200km",
  11. "location" : {
  12. "lat" : 40,
  13. "lon" : 70
  14. }
  15. }
  16. }
  17. }
  18. }
  19. }

1.2 动态映射

Elasticsearch在遇到文档中以前未遇到的字段,可以使用dynamic mapping(动态映射机制) 来确定字段的数据类型并自动把新的字段添加到类型映射。
Elastic的动态映射机制可以进行开关控制,通过设置mappings的dynamic属性,dynamic有如下设置项

  • true:遇到陌生字段就执行dynamic mapping处理机制
  • false:遇到陌生字段就忽略
  • strict:遇到陌生字段就报错 ```json

    设置为报错

    PUT /user { “settings”:{ “number_of_shards”: 3, “number_of_replicas”: 0 }, “mappings”:{ “dynamic”: “strict”, “properties”: { “name”: { “type”: “text” }, “ address”: { “type”: “object”, “dynamic”: true } } } }

插入以下文档,将会报错

user索引层设置dynamic是strict,在user层内设置age将报错

在address层设置dynamic是ture,将动态映射生成字段

PUT /user/_doc/1 { “name”: “lisi”, “age”: “20”, “address”: { “province”: “beijing”, “city”: “beijing” } }

PUT /user { “settings”:{ “number_of_shards”: 3, “number_of_replicas”: 0 }, “mappings”:{ “dynamic”: true, “properties”: { “name”: {“type”: “text”}, “address”: {“type”: “object”, “dynamic”: true} } } }

  1. <a name="DGxKb"></a>
  2. ## 1.3 自定义动态映射
  3. 如果你想在运行时增加新的字段,你可能会启用动态映射。 然而,有时候,动态映射 规则 可能不太智能。幸运的是,我们可以通过设置去自定义这些规则,以便更好的适用于你的数据。
  4. **日期检测**<br />当 Elasticsearch 遇到一个新的字符串字段时,它会检测这个字段是否包含一个可识别的日期,比如 2014-01-01 如果它像日期,这个字段就会被作为 date 类型添加。否则,它会被作为 string 类型添加。 有些时候这个行为可能导致一些问题。想象下,你有如下这样的一个文档: { "note": "2014-01-01" } <br />假设这是第一次识别 note 字段,它会被添加为 date 字段。但是如果下一个文档像这样: { "note": "Logged out" } <br />这显然不是一个日期,但为时已晚。这个字段已经是一个日期类型,这个 不合法的日期 将会造成一个异常。 <br />日期检测可以通过在根对象上设置 date_detection 为 false 来关闭
  5. ```json
  6. PUT /my_index/_doc/1
  7. {
  8. "note": "2014-01-01"
  9. }
  10. PUT /my_index/_doc/1
  11. {
  12. "note": "Logged out"
  13. }
  14. PUT /my_index
  15. {
  16. "mappings": {
  17. "date_detection": false
  18. }
  19. }

使用这个映射,字符串将始终作为 string 类型。如果需要一个 date 字段,必须手动添加。
Elasticsearch 判断字符串为日期的规则可以通过 dynamic_date_formats setting 来设置。

  1. PUT /my_index
  2. {
  3. "mappings": {
  4. "dynamic_date_formats": "MM/dd/yyyy"
  5. }
  6. }
  7. PUT /my_index/_doc/1
  8. {
  9. "note": "2014-01-01"
  10. }
  11. PUT /my_index/_doc/1
  12. {
  13. "note": "01/01/2014"
  14. }

dynamic_templates
使用 dynamic_templates 可以完全控制新生成字段的映射,甚至可以通过字段名称或数据类型来应用不同的映射。每个模板都有一个名称,你可以用来描述这个模板的用途,一个 mapping 来指定映射应该怎样使用,以及至少一个参数 (如 match) 来定义这个模板适用于哪个字段。
模板按照顺序来检测;第一个匹配的模板会被启用。例如,我们给 string 类型字段定义两个模板:
es :以 _es 结尾的字段名需要使用 spanish 分词器。
en :所有其他字段使用 english 分词器。

我们将 es 模板放在第一位,因为它比匹配所有字符串字段的 en 模板更特殊:

  1. PUT /my_index2
  2. {
  3. "mappings": {
  4. "dynamic_templates": [
  5. {
  6. "es": {
  7. "match": "*_es",
  8. "match_mapping_type": "string",
  9. "mapping": {
  10. "type": "text",
  11. "analyzer": "spanish"
  12. }
  13. }
  14. },
  15. {
  16. "en": {
  17. "match": "*",
  18. "match_mapping_type": "string",
  19. "mapping": {
  20. "type": "text",
  21. "analyzer": "english"
  22. }
  23. }
  24. }
  25. ]
  26. }
  27. }

1)匹配字段名以 _es 结尾的字段
2)匹配其他所有字符串类型字段

  • match_mapping_type 允许你应用模板到特定类型的字段上,就像有标准动态映射规则检测的一样 (例如 string 或 long)
  • match参数只匹配字段名称,path_match 参数匹配字段在对象上的完整路径,所以 address.*.name 将匹配这样的字段

    1. {
    2. "address": {
    3. "city": {
    4. "name": "New York"
    5. }
    6. }
    7. }


    2. Query DSL

    https://www.elastic.co/guide/en/elasticsearch/reference/7.3/query-dsl.html
    Elasticsearch提供了基于JSON的完整查询DSL(Domain Specifific Language 特定域的语言)来定义查询。将查询DSL视为查询的AST(抽象语法树),它由两种子句组成:

  • 叶子查询子句

叶子查询子句 在特定域中寻找特定的值,如 match,term或 range查询。

  • 复合查询子句

复合查询子句包装其他叶子查询或复合查询,并用于以逻辑方式组合多个查询(例如 bool或 dis_max查询),或更改其行为(例如 constant_score查询)。

我们在使用ElasticSearch的时候,避免不了使用DSL语句去查询,就像使用关系型数据库的时候要学会SQL语法一样。如果我们学习好了DSL语法的使用,那么在日后使用和使用Java Client调用时候也会变得非常简单。

  1. POST /索引库名/_search
  2. {
  3. "query":{
  4. "查询类型":{
  5. "查询条件":"查询条件值"
  6. }
  7. }
  8. }

这里的query代表一个查询对象,里面可以有不同的查询属性

  • 查询类型:
    • 例如: match_all , match , term , range 等等
  • 查询条件:查询条件会根据类型的不同,写法也有差异,后面详细讲解

2.1 查询所有(match_all query)

  1. POST /lagou-company-index/_search
  2. {
  3. "query":{
  4. "match_all": {}
  5. }
  6. }
  • query :代表查询对象
  • match_all :代表查询所有

    1. {
    2. "took" : 1,
    3. "timed_out" : false,
    4. "_shards" : {
    5. "total" : 1,
    6. "successful" : 1,
    7. "skipped" : 0,
    8. "failed" : 0
    9. },
    10. "hits" : {
    11. "total" : {
    12. "value" : 1,
    13. "relation" : "eq"
    14. },
    15. "max_score" : 1.0,
    16. "hits" : [
    17. {
    18. "_index" : "lagou-company-index",
    19. "_type" : "_doc",
    20. "_id" : "1",
    21. "_score" : 1.0,
    22. "_source" : {
    23. "name" : "百度",
    24. "job" : "小度用户运营经理",
    25. "payment" : "30000",
    26. "logo" : "http://www.lgstatic.com/thubnail_120x120/i/image/M00/21/3E/CgpFT1kVdzeAJNbU AABJB7x9sm8374.png"
    27. }
    28. }
    29. ]
    30. }
    31. }

    结果

  • took:查询花费时间,单位是毫秒

  • time_out:是否超时
  • _shards:分片信息
  • hits:搜索结果总览对象
    • total:搜索到的总条数
    • max_score:所有结果中文档得分的最高分
    • hits:搜索结果的文档对象数组,每个元素是一条搜索到的文档信息
      • _index:索引库
      • _type:文档类型
      • _id:文档id
      • _score:文档得分
      • _source:文档的源数据

2.2 全文搜索(full-text query)

  1. 全文搜索能够搜索已分析的文本字段,如电子邮件正文,商品描述等。使用索引期间应用于字段的同一分析器处理查询字符串。全文搜索的分类很多几个典型的如下:

2.2.1 匹配搜索(match query)

全文查询的标准查询,它可以对一个字段进行模糊、短语查询。 match queries 接收text/numerics/dates, 对它们进行分词分析, 再组织成一个boolean查询。可通过operator 指定bool组合操作(or、and 默认是 or )。
现在,索引库中有2部手机,1台电视;

  1. PUT /lagou-property
  2. {
  3. "settings": {},
  4. "mappings": {
  5. "properties": {
  6. "title": {
  7. "type": "text",
  8. "analyzer": "ik_max_word"
  9. },
  10. "images": {
  11. "type": "keyword"
  12. },
  13. "price": {
  14. "type": "float"
  15. }
  16. }
  17. }
  18. }
  19. POST /lagou-property/_doc/
  20. {
  21. "title": "小米电视4A",
  22. "images": "http://image.lagou.com/12479122.jpg",
  23. "price": 4288
  24. }
  25. POST /lagou-property/_doc/
  26. {
  27. "title": "小米手机",
  28. "images": "http://image.lagou.com/12479622.jpg",
  29. "price": 2699
  30. }
  31. POST /lagou-property/_doc/
  32. {
  33. "title": "华为手机",
  34. "images": "http://image.lagou.com/12479922.jpg",
  35. "price": 5699
  36. }
  • or关系

match 类型查询,会把查询条件进行分词,然后进行查询,多个词条之间是or的关系

  1. POST /lagou-property/_search
  2. {
  3. "query":{
  4. "match":{
  5. "title":"小米电视4A"
  6. }
  7. }
  8. }

结果:

  1. {
  2. "took" : 51,
  3. "timed_out" : false,
  4. "_shards" : {
  5. "total" : 1,
  6. "successful" : 1,
  7. "skipped" : 0,
  8. "failed" : 0
  9. },
  10. "hits" : {
  11. "total" : {
  12. "value" : 2,
  13. "relation" : "eq"
  14. },
  15. "max_score" : 2.8330114,
  16. "hits" : [
  17. {
  18. "_index" : "lagou-property",
  19. "_type" : "_doc",
  20. "_id" : "UzLef3oBYYHsZaOa-I1O",
  21. "_score" : 2.8330114,
  22. "_source" : {
  23. "title" : "小米电视4A",
  24. "images" : "http://image.lagou.com/12479122.jpg",
  25. "price" : 4288
  26. }
  27. },
  28. {
  29. "_index" : "lagou-property",
  30. "_type" : "_doc",
  31. "_id" : "VDLff3oBYYHsZaOaAY2f",
  32. "_score" : 0.52354836,
  33. "_source" : {
  34. "title" : "小米手机",
  35. "images" : "http://image.lagou.com/12479622.jpg",
  36. "price" : 2699
  37. }
  38. }
  39. ]
  40. }
  41. }

在上面的案例中,不仅会查询到电视,而且与小米相关的都会查询到,多个词之间是 or 的关系。

  • and关系

某些情况下,我们需要更精确查找,我们希望这个关系变成 and ,可以这样做:

  1. POST /lagou-property/_search
  2. {
  3. "query": {
  4. "match": {
  5. "title": {
  6. "query": "小米电视4A",
  7. "operator": "and"
  8. }
  9. }
  10. }
  11. }

结果:

  1. {
  2. "took" : 1,
  3. "timed_out" : false,
  4. "_shards" : {
  5. "total" : 1,
  6. "successful" : 1,
  7. "skipped" : 0,
  8. "failed" : 0
  9. },
  10. "hits" : {
  11. "total" : {
  12. "value" : 1,
  13. "relation" : "eq"
  14. },
  15. "max_score" : 2.8330114,
  16. "hits" : [
  17. {
  18. "_index" : "lagou-property",
  19. "_type" : "_doc",
  20. "_id" : "UzLef3oBYYHsZaOa-I1O",
  21. "_score" : 2.8330114,
  22. "_source" : {
  23. "title" : "小米电视4A",
  24. "images" : "http://image.lagou.com/12479122.jpg",
  25. "price" : 4288
  26. }
  27. }
  28. ]
  29. }
  30. }

本例中,只有同时包含 小米 和 电视 的词条才会被搜索到

2.2.2 短语搜索(match phrase query)

match_phrase 查询用来对一个字段进行短语查询,可以指定 analyzer、slop移动因子

  1. #可查询到数据
  2. GET /lagou-property/_search
  3. {
  4. "query": {
  5. "match_phrase": {
  6. "title": "小米电视"
  7. }
  8. }
  9. }
  10. #查询不到数据
  11. GET /lagou-property/_search
  12. {
  13. "query": {
  14. "match_phrase": {
  15. "title": "小米 4A"
  16. }
  17. }
  18. }
  19. #可查询到数据 加了移动因子 -> 跨度
  20. GET /lagou-property/_search
  21. {
  22. "query": {
  23. "match_phrase": {
  24. "title": {
  25. "query": "小米 4A",
  26. "slop": 2
  27. }
  28. }
  29. }
  30. }

2.2.3 query_string 查询

Query String: Query提供了无需指定某字段而对文档全文进行匹配查询的一个高级查询,同时可以指定在哪些字段上进行匹配。

  1. # 默认 指定字段
  2. GET /lagou-property/_search
  3. {
  4. "query": {
  5. "query_string": {
  6. "query": "2699"
  7. }
  8. }
  9. }
  10. GET /lagou-property/_search
  11. {
  12. "query": {
  13. "query_string": {
  14. "query": "2699",
  15. "default_field": "title"
  16. }
  17. }
  18. }
  19. # 逻辑查询
  20. GET /lagou-property/_search
  21. {
  22. "query": {
  23. "query_string": {
  24. "query": "手机 OR 小米",
  25. "default_field": "title"
  26. }
  27. }
  28. }
  29. GET /lagou-property/_search
  30. {
  31. "query": {
  32. "query_string": {
  33. "query": "手机 AND 小米",
  34. "default_field": "title"
  35. }
  36. }
  37. }
  38. # 模糊查询 ~1 表示修正 修正一个字符 ~2修正两个字符
  39. GET /lagou-property/_search
  40. {
  41. "query": {
  42. "query_string": {
  43. "query": "大米~1",
  44. "default_field": "title"
  45. }
  46. }
  47. }
  48. # 多字段支持
  49. GET /lagou-property/_search
  50. {
  51. "query": {
  52. "query_string": {
  53. "query": "2699",
  54. "fields": [
  55. "title",
  56. "price"
  57. ]
  58. }
  59. }
  60. }

2.2.4 多字段匹配搜索(multi match query)

如果你需要在多个字段上进行文本搜索,可用multi_match 。multi_match在 match的基础上支持对多个字段进行文本查询。

  1. GET /lagou-property/_search
  2. {
  3. "query": {
  4. "multi_match": {
  5. "query": "2699",
  6. "fields": [
  7. "title",
  8. "price"
  9. ]
  10. }
  11. }
  12. }

还可以使用*匹配多个字段:

  1. GET /lagou-property/_search
  2. {
  3. "query": {
  4. "multi_match": {
  5. "query": "http://image.lagou.com/12479622.jpg",
  6. "fields": [
  7. "title",
  8. "ima*"
  9. ]
  10. }
  11. }
  12. }

2.3 词条级搜索(term-level queries)

可以使用term-level queries根据结构化数据中的精确值查找文档。结构化数据的值包括日期范围、IP 地址、价格或产品ID。
与全文查询不同,term-level queries不分析搜索词。相反,词条与存储在字段级别中的术语完全匹配。

  1. PUT /book
  2. {
  3. "settings": {},
  4. "mappings": {
  5. "properties": {
  6. "description": {
  7. "type": "text",
  8. "analyzer": "ik_max_word"
  9. },
  10. "name": {
  11. "type": "text",
  12. "analyzer": "ik_max_word"
  13. },
  14. "price": {
  15. "type": "float"
  16. },
  17. "timestamp": {
  18. "type": "date",
  19. "format": "yyyy-MM-dd HH:mm:ss||yyyy-MM-dd||epoch_millis"
  20. }
  21. }
  22. }
  23. }
  24. PUT /book/_doc/1
  25. {
  26. "name": "lucene",
  27. "description": "Lucene Core is a Java library providing powerful indexing and search features, as well as spellchecking, hit highlighting and advanced analysis/tokenization capabilities. The PyLucene sub project provides Python bindings for Lucene Core. ",
  28. "price": 100.45,
  29. "timestamp": "2020-08-21 19:11:35"
  30. }
  31. PUT /book/_doc/2
  32. {
  33. "name": "solr",
  34. "description": "Solr is highly scalable, providing fully fault tolerant distributed indexing, search and analytics. It exposes Lucenes features through easy to use JSON/HTTP interfaces or native clients for Java and other languages.",
  35. "price": 320.45,
  36. "timestamp": "2020-07-21 17:11:35"
  37. }
  38. PUT /book/_doc/3
  39. {
  40. "name": "Hadoop",
  41. "description": "The Apache Hadoop software library is a framework that allows for the distributed processing of large data sets across clusters of computers using simple programming models.",
  42. "price": 620.45,
  43. "timestamp": "2020-08-22 19:18:35"
  44. }
  45. PUT /book/_doc/4
  46. {
  47. "name": "ElasticSearch",
  48. "description": "Elasticsearch是一个基于Lucene的搜索服务器。它提供了一个分布式多用户能力 的全文搜索引擎,基于RESTful web接口。Elasticsearch是用Java语言开发的,并作为Apache许可条 款下的开放源码发布,是一种流行的企业级搜索引擎。Elasticsearch用于云计算中,能够达到实时搜 索,稳定,可靠,快速,安装使用方便。官方客户端在Java、.NET(C#)、PHP、Python、Apache Groovy、Ruby和许多其他语言中都是可用的。根据DB-Engines的排名显示,Elasticsearch是最受欢 迎的企业搜索引擎,其次是Apache Solr,也是基于Lucene。",
  49. "price": 999.99,
  50. "timestamp": "2020-08-15 10:11:35"
  51. }

2.3.1 词条搜索(term query)

term 查询用于查询指定字段包含某个词项的文档

  1. POST /book/_search
  2. {
  3. "query": {
  4. "term": {
  5. "name": "solr"
  6. }
  7. }
  8. }

2.3.2 词条集合搜索(terms query)

terms 查询用于查询指定字段包含某些词项的文档

  1. GET /book/_search
  2. {
  3. "query": {
  4. "terms": {
  5. "name": [
  6. "solr",
  7. "elasticsearch"
  8. ]
  9. }
  10. }
  11. }


2.3.3 范围搜索(range query)

  • gte:大于等于
  • gt:大于
  • lte:小于等于
  • lt:小于
  • boost:查询权重 ```json

GET /book/_search { “query”: { “range”: { “price”: { “gte”: 10, “lte”: 200, “boost”: 2 } } } }

now-2d 当前时间减2天

GET /book/_search { “query”: { “range”: { “timestamp”: { “gte”: “now-2d/d”, “lt”: “now/d” } } } }

GET book/_search { “query”: { “range”: { “timestamp”: { “gte”: “18/08/2020”, “lte”: “2021”, “format”: “dd/MM/yyyy||yyyy” } } } }

  1. <a name="T3qn8"></a>
  2. ### 2.3.4 不为空搜索(exists query)
  3. 查询指定字段值不为空的文档。相当 SQL 中的 column is not null
  4. ```json
  5. GET /book/_search
  6. {
  7. "query": {
  8. "exists": {
  9. "field": "price"
  10. }
  11. }
  12. }

2.3.5 词项前缀搜索(prefifix query)

  1. GET /book/_search
  2. {
  3. "query": {
  4. "prefix": {
  5. "name": "so"
  6. }
  7. }
  8. }

2.3.6 通配符搜索(wildcard query)

  1. GET /book/_search
  2. {
  3. "query": {
  4. "wildcard": {
  5. "name": "so*r"
  6. }
  7. }
  8. }
  9. GET /book/_search
  10. {
  11. "query": {
  12. "wildcard": {
  13. "name": {
  14. "value": "lu*",
  15. "boost": 2
  16. }
  17. }
  18. }
  19. }

2.3.7 正则搜索(regexp query)

regexp允许使用正则表达式进行term查询.注意regexp如果使用不正确,会给服务器带来很严重的性能压力。比如.*开头的查询,将会匹配所有的倒排索引中的关键字,这几乎相当于全表扫描,会很慢。因此如果可以的话,最好在使用正则前,加上匹配的前缀。

  1. GET /book/_search
  2. {
  3. "query": {
  4. "regexp": {
  5. "name": "s.*"
  6. }
  7. }
  8. }
  9. GET /book/_search
  10. {
  11. "query": {
  12. "regexp": {
  13. "name": {
  14. "value": "s.*",
  15. "boost": 1.2
  16. }
  17. }
  18. }
  19. }

2.3.8 模糊搜索(fuzzy query)

  1. GET /book/_search
  2. {
  3. "query": {
  4. "fuzzy": {
  5. "name": "so"
  6. }
  7. }
  8. }
  9. GET /book/_search
  10. {
  11. "query": {
  12. "fuzzy": {
  13. "name": {
  14. "value": "so",
  15. "boost": 1,
  16. "fuzziness": 2
  17. }
  18. }
  19. }
  20. }
  21. #fuzziness 允许修正的字符数
  22. GET /book/_search
  23. {
  24. "query": {
  25. "fuzzy": {
  26. "name": {
  27. "value": "sorl",
  28. "boost": 1,
  29. "fuzziness": 2
  30. }
  31. }
  32. }
  33. }

2.3.9 ids搜索(id集合查询)

  1. GET /book/_search
  2. {
  3. "query": {
  4. "ids": {
  5. "type": "_doc",
  6. "values": [
  7. "1",
  8. "3"
  9. ]
  10. }
  11. }
  12. }

2.4 复合搜索(compound query)

1) constant_score query
用来包装另一个查询,将查询匹配的文档的评分设为一个常值

  1. GET /book/_search
  2. {
  3. "query": {
  4. "term": {
  5. "description": "solr"
  6. }
  7. }
  8. }
  9. GET /book/_search
  10. {
  11. "query": {
  12. "constant_score": {
  13. "filter": {
  14. "term": {
  15. "description": "solr"
  16. }
  17. },
  18. "boost": 1.2
  19. }
  20. }
  21. }

2) 布尔搜索(bool query)
bool 查询用bool操作来组合多个查询字句为一个查询。 可用的关键字:

  • must:必须满足
  • fifilter:必须满足,但执行的是fifilter上下文,不参与、不影响评分
  • should:或
  • must_not:必须不满足,在fifilter上下文中执行,不参与、不影响评分 ```json

POST /book/_search { “query”: { “bool”: { “must”: { “match”: { “description”: “java” } }, “filter”: { “term”: { “name”: “solr” } }, “must_not”: { “range”: { “price”: { “gte”: 200, “lte”: 300 } } }, “minimum_should_match”: 1, “boost”: 1 } } }

  1. minimum_should_match代表了最小匹配精度,如果设置minimum_should_match=1,那么should 语句中至少需要有一个条件满足。
  2. <a name="lY937"></a>
  3. ## 2.5 排序
  4. - 相关性评分排序
  5. 默认情况下,返回的结果是按照 相关性 进行排序的——最相关的文档排在最前。 在本章的后面部分,我们会解释 相关性 意味着什么以及它是如何计算的, 不过让我们首先看看 sort 参数以及如何使用它。 <br />为了按照相关性来排序,需要将相关性表示为一个数值。在 Elasticsearch 中, 相关性得分由一个浮点数进行表示,并在搜索结果中通过 _score 参数返回, 默认排序是 _score 降序,按照相关性评分升序排序如下
  6. ```json
  7. POST /book/_search
  8. {
  9. "query": {
  10. "match": {
  11. "description": "solr"
  12. }
  13. }
  14. }
  15. POST /book/_search
  16. {
  17. "query": {
  18. "match": {
  19. "description": "solr"
  20. }
  21. },
  22. "sort": [
  23. {
  24. "_score": {
  25. "order": "asc"
  26. }
  27. }
  28. ]
  29. }
  • 字段值排序 ```json

POST /book/_search { “query”: { “match_all”: {} }, “sort”: [ { “price”: { “order”: “desc” } } ] }

  1. - 多级排序
  2. 假定我们想要结合使用 price _score(得分) 进行查询,并且匹配的结果首先按照价格排序, 然后按照相关性得分排序:
  3. ```json
  4. POST /book/_search
  5. {
  6. "query": {
  7. "match_all": {}
  8. },
  9. "sort": [
  10. {
  11. "price": {
  12. "order": "desc"
  13. }
  14. },
  15. {
  16. "timestamp": {
  17. "order": "desc"
  18. }
  19. }
  20. ]
  21. }

2.6 分页

Elasticsearch中实现分页的语法非常简单:

  1. POST /book/_search
  2. {
  3. "query": {
  4. "match_all": {}
  5. },
  6. "size": 2,
  7. "from": 0
  8. }
  9. POST /book/_search
  10. {
  11. "query": {
  12. "match_all": {}
  13. },
  14. "sort": [
  15. {
  16. "price": {
  17. "order": "desc"
  18. }
  19. }
  20. ],
  21. "size": 2,
  22. "from": 2
  23. }

size:每页显示多少条
from:当前页起始索引, int start = (pageNum - 1) * size

2.7 高亮

Elasticsearch中实现高亮的语法比较简单:

  1. POST /book/_search
  2. {
  3. "query": {
  4. "match": {
  5. "name": "elasticsearch"
  6. }
  7. },
  8. "highlight": {
  9. "pre_tags": "<font color='pink'>",
  10. "post_tags": "</font>",
  11. "fields": [
  12. {
  13. "name": {}
  14. }
  15. ]
  16. }
  17. }
  18. POST /book/_search
  19. {
  20. "query": {
  21. "match": {
  22. "name": "elasticsearch"
  23. }
  24. },
  25. "highlight": {
  26. "pre_tags": "<font color='pink'>",
  27. "post_tags": "</font>",
  28. "fields": [
  29. {
  30. "name": {}
  31. },
  32. {
  33. "description": {}
  34. }
  35. ]
  36. }
  37. }
  38. POST /book/_search
  39. {
  40. "query": {
  41. "query_string": {
  42. "query": "elasticsearch"
  43. }
  44. },
  45. "highlight": {
  46. "pre_tags": "<font color='pink'>",
  47. "post_tags": "</font>",
  48. "fields": [
  49. {
  50. "name": {}
  51. },
  52. {
  53. "description": {}
  54. }
  55. ]
  56. }
  57. }

在使用match查询的同时,加上一个highlight属性:

  • pre_tags:前置标签
  • post_tags:后置标签
  • fifields:需要高亮的字段
    • name:这里声明title字段需要高亮,后面可以为这个字段设置特有配置,也可以空
  1. {
  2. "took" : 8,
  3. "timed_out" : false,
  4. "_shards" : {
  5. "total" : 1,
  6. "successful" : 1,
  7. "skipped" : 0,
  8. "failed" : 0
  9. },
  10. "hits" : {
  11. "total" : {
  12. "value" : 1,
  13. "relation" : "eq"
  14. },
  15. "max_score" : 1.6207945,
  16. "hits" : [
  17. {
  18. "_index" : "book",
  19. "_type" : "_doc",
  20. "_id" : "4",
  21. "_score" : 1.6207945,
  22. "_source" : {
  23. "name" : "ElasticSearch",
  24. "description" : "Elasticsearch是一个基于Lucene的搜索服务器。它提供了一个分布式多用户能力 的全文搜索引擎,基于RESTful web接口。Elasticsearch是用Java语言开发的,并作为Apache许可条 款下的开放源码发布,是一种流行的企业级搜索引擎。Elasticsearch用于云计算中,能够达到实时搜 索,稳定,可靠,快速,安装使用方便。官方客户端在Java、.NET(C#)、PHP、Python、Apache Groovy、Ruby和许多其他语言中都是可用的。根据DB-Engines的排名显示,Elasticsearch是最受欢 迎的企业搜索引擎,其次是Apache Solr,也是基于Lucene。",
  25. "price" : 999.99,
  26. "timestamp" : "2020-08-15 10:11:35"
  27. },
  28. "highlight" : {
  29. "name" : [
  30. "<font color='pink'>ElasticSearch</font>"
  31. ],
  32. "description" : [
  33. "<font color='pink'>Elasticsearch</font>是一个基于Lucene的搜索服务器。它提供了一个分布式多用户能力 的全文搜索引擎,基于RESTful web接口。",
  34. "<font color='pink'>Elasticsearch</font>是用Java语言开发的,并作为Apache许可条 款下的开放源码发布,是一种流行的企业级搜索引擎。",
  35. "<font color='pink'>Elasticsearch</font>用于云计算中,能够达到实时搜 索,稳定,可靠,快速,安装使用方便。",
  36. "根据DB-Engines的排名显示,<font color='pink'>Elasticsearch</font>是最受欢 迎的企业搜索引擎,其次是Apache Solr,也是基于Lucene。"
  37. ]
  38. }
  39. }
  40. ]
  41. }
  42. }

2.8 文档批量操作(bulk 和 mget)

mget 批量查询
单条查询 GET /test_index/_doc/1,如果查询多个id的文档一条一条查询,网络开销太大。

  1. GET /_mget
  2. {
  3. "docs": [
  4. {
  5. "_index": "book",
  6. "_id": 1
  7. },
  8. {
  9. "_index": "book",
  10. "_id": 2
  11. }
  12. ]
  13. }
  1. #! Elasticsearch built-in security features are not enabled. Without authentication, your cluster could be accessible to anyone. See https://www.elastic.co/guide/en/elasticsearch/reference/7.13/security-minimal-setup.html to enable security.
  2. {
  3. "docs" : [
  4. {
  5. "_index" : "book",
  6. "_type" : "_doc",
  7. "_id" : "1",
  8. "_version" : 2,
  9. "_seq_no" : 4,
  10. "_primary_term" : 1,
  11. "found" : true,
  12. "_source" : {
  13. "name" : "lucene",
  14. "description" : "Lucene Core is a Java library providing powerful indexing and search features, as well as spellchecking, hit highlighting and advanced analysis/tokenization capabilities. The PyLucene sub project provides Python bindings for Lucene Core. ",
  15. "price" : 100.45,
  16. "timestamp" : "2020-08-21 19:11:35"
  17. }
  18. },
  19. {
  20. "_index" : "book",
  21. "_type" : "_doc",
  22. "_id" : "2",
  23. "_version" : 2,
  24. "_seq_no" : 5,
  25. "_primary_term" : 1,
  26. "found" : true,
  27. "_source" : {
  28. "name" : "solr",
  29. "description" : "Solr is highly scalable, providing fully fault tolerant distributed indexing, search and analytics. It exposes Lucenes features through easy to use JSON/HTTP interfaces or native clients for Java and other languages.",
  30. "price" : 320.45,
  31. "timestamp" : "2020-07-21 17:11:35"
  32. }
  33. }
  34. ]
  35. }

同一索引下批量查询:

  1. GET /book/_mget
  2. {
  3. "docs": [
  4. {
  5. "_id": 2
  6. },
  7. {
  8. "_id": 3
  9. }
  10. ]
  11. }

搜索简化写法

  1. POST /book/_search
  2. {
  3. "query": {
  4. "ids": {
  5. "values": [
  6. "1",
  7. "4"
  8. ]
  9. }
  10. }
  11. }

bulk 批量增删改
Bulk 操作解释将文档的增删改查一些列操作,通过一次请求全都做完。减少网络传输次数。
语法:

  1. POST /_bulk
  2. {"action": {"metadata"}}
  3. {"data"}

如下操作,删除1,新增5,修改2。

  1. POST /_bulk
  2. { "delete": { "_index": "book", "_id": "1" }}
  3. { "create": { "_index": "book", "_id": "5" }}
  4. { "name": "test14","price":100.99 }
  5. { "update": { "_index": "book", "_id": "2"} }
  6. { "doc" : {"name" : "test"} }

功能:

  • delete:删除一个文档,只要1个json串就可以了 删除的批量操作不需要请求体
  • create:相当于强制创建 PUT /index/type/id/_create
  • index:普通的put操作,可以是创建文档,也可以是全量替换文档
  • update:执行的是局部更新partial update操作

格式:每个json不能换行。相邻json必须换行。
隔离:每个操作互不影响。操作失败的行会返回其失败信息。

实际用法:bulk请求一次不要太大,否则一下积压到内存中,性能会下降。所以,一次请求几千个操作、大小在几M正好。
bulk会将要处理的数据载入内存中,所以数据量是有限的,最佳的数据两不是一个确定的数据,它取决于你的硬件,你的文档大小以及复杂性,你的索引以及搜索的负载。
一般建议是1000-5000个文档,大小建议是5-15MB,默认不能超过100M,可以在es的配置文件(ES的
config下的elasticsearch.yml)中配置。
http.max_content_length: 10mb

3. Filter DSL

Elasticsearch中的所有的查询都会触发相关度得分的计算。对于那些我们不需要相关度得分的场景下,Elasticsearch以过滤器的形式提供了另一种查询功能,过滤器在概念上类似于查询,但是它们有非常快的执行速度,执行速度快主要有以下两个原因:

  • 过滤器不会计算相关度的得分,所以它们在计算上更快一些。
  • 过滤器可以被缓存到内存中,这使得在重复的搜索查询上,其要比相应的查询快出许多。

为了理解过滤器,可以将一个查询(像是match_all,match,bool等)和一个过滤器结合起来。我们以范围过滤器为例,它允许我们通过一个区间的值来过滤文档。这通常被用在数字和日期的过滤上。
下面这个例子使用一个被过滤的查询,其返回price值是在200到1000之间(闭区间)的书。

  1. POST /book/_search
  2. {
  3. "query": {
  4. "filtered": {
  5. "query": {
  6. "match_all": {}
  7. },
  8. "filter": {
  9. "range": {
  10. "price": {
  11. "gte": 200,
  12. "lte": 1000
  13. }
  14. }
  15. }
  16. }
  17. }
  18. }
  19. #5.0 之后的写法
  20. POST /book/_search
  21. {
  22. "query": {
  23. "bool": {
  24. "must": {
  25. "match_all": {}
  26. },
  27. "filter": {
  28. "range": {
  29. "price": {
  30. "gte": 200,
  31. "lte": 1000
  32. }
  33. }
  34. }
  35. }
  36. }
  37. }

分解上面的例子,被过滤的查询包含一个match_all查询(查询部分)和一个过滤器(fifilter部分)。我们可以在查询部分中放入其他查询,在fifilter部分放入其它过滤器。在上面的应用场景中,由于所有的在这个范围之内的文档都是平等的(或者说相关度都是一样的),没有一个文档比另一个文档更相关,所以这个时候使用范围过滤器就非常合适了。通常情况下,要决定是使用过滤器还是使用查询,你就需要问自己是否需要相关度得分。如果相关度是不重要的,使用过滤器,否则使用查询。查询和过滤器在概念上类似于SELECT WHERE语句。

4. 定位非法搜索及原因

  1. 在开发的时候,我们可能会写到上百行的查询语句,如果出错的话,找起来很麻烦,Elasticsearch提供了帮助开发人员定位不合法的查询的api _validate
  1. GET /book/_search?explain
  2. {
  3. "query": {
  4. "match1": {
  5. "name": "test"
  6. }
  7. }
  8. }
  9. #使用 validate
  10. GET /book/_validate/query?explain
  11. {
  12. "query": {
  13. "match1": {
  14. "name": "test"
  15. }
  16. }
  17. }

返回结果

  1. {
  2. "valid" : false,
  3. "error" : "ParsingException[unknown query [match1] did you mean any of [match, match_all]?]; nested: NamedObjectNotFoundException[[3:15] unknown field [match1]];; org.elasticsearch.common.xcontent.NamedObjectNotFoundException: [3:15] unknown field [match1]"
  4. }

在查询时,不小心把 match 写成了 match1 ,通过 validate api 可以清楚的看到错误原因
正确查询返回

  1. {
  2. "_shards" : {
  3. "total" : 1,
  4. "successful" : 1,
  5. "failed" : 0
  6. },
  7. "valid" : true,
  8. "explanations" : [
  9. {
  10. "index" : "book",
  11. "valid" : true,
  12. "explanation" : "name:test"
  13. }
  14. ]
  15. }

5. 聚合分析

5.1.聚合介绍

聚合分析是数据库中重要的功能特性,完成对一个查询的数据集中数据的聚合计算,如:找出某字段(或计算表达式的结果)的最大值、最小值,计算和、平均值等。Elasticsearch作为搜索引擎兼数据库,同样提供了强大的聚合分析能力。
对一个数据集求最大、最小、和、平均值等指标的聚合,在ES中称为指标聚合 metric 而关系型数据库中除了有聚合函数外,还可以对查询出的数据进行分组group by,再在组上进行指标聚合。在 ES 中group by 称为分桶桶聚合 bucketing
Elasticsearch聚合分析语法
在查询请求体中以aggregations节点按如下语法定义聚合分析:

  1. "aggregations" : {
  2. "<aggregation_name>" : { <!--聚合的名字 -->
  3. "<aggregation_type>" : { <!--聚合的类型 -->
  4. <aggregation_body> <!--聚合体:对哪些字段进行聚合 -->
  5. }
  6. [,"meta" : { [<meta_data_body>] } ]? <!--元 -->
  7. [,"aggregations" : { [<sub_aggregation>]+ } ]? <!--在聚合里面在定义子聚合 - ->
  8. }
  9. [,"<aggregation_name_2>" : { ... } ]*<!--聚合的名字 -->
  10. }

说明:aggregations 也可简写为 aggs

5.2 指标聚合

  • max min sum avg ```json

POST /book/_search { “size”: 0, “aggs”: { “max_price”: { “max”: { “field”: “price” } } } }

  1. - 文档计数count
  2. 示例: 统计price大于100的文档数量
  3. ```json
  4. POST /book/_count
  5. {
  6. "query": {
  7. "range": {
  8. "price": {
  9. "gt": 100
  10. }
  11. }
  12. }
  13. }
  • value_count 统计某字段有值的文档数 ```json

POST /book/_search?size=0 { “aggs”: { “price_count”: { “value_count”: { “field”: “price” } } } }

  1. - cardinality值去重计数基数
  2. ```json
  3. POST /book/_search?size=0
  4. {
  5. "aggs": {
  6. "_id_count": {
  7. "cardinality": {
  8. "field": "_id"
  9. }
  10. },
  11. "price_count": {
  12. "cardinality": {
  13. "field": "price"
  14. }
  15. }
  16. }
  17. }
  • stats 统计 count max min avg sum 5个值

    1. POST /book/_search?size=0
    2. {
    3. "aggs": {
    4. "price_stats": {
    5. "stats": {
    6. "field": "price"
    7. }
    8. }
    9. }
    10. }
  • Extended stats

    高级统计,比stats多4个统计结果: 平方和、方差、标准差、平均值加/减两个标准差的区间

    1. POST /book/_search?size=0
    2. {
    3. "aggs": {
    4. "price_stats": {
    5. "extended_stats": {
    6. "field": "price"
    7. }
    8. }
    9. }
    10. }
  • Percentiles 占比百分位对应的值统计

    1. POST /book/_search?size=0
    2. {
    3. "aggs": {
    4. "price_percents": {
    5. "percentiles": {
    6. "field": "price"
    7. }
    8. }
    9. }
    10. }

    指定分位值 ```json

POST /book/_search?size=0 { “aggs”: { “price_percents”: { “percentiles”: { “field”: “price”, “percents”: [ 75, 99, 99.9 ] } } } }

  1. - Percentiles rank 统计值小于等于指定值的文档占比
  2. 统计price小于100200的文档的占比
  3. ```json
  4. POST /book/_search?size=0
  5. {
  6. "aggs": {
  7. "gge_perc_rank": {
  8. "percentile_ranks": {
  9. "field": "price",
  10. "values": [
  11. 100,
  12. 200
  13. ]
  14. }
  15. }
  16. }
  17. }

5.3 桶聚合

Bucket Aggregations,桶聚合。
它执行的是对文档分组的操作(与sql中的group by类似),把满足相关特性的文档分到一个桶里,即桶分,输出结果往往是一个个包含多个文档的桶(一个桶就是一个group)
bucket:一个数据分组
metric:对一个数据分组执行的统计

  1. POST /book/_search
  2. {
  3. "size": 0,
  4. "aggs": {
  5. "group_by_price": {
  6. "range": {
  7. "field": "price",
  8. "ranges": [
  9. {
  10. "from": 0,
  11. "to": 200
  12. },
  13. {
  14. "from": 200,
  15. "to": 400
  16. },
  17. {
  18. "from": 400,
  19. "to": 1000
  20. }
  21. ]
  22. },
  23. "aggs": {
  24. "average_price": {
  25. "avg": {
  26. "field": "price"
  27. }
  28. }
  29. }
  30. }
  31. }
  32. }

值的个数统计

  1. "count_price": {
  2. "value_count": {
  3. "field": "price"
  4. }
  5. }

实现having 效果

  1. POST /book/_search
  2. {
  3. "size": 0,
  4. "aggs": {
  5. "group_by_price": {
  6. "range": {
  7. "field": "price",
  8. "ranges": [
  9. {
  10. "from": 0,
  11. "to": 200
  12. },
  13. {
  14. "from": 200,
  15. "to": 400
  16. },
  17. {
  18. "from": 400,
  19. "to": 1000
  20. }
  21. ]
  22. },
  23. "aggs": {
  24. "average_price": {
  25. "avg": {
  26. "field": "price"
  27. }
  28. },
  29. "having": {
  30. "bucket_selector": {
  31. "buckets_path": {
  32. "avg_price": "average_price"
  33. },
  34. "script": {
  35. "source": "params.avg_price >= 200 "
  36. }
  37. }
  38. }
  39. }
  40. }
  41. }
  42. }

6. Elasticsearch零停机索引重建

6.1 说明

Elasticsearch是一个实时的分布式搜索引擎,为用户提供搜索服务,当我们决定存储某种数据时,在创建索引的时候需要数据结构完整确定下来,与此同时索引的设定和很多固定配置将不能改变。当需要改变数据结构时就需要重建索引,为此,Elasticsearch团队提供了辅助工具帮助开发人员进行索引重建。

零停机完成索引重建的三种方案。

6.2 方案一:外部数据导入方案

1)整体介绍
系统架构设计中,有关系型数据库用来存储数据,Elasticsearch在系统架构里起到查询加速的作用,如果遇到索引重建的操作,待系统模块发布新版本后,可以从数据库将数据查询出来,重新灌到Elasticsearch即可。

2)执行步骤
建议的功能方案:数据库 + MQ + 应用模块 + Elasticsearch,可以在MQ控制台发送MQ消息来触发重导数据,按批次对数据进行导入,整个过程异步化处理,请求操作示意如下所示:
image.png
3)详细操作步骤:
1. 通过MQ的web控制台或cli命令行,发送指定的MQ消息
2. MQ消息被微服务模块的消费者消费,触发ES数据重新导入功能
3. 微服务模块从数据库里查询数据的总数及批次信息,并将每个数据批次的分页信息重新发送给MQ消息,分页信息包含查询条件和偏移量,此MQ消息还是会被微服务的MQ消息者接收处理。
4. 微服务根据接收的查询条件和分页信息,从数据库获取到数据后,根据索引结构的定义,将数据组装成ES支持的JSON格式,并执行bulk命令,将数据发送给Elasticsearch集群。
这样就可以完成索引的重建工作。

4)方案特点
MQ中间件的选型不做具体要求,常见的rabitmq、activemq、rocketmq等均可。
在微服务模块方面,提供MQ消息处理接口、数据处理模块需要事先开发的,一般是创建新的索引时,配套把重建的功能也一起做好。整体功能共用一个topic,针对每个索引,有单独的结构定义和MQ消息处理tag,代码尽可能复用。处理的批次大小需要根据实际的情况设置。
微服务模块实例会部署多个,数据是分批处理的,批次信息会一次性全部先发送给MQ,各个实例处理的数据相互不重叠,利用MQ消息的异步处理机制,可以充分利用并发的优势,加快数据重建的速度。

5)方案缺点
1. 对数据库造成读取压力,短时间内大量的读操作,会占用数据库的硬件资源,严重时可能引起数据库性能下降。
2. 网络带宽占用多,数据毕竟是从一个库传到另一个库,虽说是内网,但大量的数据传输带宽占用也需要注意。
3. 数据重建时间稍长,跟迁移的数据量大小有关。

6.3 方案二:基于scroll+bulk+索引别名方案

1)整体介绍
利用Elasticsearch自带的一些工具完成索引的重建工作,当然在方案实际落地时,可能也会依赖客户端的一些功能,比如用Java客户端持续的做scroll查询、bulk命令的封装等。数据完全自给自足,不依赖其他数据源。

2)执行步骤
假设原索引名称是book,新的索引名称为book_new,Java客户端使用别名book_alias连接Elasticsearch,该别名指向原索引book。
1. 若Java客户端没有使用别名,需要给客户端分配一个: PUT /book/_alias/book_alias
2. 新建索引book_new,将mapping信息,settings信息等按新的要求全部定义好。
3. 使用scroll api将数据批量查询出来
为了使用 scroll,初始搜索请求应该在查询中指定 scroll 参数,这可以告诉 Elasticsearch 需要保持搜索的上下文环境多久,1m 就是一分钟。

  1. GET /book/_search?scroll=1m
  2. {
  3. "query": {
  4. "match_all": {}
  5. },
  6. "sort": [
  7. "_doc"
  8. ],
  9. "size": 2
  10. }

结果:

  1. {
  2. "_scroll_id" : "FGluY2x1ZGVfY29udGV4dF91dWlkDXF1ZXJ5QW5kRmV0Y2gBFlJkcUdWYzVRU0ItaVpLS29WSnQweUEAAAAAAADJdhZpVzNfQlJOMVRBLUNvc0ZHTVY0aVB3",
  3. "took" : 0,
  4. "timed_out" : false,
  5. "_shards" : {
  6. "total" : 1,
  7. "successful" : 1,
  8. "skipped" : 0,
  9. "failed" : 0
  10. },
  11. "hits" : {
  12. "total" : {
  13. "value" : 4,
  14. "relation" : "eq"
  15. },
  16. "max_score" : null,
  17. "hits" : [
  18. {
  19. "_index" : "book",
  20. "_type" : "_doc",
  21. "_id" : "1",
  22. "_score" : null,
  23. "_source" : {
  24. "name" : "lucene",
  25. "description" : "Lucene Core is a Java library providing powerful indexing and search features, as well as spellchecking, hit highlighting and advanced analysis/tokenization capabilities. The PyLucene sub project provides Python bindings for Lucene Core. ",
  26. "price" : 100.45,
  27. "timestamp" : "2020-08-21 19:11:35"
  28. },
  29. "sort" : [
  30. 0
  31. ]
  32. },
  33. {
  34. "_index" : "book",
  35. "_type" : "_doc",
  36. "_id" : "2",
  37. "_score" : null,
  38. "_source" : {
  39. "name" : "solr",
  40. "description" : "Solr is highly scalable, providing fully fault tolerant distributed indexing, search and analytics. It exposes Lucenes features through easy to use JSON/HTTP interfaces or native clients for Java and other languages.",
  41. "price" : 320.45,
  42. "timestamp" : "2020-07-21 17:11:35"
  43. },
  44. "sort" : [
  45. 1
  46. ]
  47. }
  48. ]
  49. }
  50. }
  1. 采用bulk api将scoll查出来的一批数据,批量写入新索引
    1. POST /_bulk
    2. { "index": { "_index": "book_new", "_id": "对应的id值" }}
    3. { 查询出来的数据值 }
  2. 反复执行修改后的步骤3和步骤4,查询一批导入一批,以后可以借助Java Client或其他语言的API 支持。
    注意做3时需要指定上一次查询的 scroll_id (scroll_id 有效期是scroll值)
    1. GET /_search/scroll
    2. {
    3. "scroll": "1m",
    4. "scroll_id" : "步骤三中查询出来的值"
    5. }
  3. 切换别名book_alias到新的索引book_new上面,此时Java客户端仍然使用别名访问,也不需要修改任何代码,不需要停机。 ```json

POST /_aliases { “actions”: [ { “remove”: { “index”: “book”, “alias”: “book_alias” } }, { “add”: { “index”: “book_new”, “alias”: “book_alias” } } ] }

  1. 7. 验证别名查询的是否为新索引的数据
  2. **3)方案特点 **<br /> 在数据传输上基本自给自足,不依赖于其他数据源,Java客户端不需要停机等待数据迁移,网络传输占用带宽较小。只是scroll查询和bulk提交这部分,数据量大时需要依赖一些客户端工具。 <br />**4)补充一点 **<br /> Java客户端或其他客户端访问Elasticsearch集群时,使用别名是一个好习惯。
  3. <a name="qo3gt"></a>
  4. ## 6.4 方案三:Reindex API方案
  5. Elasticsearch v6.3.1已经支持Reindex API,它对scrollbulk做了一层封装,能够 对文档重建索引而不需要任何插件或外部工具。 <br />**1)最基础的命令:**
  6. ```json
  7. POST _reindex
  8. {
  9. "source": {
  10. "index": "book"
  11. },
  12. "dest": {
  13. "index": "book_new"
  14. }
  15. }

响应结果:

  1. {
  2. "took" : 276,
  3. "timed_out" : false,
  4. "total" : 4,
  5. "updated" : 0,
  6. "created" : 4,
  7. "deleted" : 0,
  8. "batches" : 1,
  9. "version_conflicts" : 0,
  10. "noops" : 0,
  11. "retries" : {
  12. "bulk" : 0,
  13. "search" : 0
  14. },
  15. "throttled_millis" : 0,
  16. "requests_per_second" : -1.0,
  17. "throttled_until_millis" : 0,
  18. "failures" : [ ]
  19. }

注意: 如果不手动创建新索引book_new的mapping信息,那么Elasticsearch将启动自动映射模板对数据进行类型映射,可能不是期望的类型,这点要注意一下。

2)version_type 属性
使用reindex api也是创建快照后再执行迁移的,这样目标索引的数据可能会与原索引有差异, version_type属性可以决定乐观锁并发处理的规则。
reindex api可以设置version_type属性,如下:

  1. POST _reindex
  2. {
  3. "source": {
  4. "index": "book"
  5. },
  6. "dest": {
  7. "index": "book_new",
  8. "version_type": "internal"
  9. }
  10. }