写点什么

elasticsearch 实战三部曲之三:搜索操作

作者:程序员欣宸
  • 2022 年 7 月 31 日
  • 本文字数:11804 字

    阅读完需:约 39 分钟

elasticsearch实战三部曲之三:搜索操作

欢迎访问我的 GitHub

这里分类和汇总了欣宸的全部原创(含配套源码):https://github.com/zq2599/blog_demos


  • 本文是《elasticsearch 实战三部曲》的终篇,作为 elasticsearch 的核心功能,搜索的重要性不言而喻,今天的实战都会围绕搜索展开;

环境信息

基本情况介绍

  • 本次实战的 elasticsearch 环境以及搭建完毕,是由两个机器搭建的集群,并且 elasticsearch-head 也搭建完成:

  • 一号机器,IP 地址:192.168.119.152;

  • 二号机器:IP 地址:192.168.119.153;

  • elasticsearch-head 安装在一号机器,访问地址:http://192.168.119.152:9100

  • 已经建立了索引 englishbooks,对应的数据如下所示,请用批量命令导入到 elasticsearch:


{"index":{ "_index": "englishbooks", "_type": "IT", "_id": "1" }}{"id":"1","title":"Deep Learning","language":"python","author":"Yoshua Bengio","price":549.00,"publish_time":"2016-11-18","description":"written by three experts in the field, deep learning is the only comprehensive book on the subject."}{"index":{ "_index": "englishbooks", "_type": "IT", "_id": "2" }}{"id":"2","title":"Compilers","language":"c","author":"Alfred V.Aho","price":62.50,"publish_time":"2011-01-01","description":"In the time since the 1986 edition of this book, the world of compiler designhas changed significantly."}{"index":{ "_index": "englishbooks", "_type": "IT", "_id": "3" }}{"id":"3","title":"Core Java","language":"java","author":"Horstmann","price":85.90,"publish_time":"2016-06-01","description":"The book is aimed at experienced programmers who want to learn how to write useful Java applications and applets. "}{"index":{ "_index": "englishbooks", "_type": "IT", "_id": "4" }}{"id":"4","title":"Thinking in Java","language":"java","author":"Bruce Eckel","price":70.10,"publish_time":"2015-07-06","description":"Thinking in Java should be read cover to cover by every Java programmer, then kept close at hand for frequent reference. The exercises are challenging, and the chapter on Collections is superb!"}{"index":{ "_index": "englishbooks", "_type": "IT", "_id": "5" }}{"id":"5","title":"The Go Programming Language","language":"go","author":"Alan A.A.Donovan","price":63.90,"publish_time":"2016-01-01","description":"A declaration's lexical block determines its scope, which may be large or small. The declarations of built—in types, functions, and constants like int, len, and true are in the universe block and can be referred to throughout the entire program."}
复制代码


数据格式说明

  • 为了便于和读者沟通,我们来约定一下如何在文章中表达请求和响应的信息:

  • 假设通过 Postman 工具向服务器发送一个 PUT 类型的请求,地址是:http://192.168.119.152:9200/test001/article/1

  • 请求的内容是 JSON 格式的,内容如下:


{  “id”:1,  "title":"标题a",  "posttime":"2019-01-12",  "content":"一起来熟悉文档相关的操作"}
复制代码


  • 对于上面的请求,我在文章中就以如下格式描述:


PUT test001/article/1
{ “id”:1, "title":"标题a", "posttime":"2019-01-12", "content":"一起来熟悉文档相关的操作"}
复制代码


  • 读者您看到上述内容,就可以在 postman 中发起 PUT 请求,地址是"test001/article/1"前面加上您的服务器地址,内容是上面的 JSON;

本文中的文档内容暂不涉及中文

  • 文中数据都是英文的,避免在因分词器的分词问题导致搜索不到对应的中文结果,分词器相关的知识会在另一篇文章中详细介绍;

查看所有数据

GET englishbooks/_search
{ "query":{ "match_all":{} }}
复制代码


  • 上述查询返回索引 books 的所有记录,并且文档得分收是 1;

  • 您可以将请求的整个 JSON 删除,只用 books/_search 这个 URL 来试试,也能得到所有数据,这是 match_all 的简写;

数字字段的精确匹配

  • 查询价格等于 549 的记录:


GET englishbooks/_search
{ "query":{ "constant_score":{ "filter":{ "term":{"price":549} } } }}
复制代码


  • 得到结果:


