目录

ElasticSearch 7.x

一、前言

ElasticSearch 是一个基于 Lunece 的开源搜索引擎。Lunece 是目前为止最先进、性能最好、功能最全的搜索引擎库

ElasticSearch 用 RESTful API 来隐藏 Lunece 的复杂性。

ES 创始人:Shay Banon,项目起源是老婆想弄一个搜索菜谱的软件。

Spring 项目的创始人:Rod Jhonson。ES 项目早期的投资人。

doug cutting:是 Apache Lunece 的作者,也是 hadoop 的创始人之一。

Lucene 是一套信息检索工具包! jar 包!不包含搜索引擎系统!

Lucene 和 ElasticSearch 的关系:

ElasticSearch 是基于 Lucune 做了一些封装和增强。

1.1、正向索引和倒排索引

正向索引和倒排索引是在搜索领域中非常重要的名词。

1.txt

  1. 我是小明,我喜欢看剧和打球

2.txt

  1. 我是小张,我喜欢看剧和玩游戏

正向索引和倒排索引在使用时都会进行分词处理,什么是分词处理呢?

分词处理:将一句话拆成中文语义的一个个词。

1.1.1、正向索引

如果使用的是正向索引,经过分词处理后的两个文件内容是:

1.txt

  1. 小明 喜欢 看剧 打球

2.txt

  1. 小张 喜欢 看剧 玩游戏 游戏

然后如果我们查询条件是喜欢,那么会从文章开头开始找喜欢,找到就将文档加入结果集,如果文本内容很多,会耗费大量时间。

1.1.2、倒排索引

如果使用的是倒排索引,经过分词处理后,会按照分词和文档进行映射:

关键词 文档
1.txt, 2.txt
1.txt, 2.txt
小明 1.txt
小张 2.txt
喜欢 1.txt, 2.txt
1.txt, 2.txt
看剧 1.txt, 2.txt
1.txt, 2.txt
1.txt
打球 1.txt
玩游戏 2.txt
游戏 2.txt

然后搜索时先找关键字,比如搜索喜欢,直接在关键词找到喜欢这个词。然后找到对应的文档。

二、安装

ElasticSearch:分布式搜索引擎

Kibana:ElasticSearch 可视化界面

Logstash:对数据进行采集、过滤、和输出。

movielens 数据集:https://grouplens.org/datasets/movielens/

导入数据:


三、ES 基本概念

3.1、索引

某一类文档的集合,可以类比一个数据库(或者数据库表)

3.2、文档

具体的一条数据。

  1. {
  2. "_index" : "movies", // 索引名
  3. "_type" : "_doc", // 类型
  4. "_id" : "42015", // id
  5. "_score" : 1.0,
  6. "_source" : { // 存放具体的数据
  7. "id" : "42015",
  8. "genre" : [
  9. "Action",
  10. "Adventure",
  11. "Comedy",
  12. "Drama",
  13. "Romance"
  14. ],
  15. "title" : "Casanova",
  16. "year" : 2005,
  17. "@version" : "1"
  18. }
  19. }

3.4、mapping

mapping 是 ES 每一个文档的约束信息,例如属性的类型,是否能被索引等。

3.5、DSL

DSL 是 ES 的查询语言。

3.6 传统关系型数据库和 ES 的对比

关系型数据库与 ES 进行对比。

DBMS ElasticSearch
database Index
table type(在7.0之后都是固定值_doc)
Row Docment
Column Field
Schema Mapping
SQL DSL

一个索引想像成一个数据库,一个数据库只有一张表 _doc,一张索引有多个文档(记录),一个文档有多个字段(列)。

4.1、基本 CRUD

查看所有索引:

  1. GET _cat/indices

查看某个索引的数据:

  1. # GET 索引名/_search
  2. GET movies/_search

查看某个索引下有多少条数据:

  1. # 语法:GET 索引名/_count
  2. GET movies/_count

查看指定 id 的文档的数据:

  1. # 语法:GET 索引名/_doc/ID
  2. GET movies/_doc/38701

添加一个文档:

  1. POST 索引名/_doc/文档ID
  2. {
  3. "key1": "value1",
  4. "key2": "value2"
  5. ...
  6. }

添加一个文档,指定 ID,如果 ID 已经有了会报错,没有会创建一个文档:

  1. POST 索引名/_create/文档ID
  2. {
  3. "key1": "value1",
  4. "key2": "value2"
  5. ...
  6. }

修改一个文档结构:

  1. POST 索引名/_update/文档ID
  2. {
  3. "doc": {
  4. "key1": "value1",
  5. "key2": "value2"
  6. ...
  7. }
  8. }

删除指定文档 ID 的文档:

  1. DELETE 索引名/_doc/文档ID

删除索引:

  1. DELETE 索引名

批量插入:

  1. POST user/_bulk
  2. {"index": {"_id": "1"}}
  3. {"firstname": "li"}
  4. {"index": {"_id": "2"}}
  5. {"firstname": "ya"}
  6. {"index": {}}
  7. {"firstname": "ya"}

查询,数组返回

  1. GET _mget
  2. {
  3. "docs": [
  4. {"_index": "user", "_id": "1"},
  5. {"_index": "user", "_id": "2"},
  6. {"_index": "user", "_id": "kgu6mHYB-tSK-znPeUte"}
  7. ]
  8. }

4.2、ES 的 URI 查询

查询内容有 2012 的文档:

  1. GET movies/_search?q=2012

查询 title 属性有 2012 的文档:

  1. GET movies/_search?q=2012&df=title

上面可以简写为:

  1. GET movies/_search?q=title:2012

包含 beautiful 或 mind 的:

  1. GET movies/_search?q=title:beautiful mind

只包含 beautiful 不包含 mind:

  1. GET movies/_search?q=title:(beautiful -mind)

既包含 beautiful 又包含 mind:

  1. GET movies/_search?q=title:(beautiful AND mind)

AND 必须大写,小写会当成一个条件。

获取一个短语是 beautiful mind 的:

  1. GET movies/_search?q=title:"beautiful mind"

分页,from=从第几条开始,从第一条开始 from=0,size=总共要查出几条数据

  1. GET movies/_search?q=title:2012&from=1&size=3

范围查询:

  1. # 语法:GET 索引名/_search?q=属性名:比较运算符 条件
  2. GET movies/_search?q=year:>=2016

查询电影名字包含 beautiful 或 mind,并且上映的年份在 [1990, 1992] 的所有电影

  1. GET movies/_search?q=year:(>=1990 AND <=1992) AND title:beautiful mind

查询年份大于 2011 小于等于 2013,后边的括号只能是中括号:

  1. GET movies/_search?q=year:{2011 TO 2013]

查看 title 为 min加上一个随机的字符的数据:

  1. GET movies/_search?q=title:min?

?可以代表一个字符。

查看 title 为 min加上一个或多个随机的字符的数据:

  1. GET movies/_search?q=title:mi*

可以代表多个字符。

五、Analysis

Analysis 只是一个概念,就是文本分析,将全文本转换为一系列单词的过程,也叫分词。Analysis 是通过 analyzer(分词器)来实现的,ES 内部有很多定义好的分词器,也可以使用自定义的分词器。

除了在数据写入的时候将词条进行转换,查询的时候也会使用分析器对语句进行分析

Aynalysis 是由三部分组成:

  1. <p>Hello a World, the world is beautiful</p>

1、Character Filter:将文本中的 html 标签剔除掉。

2、Tokenizer:按照规则进行分词,在英文中按照空格分词。

3、Token Filter:去掉 stop word(停顿词,a an the is are等),然后转换为小写。

5.1、内置分词器

5.2、内置分词器使用示例

  1. GET _analyze
  2. {
  3. "analyzer":"standard",
  4. "text":"Then I will take care of your life"
  5. }

结果:

  1. {
  2. "tokens" : [
  3. {
  4. "token" : "then",
  5. "start_offset" : 0,
  6. "end_offset" : 4,
  7. "type" : "<ALPHANUM>",
  8. "position" : 0
  9. },
  10. {
  11. "token" : "i",
  12. "start_offset" : 5,
  13. "end_offset" : 6,
  14. "type" : "<ALPHANUM>",
  15. "position" : 1
  16. },
  17. {
  18. "token" : "will",
  19. "start_offset" : 7,
  20. "end_offset" : 11,
  21. "type" : "<ALPHANUM>",
  22. "position" : 2
  23. },
  24. {
  25. "token" : "take",
  26. "start_offset" : 12,
  27. "end_offset" : 16,
  28. "type" : "<ALPHANUM>",
  29. "position" : 3
  30. },
  31. {
  32. "token" : "care",
  33. "start_offset" : 17,
  34. "end_offset" : 21,
  35. "type" : "<ALPHANUM>",
  36. "position" : 4
  37. },
  38. {
  39. "token" : "of",
  40. "start_offset" : 22,
  41. "end_offset" : 24,
  42. "type" : "<ALPHANUM>",
  43. "position" : 5
  44. },
  45. {
  46. "token" : "your",
  47. "start_offset" : 25,
  48. "end_offset" : 29,
  49. "type" : "<ALPHANUM>",
  50. "position" : 6
  51. },
  52. {
  53. "token" : "life",
  54. "start_offset" : 30,
  55. "end_offset" : 34,
  56. "type" : "<ALPHANUM>",
  57. "position" : 7
  58. }
  59. ]
  60. }

六、ResquestBody 深入探索

ES 是进行不了复杂的查询的,所以有了 ResquestBody 查询。

  1. GET movies/_search
  2. {
  3. "query": {
  4. "match": {
  5. "title": "beautiful mind"
  6. }
  7. }
  8. }

