ElasticSearch安装、IK、映射、索引管理、搜索管理和集群管理


一、ElasticSearch

1.1 概念

Elasticsearch是一个基于Lucene的搜索服务器。它提供了一个分布式多用户能力的全文搜索引擎,基于RESTful web接口。Elasticsearch是用Java语言开发的,并作为Apache许可条款下的开放源码发布,是一种流行的企业级搜索引擎。Elasticsearch用于云计算中,能够达到实时搜索,稳定,可靠,快速,安装使用方便。

  • 总结

    1、elasticsearch是一个基于Lucene的高扩展的分布式搜索服务器,支持开箱即用。
    2、elasticsearch隐藏了Lucene的复杂性,对外提供Restful 接口来操作索引、搜索。

  • 突出优点

    1、扩展性好,可部署上百台服务器集群,处理PB级数据。
    2、近实时的去索引数据、搜索数据。

  • es和solr选择哪个?

    如果你公司现在用的solr可以满足需求就不要换了。
    如果你公司准备进行全文检索项目的开发,建议优先考虑elasticsearch,因为像Github这样大规模的搜索都在用它。

1.2 原理与应用

1.2.1 索引结构

下图是ElasticSearch的索引结构,下边黑色部分是物理结构,上边黄色部分是逻辑结构,逻辑结构也是为了更好的去描述 ElasticSearch 的工作原理及去使用物理结构中的索引文件。

在这里插入图片描述
逻辑结构部分是一个倒排索引表:

  1. 将要搜索的文档内容分词,所有不重复的词组成分词列表。
  2. 将搜索的文档最终以Document方式存储起来。
  3. 每个词和docment都有关联。

如下:

在这里插入图片描述
现在,如果我们想搜索 quick brown ,我们只需要查找包含每个词条的文档

在这里插入图片描述
两个文档都匹配,但是第一个文档比第二个匹配度更高。如果我们使用仅计算匹配词条数量的简单 相似性算法 ,
那么,我们可以说,对于我们查询的相关性来讲,第一个文档比第二个文档更佳。

1.2.3 RESTful应用

Elasticsearch提供 RESTful Api接口进行索引、搜索,并且支持多种客户端。

在这里插入图片描述
下图是es在项目中的应用方式:
在这里插入图片描述

  1. 用户在前端搜索关键字
  2. 项目前端通过http方式请求项目服务端
  3. 项目服务端通过Http RESTful方式请求ES集群进行搜索
  4. ES集群从索引库检索数据。

二、ElasticSearch安装

2.1 Windows环境下ElasticSearch安装配置

注意:新版本要求至少jdk1.8以上。

  • 第一步、下载安装包,解压。
  • 第二步、在es根目录下创建 data 文件夹

在这里插入图片描述

bin:脚本目录,包括:启动、停止等可执行脚本
config:配置文件目录
data:索引目录,存放索引文件的地方
logs:日志目录
modules:模块目录,包括了es的功能模块
plugins :插件目录,es支持插件机制

  • 第三步、进入config文件夹,编辑elasticsearch.yml文件

    注意中文空格!注意path.data和path.logs路径配置正确!

cluster.name: xihu
node.name: lierys
network.host: 0.0.0.0
http.port: 9200
transport.tcp.port: 9300
node.master: true
node.data: true
#discovery.zen.ping.unicast.hosts: ["0.0.0.0:9300", "0.0.0.0:9301", "0.0.0.0:9302"]
discovery.zen.minimum_master_nodes: 1
bootstrap.memory_lock: false
node.max_local_storage_nodes: 1
path.data: E:\ElasticSearchSofts\elasticsearch\data
path.logs: E:\ElasticSearchSofts\elasticsearch\logs
http.cors.enabled: true
http.cors.allow-origin: /.*/
# 配置elasticsearch的集群名称
cluster.name: xihu
# 节点名通常一台物理服务器就是一个节点,es会默认随机指定一个名字,建议指定一个有意义的名称,方便管理一个或多个节点组成一个cluster集群,集群是一个逻辑的概念,节点是物理概念。
node.name: lierys
# 设置绑定主机的ip地址,设置为0.0.0.0表示绑定任何ip,允许外网访问,生产环境建议设置为具体的ip。 
network.host: 0.0.0.0
# 设置对外服务的http端口,默认为9200。
http.port: 9200
# 集群结点之间通信端口。当有集群是改变通信端口并在discovery.zen.ping.unicast.hosts中添加
transport.tcp.port: 9300
# 指定该节点是否有资格被选举成为master结点,默认是true,如果原来的master宕机会重新选举新的master。 
node.master: true
# 指定该节点是否存储索引数据,默认为true。
node.data: true
# 设置集群中master节点的初始列表。集群结点之间通信端口列表
discovery.zen.ping.unicast.hosts: ["0.0.0.0:9301", "0.0.0.0:9302", "0.0.0.0:9303"]
# 主结点数量的最少值 ,此值的公式为:(master_eligible_nodes / 2) + 1 
discovery.zen.minimum_master_nodes: 1
# 设置为true可以锁住ES使用的内存,避免内存与swap分区交换数据。
bootstrap.memory_lock: false
# 单机允许的最大存储结点数,通常单机启动一个结点建议设置为1,开发环境如果单机启动多个节点可设置大于 1
node.max_local_storage_nodes: 1
# 设置索引数据的存储路径,默认是es根目录下的data文件夹,可以设置多个存储路径,用逗号隔开。 如果没有data文件夹,自己在es根目录下创建
path.data: E:\ElasticSearchSofts\elasticsearch\data
# 设置日志文件的存储路径,默认是es根目录下的 logs文件夹 
path.logs: E:\ElasticSearchSofts\elasticsearch\logs
# 设置跨域请求
http.cors.enabled: true
http.cors.allow-origin: /.*/
  • 第四步、启动 ES

    进入bin目录,在 cmd 下运行:elasticsearch.bat

在这里插入图片描述

  • 第五步、测试

    浏览器输入:http://localhost:9200
    显示结果如下(配置不同内容则不同)说明ES启动成功

{
    "name" : "yh_node_1",
    "cluster_name" : "yunhe",
    "cluster_uuid" : "J18wPybJREyx1kjOoH8T‐g", 
    "version" : {
        "number" : "6.2.1",
        "build_hash" : "7299dc3",
        "build_date" : "2018‐02‐07T19:34:26.990113Z",
        "build_snapshot" : false,
        "lucene_version" : "7.2.1",
        "minimum_wire_compatibility_version" : "5.6.0",
        "minimum_index_compatibility_version" : "5.0.0"
    },
    "tagline" : "You Know, for Search"
}

2.2 Windows环境下HEAD插件安装

head 插件是 ES 的一个可视化管理插件,用来监视 ES 的状态,并通过 head 客户端和 ES 服务进行交互,比如创建映射、创建索引等,head的项目地址在https://github.com/mobz/elasticsearch-head 。从ES6.0开始,head插件支持使得node.js运行。

  • 第一步、安装node.js

    node.js会自动配置环境。

  • 第二步、下载head并运行

    直接下载压缩包,解压,即可使用。
    在根目录下,进入cmd窗口,运行:npm run start
    浏览器输入:http://localhost:9100,连接http://localhost:9200,连上之后会显示绿色的'集群健康值:green'

在这里插入图片描述

三、ES快速入门

ES 作为一个索引及搜索服务,对外提供丰富的REST接口,快速入门部分的实例使用head插件来测试,目的是对ES的使用方法及流程有个初步的认识。

3.1 创建索引库