{    "took": 4,    "timed_out": false,    "_shards": {        "total": 5,        "successful": 5,        "skipped": 0,        "failed": 0    },    "hits": {        "total": 1,        "max_score": 1,        "hits": [            {                "_index": "englishbooks",                "_type": "IT",                "_id": "1",                "_score": 1,                "_source": {                    "id": "1",                    "title": "Deep Learning",                    "language": "python",                    "author": "Yoshua Bengio",                    "price": 549,                    "publish_time": "2016-11-18",                    "description": "written by three experts in the field, deep learning is the only comprehensive book on the subject."                }            }        ]    }}
复制代码


  • 请求参数中使用了 constant_score 后,查询将以非评分模式来执行 term,并以一作为统一评分;

查看分词效果

  • text 类型的字段会被分词后构建倒排索引,来看看 title 字段的值为"Core Java"时的分词效果:


GET englishbooks/_analyze
{ "field":"title", "text":"Core Java"}
复制代码


  • 响应如下所示,"Core Java"被分"core"和"java"两个词,也就是说我们以词项"core"或"java"搜索 title 字段都能收到对应文档:


{    "tokens": [        {            "token": "core",            "start_offset": 0,            "end_offset": 4,            "type": "<ALPHANUM>",            "position": 0        },        {            "token": "java",            "start_offset": 5,            "end_offset": 9,            "type": "<ALPHANUM>",            "position": 1        }    ]}
复制代码


  • 需要注意的是分词后的结果都是小写,这是分词器的处理结果;

词项查询(term query)

  • 前面我们查看分词效果发现"Core Java"被分"core"和"java"两个词,现在就以"java"为关键词搜索一下试试:


GET englishbooks/_search
{ "query":{ "term":{"title":"java"} }}
复制代码


  • 结果如下,title 中有 java 关键词的两个文档都被搜到:


{    "took": 4,    "timed_out": false,    "_shards": {        "total": 5,        "successful": 5,        "skipped": 0,        "failed": 0    },    "hits": {        "total": 2,        "max_score": 0.5754429,        "hits": [            {                "_index": "englishbooks",                "_type": "IT",                "_id": "4",                "_score": 0.5754429,                "_source": {                    "id": "4",                    "title": "Thinking in Java",                    "language": "java",                    "author": "Bruce Eckel",                    "price": 70.1,                    "publish_time": "2015-07-06",                    "description": "Thinking in Java should be read cover to cover by every Java programmer, then kept close at hand for frequent reference. The exercises are challenging, and the chapter on Collections is superb!"                }            },            {                "_index": "englishbooks",                "_type": "IT",                "_id": "3",                "_score": 0.2876821,                "_source": {                    "id": "3",                    "title": "Core Java",                    "language": "java",                    "author": "Horstmann",                    "price": 85.9,                    "publish_time": "2016-06-01",                    "description": "The book is aimed at experienced programmers who want to learn how to write useful Java applications and applets. "                }            }        ]    }}
复制代码

分词查询(match query)

  • term query 的特点是将输入的内容作为一个词项来用,例如以下的查询是没有结果的:


GET englishbooks/_search
{ "query":{ "term":{"title":"core java"} }}
复制代码


  • 上述查询没有结果的原因,是因为"core java"被当做一个词项去查询了,而 title 的分词结果中只有"core"、"java"这些分词过的词项,并没有一个叫做"core java"的词项,所以搜不到结果;

  • 如果输入的查询条件"core java"也被做一次分词处理,再把处理结果"core"和"java"用来搜索,应该就能得到结果了,match query 就是用来对输入条件做分词处理的,如下:


GET englishbooks/_search
{ "query":{ "match":{"title":"Core Java"} }}
复制代码


  • 搜索结果如下,包含了 java 的两条记录都被查出来了:


{    "took": 8,    "timed_out": false,    "_shards": {        "total": 5,        "successful": 5,        "skipped": 0,        "failed": 0    },    "hits": {        "total": 2,        "max_score": 0.5754429,        "hits": [            {                "_index": "englishbooks",                "_type": "IT",                "_id": "4",                "_score": 0.5754429,                "_source": {                    "id": "4",                    "title": "Thinking in Java",                    "language": "java",                    "author": "Bruce Eckel",                    "price": 70.1,                    "publish_time": "2015-07-06",                    "description": "Thinking in Java should be read cover to cover by every Java programmer, then kept close at hand for frequent reference. The exercises are challenging, and the chapter on Collections is superb!"                }            },            {                "_index": "englishbooks",                "_type": "IT",                "_id": "3",                "_score": 0.5753642,                "_source": {                    "id": "3",                    "title": "Core Java",                    "language": "java",                    "author": "Horstmann",                    "price": 85.9,                    "publish_time": "2016-06-01",                    "description": "The book is aimed at experienced programmers who want to learn how to write useful Java applications and applets. "                }            }        ]    }}
复制代码


  • 如果我们的本意是只要"Core Java"的匹配结果,上面的结果显然是不符合要求的,此时可以给查询条件加个**"operator":"and"**属性,就会查询匹配了所有关键词的文档,注意 json 的结构略有变化,以前 title 的属性是搜索条件,现在变成了一个 json 对象,里面的 query 属性是原来的搜索条件:


