Elasticsearch教程与实例

概述

Elasticsearch简称ES,是一个全文搜索服务器,也可以作为NoSQL数据库,存储任意格式的文档和数据,同时,也可以做大数据的分析,是一个跨界开源产品。ES有如下特点:

  • 全文搜索引擎,ES是建立在Lucene上的开源搜索引擎可以用来进行全文搜索地理信息搜索。Wikipedia、GitHub、 StackOverFlow等网站均使用 ES.
  • 文档存储和查询,可以像 NoSQL 那样存储任意格式文档,并能根据条件查询文档。
  • 大数据分析,ES号称能准确实时地进行大数据分析,数据量从TB到PB,国内外很多大公司都用ES做大数据分析。
  • ES提供了REST API,用来简化对 ES 的操作。因此可以使用任何语言的客户端,同时也提供Java API,Spring Boot也对 REST API 进行了封装,简化了开发。
  • ES常常配合传统数据库一起使用,ES用来负责大数据的查询、搜索、统计分析。

本文源代码地址:https://github.com/cofecatt/cofecatt.github.io/blob/main/_posts/2021-9-9-elasticsearch.md,如果喜欢请Star!,谢谢!

ES有一些基本概念,掌握这些基本概念对理解ES有很大帮助。

  • Index, index是文档(Document)的集合,Index下面包含了Type,用于对Document进一步分类。可以理解为ES中的Index相当于数据库,而Type相当于数据库中的表,ES中可以轻易的联合Index和Type来搜索数据,数据库却不能。
  • Type,用来进一步组织Document,一个Index下可以有多个Type,比如用户信息是一个Type,用户的支付记录是一个Type。
  • Document,文档是ES能够存储和搜索的基本信息,类似数循厍衣仃效话,Document为JSON格式,文档属于 Type。
  • Node(节点),节点是集群里的一台ES Server,用于文档的存储和查询。应用可以只有一个节点,也可以由上百个节点组成集群来存储和搜索数据。每个节点都有一个节点名字,以及所属集群的名字。
  • 集群,同样集群名的节点将组合为ES集群,用来联合完成数据的存储和搜索。默认的集群名字是elasticsearch。
  • 分区(Shards)和复制(Replicas),每个Index理论上都可以包含大量的数据,超过了单个节点的存储限制,而且单个节点处理那么大的数据,将明显限制存储和搜索 性能。为了解决这个问题,ES 会进一步将Index在物理上细分为多个分区,且A区分区会按照配置复制到多个节点,Index 的分区称为主分区,复制的分区称为复制分区。 这样的好处是既保证数据不会丢失,又提高了查询的性能。每个分区是一个独立的工作单元,可以完成存储和搜索功能,每个分区能存储最多2147483519个文档。

使用REST访问Elasticsearch

1. 添加文档

使用PUT添加文档,采用curl,在命令行输入:

curl -XPOST 'localhost:9200/product/book/1?pretty' -H 'Content-Type:application/json' -d'
{
    "name": "100道菜",
    "type": "case",
    "postDate": "2021-9-9",
    "message": "介绍100道菜"
}'

product表示Index,book表示Type,数字1是文档的主键,主键可以是任意形式,如果未指定主键,ES会自动生成一个唯一主键,pretty是可选的,ES输出的时候会格式化输出的结果,更加美观。

  • 如果不熟悉curl,可以使用postman工具代替。

postDate的格式是ES默认的日期格式之一,为 yyyy-MM-dd ,ES还默认了多种格式为日期格式,ES会自动认为这些类型为日期格式,一下三种格式数据ES会处理成日期类型。

  • yyyy-MM-dd ,如2021-9-9
  • yyyy-MM-dd’T’HH:mm:ss ,如2021-9-9T13:14:21
  • yyyy-MM-dd’T’HH:mm:ss.SSS ,如2021-9-9T13:14:21.398

最后操作的响应是:

{
    "_index": "product",
    "_type": "book",
    "_id": "1",
    "result": "created",
    "_shards": {
        "total": 2,
        "successful": 1,
        "failed": 0
    },
    "created": true
}
  • 字段_id表示该文档的主键,如果在添加文档的时候未指定主键,系统默认生成一个唯一主键。
  • _shards表示分区信息,total为2表示有两个分区(包括主分区),successful为1表示成功复制了一份。
  • _version代表了文档的版本号,每一次修改都会递增,注意ES并不会存储文档修改的各个版本。

根据主键查询

输入curl -XGET ‘localhost:9200/product/book/1?pretty’

控制台输出:

{
    "_index": "product",
    "_type": "book",
    "_id": "1",
    "_version": 1,
    "found": true,
    "_source": {
        "name": "100道菜",
        "type": "case",
        "postDate": "2021-9-9",
        "message": "介绍100道菜"
    }
}

  • source表示查询的文档,这正是我们存入的文档。如果你只想看到source部分,可以加一个_source: curl -XGET ‘localhost:9200/product/book/1/_source?pretty’

根据主键更新

根据主键更新和新增主键一样,需要指定主键然后更新整个文档。