ES的索引库是一个逻辑概念,它包括了分词列表及文档列表,同一个索引库中存储了相同类型的文档。它就相当于 MySQL中的表,或相当于Mongodb中的集合。

  • 关于索引这个语:
    • 索引(名词):ES是基于Lucene构建的一个搜索服务,它要从索引库搜索符合条件索引数据。
    • 索引(动词):索引库刚创建起来是空的,将数据添加到索引库的过程称为索引。

下边介绍两种创建索引库的方法,它们的工作原理是相同的,都是客户端向ES服务发送命令。

  • 第一种、使用postman或curl这样的工具创建:
    put http://localhost:9200/索引库名称
{
    "settings":{
        "index":{
            "number_of_shards":1,
            "number_of_replicas":0
        }
    }
}
  • number_of_shards:设置分片的数量,在集群中通常设置多个分片,表示一个索引库将拆分成多片分别存储不同的结点,提高了ES的处理能力和高可用性,入门程序使用单机环境,这里设置为1。
  • number_of_replicas:设置副本的数量,设置副本是为了提高ES的高可靠性,单机环境设置为0.

如下是创建的例子,创建lierys索引库,共1个分片,0个副本
在这里插入图片描述

启动head,浏览器中http://localhost:9100/,并连接http://localhost:9200/(9200:es中设置的对外服务http端口号)
在这里插入图片描述

  • 第二种、使用head插件创建

在这里插入图片描述在这里插入图片描述在这里插入图片描述在这里插入图片描述

3.2 创建映射

3.2.1 概念说明

在索引中每个文档都包括了一个或多个field,创建映射就是向索引库中创建field的过程,下边是document和field
与关系数据库的概念的类比:

​ 文档(Document)----------------Row记录
​ 字段(Field)-------------------Columns 列

注意:6.0之前的版本有type(类型)概念,type相当于关系数据库的表,ES官方将在ES9.0版本中彻底删除type。

上边讲的创建索引库相当于关系数据库中的数据库还是表?

  1. 如果相当于数据库就表示一个索引库可以创建很多不同类型的文档,这在ES中也是允许的。
  2. 如果相当于表就表示一个索引库只能存储相同类型的文档,ES官方建议 在一个索引库中只存储相同类型的文
    档。

3.2.2 创建映射

我们要把课程信息存储到ES中,这里我们创建课程信息的映射,先来一个简单的映射,如下:
发送:post http://localhost:9200/索引库名称/类型名称/_mapping

创建类型为 lierys 的映射,共包括三个字段:

  • name
  • description
  • studymodel

由于ES6.0版本还没有将type彻底删除,所以暂时把type起一个没有特殊意义的名字。
post 请求:http://localhost:9200/lierys /doc/_mapping

表示:在 lierys 索引库下的 doc 类型下创建映射。doc是类型名,可以自定义,在ES6.0中要弱化类型的概念,
给它起一个没有具体业务意义的名称

{
    "properties":{
        "name":{
            "type":"text"
        },
        "description":{
            "type":"text"
        },
        "studymodel":{
            "type":"keyword"
        }
    }
}

在这里插入图片描述

映射创建成功,查看 head 界面

在这里插入图片描述

3.3 创建文档

ES中的文档相当于MySQL数据库表中的记录。
发送:put 或Post http://localhost:9200/lierys/doc/id值(如果不指定id值ES会自动生成ID)

{
    "name":"Bootstrap开发框架",
    "description":"Bootstrap是由Twitter推出的一个前台页面开发框架,在行业之中使用较为广泛。此开发框架包含了大量的CSS、JS程序代码,可以帮助开发者(尤其是不擅长页面开发的程序人员)轻松的实现一个不受浏览器限制的精美界面效果。",
    "studymodel":"201001"
}

使用 postman 测试
在这里插入图片描述
通过head查询数据

在这里插入图片描述

3.4 搜索文档

3.4.1 搜索文档的方式

  • 根据课程id查询文档
    发送:get http://localhost:9200/lierys/doc/NTBI2nwBILBxarnO697Z

使用postman测试
在这里插入图片描述

  • 查询所有记录
    发送 get http://localhost:9200/lierys/doc/_search

    注意:body体中不能包含数据

  • 查询名称中包括spring 关键字的的记录
    发送:get http://localhost:9200/lierys/doc/_search?q=name:bootstrap

  • 查询学习模式为201001的记录
    发送 get http://localhost:9200/lierys/doc/_search?q=studymodel:201001

3.4.2 查询结果分析

{
    "took": 6,
    "timed_out": false,
    "_shards": {
        "total": 1,
        "successful": 1,
        "skipped": 0,
        "failed": 0
    },
    "hits": {
        "total": 1,
        "max_score": 1.0,
        "hits": [
            {
                "_index": "lierys",
                "_type": "doc",
                "_id": "NTBI2nwBILBxarnO697Z",
                "_score": 1.0,
                "_source": {
                    "name": "Bootstrap开发框架",
                    "description": "Bootstrap是由Twitter推出的一个前台页面开发框架,在行业之中使用较为广泛。此开发框架包含了大量的CSS、JS程序代码,可以帮助开发者(尤其是不擅长页面开发的程序人员)轻松的实现一个不受浏览器限制的精美界面效果。",
                    "studymodel": "201001"
                }
            }
        ]
    }
}
  • took:本次操作花费的时间,单位为毫秒。
  • timed_out:请求是否超时
  • _shards:说明本次操作共搜索了哪些分片
  • hits:搜索命中的记录
  • hits.total : 符合条件的文档总数 hits.hits :匹配度较高的前N个文档
  • hits.max_score:文档匹配得分,这里为最高分
  • _score:每个文档都有一个匹配度得分,按照降序排列。
  • _source:显示了文档的原始内容

四、IK分词器

4.1 测试分词器

在添加文档时会进行分词,索引中存放的就是一个一个的词(term),当你去搜索时就是拿关键字去匹配词,最终找到词关联的文档。

测试当前索引库使用的分词器:
post 发送:localhost:9200/_analyze

{"text":"测试分词器,后边是测试内容:spring cloud实战"}

结果如下

{
    "tokens": [
        {
            "token": "测",
            "start_offset": 0,
            "end_offset": 1,
            "type": "<IDEOGRAPHIC>",
            "position": 0
        },
        {
            "token": "试",
            "start_offset": 1,
            "end_offset": 2,
            "type": "<IDEOGRAPHIC>",
            "position": 1
        },
        {
            "token": "分",
            "start_offset": 2,
            "end_offset": 3,
            "type": "<IDEOGRAPHIC>",
            "position": 2
        },
        {
            "token": "词",
            "start_offset": 3,
            "end_offset": 4,
            "type": "<IDEOGRAPHIC>",
            "position": 3
        },
        {
            "token": "器",
            "start_offset": 4,
            "end_offset": 5,
            "type": "<IDEOGRAPHIC>",
            "position": 4
        },
        {
            "token": "后",
            "start_offset": 6,
            "end_offset": 7,
            "type": "<IDEOGRAPHIC>",
            "position": 5
        },
        {
            "token": "边",
            "start_offset": 7,
            "end_offset": 8,
            "type": "<IDEOGRAPHIC>",
            "position": 6
        },
        {
            "token": "是",
            "start_offset": 8,
            "end_offset": 9,
            "type": "<IDEOGRAPHIC>",
            "position": 7
        },
        {
            "token": "测",
            "start_offset": 9,
            "end_offset": 10,
            "type": "<IDEOGRAPHIC>",
            "position": 8
        },
        {
            "token": "试",
            "start_offset": 10,
            "end_offset": 11,
            "type": "<IDEOGRAPHIC>",
            "position": 9
        },
        {
            "token": "内",
            "start_offset": 11,
            "end_offset": 12,
            "type": "<IDEOGRAPHIC>",
            "position": 10
        },
        {
            "token": "容",
            "start_offset": 12,
            "end_offset": 13,
            "type": "<IDEOGRAPHIC>",
            "position": 11
        },
        {
            "token": "spring",
            "start_offset": 14,
            "end_offset": 20,
            "type": "<ALPHANUM>",
            "position": 12
        },
        {
            "token": "cloud",
            "start_offset": 21,
            "end_offset": 26,
            "type": "<ALPHANUM>",
            "position": 13
        },
        {
            "token": "实",
            "start_offset": 26,
            "end_offset": 27,
            "type": "<IDEOGRAPHIC>",
            "position": 14
        },
        {
            "token": "战",
            "start_offset": 27,
            "end_offset": 28,
            "type": "<IDEOGRAPHIC>",
            "position": 15
        }
    ]
}