GET englishbooks/_search
{ "query":{ "match":{ "title":{ "query":"Core Java", "operator":"and" } } }}
复制代码


  • 这次的搜索结果就是同时匹配了"core"和"java"两个词项的记录了(为什么 core 和 java 是小写? 因为"Core Java"被分词后改为了小写,再去搜索的):


{    "took": 11,    "timed_out": false,    "_shards": {        "total": 5,        "successful": 5,        "skipped": 0,        "failed": 0    },    "hits": {        "total": 1,        "max_score": 0.5753642,        "hits": [            {                "_index": "englishbooks",                "_type": "IT",                "_id": "3",                "_score": 0.5753642,                "_source": {                    "id": "3",                    "title": "Core Java",                    "language": "java",                    "author": "Horstmann",                    "price": 85.9,                    "publish_time": "2016-06-01",                    "description": "The book is aimed at experienced programmers who want to learn how to write useful Java applications and applets. "                }            }        ]    }}
复制代码

match_phrase 搜索

  • match_phrase 搜索和前面的 match 搜索相似,并且有以下两个特点:

  • 分词后的所有词项都要匹配上,也就是前面的**"operator":"and"**属性的效果;

  • 分析后的词项顺序要和搜索字段的顺序一致,才能匹配上;


GET englishbooks/_search
{ "query":{ "match_phrase":{"title":"Core Java"} }}
复制代码


  • 上述查询可以搜索到结果,但如果将"Core Java"改成"Java Core"就搜不到结果了,但是 match query 用"Java Core"是可以搜到结果的;

match_phrase_prefix 搜索

  • match_phrase_prefix 的功能和前面的 match_phrase 类似,不过 match_phrase_prefix 支持最后一个词项做前缀匹配,如下所示,"Core J"这个搜索条件用 match_phrase 是搜不到结果的,但是 match_phrase_prefix 可以,因为"J"可以作为前缀和"Java"匹配:


GET englishbooks/_search
{ "query":{ "match_phrase":{"title":"Core J"} }}
复制代码

multi_match 搜素

  • multi_match 是在 match 的基础上支持多字段搜索,以下查询就是用"1986"和"deep"这两个词项,同时搜索 title 和 description 两个字段:


GET englishbooks/_search
{ "query":{ "multi_match":{ "query":"1986 deep", "fields":["title", "description"] } }}
复制代码


  • 响应如下,可见 title 和 description 中含有词项"1986"或者"deep"的文档都被返回了:


{    "took": 4,    "timed_out": false,    "_shards": {        "total": 5,        "successful": 5,        "skipped": 0,        "failed": 0    },    "hits": {        "total": 2,        "max_score": 0.79237825,        "hits": [            {                "_index": "englishbooks",                "_type": "IT",                "_id": "2",                "_score": 0.79237825,                "_source": {                    "id": "2",                    "title": "Compilers",                    "language": "c",                    "author": "Alfred V.Aho",                    "price": 62.5,                    "publish_time": "2011-01-01",                    "description": "In the time since the 1986 edition of this book, the world of compiler designhas changed significantly."                }            },            {                "_index": "englishbooks",                "_type": "IT",                "_id": "1",                "_score": 0.2876821,                "_source": {                    "id": "1",                    "title": "Deep Learning",                    "language": "python",                    "author": "Yoshua Bengio",                    "price": 549,                    "publish_time": "2016-11-18",                    "description": "written by three experts in the field, deep learning is the only comprehensive book on the subject."                }            }        ]    }}
复制代码

terms query

  • terms 是 term 查询的升级,用来查询多个词项:


GET englishbooks/_search
{ "query":{ "terms":{ "title":["deep", "core"] } }}
复制代码


  • 响应如下,title 中含有 deep 和 core 的文档都被查到:


{    "took": 5,    "timed_out": false,    "_shards": {        "total": 5,        "successful": 5,        "skipped": 0,        "failed": 0    },    "hits": {        "total": 2,        "max_score": 1,        "hits": [            {                "_index": "englishbooks",                "_type": "IT",                "_id": "1",                "_score": 1,                "_source": {                    "id": "1",                    "title": "Deep Learning",                    "language": "python",                    "author": "Yoshua Bengio",                    "price": 549,                    "publish_time": "2016-11-18",                    "description": "written by three experts in the field, deep learning is the only comprehensive book on the subject."                }            },            {                "_index": "englishbooks",                "_type": "IT",                "_id": "3",                "_score": 1,                "_source": {                    "id": "3",                    "title": "Core Java",                    "language": "java",                    "author": "Horstmann",                    "price": 85.9,                    "publish_time": "2016-06-01",                    "description": "The book is aimed at experienced programmers who want to learn how to write useful Java applications and applets. "                }            }        ]    }}
复制代码

范围查询

  • range query 是范围查询,例如查询 publish_time 在"2016-01-01"到"2016-12-31"之间的文档:


GET englishbooks/_search
{ "query":{ "range":{ "publish_time":{ "gte":"2016-01-01", "lte":"2016-12-31", "format":"yyyy-MM-dd" } } }}
复制代码


  • 篇幅所限,此处略去返回结果;

exists query

  • exists query 返回的是字段中至少有一个非空值的文档:


GET englishbooks/_search
{ "query":{ "exists":{ "field":"author" } }}
复制代码

前缀查询

  • 用于查询某个字段是否以给定前缀开始:


GET englishbooks/_search
{ "query":{ "prefix":{ "title":"cor" } }}
复制代码


  • 以上请求可以查到 title 字段为"Core Java"的文档:


{    "took": 6,    "timed_out": false,    "_shards": {        "total": 5,        "successful": 5,        "skipped": 0,        "failed": 0    },    "hits": {        "total": 1,        "max_score": 1,        "hits": [            {                "_index": "englishbooks",                "_type": "IT",                "_id": "3",                "_score": 1,                "_source": {                    "id": "3",                    "title": "Core Java",                    "language": "java",                    "author": "Horstmann",                    "price": 85.9,                    "publish_time": "2016-06-01",                    "description": "The book is aimed at experienced programmers who want to learn how to write useful Java applications and applets. "                }            }        ]    }}
复制代码

通配符查询

  • 以下查询,可以搜到 title 字段中含有"core"的文档,另外需要注意的是,"?"匹配一个字符,"*"匹配零个或者多个字符:


GET englishbooks/_search
{ "query":{ "wildcard":{ "title":"cor?" } }}
复制代码

正则表达式

  • 使用属性 regexp 可以进行正则表达式查询,例如查找 description 字段带有 4 位数字的分词的文档:


GET englishbooks/_search
{ "query":{ "regexp":{ "description":"[0-9]{4}" } }}
复制代码


  • 查找结果如下,description 字段中带有数字 1986


{    "took": 4,    "timed_out": false,    "_shards": {        "total": 5,        "successful": 5,        "skipped": 0,        "failed": 0    },    "hits": {        "total": 1,        "max_score": 1,        "hits": [            {                "_index": "englishbooks",                "_type": "IT",                "_id": "2",                "_score": 1,                "_source": {                    "id": "2",                    "title": "Compilers",                    "language": "c",                    "author": "Alfred V.Aho",                    "price": 62.5,                    "publish_time": "2011-01-01",                    "description": "In the time since the 1986 edition of this book, the world of compiler designhas changed significantly."                }            }        ]    }}
复制代码

模糊查询(fuzzy query)

  • fuzzy 是通过计算词项与文档的编辑距离来得到结果的,例如查找 description 字段还有分词"1986"的时候,不小心输入了"1987",通过 fuzzy 查询也能得到结果,只是得分变低了,请求内容如下所示:


GET englishbooks/_search
{ "query":{ "fuzzy":{ "description":"1987" } }}
复制代码


  • 搜索到的文档如下所示,得分只有 0.5942837,低于用"1986"查询的 0.79237825:


{    "took": 5,    "timed_out": false,    "_shards": {        "total": 5,        "successful": 5,        "skipped": 0,        "failed": 0    },    "hits": {        "total": 1,        "max_score": 0.5942837,        "hits": [            {                "_index": "englishbooks",                "_type": "IT",                "_id": "2",                "_score": 0.5942837,                "_source": {                    "id": "2",                    "title": "Compilers",                    "language": "c",                    "author": "Alfred V.Aho",                    "price": 62.5,                    "publish_time": "2011-01-01",                    "description": "In the time since the 1986 edition of this book, the world of compiler designhas changed significantly."                }            }        ]    }}
复制代码


  • 需要注意的是,fuzzy 查询时消耗资源较大;