curl -XPUT 'localhost:9200/product/book/1?pretty' -H 'Content-Type:application/json' -d'
{
    "name": "108道菜",
    "type": "case",
    "postDate": "2021-9-9",
    "message": "介绍100道菜"
}'

输出:

{
    "_index": "product",
    "_type": "book",
    "_id": "1",
    "_version": 2,
    "result": "updated",
    "_shards": {
        "total": 2,
        "successful": 1,
        "failed": 0
    },
    "created": false
}

修改文档同添加文档类似,不同的地方是_version递增了,result的值是updated,表示更新成功。如果只是想局部更新,可以采用POST,使用_update,比如只更新message字段:

curl -XPOST 'localhost:9200/product/book/1/_update?pretty' -H 'Content-Type:application/json' -d'
{
    "doc": {
        "message": "介绍炸酱面,卤煮等!"
    }
}'

字段doc包含了要更新的片段,如果并发修改文档,可以使用version字段实现乐观锁,如果修改的文档的version和传入的version不一致,则修改失败。

curl -XPOST 'localhost:9200/product/book/1/_update?pretty&version=2' -H 'Content-Type:application/json' -d'
{
    "doc": {
        "message": "介绍炸酱面,卤煮等!"
    }
}'

如果当前文档版本号不是2的话,则修改失败,控制台抛出如下错误:

{
    "error":{
        "root_cause":[
            "type": "version_conflict_engine_exception",
            ...
        ],
        "type": "version_conflict_engine_exception",
        ...
    },
    "status": 409
}

可以看到status为409,type字段为version_conflict_engine_exception,表示版本冲突,更新失败。

根据主键删除

使用DELETE删除指定主键的文档:

curl -XDELETE ‘localhost:9200/product/book/1?pretty’ 控制台输出:

{
   "found": true,
   "_index": "product",
   "_type": "book",
   "_id": "1",
   "_version": 10,
   "result": "delete",
   "_shards": {
       "total": 2,
       "successful": 1,
       "failed": 0
   }
}

如果删除不存在的文档,found值为false。

搜索文档

ES提供了强大的搜索功能,搜索参数可以在url后面,也可以放到body中。使用GET方法:

curl -G –data-urlencode ‘q=message:炸酱面’ ‘localhost:9200/product/book/_search?pretty’ 或者更通用的POST方法:

curl -XPOST 'localhost:9200/product/book/_search?pretty' -H 'Content-Type:application/json' -d'
{
    "query": {
        "match": { "message": "介绍"}
    }
}'

注意: 因为关键字里包含了中文,需要curl进行RUL编码,所以才使用了–data-urlencode ‘q=message:炸酱面’,参数 “-G” 表示这是一个GET请求, 如果不加 “-G” ,curl默认发出POST请求,导致ES返回一个406不支持POST请求错误响应。

无论哪种查询,都会有类似如下的响应:

{
    "took": 7,
    "timed_out": false,
    "_shards": {
        "total": 5,
        "successful": 5,
        "failed": 0
    },
    "hits": {
        "total": 2,
        "max_score": 0.789035,
        "hits": [
            {
                "_index": "product",
                "_type": "book",
                "_id": "2",
                "_score": 0.789035,
                "_source": {
                    "name": "100道菜",
                    "type": "case",
                    "postDate": "2021-9-9",
                    "message": "介绍100道菜"
                }
            },
            {
                "_index": "product",
                "_type": "book",
                "_id": "1",
                "_score": 0.78903234,
                "_source": {
                    "name": "100道菜",
                    "type": "case",
                    "postDate": "2021-9-9",
                    "message": "介绍炸酱面,卤煮等!"
                }
            }
        ]
    }
}

hits包含了查询结果,在本例中,只有两条,Index是product,Type是book,主键是1和2,_score是搜索引擎概念,表示查询相关度,分数越高,表示此文档与关键字期望的结果匹配程度高。 除了全文搜索,还可以精度搜索,使用term进行精确搜索:

curl -XGET 'localhost:9200/product/book/_search?pretty' -H 'Content-Type:application/json' -d'
{
    "query": {
        "term": { "type": "food"}
    }
}'

如果需要完成翻页功能,可以使用from和size:

curl -XGET 'localhost:9200/product/book/_search?pretty' -H 'Content-Type:application/json' -d'
{
    "from": 0, "size": 5,
    "query": {
        "term": { "type": "food"}
    }
}'

如果需要知道查询的总数,则使用_count代替_search。 查询书中类型为菜谱的书的总数:

curl -XGET 'localhost:9200/product/book/_count' -H 'Content-Type:application/json' -d'
{
    "query": {
        "term": { "type": "food"}
    }
}'

如果要联合条件查询,则可以使用must关键字:

curl -XGET 'localhost:9200/product/book/_search?pretty' -H 'Content-Type:application/json' -d'
{
    "from": 0, "size": 5,
    "query": {
        "bool": {
            "must": {"match": { "type": "food"}},
            "must": {"term": { "message": "介绍"}}
        }
    }
}'

参考资料

[SpringBoot2 精髓 李家智著]
Elasticsearch 官方文档


原始链接 版权声明:自由转载-非商用-非衍生-保持署名 | Creative Commons BY-NC-ND 4.0