会发现分词的效果将 “测试” 这个词拆分成两个单字“测”和“试”,这是因为当前索引库使用的分词器对中文就是单字分词。

4.2 安装IK分词器

使用IK分词器可以实现对中文分词的效果。

下载IK分词器,并将解压的文件拷贝到ES安装目录的plugins下的ik目录下
在这里插入图片描述

测试分词效果:
发送:post localhost:9200/_analyze

{"text":"测试分词器,后边是测试内容:spring cloud实战","analyzer":"ik_max_word" }

测试结果:

{
    "tokens": [
        {
            "token": "测试",
            "start_offset": 0,
            "end_offset": 2,
            "type": "CN_WORD",
            "position": 0
        },
        {
            "token": "分词器",
            "start_offset": 2,
            "end_offset": 5,
            "type": "CN_WORD",
            "position": 1
        },
        {
            "token": "分词",
            "start_offset": 2,
            "end_offset": 4,
            "type": "CN_WORD",
            "position": 2
        },
        {
            "token": "器",
            "start_offset": 4,
            "end_offset": 5,
            "type": "CN_CHAR",
            "position": 3
        },
        {
            "token": "后边",
            "start_offset": 6,
            "end_offset": 8,
            "type": "CN_WORD",
            "position": 4
        },
        {
            "token": "是",
            "start_offset": 8,
            "end_offset": 9,
            "type": "CN_CHAR",
            "position": 5
        },
        {
            "token": "测试",
            "start_offset": 9,
            "end_offset": 11,
            "type": "CN_WORD",
            "position": 6
        },
        {
            "token": "内容",
            "start_offset": 11,
            "end_offset": 13,
            "type": "CN_WORD",
            "position": 7
        },
        {
            "token": "spring",
            "start_offset": 14,
            "end_offset": 20,
            "type": "ENGLISH",
            "position": 8
        },
        {
            "token": "cloud",
            "start_offset": 21,
            "end_offset": 26,
            "type": "ENGLISH",
            "position": 9
        },
        {
            "token": "实战",
            "start_offset": 26,
            "end_offset": 28,
            "type": "CN_WORD",
            "position": 10
        }
    ]
}

4.3 两种分词模式

ik分词器有两种分词模式:ik_max_word和ik_smart 模式。

1. ik_max_word
会将文本做最细粒度的拆分,比如会将“中华人民共和国人民大会堂”拆分为“中华人民共和国、中华人民、中华、华人、人民共和国、人民、共和国、大会堂、大会、会堂等词语。
2. ik_smart
会做最粗粒度的拆分,比如会将“中华人民共和国人民大会堂”拆分为中华人民共和国、人民大会堂。

测试两种分词模式:发送:post localhost:9200/_analyze


{"text":"中华人民共和国人民大会堂","analyzer":"ik_max_word" }

{"text":"中华人民共和国人民大会堂","analyzer":"ik_smart" }

4.4 自定义词库

如果要让分词器支持一些专有词语,可以自定义词库。 iK 分词器自带一个 main.dic 的文件,此文件为词库文件

在这里插入图片描述
在上边的目录中新建一个my.dic文件(注意文件格式为utf-8(不要选择utf-8 BOM)),可以在其中自定义词汇:
在这里插入图片描述
在IKAnalyzer.cfg.xml配置文件中配置 my.dic
在这里插入图片描述
重启ES,测试分词效果:
发送:post localhost:9200/_analyze

{"text":"测试分词器,后边是测试内容:spring cloud实战","analyzer":"ik_max_word" }

在这里插入图片描述

五、映射

5.1 映射维护方法

  • 查询所有索引的映射:
    GET: http://localhost:9200/_mapping

  • 创建映射
    post :http://localhost:9200/yh_course/doc/_mapping

   {
       "properties": {  
           "name": {
               "type": "text"
           },
           "description": {
               "type": "text"
           },
           "studymodel": {
               "type": "keyword"
           }
       }
   }
  • 更新映射
    映射创建成功可以添加新字段,已有字段不允许更新

  • 删除映射
    通过删除索引来删除映射

5.2 常见维护类型

5.2.1 text文本字段

在这里插入图片描述

字符串包括text和keyword两种类型:

  1. text
    通过 analyzer 属性指定分词器。
    下边指定name的字段类型为text,使用ik分词器的ik_max_word分词模式

    "name": {
        "type": "text",
        "analyzer":"ik_max_word"
    }
    

    上边指定了analyzer是指在索引和搜索都使用ik_max_word,如果单独想定义搜索时使用的分词器则可以通过
    search_analyzer属性。

    对于ik分词器建议是索引时使用ik_max_word将搜索内容进行细粒度分词,搜索时使用ik_smart提高搜索精确性

    "name": {
        "type": "text",
        "analyzer":"ik_max_word",
        "search_analyzer":"ik_smart"
    }
    
  2. index
    通过index属性指定是否索引。默认为 index=true ,即要进行索引,只有进行索引才可以从索引库搜索到。
    但是也有一些内容不需要索引,比如:商品图片地址只被用来展示图片,不进行搜索图片,此时可以将 index设置为 false。

    删除索引,重新创建映射,将 pic 的 index 设置为 false,尝试根据 pic去搜索,结果搜索不到数据

    "pic": {
        "type": "text",  
        "index":false
    }
    
  3. store

    是否在source之外存储,每个文档索引后会在 ES中保存一份原始文档,存放在"source"中,一般情况下不需要设置 store为true,因为在source中已经有一份原始文档了。

创建新映射:Post http://localhost:9200/lierys/doc/_mapping

{
    "properties":{
        "name":{
            "type":"text",
            "analyzer":"ik_max_word",
            "search_analyzer":"ik_smart"
        },
        "description":{
            "type":"text",
            "analyzer":"ik_max_word",
            "search_analyzer":"ik_smart"
        },
        "pic":{
            "type":"text",
            "index":false
        },
        "studymodel":{
            "type":"text"
        }
    }
}

插入文档:http://localhost:9200/lierys/doc/4028e58161bcf7f40161bcf8b77c0000

{
    "name":"Bootstrap开发框架",
    "description":"Bootstrap是由Twitter推出的一个前台页面开发框架,在行业之中使用较为广泛。此开发框架包含了大量的CSS、JS程序代码,可以帮助开发者(尤其是不擅长页面开发的程序人员)轻松的实现一个不受浏览器限制的精美界面效果。",
    "studymodel":"201002",
    "pic":"group1/M00/00/01/wKhlQFqO4MmAOP53AAAcwDwm6SU490.jpg"
}

查询测试

Get http://localhost:9200/lierys/_search?q=name:开发
Get http://localhost:9200/lierys/_search?q=description:开发
Get http://localhost:9200/lierys/_search?q=pic:group1/M00/00/01/wKhlQFqO4MmAOP53AAAcwDwm6SU490.jpg
Get http://localhost:9200/lierys/_search?q=studymodel:201002