复合查询

  • 常用到的复合查询是 bool query,可以用下表中的条件组合查询:



  • 以下条件,搜索的是 title 中带有 java,但是不包含 core 的文档:


GET englishbooks/_search
{ "query":{ "bool":{ "must":{ "term":{"title":"java"} }, "must_not":[ {"term":{"title":"core"}} ] } }}
复制代码


  • 得到的文档中,带有 core 词项的已经被过滤了:


{    "took": 3,    "timed_out": false,    "_shards": {        "total": 5,        "successful": 5,        "skipped": 0,        "failed": 0    },    "hits": {        "total": 1,        "max_score": 0.5754429,        "hits": [            {                "_index": "englishbooks",                "_type": "IT",                "_id": "4",                "_score": 0.5754429,                "_source": {                    "id": "4",                    "title": "Thinking in Java",                    "language": "java",                    "author": "Bruce Eckel",                    "price": 70.1,                    "publish_time": "2015-07-06",                    "description": "Thinking in Java should be read cover to cover by every Java programmer, then kept close at hand for frequent reference. The exercises are challenging, and the chapter on Collections is superb!"                }            }        ]    }}
复制代码

脚本查询

  • 可用脚本进行查询,如下是查询价格大于 100 的所有文档:


GET englishbooks/_search
{ "query":{ "bool":{ "must":{ "script":{ "script":{ "inline":"doc['price'].value>500", "lang":"painless" } } } } }}
复制代码


  • 得到的结果只有 price 大于 500 的文档:


{    "took": 8,    "timed_out": false,    "_shards": {        "total": 5,        "successful": 5,        "skipped": 0,        "failed": 0    },    "hits": {        "total": 1,        "max_score": 1,        "hits": [            {                "_index": "englishbooks",                "_type": "IT",                "_id": "1",                "_score": 1,                "_source": {                    "id": "1",                    "title": "Deep Learning",                    "language": "python",                    "author": "Yoshua Bengio",                    "price": 549,                    "publish_time": "2016-11-18",                    "description": "written by three experts in the field, deep learning is the only comprehensive book on the subject."                }            }        ]    }}
复制代码

指定排序字段

  • 默认的排序方式是按照评分来排序的(也就是相关度排序),可以用 sort 属性来设置排序字段,下面的请求指定了按照 price 字段降序排序:


{  "query":{    "term":{"title":"java"}  },  "sort":[    {"price":{"order":"desc"}}  ]}
复制代码


  • 得到结果:


{    "took": 4,    "timed_out": false,    "_shards": {        "total": 5,        "successful": 5,        "skipped": 0,        "failed": 0    },    "hits": {        "total": 2,        "max_score": null,        "hits": [            {                "_index": "englishbooks",                "_type": "IT",                "_id": "3",                "_score": null,                "_source": {                    "id": "3",                    "title": "Core Java",                    "language": "java",                    "author": "Horstmann",                    "price": 85.9,                    "publish_time": "2016-06-01",                    "description": "The book is aimed at experienced programmers who want to learn how to write useful Java applications and applets. "                },                "sort": [                    85.9                ]            },            {                "_index": "englishbooks",                "_type": "IT",                "_id": "4",                "_score": null,                "_source": {                    "id": "4",                    "title": "Thinking in Java",                    "language": "java",                    "author": "Bruce Eckel",                    "price": 70.1,                    "publish_time": "2015-07-06",                    "description": "Thinking in Java should be read cover to cover by every Java programmer, then kept close at hand for frequent reference. The exercises are challenging, and the chapter on Collections is superb!"                },                "sort": [                    70.1                ]            }        ]    }}
复制代码


  • 以上就是常用的搜索操作了,至此,《elasticsearch 实战三部曲》系列就全部完成,三篇文章列举的是一些常用的基本操作,希望能帮助读者您快速熟悉 elasticsearch,后面咱们再一起深入实战;

欢迎关注 InfoQ:程序员欣宸

学习路上,你不孤单,欣宸原创一路相伴...

发布于: 刚刚阅读数: 3
用户头像

搜索"程序员欣宸",一起畅游Java宇宙 2018.04.19 加入

前腾讯、前阿里员工,从事Java后台工作,对Docker和Kubernetes充满热爱,所有文章均为作者原创,个人Github:https://github.com/zq2599/blog_demos

评论

发布
暂无评论
elasticsearch实战三部曲之三:搜索操作_Java_程序员欣宸_InfoQ写作社区