在说 Analysis 的时候,说不仅仅在输入的时候会做分词处理,查询的时候也会。在查询的时候会将 beautiful mind 按照 Ananlysis 的三步分词处理的方式,最后会拆成``beautiful mind,然后进行查询,所以只要结果中包含 beautifulmind` 就可以。

6.1、term 查询

1、term 和 terms(不进行分词处理,完全匹配)

term 查询不会对输入进行分词处理。match 会做分词处理。

title 包含 beanutiful 的 文档:

  1. GET movies/_search
  2. {
  3. "query": {
  4. "term": {
  5. "title":
  6. "beautiful"
  7. }
  8. }
  9. }

title 包含 beanutiful 或 mind 的文档:

  1. GET movies/_search
  2. {
  3. "query": {
  4. "terms": {
  5. "title": [
  6. "beautiful",
  7. "mind"
  8. ]
  9. }
  10. }
  11. }

一个查询的单词用 term,多个用 terms。

2、range (范围查询)

查询上映在 2016 到 2018 年的所有电影,再根据上映的时间倒叙进行排序。

  1. GET movies/_search
  2. {
  3. "query": {
  4. "range": {
  5. "year":{
  6. "gte": 2016,
  7. "lte": 2018
  8. }
  9. }
  10. },
  11. "sort": [
  12. {
  13. "year": {
  14. "order": "desc"
  15. }
  16. }
  17. ]
  18. }

查(query)什么,查的是范围(range),查什么范围,年份(year),结果按照顺序(order),按照倒叙(desc)。

gte:大于等于。

lte:小于等于。

复合查询:match 和 range 同时使用。

  1. GET movies/_search
  2. {
  3. "query": {
  4. "bool": {
  5. "must": [
  6. {
  7. "match": {
  8. "title": "beautiful mind"
  9. }
  10. },
  11. {
  12. "range": {
  13. "year": {
  14. "gte": 1990,
  15. "lte": 1992
  16. }
  17. }
  18. }
  19. ]
  20. }
  21. }
  22. }
3、Constant Score(不进行相关性算分,查询的结果会进行缓存)

不会进行相关性算分,会节省大量时间。查询的结果会进行缓存。Constant Score 只能用 term 查询。

查询 title 中包含 beautiful 的所有电影

  1. GET movies/_search
  2. {
  3. "query": {
  4. "constant_score": {
  5. "filter": {
  6. "term": {
  7. "title": "beautiful"
  8. }
  9. }
  10. }
  11. }
  12. }

会发现查询结果的 _score:1.0。没有进行相关性算分。

  1. "hits" : [
  2. {
  3. "_index" : "movies",
  4. "_type" : "_doc",
  5. "_id" : "66701",
  6. "_score" : 1.0,
  7. "_source" : {
  8. "id" : "66701",
  9. "genre" : [
  10. "Comedy",
  11. "Drama"
  12. ],
  13. "title" : "Beautiful Ohio",
  14. "year" : 2006,
  15. "@version" : "1"
  16. }
  17. },
  18. ]

6.2、全文查询

全文查询的种类有: Match Query、Match Phrase Query、Query String Query 等

6.2.1、match(完全匹配)

查询包含输入条件的内容。

查询电影名包含 beautiful 的电影 ,每页 10 条,取第 2 页的数据。

  1. GET movies/_search
  2. {
  3. "query": {
  4. "match": {
  5. "title": "beautiful"
  6. }
  7. },
  8. "from": 10,
  9. "size": 10
  10. }

查询电影名包含 beautiful 的电影,但是查询的结果只显示 id 和 title

  1. GET movies/_search
  2. {
  3. "_source": ["title", "id"],
  4. "query": {
  5. "match": {
  6. "title": "beautiful"
  7. }
  8. }
  9. }

只想查部分属性,使用 _source。

6.2.2、match_phrase(短语完全匹配)

匹配短语,整个输入都做一个整体查询条件。

  1. GET movies/_search
  2. {
  3. "query": {
  4. "match_phrase": {
  5. "title": "beautiful mind"
  6. }
  7. }
  8. }

查出来的结果:

  1. {
  2. "took" : 15,
  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" : 13.509613,
  16. "hits" : [
  17. {
  18. "_index" : "movies",
  19. "_type" : "_doc",
  20. "_id" : "4995",
  21. "_score" : 13.509613,
  22. "_source" : {
  23. "id" : "4995",
  24. "genre" : [
  25. "Drama",
  26. "Romance"
  27. ],
  28. "title" : "Beautiful Mind, A",
  29. "year" : 2001,
  30. "@version" : "1"
  31. }
  32. }
  33. ]
  34. }
  35. }
6.2.3、 multi_match(条件与多结果完全匹配)

查询的条件和多个结果匹配的的结果。


  1. GET movies/_search
  2. {
  3. "query": {
  4. "multi_match": {
  5. "query": "beautiful",
  6. "fields": ["title", "genre"]
  7. }
  8. }
  9. }
6.2.4、match_all(查询所有)

查询所有的数据。

  1. GET movies/_search
  2. {
  3. "query": {
  4. "match_all": {
  5. }
  6. }
  7. }

这个就相当于:

  1. GET movies/_search
6.2.5、query_string(AND 或 OR 条件组合)

条件用 AND 组合时可以用这个, OR 也可以。

  1. GET movies/_search
  2. {
  3. "query": {
  4. "query_string": {
  5. "default_field": "title",
  6. "query": "beautiful OR mind"
  7. }
  8. }
  9. }
  10. GET movies/_search
  11. {
  12. "query": {
  13. "query_string": {
  14. "default_field": "title",
  15. "query": "beautiful AND mind"
  16. }
  17. }
  18. }

AND 和 OR 一定要大写,不然会被当作查询条件进行查询。

6.2.6、simple_query_string
  1. GET movies/_search
  2. {
  3. "query": {
  4. "simple_query_string": {
  5. "query": "beautiful+ -mind",
  6. "fields": ["title"]
  7. }
  8. }
  9. }

6.3、模糊查询-fuzzy

关键字:fuzzy

  1. GET movies/_search
  2. {
  3. "query": {
  4. "fuzzy": {
  5. "title": {
  6. "value": "neverendogn",
  7. "fuzziness": 2
  8. }
  9. }
  10. }
  11. }

fuzziness 的值只能为大于等于0,小于等于2,有小数点。值为 0 时,必须是完全匹配,值为 1 时,可以有 1 个字母和搜索中的不一样,值 为 2 时,可以有 2 个字母和搜索中的不一样。

6.4、多条件查询

1、must

多个条件同时满足。会进行相关性算分。

  1. GET movies/_search
  2. {
  3. "query": {
  4. "bool": {
  5. "must": [
  6. {
  7. "match": {
  8. "title": "beautiful"
  9. }
  10. },
  11. {
  12. "range": {
  13. "year": {
  14. "gte": 1990,
  15. "lte": 2000
  16. }
  17. }
  18. }
  19. ]
  20. }
  21. }
  22. }

语法是:

  1. GET 索引名/_search
  2. {
  3. "query": {
  4. "bool": {
  5. "must": [
  6. {
  7. 条件查询1
  8. },
  9. {
  10. 条件查询2
  11. }
  12. ...
  13. ]
  14. }
  15. }
  16. }
2、must_not

多个条件同时不满足。不会进行相关性算分。

  1. GET movies/_search
  2. {
  3. "query": {
  4. "bool": {
  5. "must_not": [
  6. {
  7. "match": {
  8. "title": "mind"
  9. }
  10. }
  11. ]
  12. }
  13. }
  14. }

语法是:

  1. GET 索引名/_search
  2. {
  3. "query": {
  4. "bool": {
  5. "must_not": [
  6. {
  7. 条件查询1
  8. },
  9. {
  10. 条件查询2
  11. }
  12. ...
  13. ]
  14. }
  15. }
  16. }
  1. {
  2. "took" : 5,
  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" : 10000,
  13. "relation" : "gte"
  14. },
  15. "max_score" : 0.0,
  16. "hits" : [
  17. {
  18. "_index" : "movies",
  19. "_type" : "_doc",
  20. "_id" : "42015",
  21. "_score" : 0.0,
  22. "_source" : {
  23. "id" : "42015",
  24. "genre" : [
  25. "Action",
  26. "Adventure",
  27. "Comedy",
  28. "Drama",
  29. "Romance"
  30. ],
  31. "title" : "Casanova",
  32. "year" : 2005,
  33. "@version" : "1"
  34. }
  35. },
  36. {
  37. "_index" : "movies",
  38. "_type" : "_doc",
  39. "_id" : "42018",
  40. "_score" : 0.0,
  41. "_source" : {
  42. "id" : "42018",
  43. "genre" : [
  44. "Comedy",
  45. "Drama"
  46. ],
  47. "title" : "Mrs. Henderson Presents",
  48. "year" : 2005,
  49. "@version" : "1"
  50. }
  51. }
  52. ]
  53. }
  54. }

"_score" : 0.0 都是一样的,没有进行相关性算分。

3、filter

多个条件同时满足。不会进行相关性算分。

  1. GET movies/_search
  2. {
  3. "query": {
  4. "bool": {
  5. "filter": [
  6. {
  7. "match": {
  8. "title": "beautiful"
  9. }
  10. },
  11. {
  12. "range": {
  13. "year": {
  14. "gte": 1990,
  15. "lte": 2000
  16. }
  17. }
  18. }
  19. ]
  20. }
  21. }
  22. }

"_score" : 0.0 ,都是一样的,和 must 的功能不一样,都是多条件同时满足,但是不会进行相关性算分。

4、should

或者的关系 ,会对查询的结果进行相关性算分。

  1. GET movies/_search
  2. {
  3. "query": {
  4. "bool": {
  5. "should": [
  6. {
  7. "match": {
  8. "title": "beautiful"
  9. }
  10. },
  11. {
  12. "match": {
  13. "title": "mind"
  14. }
  15. }
  16. ]
  17. }
  18. }
  19. }

语法是:

  1. GET 索引名/_search
  2. {
  3. "query": {
  4. "bool": {
  5. "should": [
  6. {
  7. 查询条件1
  8. },
  9. {
  10. 查询条件2
  11. }
  12. ]
  13. }
  14. }
  15. }

七、Mapping

Mapping 的作用:

  • 定义字段的数据类型。

  • 设置该字段是否可以被倒排索引。

7.1、数据类型

类型名 描述名
Text/Keyword 字符串,默认的类型
Date 日期类型
Integer/Float/Long 数字类型
Boolean 布尔类型

Text 和 KeyWord:是否分词。https://www.cnblogs.com/qlqwjy/p/13462750.html

7.2、定义 Mapping

语法如下:

  1. PUT users
  2. {
  3. "mappings": {
  4. // 在这里定义 mapping
  5. }
  6. }

定义 mapping 的建议方式:推荐导入样本文档到临时索引,ES 会自动生成 mapping 信息,用 api 查询 mapping 的定义,根据自己的需求修改这份 mapping 信息,然后创建索引,删除临时索引。

ES 的 mapping 设置好后,是不可以修改的,所以不像关系型数据库那样,可以直接修改,所以只能这样从左。

示例

添加一个索引一个文档:

  1. PUT users/_doc/1
  2. {
  3. "name": "Jack",
  4. "age": 18,
  5. "height": 180.0,
  6. "isRich": "true"
  7. }

索引的 mapping 会自动生成,我们看一下自动生成的索引,使用命令查询:

  1. GET users/_mapping

查询结果:

  1. {
  2. "users" : {
  3. "mappings" : {
  4. "properties" : {
  5. "age" : {
  6. "type" : "long"
  7. },
  8. "height" : {
  9. "type" : "float"
  10. },
  11. "isRich" : {
  12. "type" : "text",
  13. "fields" : {
  14. "keyword" : {
  15. "type" : "keyword",
  16. "ignore_above" : 256
  17. }
  18. }
  19. },
  20. "name" : {
  21. "type" : "text",
  22. "fields" : {
  23. "keyword" : {
  24. "type" : "keyword",
  25. "ignore_above" : 256
  26. }
  27. }
  28. }
  29. }
  30. }
  31. }
  32. }

把 mappings 后面内容复制一下:

  1. "mappings" : {
  2. "properties" : {
  3. "age" : {
  4. "type" : "long"
  5. },
  6. "height" : {
  7. "type" : "float"
  8. },
  9. "isRich" : {
  10. "type" : "text",
  11. "fields" : {
  12. "keyword" : {
  13. "type" : "keyword",
  14. "ignore_above" : 256
  15. }
  16. }
  17. },
  18. "name" : {
  19. "type" : "text",
  20. "fields" : {
  21. "keyword" : {
  22. "type" : "keyword",
  23. "ignore_above" : 256
  24. }
  25. }
  26. }
  27. }
  28. }

去掉 fields 这些:

  1. "mappings" : {
  2. "properties" : {
  3. "age" : {
  4. "type" : "long"
  5. },
  6. "height" : {
  7. "type" : "float"
  8. },
  9. "isRich" : {
  10. "type" : "text"
  11. },
  12. "name" : {
  13. "type" : "text"
  14. }
  15. }
  16. }

将 isRich 的类型改为 boolean,加上 PUT users

  1. PUT users
  2. {
  3. "mappings" : {
  4. "properties" : {
  5. "age" : {
  6. "type" : "long"
  7. },
  8. "height" : {
  9. "type" : "float"
  10. },
  11. "isRich" : {
  12. "type" : "boolean"
  13. },
  14. "name" : {
  15. "type" : "text"
  16. }
  17. }
  18. }
  19. }

然后删除索引

  1. DELETE users

执行上面的创建 mapping 语句,再重新建立索引添加一条文档:

  1. PUT users
  2. {
  3. "mappings" : {
  4. "properties" : {
  5. "age" : {
  6. "type" : "long"
  7. },
  8. "height" : {
  9. "type" : "float"
  10. },
  11. "isRich" : {
  12. "type" : "boolean"
  13. },
  14. "name" : {
  15. "type" : "text"
  16. }
  17. }
  18. }
  19. }
  1. PUT users/_doc/1
  2. {
  3. "name": "Jack",
  4. "age": 18,
  5. "height": 180.0,
  6. "isRich": "true"
  7. }

查看现在的索引 mapping:

  1. GET users/_mapping

查询结果:

  1. {
  2. "users" : {
  3. "mappings" : {
  4. "properties" : {
  5. "age" : {
  6. "type" : "long"
  7. },
  8. "height" : {
  9. "type" : "float"
  10. },
  11. "isRich" : {
  12. "type" : "boolean"
  13. },
  14. "name" : {
  15. "type" : "text"
  16. }
  17. }
  18. }
  19. }
  20. }

发现每个字段所对应的类型是我们提前定义好的类型。

搜索时匹配

7.3、常见参数

7.3.1、index

可以给属性添加一个布尔类型的 index 属性,可以控制属性是否被倒排索引。

  1. PUT test
  2. {
  3. "mappings" : {
  4. "properties" : {
  5. "name" : {
  6. "type" : "text",
  7. "index": false
  8. }
  9. }
  10. }
  11. }

为 false 的时候不可以进行倒排索引。

插入两条数据

  1. POST test/_doc/1
  2. {
  3. "name": "zhangsan"
  4. }
  5. POST test/_doc/2
  6. {
  7. "name": "xiaoqi"
  8. }

查询:

  1. GET test/_search
  2. {
  3. "query": {
  4. "match": {
  5. "name": "zhangsan"
  6. }
  7. }
  8. }

会报错。

  1. {
  2. "error" : {
  3. "root_cause" : [
  4. {
  5. "type" : "query_shard_exception",
  6. "reason" : "failed to create query: Cannot search on field [name] since it is not indexed.",
  7. "index_uuid" : "YCjgmE81QkSHLohZdY2JBw",
  8. "index" : "test"
  9. }
  10. ],
  11. "type" : "search_phase_execution_exception",
  12. "reason" : "all shards failed",
  13. "phase" : "query",
  14. "grouped" : true,
  15. "failed_shards" : [
  16. {
  17. "shard" : 0,
  18. "index" : "test",
  19. "node" : "zTWkfiA2RG2GUVGMgRnuhQ",
  20. "reason" : {
  21. "type" : "query_shard_exception",
  22. "reason" : "failed to create query: Cannot search on field [name] since it is not indexed.",
  23. "index_uuid" : "YCjgmE81QkSHLohZdY2JBw",
  24. "index" : "test",
  25. "caused_by" : {
  26. "type" : "illegal_argument_exception",
  27. "reason" : "Cannot search on field [name] since it is not indexed."
  28. }
  29. }
  30. }
  31. ]
  32. },
  33. "status" : 400
  34. }
7.3.2、null_value

字段的值如果是 null,是无法进行搜索的,这个时候我们可以使用 null_valuenull_value 会将属性转化为 null_value 的值。

使用 null_value 时,只能用 keyword 类型。

示例

添加数据:

  1. POST test/_doc/1
  2. {
  3. "name": "zhangsan"
  4. }
  5. POST test/_doc/2
  6. {
  7. "name": "xiaoqi"
  8. }
  9. POST test/_doc/3
  10. {
  11. "name": null
  12. }

搜索值为 null :

  1. GET test/_search
  2. {
  3. "query": {
  4. "match": {
  5. "name": null
  6. }
  7. }
  8. }

运行结果会报错:

  1. {
  2. "error" : {
  3. "root_cause" : [
  4. {
  5. "type" : "parsing_exception",
  6. "reason" : "No text specified for text query",
  7. "line" : 5,
  8. "col" : 5
  9. }
  10. ],
  11. "type" : "parsing_exception",
  12. "reason" : "No text specified for text query",
  13. "line" : 5,
  14. "col" : 5
  15. },
  16. "status" : 400
  17. }

我们可以使用 null_value:

  1. PUT test
  2. {
  3. "mappings" : {
  4. "properties" : {
  5. "name" : {
  6. "type" : "keyword",
  7. "null_value": "null"
  8. }
  9. }
  10. }
  11. }

然后插入数据:

  1. POST test/_doc/1
  2. {
  3. "name": "zhangsan"
  4. }
  5. POST test/_doc/2
  6. {
  7. "name": "xiaoqi"
  8. }
  9. POST test/_doc/3
  10. {
  11. "name": null
  12. }

然后查询:

  1. GET test/_search
  2. {
  3. "query": {
  4. "match": {
  5. "name": "null"
  6. }
  7. }
  8. }

查询结果:

  1. {
  2. "took" : 0,
  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" : 0.9808291,
  16. "hits" : [
  17. {
  18. "_index" : "test",
  19. "_type" : "_doc",
  20. "_id" : "3",
  21. "_score" : 0.9808291,
  22. "_source" : {
  23. "name" : null
  24. }
  25. }
  26. ]
  27. }
  28. }

八、ES 聚合查询

语法格式:

  1. GET indexName/_search {
  2. "aggs": {
  3. "聚合名称(自定义的)": {
  4. "聚合类型(ES 提供的)": {
  5. "field": "要做聚合的字段名"
  6. }
  7. }
  8. }

创建索引类型,新增文档:

  1. PUT employee
  2. {
  3. "mappings": {
  4. "properties": {
  5. "id": {
  6. "type": "integer" },
  7. "name": {
  8. "type": "keyword" },
  9. "job": {
  10. "type": "keyword" },
  11. "age": {
  12. "type": "integer" },
  13. "gender": {
  14. "type": "keyword" }
  15. }
  16. }
  17. }
  18. PUT employee/_bulk
  19. {"index": {"_id": 1}}
  20. {"id": 1, "name": "Bob", "job": "java", "age": 21, "sal": 8000, "gender": "female"}
  21. {"index": {"_id": 2}}
  22. {"id": 2, "name": "Rod", "job": "html", "age": 31, "sal": 18000, "gender": "female"}
  23. {"index": {"_id": 3}}
  24. {"id": 3, "name": "Gaving", "job": "java", "age": 24, "sal": 12000, "gender": "male"}
  25. {"index": {"_id": 4}}
  26. {"id": 4, "name": "King", "job": "dba", "age": 26, "sal": 15000, "gender": "female"}
  27. {"index": {"_id": 5}}
  28. {"id": 5, "name": "Jonhson", "job": "dba", "age": 29, "sal": 16000, "gender": "male"}
  29. {"index": {"_id": 6}}
  30. {"id": 6, "name": "Douge", "job": "java", "age": 41, "sal": 20000, "gender": "female"}
  31. {"index": {"_id": 7}}
  32. {"id": 7, "name": "cutting", "job": "dba", "age": 27, "sal": 7000, "gender": "male"}
  33. {"index": {"_id": 8}}
  34. {"id": 8, "name": "Bona", "job": "html", "age": 22, "sal": 14000, "gender": "female"}
  35. {"index": {"_id": 9}}
  36. {"id": 9, "name": "Shyon", "job": "dba", "age": 20, "sal": 19000, "gender": "female"}
  37. {"index": {"_id": 10}}
  38. {"id": 10, "name": "James", "job": "html", "age": 18, "sal": 22000, "gender": "male"}
  39. {"index": {"_id": 11}}
  40. {"id": 11, "name": "Golsling", "job": "java", "age": 32, "sal": 23000, "gender": "female"}
  41. {"index": {"_id": 12}}
  42. {"id": 12, "name": "Lily", "job": "java", "age": 24, "sal": 2000, "gender": "male"}
  43. {"index": {"_id": 13}}
  44. {"id": 13, "name": "Jack", "job": "html", "age": 23, "sal": 3000, "gender": "female"}
  45. {"index": {"_id": 14}}
  46. {"id": 14, "name": "Rose", "job": "java", "age": 36, "sal": 6000, "gender": "female"}
  47. {"index": {"_id": 15}}
  48. {"id": 15, "name": "Will", "job": "dba", "age": 38, "sal": 4500, "gender": "male"}
  49. {"index": {"_id": 16}}
  50. {"id": 16, "name": "smith", "job": "java", "age": 32, "sal": 23000, "gender": "male"}

8.1、单值输出

8.1.1、总和

查员工工资的总和

  1. # 查询工资的总和
  2. GET employee/_search
  3. {
  4. "size": 0,
  5. "aggs": {
  6. "sum_sal": {
  7. "sum": {
  8. "field": "sal"
  9. }
  10. }
  11. }
  12. }
8.1.2、平均

查询员工的平均工资

  1. GET employee/_search
  2. {
  3. "size": 0,
  4. "aggs": {
  5. "other_avg": {
  6. "avg": {
  7. "field": "sal"
  8. }
  9. }
  10. }
  11. }
8.1.3、总数

查询总共有多少岗位

  1. # 查询总共有多少岗位
  2. GET employee/_search
  3. {
  4. "size": 0,
  5. "aggs": {
  6. "jon_sum": {
  7. "cardinality": {
  8. "field": "job"
  9. }
  10. }
  11. }
  12. }
8.1.4、最大值最小值

查询工资的最大值、最小值

  1. GET employee/_search
  2. {
  3. "size": 0,
  4. "aggs": {
  5. "sal_max": {
  6. "max": {
  7. "field": "sal"
  8. }
  9. }
  10. }
  11. }
  12. GET employee/_search
  13. {
  14. "size": 0,
  15. "aggs": {
  16. "sal_min": {
  17. "min": {
  18. "field": "sal"
  19. }
  20. }
  21. }
  22. }

8.2、多值的输出

8.2.1、查询信息(stats)

信息包括:总数量、最小值、最大值、平均值、总和。stats 使用时属性只能是类型

查询员工工资信息

  1. # 查询员工工资信息
  2. GET employee/_search
  3. {
  4. "size": 0,
  5. "aggs": {
  6. "sal_info": {
  7. "stats": {
  8. "field": "sal"
  9. }
  10. }
  11. }
  12. }

运行结果:

  1. {
  2. "took" : 2,
  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" : 16,
  13. "relation" : "eq"
  14. },
  15. "max_score" : null,
  16. "hits" : [ ]
  17. },
  18. "aggregations" : {
  19. "sal_info" : {
  20. "count" : 16,
  21. "min" : 2000.0,
  22. "max" : 23000.0,
  23. "avg" : 13281.25,
  24. "sum" : 212500.0
  25. }
  26. }
  27. }
8.2.2、分组查询(terms)

查询到达不同国家的航班数量

  1. GET kibana_sample_data_flights/_search
  2. {
  3. "size": 0,
  4. "aggs": {
  5. "dest_country_info": {
  6. "terms": {
  7. "field": "DestCountry"
  8. }
  9. }
  10. }
  11. }

查询结果:

  1. {
  2. "took" : 3,
  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" : 10000,
  13. "relation" : "gte"
  14. },
  15. "max_score" : null,
  16. "hits" : [ ]
  17. },
  18. "aggregations" : {
  19. "dest_country_info" : {
  20. "doc_count_error_upper_bound" : 0,
  21. "sum_other_doc_count" : 3187,
  22. "buckets" : [
  23. {
  24. "key" : "IT",
  25. "doc_count" : 2371
  26. },
  27. {
  28. "key" : "US",
  29. "doc_count" : 1987
  30. },
  31. {
  32. "key" : "CN",
  33. "doc_count" : 1096
  34. },
  35. {
  36. "key" : "CA",
  37. "doc_count" : 944
  38. },
  39. {
  40. "key" : "JP",
  41. "doc_count" : 774
  42. },
  43. {
  44. "key" : "RU",
  45. "doc_count" : 739
  46. },
  47. {
  48. "key" : "CH",
  49. "doc_count" : 691
  50. },
  51. {
  52. "key" : "GB",
  53. "doc_count" : 449
  54. },
  55. {
  56. "key" : "AU",
  57. "doc_count" : 416
  58. },
  59. {
  60. "key" : "PL",
  61. "doc_count" : 405
  62. }
  63. ]
  64. }
  65. }
  66. }
8.2.3、子查询

查询不同目的地航班次数以及不同目的地不同天气的统计信息

  1. # 查询不同目的地航班次数以及不同目的地天气的统计信息
  2. GET kibana_sample_data_flights/_search
  3. {
  4. "size": 0,
  5. "aggs": {
  6. "dest_country_info": {
  7. "terms": {
  8. "field": "DestCountry"
  9. },
  10. "aggs": {
  11. "dest_country_weather_info": {
  12. "terms": {
  13. "field": "DestWeather"
  14. }
  15. }
  16. }
  17. }
  18. }
  19. }

运行结果:

  1. {
  2. "took" : 28,
  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" : 10000,
  13. "relation" : "gte"
  14. },
  15. "max_score" : null,
  16. "hits" : [ ]
  17. },
  18. "aggregations" : {
  19. "dest_country_info" : {
  20. "doc_count_error_upper_bound" : 0,
  21. "sum_other_doc_count" : 3187,
  22. "buckets" : [
  23. {
  24. "key" : "IT",
  25. "doc_count" : 2371,
  26. "dest_country_weather_info" : {
  27. "doc_count_error_upper_bound" : 0,
  28. "sum_other_doc_count" : 0,
  29. "buckets" : [
  30. {
  31. "key" : "Clear",
  32. "doc_count" : 428
  33. },
  34. {
  35. "key" : "Sunny",
  36. "doc_count" : 424
  37. },
  38. {
  39. "key" : "Rain",
  40. "doc_count" : 417
  41. },
  42. {
  43. "key" : "Cloudy",
  44. "doc_count" : 414
  45. },
  46. {
  47. "key" : "Heavy Fog",
  48. "doc_count" : 182
  49. },
  50. {
  51. "key" : "Damaging Wind",
  52. "doc_count" : 173
  53. },
  54. {
  55. "key" : "Hail",
  56. "doc_count" : 169
  57. },
  58. {
  59. "key" : "Thunder & Lightning",
  60. "doc_count" : 164
  61. }
  62. ]
  63. }
  64. },
  65. {
  66. "key" : "US",
  67. "doc_count" : 1987,
  68. "dest_country_weather_info" : {
  69. "doc_count_error_upper_bound" : 0,
  70. "sum_other_doc_count" : 0,
  71. "buckets" : [
  72. {
  73. "key" : "Rain",
  74. "doc_count" : 371
  75. },
  76. {
  77. "key" : "Clear",
  78. "doc_count" : 346
  79. },
  80. {
  81. "key" : "Sunny",
  82. "doc_count" : 345
  83. },
  84. {
  85. "key" : "Cloudy",
  86. "doc_count" : 330
  87. },
  88. {
  89. "key" : "Heavy Fog",
  90. "doc_count" : 157
  91. },
  92. {
  93. "key" : "Thunder & Lightning",
  94. "doc_count" : 155
  95. },
  96. {
  97. "key" : "Hail",
  98. "doc_count" : 142
  99. },
  100. {
  101. "key" : "Damaging Wind",
  102. "doc_count" : 141
  103. }
  104. ]
  105. }
  106. },
  107. {
  108. "key" : "CN",
  109. "doc_count" : 1096,
  110. "dest_country_weather_info" : {
  111. "doc_count_error_upper_bound" : 0,
  112. "sum_other_doc_count" : 0,
  113. "buckets" : [
  114. {
  115. "key" : "Sunny",
  116. "doc_count" : 209
  117. },
  118. {
  119. "key" : "Rain",
  120. "doc_count" : 207
  121. },
  122. {
  123. "key" : "Clear",
  124. "doc_count" : 192
  125. },
  126. {
  127. "key" : "Cloudy",
  128. "doc_count" : 173
  129. },
  130. {
  131. "key" : "Thunder & Lightning",
  132. "doc_count" : 86
  133. },
  134. {
  135. "key" : "Hail",
  136. "doc_count" : 81
  137. },
  138. {
  139. "key" : "Heavy Fog",
  140. "doc_count" : 79
  141. },
  142. {
  143. "key" : "Damaging Wind",
  144. "doc_count" : 69
  145. }
  146. ]
  147. }
  148. },
  149. {
  150. "key" : "CA",
  151. "doc_count" : 944,
  152. "dest_country_weather_info" : {
  153. "doc_count_error_upper_bound" : 0,
  154. "sum_other_doc_count" : 0,
  155. "buckets" : [
  156. {
  157. "key" : "Clear",
  158. "doc_count" : 197
  159. },
  160. {
  161. "key" : "Rain",
  162. "doc_count" : 173
  163. },
  164. {
  165. "key" : "Cloudy",
  166. "doc_count" : 156
  167. },
  168. {
  169. "key" : "Sunny",
  170. "doc_count" : 148
  171. },
  172. {
  173. "key" : "Damaging Wind",
  174. "doc_count" : 80
  175. },
  176. {
  177. "key" : "Thunder & Lightning",
  178. "doc_count" : 69
  179. },
  180. {
  181. "key" : "Heavy Fog",
  182. "doc_count" : 62
  183. },
  184. {
  185. "key" : "Hail",
  186. "doc_count" : 59
  187. }
  188. ]
  189. }
  190. },
  191. {
  192. "key" : "JP",
  193. "doc_count" : 774,
  194. "dest_country_weather_info" : {
  195. "doc_count_error_upper_bound" : 0,
  196. "sum_other_doc_count" : 0,
  197. "buckets" : [
  198. {
  199. "key" : "Rain",
  200. "doc_count" : 152
  201. },
  202. {
  203. "key" : "Sunny",
  204. "doc_count" : 138
  205. },
  206. {
  207. "key" : "Clear",
  208. "doc_count" : 130
  209. },
  210. {
  211. "key" : "Cloudy",
  212. "doc_count" : 123
  213. },
  214. {
  215. "key" : "Damaging Wind",
  216. "doc_count" : 66
  217. },
  218. {
  219. "key" : "Heavy Fog",
  220. "doc_count" : 58
  221. },
  222. {
  223. "key" : "Thunder & Lightning",
  224. "doc_count" : 57
  225. },
  226. {
  227. "key" : "Hail",
  228. "doc_count" : 50
  229. }
  230. ]
  231. }
  232. },
  233. {
  234. "key" : "RU",
  235. "doc_count" : 739,
  236. "dest_country_weather_info" : {
  237. "doc_count_error_upper_bound" : 0,
  238. "sum_other_doc_count" : 0,
  239. "buckets" : [
  240. {
  241. "key" : "Cloudy",
  242. "doc_count" : 149
  243. },
  244. {
  245. "key" : "Rain",
  246. "doc_count" : 128
  247. },
  248. {
  249. "key" : "Clear",
  250. "doc_count" : 122
  251. },
  252. {
  253. "key" : "Sunny",
  254. "doc_count" : 117
  255. },
  256. {
  257. "key" : "Thunder & Lightning",
  258. "doc_count" : 62
  259. },
  260. {
  261. "key" : "Hail",
  262. "doc_count" : 56
  263. },
  264. {
  265. "key" : "Damaging Wind",
  266. "doc_count" : 55
  267. },
  268. {
  269. "key" : "Heavy Fog",
  270. "doc_count" : 50
  271. }
  272. ]
  273. }
  274. },
  275. {
  276. "key" : "CH",
  277. "doc_count" : 691,
  278. "dest_country_weather_info" : {
  279. "doc_count_error_upper_bound" : 0,
  280. "sum_other_doc_count" : 0,
  281. "buckets" : [
  282. {
  283. "key" : "Cloudy",
  284. "doc_count" : 135
  285. },
  286. {
  287. "key" : "Sunny",
  288. "doc_count" : 134
  289. },
  290. {
  291. "key" : "Clear",
  292. "doc_count" : 128
  293. },
  294. {
  295. "key" : "Rain",
  296. "doc_count" : 115
  297. },
  298. {
  299. "key" : "Heavy Fog",
  300. "doc_count" : 51
  301. },
  302. {
  303. "key" : "Hail",
  304. "doc_count" : 46
  305. },
  306. {
  307. "key" : "Damaging Wind",
  308. "doc_count" : 41
  309. },
  310. {
  311. "key" : "Thunder & Lightning",
  312. "doc_count" : 41
  313. }
  314. ]
  315. }
  316. },
  317. {
  318. "key" : "GB",
  319. "doc_count" : 449,
  320. "dest_country_weather_info" : {
  321. "doc_count_error_upper_bound" : 0,
  322. "sum_other_doc_count" : 0,
  323. "buckets" : [
  324. {
  325. "key" : "Rain",
  326. "doc_count" : 93
  327. },
  328. {
  329. "key" : "Sunny",
  330. "doc_count" : 81
  331. },
  332. {
  333. "key" : "Clear",
  334. "doc_count" : 77
  335. },
  336. {
  337. "key" : "Cloudy",
  338. "doc_count" : 71
  339. },
  340. {
  341. "key" : "Heavy Fog",
  342. "doc_count" : 34
  343. },
  344. {
  345. "key" : "Hail",
  346. "doc_count" : 32
  347. },
  348. {
  349. "key" : "Damaging Wind",
  350. "doc_count" : 31
  351. },
  352. {
  353. "key" : "Thunder & Lightning",
  354. "doc_count" : 30
  355. }
  356. ]
  357. }
  358. },
  359. {
  360. "key" : "AU",
  361. "doc_count" : 416,
  362. "dest_country_weather_info" : {
  363. "doc_count_error_upper_bound" : 0,
  364. "sum_other_doc_count" : 0,
  365. "buckets" : [
  366. {
  367. "key" : "Rain",
  368. "doc_count" : 80
  369. },
  370. {
  371. "key" : "Cloudy",
  372. "doc_count" : 75
  373. },
  374. {
  375. "key" : "Clear",
  376. "doc_count" : 73
  377. },
  378. {
  379. "key" : "Sunny",
  380. "doc_count" : 57
  381. },
  382. {
  383. "key" : "Hail",
  384. "doc_count" : 38
  385. },
  386. {
  387. "key" : "Thunder & Lightning",
  388. "doc_count" : 34
  389. },
  390. {
  391. "key" : "Heavy Fog",
  392. "doc_count" : 32
  393. },
  394. {
  395. "key" : "Damaging Wind",
  396. "doc_count" : 27
  397. }
  398. ]
  399. }
  400. },
  401. {
  402. "key" : "PL",
  403. "doc_count" : 405,
  404. "dest_country_weather_info" : {
  405. "doc_count_error_upper_bound" : 0,
  406. "sum_other_doc_count" : 0,
  407. "buckets" : [
  408. {
  409. "key" : "Clear",
  410. "doc_count" : 74
  411. },
  412. {
  413. "key" : "Rain",
  414. "doc_count" : 71
  415. },
  416. {
  417. "key" : "Cloudy",
  418. "doc_count" : 67
  419. },
  420. {
  421. "key" : "Sunny",
  422. "doc_count" : 66
  423. },
  424. {
  425. "key" : "Thunder & Lightning",
  426. "doc_count" : 37
  427. },
  428. {
  429. "key" : "Damaging Wind",
  430. "doc_count" : 30
  431. },
  432. {
  433. "key" : "Hail",
  434. "doc_count" : 30
  435. },
  436. {
  437. "key" : "Heavy Fog",
  438. "doc_count" : 30
  439. }
  440. ]
  441. }
  442. }
  443. ]
  444. }
  445. }
  446. }

一个查询中只能直接嵌套一个查询。但是可以间接嵌套多个。

嵌套查询练习

  1. # 查询每个岗位下工资的信息(平均工资、最高工资、最少工资)
  2. GET employee/_search
  3. {
  4. "size": 0,
  5. "aggs": {
  6. "every_job": {
  7. "terms": {
  8. "field": "job"
  9. }
  10. , "aggs": {
  11. "sal_info": {
  12. "stats": {
  13. "field": "sal"
  14. }
  15. }
  16. }
  17. }
  18. }
  19. }
  20. # 查询不同工种男女员工的数量,然后统计不同工种下的男女员工的工资信息。
  21. GET employee/_search
  22. {
  23. "size": 0,
  24. "aggs": {
  25. "diff_job": {
  26. "terms": {
  27. "field": "job"
  28. }, "aggs": {
  29. "gender_cnt": {
  30. "terms": {
  31. "field": "gender"
  32. }, "aggs": {
  33. "employee_info": {
  34. "stats": {
  35. "field": "sal"
  36. }
  37. }
  38. }
  39. }
  40. }
  41. }
  42. }
  43. }
8.2.4、限制查询(top_hits)

查询年龄最大的两位员工的信息

  1. GET employee/_search
  2. {
  3. "size": 0,
  4. "aggs": {
  5. "older_two_emp": {
  6. "top_hits": {
  7. "size": 2,
  8. "sort": [
  9. {
  10. "age": {
  11. "order": "desc"
  12. }
  13. }
  14. ]
  15. }
  16. }
  17. }
  18. }

在一个查询结果中前几个,就用 top_hits

8.2.5、范围查询-range

range 的区间是前闭后开的。

  1. # 查询不同工资区间员工工资的统计信息
  2. GET employee/_search
  3. {
  4. "size": 0,
  5. "aggs": {
  6. "range_sal_info": {
  7. "range": {
  8. "field": "sal",
  9. "ranges": [
  10. {
  11. "key": "0 <= sal < 10001",
  12. "to": 10001
  13. },
  14. {
  15. "key": "10001 <= sal < 20001",
  16. "from": 10001,
  17. "to": 20001
  18. },
  19. {
  20. "key": "20001 <= sal < 30001",
  21. "from": 20001,
  22. "to": 30001
  23. }
  24. ]
  25. }
  26. }
  27. }
  28. }

程序结果:

  1. {
  2. "took" : 4,
  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" : 16,
  13. "relation" : "eq"
  14. },
  15. "max_score" : null,
  16. "hits" : [ ]
  17. },
  18. "aggregations" : {
  19. "range_sal_info" : {
  20. "buckets" : [
  21. {
  22. "key" : "0 <= sal < 10001",
  23. "to" : 10001.0,
  24. "doc_count" : 6
  25. },
  26. {
  27. "key" : "10001 <= sal < 20001",
  28. "from" : 10001.0,
  29. "to" : 20001.0,
  30. "doc_count" : 7
  31. },
  32. {
  33. "key" : "20001 <= sal < 30001",
  34. "from" : 20001.0,
  35. "to" : 30001.0,
  36. "doc_count" : 3
  37. }
  38. ]
  39. }
  40. }
  41. }
  1. {
  2. "key": "20001 <= sal < 30001",
  3. "from": 20001,
  4. "to": 30001
  5. }

范围就是 [20001, 30001),range 是前开后闭区间

8.2.6、查询的结果直方图显示出来-histogram
  1. # 以直方图的方式,每 3000 元为一个区间查询员工信息
  2. GET employee/_search
  3. {
  4. "size": 0,
  5. "aggs": {
  6. "range_sal_info": {
  7. "histogram": {
  8. "field": "sal",
  9. "interval": 3000,
  10. "extended_bounds": {
  11. "min": 0,
  12. "max": 15000
  13. }
  14. }
  15. }
  16. }
  17. }

field:字段名

interval:指定多少为一个区间

extended_bounds:有属性 max,指定最大的区间,不够会补。

运行结果:

  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" : 16,
  13. "relation" : "eq"
  14. },
  15. "max_score" : null,
  16. "hits" : [ ]
  17. },
  18. "aggregations" : {
  19. "range_sal_info" : {
  20. "buckets" : [
  21. {
  22. "key" : 0.0,
  23. "doc_count" : 1
  24. },
  25. {
  26. "key" : 3000.0,
  27. "doc_count" : 2
  28. },
  29. {
  30. "key" : 6000.0,
  31. "doc_count" : 3
  32. },
  33. {
  34. "key" : 9000.0,
  35. "doc_count" : 0
  36. },
  37. {
  38. "key" : 12000.0,
  39. "doc_count" : 2
  40. },
  41. {
  42. "key" : 15000.0,
  43. "doc_count" : 2
  44. },
  45. {
  46. "key" : 18000.0,
  47. "doc_count" : 3
  48. },
  49. {
  50. "key" : 21000.0,
  51. "doc_count" : 3
  52. }
  53. ]
  54. }
  55. }
  56. }
8.2.7、结果中找最低的-min_bucket

查询平均工资最低的工种

  1. # 查询平均工资最低的工种
  2. GET employee/_search
  3. {
  4. "size": 0,
  5. "aggs": {
  6. "job_info": {
  7. "terms": {
  8. "field": "job"
  9. },
  10. "aggs": {
  11. "diff_job_avg_sal": {
  12. "avg": {
  13. "field": "sal"
  14. }
  15. }
  16. }
  17. },
  18. "min_avg_sal_job": {
  19. "min_bucket": {
  20. "buckets_path": "job_info>diff_job_avg_sal"
  21. }
  22. }
  23. }
  24. }

buckets_path:是聚合的路径,聚合名字 a>聚合名字 b...

运行结果:

  1. {
  2. "took" : 3,
  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" : 16,
  13. "relation" : "eq"
  14. },
  15. "max_score" : null,
  16. "hits" : [ ]
  17. },
  18. "aggregations" : {
  19. "job_info" : {
  20. "doc_count_error_upper_bound" : 0,
  21. "sum_other_doc_count" : 0,
  22. "buckets" : [
  23. {
  24. "key" : "java",
  25. "doc_count" : 7,
  26. "diff_job_avg_sal" : {
  27. "value" : 13428.57142857143
  28. }
  29. },
  30. {
  31. "key" : "dba",
  32. "doc_count" : 5,
  33. "diff_job_avg_sal" : {
  34. "value" : 12300.0
  35. }
  36. },
  37. {
  38. "key" : "html",
  39. "doc_count" : 4,
  40. "diff_job_avg_sal" : {
  41. "value" : 14250.0
  42. }
  43. }
  44. ]
  45. },
  46. "min_avg_sal_job" : {
  47. "value" : 12300.0,
  48. "keys" : [
  49. "dba"
  50. ]
  51. }
  52. }
  53. }

8.3、聚合之全局过滤与局部过滤-filter

上面用的都是全局过滤。做一个局部过滤

求 30 岁以上的员工的平均工资和所有员工的平均工资

  1. # 求 30 岁以上的员工的平均工资和所有员工的平均工资
  2. GET employee/_search
  3. {
  4. "size": 0,
  5. "aggs": {
  6. "all_emp_avg_sal": {
  7. "avg": {
  8. "field": "sal"
  9. }
  10. },
  11. "gt_30_emp_avg_info":{
  12. "filter": {
  13. "range": {
  14. "age": {
  15. "gte": 30
  16. }
  17. }
  18. },
  19. "aggs": {
  20. "gt_30_emp_avg_sal": {
  21. "avg": {
  22. "field": "sal"
  23. }
  24. }
  25. }
  26. }
  27. }
  28. }

查询结果:

  1. {
  2. "took" : 13,
  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" : 16,
  13. "relation" : "eq"
  14. },
  15. "max_score" : null,
  16. "hits" : [ ]
  17. },
  18. "aggregations" : {
  19. "all_emp_avg_sal" : {
  20. "value" : 13281.25
  21. },
  22. "gt_30_emp_avg_info" : {
  23. "doc_count" : 6,
  24. "gt_30_emp_avg_sal" : {
  25. "value" : 15750.0
  26. }
  27. }
  28. }
  29. }

ES 建议搜索

语法

  1. GET 索引名/_search
  2. {
  3. "suggest": {
  4. "自定义的名字": {
  5. "text": "想要查询建议搜索值",
  6. "term": {
  7. "FIELD": "字段名"
  8. }
  9. }
  10. }
  11. }

查询电影中名字为 beauti 的建议搜索

  1. GET movies/_search
  2. {
  3. "suggest": {
  4. "title_suggest": {
  5. "text": "beauti",
  6. "term": {
  7. "field": "title"
  8. }
  9. }
  10. }
  11. }

查询结果:

  1. {
  2. "took" : 7,
  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" : 0,
  13. "relation" : "eq"
  14. },
  15. "max_score" : null,
  16. "hits" : [ ]
  17. },
  18. "suggest" : {
  19. "title_suggest" : [
  20. {
  21. "text" : "beauti",
  22. "offset" : 0,
  23. "length" : 6,
  24. "options" : [
  25. {
  26. "text" : "beauty",
  27. "score" : 0.8333333,
  28. "freq" : 66
  29. },
  30. {
  31. "text" : "beati",
  32. "score" : 0.8,
  33. "freq" : 1
  34. },
  35. {
  36. "text" : "beasts",
  37. "score" : 0.6666666,
  38. "freq" : 9
  39. },
  40. {
  41. "text" : "beauties",
  42. "score" : 0.6666666,
  43. "freq" : 5
  44. },
  45. {
  46. "text" : "beastie",
  47. "score" : 0.6666666,
  48. "freq" : 2
  49. }
  50. ]
  51. }
  52. ]
  53. }
  54. }

options 后跟的是建议的结果。注意,用上面的查询,只有在值在数据中没有时,ES 才会给出建议搜索值,否则是无法给出的。比如将 beauti 换成 beauty,结果集中有,options 中就不会显示。

  1. GET movies/_search
  2. {
  3. "suggest": {
  4. "title_suggest": {
  5. "text": "beauty",
  6. "term": {
  7. "field": "title"
  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" : 0,
  13. "relation" : "eq"
  14. },
  15. "max_score" : null,
  16. "hits" : [ ]
  17. },
  18. "suggest" : {
  19. "title_suggest" : [
  20. {
  21. "text" : "beauty",
  22. "offset" : 0,
  23. "length" : 6,
  24. "options" : [ ]
  25. }
  26. ]
  27. }
  28. }

此时 ES 就没有给出建议搜索。可以使用 suggest_mode 来控制。

  1. GET movies/_search
  2. {
  3. "suggest": {
  4. "title_suggest": {
  5. "text": "beauty",
  6. "term": {
  7. "field": "title",
  8. "suggest_mode": "always"
  9. }
  10. }
  11. }
  12. }

suggest_mode 代表 ES 的建议模式,有 3 个值:

  • missing:倒排索引没有的时候才给建议
  • always:无论倒排索引中有没有都给建议
  • popular:搜索建议中比较流行常用的单词

ES 的自动补全功能

我们在浏览器搜索引擎中经常见到 ES 的自动补全功能:

我们输入一个词,就会从数据中匹配。所以自动补全功能对性能要求很高。针对这个,ES 没有采取倒排索引的方式,数据类型要改为 completion

定义 mapping
  1. PUT movies
  2. {
  3. "mappings" : {
  4. "properties" : {
  5. "@version" : {
  6. "type" : "text",
  7. "fields" : {
  8. "keyword" : {
  9. "type" : "keyword",
  10. "ignore_above" : 256
  11. }
  12. }
  13. },
  14. "genre" : {
  15. "type" : "text",
  16. "fields" : {
  17. "keyword" : {
  18. "type" : "keyword",
  19. "ignore_above" : 256
  20. }
  21. }
  22. },
  23. "id" : {
  24. "type" : "text",
  25. "fields" : {
  26. "keyword" : {
  27. "type" : "keyword",
  28. "ignore_above" : 256
  29. }
  30. }
  31. },
  32. "title" : {
  33. "type" : "completion"
  34. },
  35. "year" : {
  36. "type" : "long"
  37. }
  38. }
  39. }
  40. }

title 的数据类型设置为 completion。这样才可以使用 ES 的自动补全功能。

前缀搜索
  1. GET movies/_search
  2. {
  3. "_source": [""],
  4. "suggest": {
  5. "title_prefix_suggest": {
  6. "prefix": "bea",
  7. "completion": {
  8. "field" : "title",
  9. "skip_duplicates": true,
  10. "size":10
  11. }
  12. }
  13. }
  14. }

prefix:前缀,ES 会自动补全。

completion:

  • field:需要自动补全的值对应的的字段。
  • skip_duplicates:去掉重复内容。
  • size:查出的结果数量,默认值是 5。

ES 高亮显示-highlight

平时在使用浏览器搜索时就会看到查询的内容高亮显示

将 title 和 genere 中的所有 romance 进行一个高亮显示

  1. GET movies/_search
  2. {
  3. "query": {
  4. "multi_match": {
  5. "query": "romance",
  6. "fields": ["title", "genre"]
  7. }
  8. },
  9. "highlight": {
  10. "pre_tags": "<span>",
  11. "post_tags": "</span>",
  12. "fields": {
  13. "title": {},
  14. "genre": {
  15. "pre_tags": "<em>",
  16. "post_tags": "</em>"
  17. }
  18. }
  19. }
  20. }

先用了 query 查询,然后使用 highlight 高亮显示。

pre_tags:默认值是 <em>,会加在高亮词的前面。

post_tags:默认值是 </em>,会加在高亮词的后面。

  1. {
  2. "took" : 4,
  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" : 7734,
  13. "relation" : "eq"
  14. },
  15. "max_score" : 9.840833,
  16. "hits" : [
  17. {
  18. "_index" : "movies",
  19. "_type" : "_doc",
  20. "_id" : "2894",
  21. "_score" : 9.840833,
  22. "_source" : {
  23. "year" : 1999,
  24. "id" : "2894",
  25. "title" : "Romance",
  26. "@version" : "1",
  27. "genre" : [
  28. "Drama",
  29. "Romance"
  30. ]
  31. },
  32. "highlight" : {
  33. "genre" : [
  34. "<em>Romance</em>"
  35. ],
  36. "title" : [
  37. "<span>Romance</span>"
  38. ]
  39. }
  40. },
  41. {
  42. "_index" : "movies",
  43. "_type" : "_doc",
  44. "_id" : "116867",
  45. "_score" : 9.840833,
  46. "_source" : {
  47. "year" : 1930,
  48. "id" : "116867",
  49. "title" : "Romance",
  50. "@version" : "1",
  51. "genre" : [
  52. "Drama",
  53. "Romance"
  54. ]
  55. },
  56. "highlight" : {
  57. "genre" : [
  58. "<em>Romance</em>"
  59. ],
  60. "title" : [
  61. "<span>Romance</span>"
  62. ]
  63. }
  64. },
  65. {
  66. "_index" : "movies",
  67. "_type" : "_doc",
  68. "_id" : "124991",
  69. "_score" : 9.840833,
  70. "_source" : {
  71. "year" : 2008,
  72. "id" : "124991",
  73. "title" : "Romance",
  74. "@version" : "1",
  75. "genre" : [
  76. "Romance"
  77. ]
  78. },
  79. "highlight" : {
  80. "genre" : [
  81. "<em>Romance</em>"
  82. ],
  83. "title" : [
  84. "<span>Romance</span>"
  85. ]
  86. }
  87. },
  88. {
  89. "_index" : "movies",
  90. "_type" : "_doc",
  91. "_id" : "555",
  92. "_score" : 8.284594,
  93. "_source" : {
  94. "year" : 1993,
  95. "id" : "555",
  96. "title" : "True Romance",
  97. "@version" : "1",
  98. "genre" : [
  99. "Crime",
  100. "Thriller"
  101. ]
  102. },
  103. "highlight" : {
  104. "title" : [
  105. "True <span>Romance</span>"
  106. ]
  107. }
  108. },
  109. {
  110. "_index" : "movies",
  111. "_type" : "_doc",
  112. "_id" : "3501",
  113. "_score" : 8.284594,
  114. "_source" : {
  115. "year" : 1985,
  116. "id" : "3501",
  117. "title" : "Murphy's Romance",
  118. "@version" : "1",
  119. "genre" : [
  120. "Comedy",
  121. "Romance"
  122. ]
  123. },
  124. "highlight" : {
  125. "genre" : [
  126. "<em>Romance</em>"
  127. ],
  128. "title" : [
  129. "Murphy's <span>Romance</span>"
  130. ]
  131. }
  132. },
  133. {
  134. "_index" : "movies",
  135. "_type" : "_doc",
  136. "_id" : "5769",
  137. "_score" : 8.284594,
  138. "_source" : {
  139. "year" : 1981,
  140. "id" : "5769",
  141. "title" : "Modern Romance",
  142. "@version" : "1",
  143. "genre" : [
  144. "Comedy",
  145. "Romance"
  146. ]
  147. },
  148. "highlight" : {
  149. "genre" : [
  150. "<em>Romance</em>"
  151. ],
  152. "title" : [
  153. "Modern <span>Romance</span>"
  154. ]
  155. }
  156. },
  157. {
  158. "_index" : "movies",
  159. "_type" : "_doc",
  160. "_id" : "40342",
  161. "_score" : 8.284594,
  162. "_source" : {
  163. "year" : 2005,
  164. "id" : "40342",
  165. "title" : "Romance & Cigarettes",
  166. "@version" : "1",
  167. "genre" : [
  168. "Comedy",
  169. "Drama",
  170. "Musical",
  171. "Romance"
  172. ]
  173. },
  174. "highlight" : {
  175. "genre" : [
  176. "<em>Romance</em>"
  177. ],
  178. "title" : [
  179. "<span>Romance</span> & Cigarettes"
  180. ]
  181. }
  182. },
  183. {
  184. "_index" : "movies",
  185. "_type" : "_doc",
  186. "_id" : "133712",
  187. "_score" : 8.284594,
  188. "_source" : {
  189. "year" : 1977,
  190. "id" : "133712",
  191. "title" : "Office Romance",
  192. "@version" : "1",
  193. "genre" : [
  194. "Comedy",
  195. "Romance"
  196. ]
  197. },
  198. "highlight" : {
  199. "genre" : [
  200. "<em>Romance</em>"
  201. ],
  202. "title" : [
  203. "Office <span>Romance</span>"
  204. ]
  205. }
  206. },
  207. {
  208. "_index" : "movies",
  209. "_type" : "_doc",
  210. "_id" : "149446",
  211. "_score" : 8.284594,
  212. "_source" : {
  213. "year" : 2010,
  214. "id" : "149446",
  215. "title" : "Petty Romance",
  216. "@version" : "1",
  217. "genre" : [
  218. "Comedy",
  219. "Drama"
  220. ]
  221. },
  222. "highlight" : {
  223. "title" : [
  224. "Petty <span>Romance</span>"
  225. ]
  226. }
  227. },
  228. {
  229. "_index" : "movies",
  230. "_type" : "_doc",
  231. "_id" : "150016",
  232. "_score" : 8.284594,
  233. "_source" : {
  234. "year" : 2012,
  235. "id" : "150016",
  236. "title" : "Brasserie Romance",
  237. "@version" : "1",
  238. "genre" : [
  239. "Comedy",
  240. "Drama"
  241. ]
  242. },
  243. "highlight" : {
  244. "title" : [
  245. "Brasserie <span>Romance</span>"
  246. ]
  247. }
  248. }
  249. ]
  250. }
  251. }

将 2012 年电影的名字中包含 romance 的电影,将 title 中 romance 进行高亮显示,同时将这些电影中 genre 包含 children 的单词进行高亮显示。

  1. # 将 2012 年电影的名字中包含 romance 的电影,将 title 中 romance 进行高亮显示,同时将这些电影中 genre 包含 children 的单词进行高亮显示。
  2. GET movies/_search
  3. {
  4. "query": {
  5. "bool": {
  6. "must": [
  7. {
  8. "term": {
  9. "year": "2012"
  10. }
  11. },
  12. {
  13. "match": {
  14. "title": "romance"
  15. }
  16. }
  17. ]
  18. }
  19. },
  20. "highlight": {
  21. "fields": {
  22. "title": {},
  23. "genre": {
  24. "pre_tags": "<sapn>",
  25. "post_tags": "</span>",
  26. "highlight_query":{
  27. "match": {
  28. "genre": "children"
  29. }
  30. }
  31. }
  32. }
  33. }
  34. }

九、ES springboot 结合

springboot 官网 ES 相关:https://docs.spring.io/spring-data/elasticsearch/docs/4.1.2/reference/html/#reference

版本 4.0 以来已弃用类‎TransportClient``TransportClient

版本对应关系:

普通查询

引入依赖

sprigboot 2.4.1 + jdk8 + spring-boot-starter-data-elasticsearch 4.1.0

  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  3. xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
  4. <modelVersion>4.0.0</modelVersion>
  5. <parent>
  6. <groupId>org.springframework.boot</groupId>
  7. <artifactId>spring-boot-starter-parent</artifactId>
  8. <version>2.4.1</version>
  9. <relativePath/> <!-- lookup parent from repository -->
  10. </parent>
  11. <groupId>com.passerbywl</groupId>
  12. <artifactId>esspringboot</artifactId>
  13. <version>0.0.1-SNAPSHOT</version>
  14. <name>esspringboot</name>
  15. <description>Demo project for Spring Boot</description>
  16. <properties>
  17. <java.version>1.8</java.version>
  18. </properties>
  19. <dependencies>
  20. <dependency>
  21. <groupId>org.springframework.boot</groupId>
  22. <artifactId>spring-boot-starter-web</artifactId>
  23. </dependency>
  24. <dependency>
  25. <groupId>org.projectlombok</groupId>
  26. <artifactId>lombok</artifactId>
  27. <optional>true</optional>
  28. </dependency>
  29. <!-- 引入 elasticsearch springboot start-->
  30. <dependency>
  31. <groupId>org.springframework.boot</groupId>
  32. <artifactId>spring-boot-starter-data-elasticsearch</artifactId>
  33. </dependency>
  34. <dependency>
  35. <groupId>org.springframework.boot</groupId>
  36. <artifactId>spring-boot-starter-test</artifactId>
  37. <scope>test</scope>
  38. </dependency>
  39. </dependencies>
  40. <build>
  41. <plugins>
  42. <plugin>
  43. <groupId>org.springframework.boot</groupId>
  44. <artifactId>spring-boot-maven-plugin</artifactId>
  45. <configuration>
  46. <excludes>
  47. <exclude>
  48. <groupId>org.projectlombok</groupId>
  49. <artifactId>lombok</artifactId>
  50. </exclude>
  51. </excludes>
  52. </configuration>
  53. </plugin>
  54. </plugins>
  55. </build>
  56. </project>
Client
  1. package com.passerbywl.esspringboot.config;
  2. import org.elasticsearch.client.RestHighLevelClient;
  3. import org.springframework.context.annotation.Bean;
  4. import org.springframework.context.annotation.Configuration;
  5. import org.springframework.data.elasticsearch.client.ClientConfiguration;
  6. import org.springframework.data.elasticsearch.client.RestClients;
  7. import org.springframework.data.elasticsearch.config.AbstractElasticsearchConfiguration;
  8. /**
  9. * @author liyanan
  10. * @create 2021-01-04 17:45
  11. */
  12. @Configuration
  13. public class RestClientConfig extends AbstractElasticsearchConfiguration {
  14. @Override
  15. @Bean
  16. public RestHighLevelClient elasticsearchClient() {
  17. final ClientConfiguration clientConfiguration = ClientConfiguration.builder()
  18. .connectedTo("localhost:9200")
  19. .build();
  20. return RestClients.create(clientConfiguration).rest();
  21. }
  22. }
实体类
  1. package com.passerbywl.esspringboot.entity;
  2. import lombok.Builder;
  3. import lombok.Data;
  4. import org.springframework.data.annotation.Id;
  5. import org.springframework.data.elasticsearch.annotations.Document;
  6. import java.util.List;
  7. /**
  8. * @author liyanan
  9. * @create 2020-12-28 19:36
  10. */
  11. @Data
  12. @Builder
  13. @Document(indexName = "movies")
  14. public class Movies {
  15. @Id
  16. private String id;
  17. private String title;
  18. private List<String> genre;
  19. private long year;
  20. }
Controller
  1. package com.passerbywl.esspringboot.controller;
  2. import com.passerbywl.esspringboot.entity.Movies;
  3. import com.passerbywl.esspringboot.repository.MoviesRepository;
  4. import org.springframework.data.domain.Page;
  5. import org.springframework.data.domain.PageRequest;
  6. import org.springframework.web.bind.annotation.GetMapping;
  7. import org.springframework.web.bind.annotation.RequestMapping;
  8. import org.springframework.web.bind.annotation.RequestParam;
  9. import org.springframework.web.bind.annotation.RestController;
  10. import java.util.List;
  11. /**
  12. * @author liyanan
  13. * @create 2020-12-28 20:01
  14. */
  15. @RestController
  16. @RequestMapping("/movies")
  17. public class MoviesController {
  18. private MoviesRepository moviesRepository;
  19. public MoviesController(MoviesRepository moviesRepository) {
  20. this.moviesRepository = moviesRepository;
  21. }
  22. @GetMapping("/getByName")
  23. public List<Movies> getPageData(@RequestParam String title,
  24. @RequestParam(defaultValue = "0") Integer page,
  25. @RequestParam(defaultValue = "10") Integer size) {
  26. PageRequest pageable = PageRequest.of(page, size);
  27. Page<Movies> pageData = moviesRepository.findByTitle(title, pageable);
  28. return pageData.getContent();
  29. }
  30. }

运行 springboot 程序,然后测试:

Repository
  1. package com.passerbywl.esspringboot.repository;
  2. import com.passerbywl.esspringboot.entity.Movies;
  3. import org.springframework.data.domain.Page;
  4. import org.springframework.data.domain.Pageable;
  5. import org.springframework.data.elasticsearch.repository.ElasticsearchRepository;
  6. /**
  7. * @author liyanan
  8. * @create 2020-12-28 19:39
  9. * ElasticsearchRepository<实体类型, id类型>
  10. */
  11. public interface MoviesRepository extends ElasticsearchRepository<Movies, String> {
  12. Page<Movies> findByTitle(String title, Pageable pageable);
  13. }

ElasticsearchRepository<实体类型, id 类型>

关于这些方法,官网有相关介绍:https://docs.spring.io/spring-data/elasticsearch/docs/4.1.2/reference/html/#elasticsearch.query-methods.criterions

ES + SpringBoot 实现自动补全简单实现

ES 语句:

  1. GET movies/_search
  2. {
  3. "_source": [""],
  4. "suggest": {
  5. "title_prefix_suggest": {
  6. "prefix": "bea",
  7. "completion": {
  8. "field" : "title",
  9. "skip_duplicates": true,
  10. "size":10
  11. }
  12. }
  13. }
  14. }

对应的建议语句:

  1. package com.passerbywl.esspringboot.controller;
  2. import org.elasticsearch.search.suggest.Suggest;
  3. import org.elasticsearch.search.suggest.SuggestBuilder;
  4. import org.elasticsearch.search.suggest.completion.CompletionSuggestionBuilder;
  5. import org.springframework.data.elasticsearch.core.ElasticsearchOperations;
  6. import org.springframework.data.elasticsearch.core.mapping.IndexCoordinates;
  7. import org.springframework.web.bind.annotation.CrossOrigin;
  8. import org.springframework.web.bind.annotation.GetMapping;
  9. import org.springframework.web.bind.annotation.RequestParam;
  10. import org.springframework.web.bind.annotation.RestController;
  11. import java.util.ArrayList;
  12. import java.util.List;
  13. /**
  14. * @author liyanan
  15. * @create 2021-01-07 10:30
  16. */
  17. @RestController
  18. @CrossOrigin("*")
  19. public class MoviesSuggestController {
  20. private ElasticsearchOperations elasticsearchOperations;
  21. public MoviesSuggestController(ElasticsearchOperations elasticsearchOperations) {
  22. this.elasticsearchOperations = elasticsearchOperations;
  23. }
  24. @GetMapping("/movie/suggest")
  25. public List<String> suggest(@RequestParam String prefix) {
  26. List<String> result = new ArrayList<>();
  27. CompletionSuggestionBuilder completionSuggestionBuilder =
  28. new CompletionSuggestionBuilder("title")
  29. .skipDuplicates(true)
  30. .prefix(prefix)
  31. .size(10);
  32. SuggestBuilder suggestBuilder = new SuggestBuilder().addSuggestion("title_suggest", completionSuggestionBuilder);
  33. // 获取 Suggest
  34. Suggest suggest = elasticsearchOperations.suggest(suggestBuilder, IndexCoordinates.of("movies")).getSuggest();
  35. List<? extends Suggest.Suggestion.Entry<? extends Suggest.Suggestion.Entry.Option>> entries = suggest.getSuggestion("title_suggest").getEntries();
  36. entries.forEach(entry -> {
  37. List<? extends Suggest.Suggestion.Entry.Option> options = entry.getOptions();
  38. options.forEach(op -> {
  39. result.add(op.getText().toString());
  40. });
  41. });
  42. return result;
  43. }
  44. }

Controller 上面要配置跨域,否则下面的 html 无法访问到 SpringBoot 服务。

index.html

  1. <!DOCTYPE html>
  2. <html lang="en">
  3. <head>
  4. <meta charset="UTF-8">
  5. <title>Title</title>
  6. <style>
  7. * {
  8. margin: 0;
  9. padding: 0;
  10. }
  11. input {
  12. width: 400px;
  13. height: 24px;
  14. margin-left: 100px;
  15. }
  16. div {
  17. width: 400px;
  18. height: 500px;
  19. margin-left: 100px;
  20. background: bisque;
  21. }
  22. </style>
  23. </head>
  24. <body>
  25. <input type="text" oninput="getAutoCompletedHints(this)">
  26. <div id="content"></div>
  27. </body>
  28. <script src="https://cdn.bootcdn.net/ajax/libs/jquery/2.2.1/jquery.js"></script>
  29. <script>
  30. var content = document.getElementById("content");
  31. function getAutoCompletedHints(inputDom) {
  32. let prefix = inputDom.value;
  33. if (prefix.trim()) {
  34. content.innerHTML = '';
  35. $.getJSON("http://localhost:8080/movie/suggest/?prefix=" + prefix.trim(), {},
  36. function (_data) {
  37. for (let i = 0; i < _data.length; i++) {
  38. let pTag = document.createElement('p');
  39. pTag.innerText = _data[i];
  40. content.appendChild(pTag);
  41. }
  42. });
  43. }
  44. }
  45. </script>
  46. </html>

ElasticSearch 7.x 学习的更多相关文章

  1. ElasticSearch权威指南学习(索引管理)

    创建索引 当我们需要确保索引被创建在适当数量的分片上,在索引数据之前设置好分析器和类型映射. 手动创建索引,在请求中加入所有设置和类型映射,如下所示: PUT /my_index { "se ...

  2. 搜索引擎Elasticsearch REST API学习

    Elasticsearch为开发者提供了一套基于Http协议的Restful接口,只需要构造rest请求并解析请求返回的json即可实现访问Elasticsearch服务器.Elasticsearch ...

  3. ElasticSearch基础入门学习笔记

    前言 本笔记的内容主要是在从0开始学习ElasticSearch中,按照官方文档以及自己的一些测试的过程. 安装 由于是初学者,按照官方文档安装即可.前面ELK入门使用主要就是讲述了安装过程,这里不再 ...

  4. Elasticsearch的配置学习笔记

    文/朱季谦 Elasticsearch是一个基于Lucene的搜索服务器.它提供一个分布式多用户能力的全文搜索引擎,基于RESTful web接口,Elasticsearch是用Java语言开发的. ...

  5. Elasticsearch基础知识学习

    概要 ElasticSearch是一个基于Lucene的搜索服务器.它提供了一个分布式多用户能力的全文搜索引擎,基于RESTful web接口.Elasticsearch是用Java开发的,并作为Ap ...

  6. ElasticSearch权威指南学习(分布式搜索)

    查询阶段 在初始化查询阶段(query phase),查询被向索引中的每个分片副本(原本或副本)广播. 每个分片在本地执行搜索并且建立了匹配document的优先队列(priority queue). ...

  7. ElasticSearch权威指南学习(排序)

    排序方式 相关性排序 默认情况下,结果集会按照相关性进行排序 -- 相关性越高,排名越靠前. 相关性分值会用_score字段来给出一个浮点型的数值,所以默认情况下,结果集以_score进行倒序排列. ...

  8. ElasticSearch权威指南学习(结构化查询)

    请求体查询 简单查询语句(lite)是一种有效的命令行adhoc查询.但是,如果你想要善用搜索,你必须使用请求体查询(request body search)API. 空查询 我们以最简单的 sear ...

  9. ElasticSearch权威指南学习(映射和分析)

    概念 映射(mapping)机制用于进行字段类型确认,将每个字段匹配为一种确定的数据类型(string, number, booleans, date等).+ 分析(analysis)机制用于进行全文 ...

  10. ElasticSearch权威指南学习(分布式文档存储)

    路由文档到分片 当你索引一个文档,它被存储在单独一个主分片上.Elasticsearch是如何知道文档属于哪个分片的呢?当你创建一个新文档,它是如何知道是应该存储在分片1还是分片2上的呢? 进程不能是 ...

随机推荐

  1. 用 UniRx 实现 Timeline 式的异步操作

      没接触 UniRx 之前,我在 Unity 中通常用 Coroutine 或 Callback 来实现异步操作.根据我的任务,一般都是去实现游戏组件的演出,比如:敌方角色图形显示后,我方角色 UI ...

  2. Java 8中字符串拼接新姿势:StringJoiner

    介绍 StringJoiner是java.util包中的一个类,用于构造一个由分隔符分隔的字符序列(可选),并且可以从提供的前缀开始并以提供的后缀结尾.虽然这也可以在StringBuilder类的帮助 ...

  3. 多路复用器Select、Poll、Epoll区别梳理

    注意:本文是本人的学习总结,可能存在理解上的错误,请带着怀疑眼光看待,如果有不准确的地方欢迎指出,疑义相与析.为了叙述完整性,前面有一些前置知识,可以根据目录直接看后面的详解部分. 前置知识 用户态与 ...

  4. IdentityServer4之Implicit和纯前端好像很配哦

    前言 上一篇Resource Owner Password Credentials模式虽然有用户参与,但对于非信任的第三方的来说,使用这种模式是有风险的,所以相对用的不多:这里接着说说implicit ...

  5. Python程序中首行#!/usr/bin/env python的作用

    1.通常我们在pycharm中写程序的时候会在首行写上#!/usr/bin/env python 如: #!/usr/bin/env python3#-*-coding: UTF-8 -*-#Auth ...

  6. Language Guide (proto3) | proto3 语言指南(开篇)

    前言 近日在学习gRPC框架的相关知识时接触到Protobuf(protocol-buffers,协议缓冲区),proto3等知识.网上很多文章/帖子经常把gRPC与proto3放在一起,为避免初学者 ...

  7. (六)整合 QuartJob ,实现定时器实时管理

    整合 QuartJob ,实现定时器实时管理 1.QuartJob简介 1.1 核心API 2.SpringBoot整合QuartJob 2.1 项目结构 2.2 定时器配置 2.3 定时器管理工具 ...

  8. Spring boot 异步线程池

    package com.loan.msg.config; import org.springframework.aop.interceptor.AsyncUncaughtExceptionHandle ...

  9. checkbox限制选中个数

    <!DOCTYPE html><html><head><meta http-equiv="Content-Type" content=&q ...

  10. vb.net和C#两兄弟谁的封装性更好?

    引言: 虽然没怎样接触过vb.net,但是大概对于vb6还是比较了解的.前者是从后者基础上发展而来的.后来接触了C#编程语言,起初没有太关心她和vb.net有啥不同的地方,话说都是面向对象的,而且都是 ...