通过测试发现:name和description都支持全文检索,pic不可作为查询条件

5.2.2 keyword关键字

上边介绍的text文本字段在映射时要设置分词器,keyword字段为关键字字段,通常搜索keyword是按照整体搜索,所以创建keyword字段的索引时是不进行分词的,比如:邮政编码、手机号码、身份证等。

keyword字段通常用于过虑、排序、聚合等。

更改映射

{
    "properties": {  
        "studymodel":{
            "type":"keyword"
        },
        "name":{
            "type":"keyword"
        }
    }  
}

插入文档

{
    "name":"java编程基础",
    "description":"java语言是世界第一编程语言,在软件开发领域使用人数最多。",
    "pic":"group1/M00/00/01/wKhlQFqO4MmAOP53AAAcwDwm6SU490.jpg",
    "studymodel":"201001"
}

根据studymodel查询文档
搜索:http://localhost:9200/yh_course/_search?q=name:java
name是keyword类型,所以查询方式是精确查询

5.2.3 date日期类型

日期类型不用设置分词器。
通常日期类型的字段用于排序。

format:通过format设置日期格式

例子:
下边的设置允许date字段存储年月日时分秒、年月日及毫秒三种格式

{
    "properties":{
        "timestamp":{
            "type":"date",
            "format":"yyyy‐MM‐dd HH:mm:ss||yyyy‐MM‐dd"
        }
    }
}

插入文档:
Post :http://localhost:9200/lierys/doc/3

{
    "name": "spring开发基础",
    "description": "spring 在java领域非常流行,java程序员都在用。", 
    "studymodel": "201001",
    "pic":"group1/M00/00/01/wKhlQFqO4MmAOP53AAAcwDwm6SU490.jpg",  
    "timestamp":"2020‐07‐04 18:28:58"
}

5.2.4 数值类型

在这里插入图片描述

尽量选择范围小的类型,提高搜索效率
对于浮点数尽量用比例因子,比如一个价格字段,单位为元,我们将比例因子设置为100这在ES中会按 分 存
储,映射如下

"price": {
    "type": "scaled_float",  
    "scaling_factor": 100
}

由于比例因子为100,如果我们输入的价格是23.45则ES中会将23.45乘以100存储在ES中。如果输入的价格是23.456,ES会将23.456乘以100再取一个接近原始值的数,得出2346。

使用比例因子的好处是整型比浮点型更易压缩,节省磁盘空间。如果比例因子不适合,则从下表选择范围小的去用

在这里插入图片描述

六、索引管理

6.1 搭建过程

6.1.1 ES客户端

ES 提供多种不同的客户端:

  1. TransportClient
    ES提供的传统客户端,官方计划8.0版本删除此客户端。

  2. RestClient
    RestClient是官方推荐使用的,它包括两种:Java Low Level REST Client和 Java High Level REST Client。
    ES在6.0之后提供 Java High Level REST Client, 两种客户端官方更推荐使用 Java High Level REST Client,不过当前它还处于完善中,有些功能还没有。

本教程准备采用 Java High Level REST Client,如果它有不支持的功能,则使用Java Low Level REST Client。

添加依赖:

<dependency>
    <groupId>org.elasticsearch.client</groupId>
    <artifactId>elasticsearch‐rest‐high‐level‐client</artifactId>
    <version>6.2.1</version>
</dependency>
<dependency>
    <groupId>org.elasticsearch</groupId>
    <artifactId>elasticsearch</artifactId>
    <version>6.2.1</version>
</dependency>

6.1.2 创建搜索工程

创建搜索工程(maven工程):yh-service-search,添加RestHighLevelClient依赖及junit依赖。

  • pom.xml
<parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.5.5</version>
    </parent>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.elasticsearch.client</groupId>
            <artifactId>elasticsearch-rest-high-level-client</artifactId>
            <version>6.2.1</version>
            <exclusions>
                <exclusion>
                    <groupId>org.elasticsearch</groupId>
                    <artifactId>elasticsearch</artifactId>
                </exclusion>
                <exclusion>
                    <groupId>org.elasticsearch.client</groupId>
                    <artifactId>elasticsearch-rest-client</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
        <dependency>
            <groupId>org.elasticsearch.client</groupId>
            <artifactId>elasticsearch-rest-client</artifactId>
            <version>6.2.1</version>
        </dependency>
        <dependency>
            <groupId>org.elasticsearch</groupId>
            <artifactId>elasticsearch</artifactId>
            <version>6.2.1</version>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
        </dependency>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>
  • 配置文件 application.yml
server:
  port: 8080
es:
  hostlist: ${eshostlist:127.0.0.1:9200} #多个地址的时候使用逗号隔开
  • 配置类ESConfig.java
import org.apache.http.HttpHost;
import org.elasticsearch.client.RestClient;
import org.elasticsearch.client.RestHighLevelClient;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class ESConfig {
    @Value("${es.hostlist}")
    private String hostlist;

    @Bean
    // Java High Level Rest Client
    public RestHighLevelClient restHighLevelClient() {
        //解析hostlist配置信息
        String[] split = hostlist.split(",");
        //创建HttpHost数组,其中存放es主机和端口的配置信息
        HttpHost[] httpHostArray = new HttpHost[split.length];
        for (int i = 0; i < split.length; i++) {
            String item = split[i];
            httpHostArray[i] =
                    new HttpHost(
                            item.split(":")[0],
                            Integer.parseInt(item.split(":")[1]), "http");
        }
        //创建RestHighLevelClient客户端
        return new RestHighLevelClient(RestClient.builder(httpHostArray));
    }
}

  • 启动类
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class Application {
    public static void main(String[] args) {
        SpringApplication.run(Application.class,args);
    }
}

6.2 常见索引库

6.2.1 API

创建索引:
put http://localhost:9200/索引名称

{
    "settings":{
        "index":{
            "number_of_shards":1,#分片的数量
            "number_of_replicas":0#副本数量
        }   
    }
}

创建映射:
发送:put http://localhost:9200/索引库名称/类型名称/_mapping

创建类型为 lierys02的映射,共包括三个字段:name、description、studymodel
http://localhost:9200/lierys02/doc/mapping

{
    "properties":{
        "name":{
            "type":"text"
        },
        "description":{
            "type":"text"
        },
        "studymodel":{
            "type":"keyword"
        }
    }
}
6.2.2 Java Client
@Test
    //创建索引库
    public void testCreateIndex() throws IOException {
        //创建索引请求对象,并设置索引名称
        CreateIndexRequest createIndexRequest = new CreateIndexRequest("lierys02");
        //设置索引参数
        createIndexRequest.settings(Settings.builder().put("number_of_shards", 1)
                .put("number_of_replicas", 0));
        //设置映射
        createIndexRequest.mapping("doc", "{\n" +
                "    \"properties\":{\n" +
                "        \"name\":{\n" +
                "            \"type\":\"text\"\n" +
                "        },\n" +
                "        \"description\":{\n" +
                "            \"type\":\"text\"\n" +
                "        },\n" +
                "        \"studymodel\":{\n" +
                "            \"type\":\"keyword\"\n" +
                "        }\n" +
                "    }\n" +
                "}", XContentType.JSON);
        //创建索引操作客户端
        IndicesClient indices = client.indices();
        //创建相应对象
        CreateIndexResponse createIndexResponse = indices.create(createIndexRequest);
        //得到响应对象
        boolean acknowledged = createIndexResponse.isAcknowledged();
        //删除结果
        System.out.println("创建索引是否成功:" + acknowledged);
    }

    @Test
    //删除索引库
    public void testDeleteIndex() throws IOException {
        //删除索引请求对象
        DeleteIndexRequest deleteIndexRequest = new DeleteIndexRequest("lierys02");
        DeleteIndexResponse deleteIndexResponse = client.indices().delete(deleteIndexRequest);
        boolean acknowledged = deleteIndexResponse.isAcknowledged();
        System.out.println("删除索引库是否成功:" + acknowledged);
    }

6.3 添加文档

6.3.1 API

格式如下: PUT /{index}/{type}/{id} { "field": "value", ... }

如果不指定id,ES会自动生成。

一个例子:
put http://localhost:9200/lierys02/doc

{
    "name":"Bootstrap开发框架",
    "description":"Bootstrap是由Twitter推出的一个前台页面开发框架,在行业之中使用较为广泛。此开发框架包含了大量的CSS、JS程序代码,可以帮助开发者(尤其是不擅长页面开发的程序人员)轻松的实现一个不受浏览器限制的精美界面效果。",
    "studymodel":"201001"
}

6.3.2 Java Client

@Test
    //添加文档
    public void testAddDoc() throws IOException {
        //准备json数据
        HashMap<String, Object> jsonMap = new HashMap<>();
        jsonMap.put("name", "SpringBoot开发框架");
        jsonMap.put("description", "Bootstrap是由Twitter推出的一个前台页面开发框架");
        jsonMap.put("studymodel", "201002");
        //索引请求对象、指定索引库和文档类型
        IndexRequest indexRequest = new IndexRequest("lierys01", "doc");
        //指定索引文档内容
        indexRequest.source(jsonMap);
        //索引响应对象
        IndexResponse indexResponse = client.index(indexRequest);
        //获取响应结果
        System.out.println("添加文档是否成功:" + indexResponse);
    }

6.4 查询文档

6.4.1 API

格式如下: GET /{index}/{type}/{id}

6.4.2 Java Client
@Test
    //查询文档
    // GET /{index}/{type}/{id}
    public void getDoc() throws IOException {
        //获取请求对象,指定索引库、文档类型、id
        GetRequest getRequest = new GetRequest("lierys01", "doc", "tkb7rHwBJD2VnIX1mQHR");
        //响应对象
        GetResponse getResponse = client.get(getRequest);
        //响应对象是否存在
        boolean exists = getResponse.isExists();
        //获取响应对象是否存在的结果
        System.out.println(exists);
        //响应结果集
        Map<String, Object> sourceAsMap = getResponse.getSourceAsMap();
        //获取查询结果集
        System.out.println("查询文档的map集:" + sourceAsMap);
    }

6.5 更新文档

6.5.1 Api

ES更新文档的顺序是:先检索到文档、将原来的文档标记为删除、创建新文档、删除旧文档,创建新文档就会重建索引。

通过请求Url有两种方法:

  1. 完全替换
    Post:http://localhost:9200/lierys02/doc/3
{
    "name":"Bootstrap开发框架",
    "description":"Bootstrap是由Twitter推出的一个前台页面开发框架,在行业之中使用较为广泛。此开发框架包含了大量的CSS、JS程序代码,可以帮助开发者(尤其是不擅长页面开发的程序人员)轻松的实现一个不受浏览器限制的精美界面效果。",
    "studymodel":"201002"
}
  1. 局部更新
    下边的例子是只更新price字段。
    post: http://localhost:9200/lierys02/doc/3/
{
    "studymodel":"201002"
}

6.5.2 Java Client

使用 Client Api更新文档的方法同上边第二种局部更新方法。
可以指定文档的部分字段也可以指定完整的文档内容

@Test
    //更新文档:将原数据覆盖(完全替换和局部更新)
    public void updateDoc() throws IOException {
        //更新请求对象、指定索引库、文档类型和id
        UpdateRequest updateRequest = new UpdateRequest("lierys01", "doc", "tkb7rHwBJD2VnIX1mQHR");
        //准备需要更新的json数据
        HashMap<String, Object> jsonMap = new HashMap<>();
        jsonMap.put("studymodel", "201003");
        //更新数据
        updateRequest.doc(jsonMap);
        //更新响应对象
        UpdateResponse updateResponse = client.update(updateRequest);
        RestStatus status = updateResponse.status();
        //获取响应结果
        System.out.println("更新文档是否成功:" + status);
    }

6.6 删除文档

6.6.1 Api

根据id删除,格式如下:

DELETE /{index}/{type}/{id}

搜索匹配删除,将搜索出来的记录删除,格式如下:

POST /{index}/{type}/_delete_by_query

下边是搜索条件例子:

{
    "query":{
        "term":{
            "studymodel":"201001"
        }
    }
}

上边例子的搜索匹配删除会将studymodel为201001的记录全部删除。

6.6.2 Java Client

@Test
    //删除文档
    public void testDeleteDoc() throws IOException {
        //删除文档id
        String id = "t0YIrXwBJD2VnIX1KwGt";
        //删除索引请求对象
        DeleteRequest deleteRequest = new DeleteRequest("lierys01", "doc", id);
        //删除索引响应对象
        DeleteResponse deleteResponse = client.delete(deleteRequest);
        //响应结果
        RestStatus restStatus = deleteResponse.status();
        //获取响应结果
        System.out.println("删除文档是否成功:" + restStatus);
    }

搜索匹配删除还没有具体的api,可以采用先搜索出文档id,根据文档id删除

七、搜索管理

7.1 准备环境

7.1.1 创建映射

创建lierys索引库。
创建如下映射
post:http://localhost:9200/lierys/doc/_mapping

{
    "properties":{
        "name":{
            "type":"text"
        },
        "description":{
            "type":"text"
        },
        "studymodel":{
            "type":"keyword"
        }
    }
}

7.1.2 插入原始数据

{
    "name":"Bootstrap开发框架",
    "description":"Bootstrap是由Twitter推出的一个前台页面开发框架,在行业之中使用较为广泛。此开发框架包含了大量的CSS、JS程序代码,可以帮助开发者(尤其是不擅长页面开发的程序人员)轻松的实现一个不受浏览器限制的精美界面效果。",
    "studymodel":"201001"
}

7.2 简单搜索

简单搜索就是通过url进行查询,以get方式请求ES。
格式:

get ../_search?q=.....          
	http://localhost:9200/lierys/doc/_search?q=name:开发

q:搜索字符串

例子:
http://localhost:9200/lierys/doc/_search?q=name:spring 搜索name中包括spring的文档。

7.3 DSL搜索

DSL (Domain Specific Language) 是 ES 提出的基于 json 的搜索方式,在搜索时传入特定的 json 格式的数据来完成不 同的搜索需求。

DSL 比 URI 搜索方式功能强大,在项目中建议使用DSL方式来完成搜索。

7.3.1 查询所有文档 match_all

查询所有索引库的文档。
发送:post http://localhost:9200/_search
查询指定索引库指定类型下的文档。(通过使用此方法)
发送:post http://localhost:9200/lierys/doc/_search

{
    "query":{
        "match_all":{}
    },
    "_source":["name","studymodel"]
}

_source:source源过虑设置,指定结果中所包括的字段有哪些。

结果说明:

took:本次操作花费的时间,单位为毫秒。
timed_out:请求是否超时
_shards:说明本次操作共搜索了哪些分片
hits:搜索命中的记录
hits.total : 符合条件的文档总数 hits.hits :匹配度较高的前N个文档
hits.max_score:文档匹配得分,这里为最高分
_score:每个文档都有一个匹配度得分,按照降序排列。
_source:显示了文档的原始内容。

Java Client

@SpringBootTest
@RunWith(SpringRunner.class)
public class TestSearch {
    @Autowired
    RestHighLevelClient client;
    @Autowired
    RestClient restClient;
    //搜索type下的全部记录  
    @Test
    public void testSearchAll() throws IOException {
        SearchRequest searchRequest = new SearchRequest("lierys");
        searchRequest.types("doc");
        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder(); 
        searchSourceBuilder.query(QueryBuilders.matchAllQuery());
        //source源字段过虑
        searchSourceBuilder.fetchSource(new String[]{"name","studymodel"}, new String[]{}); 
        searchRequest.source(searchSourceBuilder);
        SearchResponse searchResponse = client.search(searchRequest);
        SearchHits hits = searchResponse.getHits();
        SearchHit[] searchHits = hits.getHits();
        for (SearchHit hit : searchHits) {
            String index = hit.getIndex();
            String type = hit.getType();
            String id = hit.getId();
            float score = hit.getScore();
            String sourceAsString = hit.getSourceAsString();
            Map<String, Object> sourceAsMap = hit.getSourceAsMap();
            String name = (String) sourceAsMap.get("name");
            String studymodel = (String) sourceAsMap.get("studymodel");
            String description = (String) sourceAsMap.get("description");
            System.out.println(name);
            System.out.println(studymodel);
            System.out.println(description);
        }
    }
}

7.3.2 分页查询 from...size

ES 支持分页查询,传入两个参数:from 和 size。
form:表示起始文档的下标,从0开始。
size:查询的文档数量。
发送:post http://localhost:9200/lierys/doc/

{ 
    "from" : 0, "size" : 1, 
    "query": { 
        "match_all": {} 
    }, 
    "_source" : ["name","studymodel"] 
}

Java Client

SearchRequest searchRequest = new SearchRequest("lierys"); 
searchRequest.types("doc"); 
SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder(); 
searchSourceBuilder.query(QueryBuilders.matchAllQuery()); 
//分页查询,设置起始下标,从0开始 
searchSourceBuilder.from(0); 
//每页显示个数 
searchSourceBuilder.size(10); 
//source源字段过虑 
searchSourceBuilder.fetchSource(new String[]{"name","studymodel"}, new String[]{}); 
searchRequest.source(searchSourceBuilder); 
SearchResponse searchResponse = client.search(searchRequest); 

7.3.3 Term Query term

Term Query 为精确查询,在搜索时会整体匹配关键字,不再将关键字分词。
发送:post http://localhost:9200/lierys/doc/_search

注意:使用小写进行搜索

{
    "query":{
        "term":{
            "name":"bootstrap"
        }
    },
    "_source":["name","studymodel"]
}

上边的搜索会查询name包括“spring”这个词的文档
Java Client

SearchRequest searchRequest = new SearchRequest("lierys"); 
searchRequest.types("doc"); 
SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder(); 
searchSourceBuilder.query(QueryBuilders.termQuery("name","spring")); 
//source源字段过虑 
searchSourceBuilder.fetchSource(new String[]{"name","studymodel"}, new String[]{}); 
searchRequest.source(searchSourceBuilder); 
SearchResponse searchResponse = client.search(searchRequest); 

7.3.4 根据id精确匹配 ids

ES提供根据多个id值匹配的方法:
测试

post: http://127.0.0.1:9200/lierys/doc/_search

{
    "query":{
        "ids":{
            "type":"doc",
            "values":["3","1","2"]
        }
    }
}
String[] split = new String[]{"1","2"}; 
List<String> idList = Arrays.asList(split); 
searchSourceBuilder.query(QueryBuilders.termsQuery("_id", idList)); 

7.3.5 match Query match

  • 基本使用
    match Query即全文检索,它的搜索方式是先将搜索字符串分词,再使用各各词条从索引中搜索。
    match query与Term query区别是match query在搜索前先将搜索关键字分词,再拿各各词语去索引中搜索。
    发送:post http://localhost:9200/yh_course/doc/_search
   {
       "query":{
           "match":{
               "description":{
                   "query":"spring开发",
                   "operator":"or"
               }
           }
       }
   }

query:搜索的关键字,对于英文关键字如果有多个单词则中间要用半角逗号分隔,而对于中文关键字中间可以用逗号分隔也可以不用。

operator:or 表示 只要有一个词在文档中出现则就符合条件,and表示每个词都在文档中出现则才符合条件。
上边的搜索的执行过程是:

  1. 将“spring开发”分词,分为spring、开发两个词
  2. 再使用spring和开发两个词去匹配索引中搜索。
  3. 由于设置了operator为or,只要有一个词匹配成功则就返回该文档

Java Client

//根据关键字搜索
   @Test
   public void testMatchQuery() throws IOException {
       SearchRequest searchRequest = new SearchRequest("lierys");
       searchRequest.types("doc");
       SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
       //source源字段过虑
       searchSourceBuilder.fetchSource(new String[]{"name","studymodel"}, new String[]{}); 
       //匹配关键字
       searchSourceBuilder.query(
           QueryBuilders.matchQuery("description", "spring开发").operator(Operator.OR));
       searchRequest.source(searchSourceBuilder);                                          	SearchResponse searchResponse = client.search(searchRequest);
       SearchHits hits = searchResponse.getHits();
       SearchHit[] searchHits = hits.getHits();
       
       for (SearchHit hit : searchHits) {
       	String index = hit.getIndex();
           String type = hit.getType();
           String id = hit.getId();
           float score = hit.getScore();
           String sourceAsString = hit.getSourceAsString();
           Map<String, Object> sourceAsMap = hit.getSourceAsMap();
           String name = (String) sourceAsMap.get("name");
           String studymodel = (String) sourceAsMap.get("studymodel");
           String description = (String) sourceAsMap.get("description");
           System.out.println(name);
           System.out.println(studymodel);
           System.out.println(description);
   	}
   }
  • minimum_should_match

    上边使用的operator = or表示只要有一个词匹配上就得分,如果实现三个词至少有两个词匹配如何实现?
    使用minimum_should_match可以指定文档匹配词的占比:(设置匹配度)

    比如搜索语句如下:

   {
       "query":{
           "match":{
               "description":{
                   "query":"spring开发框架",
                   "minimum_should_match":"80%"
               }
           }
       }
   }

“spring开发框架”会被分为三个词:spring、开发、框架

设置"minimum_should_match": "80%"表示,三个词在文档的匹配占比为80%,即3*0.8=2.4,向上取整得2,表示至少有两个词在文档中要匹配成功。

对应的 RestClient 如下:

   //匹配关键字
   MatchQueryBuilder matchQueryBuilder = QueryBuilders.matchQuery("description", "前台页面开发框架架构").minimumShouldMatch("80%");//设置匹配占比
   
   searchSourceBuilder.query(matchQueryBuilder);

7.3.6 multi Query multi_match

上边学习的termQuery和matchQuery一次只能匹配一个Field,本节学习multiQuery,一次可以匹配多个字段。

  • 基本使用
    单项匹配是在一个field中去匹配,多项匹配是拿关键字去多个Field中匹配。

    例子
    发送:post http://localhost:9200/lierys/doc/_search

    拿关键字 “spring css”去匹配name 和description字段

   {
       "query":{
           "multi_match":{
               "query":"spring css",
               "minimum_should_match":"50%",
               "fields":["name","description"]
           }
       }
   }
  • 提升boost

    匹配多个字段时可以提升字段的boost(权重)来提高得分

    例子
    提升boost之前,执行下边的查询

   { 
       "query": { 
           "multi_match" : { 
               "query" : "spring框架", 
               "minimum_should_match": "50%", 
               "fields": [ "name", "description" ] 
           } 
       } 
   }

通过查询发现Bootstrap排在前边。

提升boost,通常关键字匹配上name的权重要比匹配上description的权重高,这里可以对name的权重提升

   { 
       "query": { 
           "multi_match" : { 
               "query" : "spring框架", 
               "minimum_should_match": "50%", 
               "fields": [ "name^10", "description" ] 
           } 
       } 
   }

“name^10” 表示权重提升10倍,执行上边的查询,发现name中包括spring关键字的文档排在前边

Java Client

MultiMatchQueryBuilder multiMatchQueryBuilder = QueryBuilders.multiMatchQuery("spring框架", "name", "description") .minimumShouldMatch("50%"); 

multiMatchQueryBuilder.field("name",10);//提升boost

7.3.7 布尔查询 bool

布尔查询对应于Lucene的BooleanQuery查询,实现将多个查询组合起来。
三个参数:

  • must:文档必须匹配must所包括的查询条件,相当于 “AND”
  • should:文档应该匹配should所包括的查询条件其中的一个或多个,相当于 “OR”
  • must_not:文档不能匹配must_not所包括的该查询条件,相当于“NOT”

分别使用must、should、must_not测试下边的查询:
发送:POST http://localhost:9200/lierys/doc/

{ 
    "_source" : [ "name", "studymodel", "description"], 
    "from" : 0, "size" : 1, 
    "query": { 
        "bool" : { 
            "must":[ 
                { 
                    "multi_match" : { 
                        "query" : "spring框架",
                        "minimum_should_match": "50%", 
                        "fields": [ "name^10", "description" ] 
                    } 
                }, 
                { 
                    "term":{ 
                        "studymodel" : "201001" 
                    } 
                } 
            ] 
        } 
    } 
}
  • must:表示必须,多个查询条件必须都满足。(通常使用must)(必须满足全部条件)
  • should:表示或者,多个查询条件只要有一个满足即可。(满足一个条件即可)
  • must_not:表示非

Java Client

//BoolQuery,将搜索关键字分词,拿分词去索引库搜索 
@Test 
public void testBoolQuery() throws IOException { 
    //创建搜索请求对象 
    SearchRequest searchRequest= new SearchRequest("lierys"); 
    searchRequest.types("doc"); 
    //创建搜索源配置对象 
    SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder(); 
    searchSourceBuilder.fetchSource(new String[]{"name","pic","studymodel"},new String[]{}); 
    //multiQuery 
    String keyword = "spring开发框架"; 
    MultiMatchQueryBuilder multiMatchQueryBuilder = QueryBuilders.multiMatchQuery(
        "spring框架", "name", "description") .minimumShouldMatch("50%"); 
    
    multiMatchQueryBuilder.field("name",10); 
    //TermQuery 
    TermQueryBuilder termQueryBuilder = QueryBuilders.termQuery("studymodel", "201001"); 
    //布尔查询 
    BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery(); 
    boolQueryBuilder.must(multiMatchQueryBuilder); 
    boolQueryBuilder.must(termQueryBuilder); 
    //设置布尔查询对象 
    searchSourceBuilder.query(boolQueryBuilder); 
    searchRequest.source(searchSourceBuilder);//设置搜索源配置 
    SearchResponse searchResponse = client.search(searchRequest); 
    SearchHits hits = searchResponse.getHits(); 
    SearchHit[] searchHits = hits.getHits(); 
    for(SearchHit hit:searchHits){ 
        Map<String, Object> sourceAsMap = hit.getSourceAsMap(); 
        System.out.println(sourceAsMap); 
    } 
}

7.3.8 过滤器 filter

过虑是针对搜索的结果进行过虑,过虑器主要判断的是文档是否匹配,不去计算和判断文档的匹配度得分,所以过虑器性能比查询要高,且方便缓存,推荐尽量使用过虑器去实现查询或者过虑器和查询共同使用。

过虑器在布尔查询中使用,下边是在搜索结果的基础上进行过虑
发送:POST http://localhost:9200/lierys/doc/

{
    "_source":["name","studymodel","description","price"],
    "query":{
        "bool":{
            "must":[
                {
                    "multi_match":{
                        "query":"spring 框架",
                        "minimum_should_match":"50%",
                        "fields":["description"]
                    }
                }
            ],
            "filter":[
                {"term":{"studymodel":"201001"}},
                {"range":{"price":{"gte":30,"lte":100}}}
            ]
        }
    }
}

range范围过虑,保留大于等于60 并且小于等于100的记录
term项匹配过虑,保留studymodel等于"201001"的记录。

注意:range和term一次只能对一个Field设置范围过虑

Java Client

//布尔查询使用过虑器 
@Test 
public void testFilter() throws IOException { 
    SearchRequest searchRequest = new SearchRequest("lierys"); 
    searchRequest.types("doc"); 
    SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder(); 
    //source源字段过虑 
    searchSourceBuilder.fetchSource(new String[]{"name","studymodel","price","description"}, 
                                    new String[]{}); 
    searchRequest.source(searchSourceBuilder); 
    //匹配关键字 
    MultiMatchQueryBuilder multiMatchQueryBuilder = QueryBuilders.multiMatchQuery(
        "spring框架", "name", "description"); 

    //设置匹配占比 
    multiMatchQueryBuilder.minimumShouldMatch("50%"); 
    
    //提升另个字段的Boost值 
    multiMatchQueryBuilder.field("name",10); 
    searchSourceBuilder.query(multiMatchQueryBuilder); 
    
    //布尔查询 
    BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery(); 
    boolQueryBuilder.must(searchSourceBuilder.query()); 
    
    //过虑 
    boolQueryBuilder.filter(QueryBuilders.termQuery("studymodel", "201001")); 
    boolQueryBuilder.filter(QueryBuilders.rangeQuery("price").gte(60).lte(100)); 
    SearchResponse searchResponse = client.search(searchRequest); 
    SearchHits hits = searchResponse.getHits(); 
    SearchHit[] searchHits = hits.getHits(); 
    
    for (SearchHit hit : searchHits) { 
    	String index = hit.getIndex(); 
        String type = hit.getType(); 
        String id = hit.getId(); 
        float score = hit.getScore(); 
        String sourceAsString = hit.getSourceAsString(); 
        Map<String, Object> sourceAsMap = hit.getSourceAsMap(); 
        String name = (String) sourceAsMap.get("name"); 
        String studymodel = (String) sourceAsMap.get("studymodel"); 
        String description = (String) sourceAsMap.get("description"); 
        System.out.println(name); 
        System.out.println(studymodel); 
        System.out.println(description); 
	}
}

7.3.9 排序 sort

可以在字段上添加一个或多个排序,支持在keyword、date、float等类型上添加,text类型的字段上不允许添加排序。

发送POST http://localhost:9200/lierys/doc/_search
过虑0–10元价格范围的文档,并且对结果进行排序,先按studymodel降序,再按价格升序

{
    "_source":["name","studymodel","description","price"],
    "sort":[
        {
            "timestamp":"desc"
        }
    ]
}

java client

@Test 
public void testSort() throws IOException { 
    SearchRequest searchRequest = new SearchRequest("lierys"); 
    searchRequest.types("doc"); 
    SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder(); 
    //source源字段过虑 
    searchSourceBuilder.fetchSource(new String[]{"name","studymodel","price","description"}, 
                                    new String[]{}); 
    searchRequest.source(searchSourceBuilder); 
    //布尔查询 
    BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery(); 
    //过虑 
    boolQueryBuilder.filter(QueryBuilders.rangeQuery("price").gte(0).lte(100));  //排序 
    searchSourceBuilder.sort(new FieldSortBuilder("studymodel").order(SortOrder.DESC)); 
    searchSourceBuilder.sort(new FieldSortBuilder("price").order(SortOrder.ASC)); 
    SearchResponse searchResponse = client.search(searchRequest); 
    SearchHits hits = searchResponse.getHits(); 
    SearchHit[] searchHits = hits.getHits(); 
    for (SearchHit hit : searchHits) { 
        String index = hit.getIndex(); 
        String type = hit.getType(); 
        String id = hit.getId(); 
        float score = hit.getScore(); 
        String sourceAsString = hit.getSourceAsString(); 
        Map<String, Object> sourceAsMap = hit.getSourceAsMap(); 
        String name = (String) sourceAsMap.get("name"); 
        String studymodel = (String) sourceAsMap.get("studymodel"); 
        String description = (String) sourceAsMap.get("description"); 
        System.out.println(name); 
        System.out.println(studymodel); 
        System.out.println(description); 
    } 
}

7.3.10 高亮显示 highlight

高亮显示可以将搜索结果一个或多个字突出显示,以便向用户展示匹配关键字的位置。
在搜索语句中添加highlight即可实现,如下:
Post: http://127.0.0.1:9200/lierys/doc/_search

{ 
    "_source" : [ "name", "studymodel", "description","price"], 
    "query": { 
        "bool" : { 
            "must":[ 
                { 
                    "multi_match" : { 
                        "query" : "开发框架", 
                        "minimum_should_match": "50%", 
                        "fields": [ "name^10", "description" ], 
                        "type":"best_fields" 
                    } 
                } 
            ], 
            "filter": [ 
                { "range": { "price": { "gte": 0 ,"lte" : 100}}} 
            ] 
        } 
    }, 
    "sort" : [ 
        { 
            "price" : "asc" 
        } 
    ], 
    "highlight": { 
        "pre_tags": ["<tag1>"], 
        "post_tags": ["</tag2>"], 
        "fields": { 
            "name": {}, 
            "description":{} 
        } 
    } 
}

Java Client

@Test
public void testHighlight() throws IOException {
    SearchRequest searchRequest = new SearchRequest("lierys");
    searchRequest.types("doc");
    SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
    //source源字段过虑
    searchSourceBuilder.fetchSource(new String[]{"name","studymodel","price","description"}, 
                                    new String[]{});
    searchRequest.source(searchSourceBuilder);
    //匹配关键字
    MultiMatchQueryBuilder multiMatchQueryBuilder = QueryBuilders.multiMatchQuery("开发", 
                                                                                  "name", "description");
    searchSourceBuilder.query(multiMatchQueryBuilder);
    //布尔查询
    BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
    boolQueryBuilder.must(searchSourceBuilder.query());
    //过虑
    boolQueryBuilder.filter(QueryBuilders.rangeQuery("price").gte(0).lte(100));
    //排序
    searchSourceBuilder.sort(new FieldSortBuilder("studymodel").order(SortOrder.DESC));
    searchSourceBuilder.sort(new FieldSortBuilder("price").order(SortOrder.ASC));
    //高亮设置
    HighlightBuilder highlightBuilder = new HighlightBuilder();
    highlightBuilder.preTags("<tag>");//设置前缀
    highlightBuilder.postTags("</tag>");//设置后缀
    // 设置高亮字段
    highlightBuilder.fields().add(new HighlightBuilder.Field("name"));
    //     highlightBuilder.fields().add(new HighlightBuilder.Field("description"));
    searchSourceBuilder.highlighter(highlightBuilder);
    SearchResponse searchResponse = client.search(searchRequest);
    SearchHits hits = searchResponse.getHits();
    SearchHit[] searchHits = hits.getHits();
    for (SearchHit hit : searchHits) {
        Map<String, Object> sourceAsMap = hit.getSourceAsMap();
        //名称
        String name = (String) sourceAsMap.get("name");
        //取出高亮字段内容
        Map<String, HighlightField> highlightFields = hit.getHighlightFields();
        if(highlightFields!=null){
            HighlightField nameField = highlightFields.get("name");
            if(nameField!=null){
                Text[] fragments = nameField.getFragments();
                StringBuffer stringBuffer = new StringBuffer();
                for (Text str : fragments) {
                    stringBuffer.append(str.string());
                }
                name = stringBuffer.toString();
            }
        }
        String index = hit.getIndex();
        String type = hit.getType();
        String id = hit.getId();
        float score = hit.getScore();
        String sourceAsString = hit.getSourceAsString();
        String studymodel = (String) sourceAsMap.get("studymodel");
        String description = (String) sourceAsMap.get("description");
        System.out.println(name);
        System.out.println(studymodel);
        System.out.println(description);
    }
}

八、集群管理

8.1 集群结构

ES通常以集群方式工作,这样做不仅能够提高 ES的搜索能力还可以处理大数据搜索的能力,同时也增加了系统的
容错能力及高可用,ES可以实现PB级数据的搜索。

下图是ES集群结构的示意图:
在这里插入图片描述

从上图总结以下概念:

  • 结点
    ES集群由多个服务器组成,每个服务器即为一个Node结点(该服务只部署了一个ES进程)。
  • 分片
    当我们的文档量很大时,由于内存和硬盘的限制,同时也为了提高ES的处理能力、容错能力及高可用能力,我们将索引分成若干分片,每个分片可以放在不同的服务器,这样就实现了多个服务器共同对外提供索引及搜索服务。
    一个搜索请求过来,会分别从各各分片去查询,最后将查询到的数据合并返回给用户。
  • 副本
    为了提高ES的高可用同时也为了提高搜索的吞吐量,我们将分片复制一份或多份存储在其它的服务器,这样即使当前的服务器挂掉了,拥有副本的服务器照常可以提供服务。
  • 主结点
    一个集群中会有一个或多个主结点,主结点的作用是集群管理,比如增加节点,移除节点等,主结点挂掉后ES会重新选一个主结点。
  • 结点转发
    每个结点都知道其它结点的信息,我们可以对任意一个结点发起请求,接收请求的结点会转发给其它结点查询数据

8.2 搭建集群

下边的例子实现创建一个2结点的集群,并且索引的分片我们设置2片,每片一个副本。

将ES解压两份,配置elasticsearch.yml文件

cluster.name: xihu
node.name: liery1
network.host: 0.0.0.0
http.port: 9200
transport.tcp.port: 9300
node.master: true
node.data: true
discovery.zen.ping.unicast.hosts: ["0.0.0.0:9300", "0.0.0.0:9301"]
discovery.zen.minimum_master_nodes: 1
bootstrap.memory_lock: false
node.max_local_storage_nodes: 2
path.data: E:\ElasticSearchSofts\elasticsearch1\data
path.logs: E:\ElasticSearchSofts\elasticsearch1\logs
http.cors.enabled: true
http.cors.allow-origin: /.*/
cluster.name: xihu
node.name: liery2
network.host: 0.0.0.0
http.port: 9201
transport.tcp.port: 9301
node.master: true
node.data: true
discovery.zen.ping.unicast.hosts: ["0.0.0.0:9300", "0.0.0.0:9301"]
discovery.zen.minimum_master_nodes: 1
bootstrap.memory_lock: false
node.max_local_storage_nodes: 2
path.data: E:\ElasticSearchSofts\elasticsearch2\data
path.logs: E:\ElasticSearchSofts\elasticsearch2\logs
http.cors.enabled: true
http.cors.allow-origin: /.*/

注意:

  • cluster.name集群名一致
  • node.name节点名不能相同
  • http.port 对外通信http端口号
  • transport.tcp.port 集群结点之间通信端口
  • discovery.zen.ping.unicast.hosts所有集群结点之间通信端口
  • node.max_local_storage_nodes 最大节点个数
  • 索引库、映射、文档等操作都和单个节点的操作系统。

每日一点点进步
不进则退

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

璃尔 °

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值