一、ElasticSearch 介绍

 1.1 介绍

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

  我们建立一个网站或应用程序,并要添加搜索功能,但是想要完成搜索工作的创建是非常困难的。我们希望搜索解决方案要运行速度快,我们希望能有一个零配器和一个完全免费的搜索模式,我们希望能够简单地使用JSON通过HTTP来索引数据,我们希望我们的搜索服务器始终可用,我们希望能够从一台开始并扩展到数百台,我们要实时搜索,我们要简单的多租户,我们希望建立一个云的解决方案。因此我们利用Elasticsearch来解决所有这些问题及可能出现的更多其它问题。

官方网址:https://www.elastic.co/cn/products/elasticsearch
Github :https://github.com/elastic/elasticsearch

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

突出优点:

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

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

1.2 原理与应用

1.2.1索引结构

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

逻辑结构部分是一个倒排索引表:
1、将要搜索的文档内容分词,所有不重复的词组成分词列表
2、将搜索的文档最终以Document方式存储起来(形成文档列表)。
3、每个词和docment都有关联

如下:

 现在,如果我们想搜索 quick brown ,我们只需要查找包含每个词条的文档:

两个文档都匹配,但是第一个文档比第二个匹配度更高。如果我们使用仅计算匹配词条数量的简单 相似性算法 ,那么,我们可以说,对于我们查询的相关性来讲,第一个文档比第二个文档更佳。
1.2.3 RESTful应用方法

如何使用es?

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

下图是es在项目中的应用方式:

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

二、ElasticaSearch 安装

2.0、安装es7

下载地址:https://www.elastic.co/cn/downloads/elasticsearch

下载后如下所示:

 解压到当前文件夹,目录结构如下:

es的配置修改我们主要修改两个文件 jvm.options、elasticsearch.yml这两个文件

jvm中内存分配大小根据自己电脑配置来分配 一般16G以上的电脑可以采用默认配置或者更大的内存,小于16G的可以说适当调小内存大小,为了防止频繁的GC一般设置Xms、Xmx为同样大小。

由于我的内存为16G,可以不用修改。

一般情况下我们不修改elasticsearch.yml中的配置文件
elasticsearch.yml中可以修改es的集群名称,节点名称,数据存储和日志文件的路径,内存大小,访问ip限制和restFul api端口号等配置

cluster.name: elasticsearch
node.name: node-1
path.data: D:\install\ElasticSearch\es7\elasticsearch-7.8.1\data
path.logs: D:\install\ElasticSearch\es7\elasticsearch-7.8.1\logs
network.host: 0.0.0.0
http.port: 9200
cluster.initial_master_nodes: ["node-1"]
http.cors.enabled: true
http.cors.allow-origin: /.*/

双击elasticsearch.bat启动,启动是报错:

原因分析:首先我们要知道es7版本之后的安装包解压完会携带jdk11版本。但是执行elasticsearch.bat的时候会首先加载我们环境变量中的jdk,大部分人的jdk都是1.8的,所以执行elasticsearch.bat我们会见到这样的报错

解决办法:

找到elasticsearch.bat这个文件,发现调用了elasticsearch-env.bat,代码如下

CALL "%~dp0elasticsearch-env.bat" || exit /b 1

(1)、对于es7.8.1修改如下:

打开elasticsearch-env.bat 这个文件,将如下代码注释掉

if "%JAVA_HOME%" == "" (
  set JAVA="%ES_HOME%\jdk\bin\java.exe"
  set JAVA_HOME="%ES_HOME%\jdk"
  set JAVA_TYPE=bundled jdk
) else (
  set JAVA="%JAVA_HOME%\bin\java.exe"
  set JAVA_TYPE=JAVA_HOME
)

添加如下代码

set JAVA="%ES_HOME%\jdk\bin\java.exe"
set JAVA_HOME="%ES_HOME%\jdk"
set JAVA_TYPE=bundled jdk

即:

rem if "%JAVA_HOME%" == "" (
rem  set JAVA="%ES_HOME%\jdk\bin\java.exe"
rem  set JAVA_HOME="%ES_HOME%\jdk"
rem  set JAVA_TYPE=bundled jdk
rem ) else (
rem  set JAVA="%JAVA_HOME%\bin\java.exe"
rem  set JAVA_TYPE=JAVA_HOME
rem )

set JAVA="%ES_HOME%\jdk\bin\java.exe"
set JAVA_HOME="%ES_HOME%\jdk"
set JAVA_TYPE=bundled jdk

(2)、对于es7.6.2修改如下:

将如下内容

if defined JAVA_HOME (
  set JAVA="%JAVA_HOME%\bin\java.exe"
  set JAVA_TYPE=JAVA_HOME
) else (
  set JAVA="%ES_HOME%\jdk\bin\java.exe"
  set JAVA_HOME="%ES_HOME%\jdk"
  set JAVA_TYPE=bundled jdk
)

修改为

set JAVA="%ES_HOME%\jdk\bin\java.exe"
  set JAVA_HOME="%ES_HOME%\jdk"
  set JAVA_TYPE=bundled jdk

rem if defined JAVA_HOME (
rem   set JAVA="%JAVA_HOME%\bin\java.exe"
rem   set JAVA_TYPE=JAVA_HOME
rem ) else (
rem   set JAVA="%ES_HOME%\jdk\bin\java.exe"
rem   set JAVA_HOME="%ES_HOME%\jdk"
rem   set JAVA_TYPE=bundled jdk
rem )
set JAVA="%ES_HOME%\jdk\bin\java.exe"
set JAVA_HOME="%ES_HOME%\jdk"
set JAVA_TYPE=bundled jdk

也就用elasticsearch自带的jdk,不需要下载jdk11了!再次启动

启动head插件,浏览器访问:

2.1、安装es6

安装配置:

1、新版本要求至少jdk1.8以上。2、支持tar、zip、rpm等多种安装方式。

在windows下开发建议使用ZIP安装方式。

3、支持docker方式安装

详细参见:https://www.elastic.co/guide/en/elasticsearch/reference/current/install-elasticsearch.html

下载 ES: Elasticsearch 6.2.1 https://www.elastic.co/cn/downloads/elasticsearch

解压 elasticsearch-6.2.1.zip

由于没有data目录,我们自行创建一个data目录。

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

2.2 配置文件

2.2.1 三个配置文件

ES的配置文件的地址根据安装形式的不同而不同:
使用zip、tar安装,配置文件的地址在安装目录的config下
使用RPM安装,配置文件在/etc/elasticsearch下。
使用MSI安装,配置文件的地址在安装目录的config下,并且会自动将config目录地址写入环境变量ES_PATH_CONF。

本教程使用的zip包安装,配置文件在ES安装目录的config下。

配置文件如下:
elasticsearch.yml : 用于配置Elasticsearch运行参数 jvm.options : 用于配置Elasticsearch JVM设置
log4j2.properties: 用于配置Elasticsearch日志

2.2.2 elasticsearch.yml

配置格式是YAML,可以采用如下两种方式:

方式1:层次方式
path: data: /var/lib/elasticsearch logs: /var/log/elasticsearch
方式2:属性方式
path.data: /var/lib/elasticsearch path.logs: /var/log/elasticsearch

本项目采用方式2,例子如下:

cluster.name: elasticsearch
node.name: xc_node_1
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: D:\ElasticSearch\elasticsearch‐6.2.1\data
path.logs: D:\ElasticSearch\elasticsearch‐6.2.1\logs
http.cors.enabled: true
http.cors.allow‐origin: /.*/

将上述配置拷贝到elasticsearch.yml文件中。

注意path.data和path.logs路径配置正确。

path.data: D:\install\ElasticSearch\elasticsearch-1\data
path.logs: D:\install\ElasticSearch\elasticsearch-1\logs

常用的配置项如下:

  cluster.name:配置elasticsearch的集群名称,默认是elasticsearch。建议修改成一个有意义的名称。

  node.name:节点名,通常一台物理服务器就是一个节点,es会默认随机指定一个名字,建议指定一个有意义的名称,方便管理一个或多个节点组成一个cluster集群,集群是一个逻辑的概念,节点是物理概念,后边章节会详细介绍。

  path.conf: 设置配置文件的存储路径,tar或zip包安装默认在es根目录下的config文件夹,rpm安装默认在/etc/elasticsearch

  path.data: 设置索引数据的存储路径,默认是es根目录下的data文件夹,可以设置多个存储路径,用逗号隔开。

  path.logs: 设置日志文件的存储路径,默认是es根目录下的logs文件夹

  path.plugins: 设置插件的存放路径,默认是es根目录下的plugins文件夹

  bootstrap.memory_lock: true 设置为true可以锁住ES使用的内存,避免内存与swap分区交换数据。

  network.host: 设置绑定主机的ip地址,设置为0.0.0.0表示绑定任何ip,允许外网任何IP访问生产环境建议设置为具体的ip

  http.port: 9200 设置对外服务的http端口,默认为9200。

  transport.tcp.port: 9300 集群结点之间通信端口

  node.master: 指定该节点(主节点)是否有资格被选举成为master结点,默认是true,如果原来的master宕机会重新选举新的master。

  node.data: 指定该节点(数据节点)是否存储索引数据,默认为true。

  discovery.zen.ping.unicast.hosts: ["host1:port", "host2:port", "..."] 设置集群中master节点的初始列表。

  discovery.zen.ping.timeout: 3s 设置ES自动发现节点连接超时的时间,默认为3秒,如果网络延迟高可设置大些。

  discovery.zen.minimum_master_nodes:主结点数量的最少值 ,此值的公式为:(master_eligible_nodes / 2) + 1 ,比如:有3个符合要求的主结点,那么这里要设置为2。

  node.max_local_storage_nodes:单机允许的最大存储结点数,通常单机启动一个结点建议设置为1,开发环境如果单机启动多个节点可设置大于1.

2.2.3 jvm.options

设置最小及最大的JVM堆内存大小:
在jvm.options中设置 -Xms和-Xmx:
1) 两个值设置为相等。2) 将 Xmx 设置为不超过物理内存的一半。

2.2.4 log4j2.properties

日志文件设置,ES使用log4j,注意日志级别的配置。生产环境一定不要配置成debug或info,而是配置成debug。

2.2.5 系统配置

在linux上根据系统资源情况,可将每个进程最多允许打开的文件数设置大些。

su limit -n 查询当前文件数

使用命令设置 limit:
先切换到root,设置完成再切回elasticsearch用户。

sudo su 
ulimit ‐n 65536
su elasticsearch

也可通过下边的方式修改文件进行持久设置
/etc/security/limits.conf
将下边的行加入此文件:

elasticsearch  ‐  nofile  65536

2.3 启动ES

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

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

{
  "name" : "xc_node_1",
  "cluster_name" : "xuecheng",
  "cluster_uuid" : "TPFoQ49fR7aghVVAc5WmcA",
  "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"
}

docker-compose安装elasticsearch

创建es文件夹

mkdir -p /usr/local/es
cd /usr/local/es

创建docker-compose.yml文件

vi docker-compose.yml

docker-compose.yml内容如下:

version: '3'
services:
  elasticsearch:
    image: elasticsearch:7.12.1
    container_name: elasticsearch
    restart: always
    environment:
      - bootstrap.memory_lock=true
      - http.cors.enabled=true
      - http.cors.allow-origin=*
      - "discovery.type=single-node"
      - "ES_JAVA_OPTS=-Xms512m -Xmx512m"
    ulimits:
      memlock:
        soft: -1
        hard: -1
    volumes:
      - /usr/local/elasticsearch/plugins:/usr/share/elasticsearch/plugins
    ports:
      - 9200:9200
      - 9300:9300
  kibana:
    image: kibana:7.12.1
    container_name: kibana
    environment:
      - SERVER_NAME=kibana
      - ELASTICSEARCH_URL=http://192.168.101.65:9200
      - XPACK_MONITORING_ENABLED=true
    ports:
      - 5601:5601
    depends_on:
      - elasticsearch
    external_links:
      - elasticsearch

 

因为在创建elasticsearch容器的时候,映射了目录,所以可以在宿主机上进行配置ik中文分词器

在去选择ik分词器的时候,需要与elasticsearch的版本好对应上

把资料中的 elasticsearch-analysis-ik-7.12.1.zip 上传到服务器上,放到对应目录(plugins)解压

下面我们需要把课前资料中的ik分词器解压缩,重命名为ik

 

启动

docker-compose up -d

最后将kibana切换为中文,在config/kibana.yml中添加i18n.locale: "zh-CN"

 操作如下:

 

 

 

2.4 head 插件安装

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

从ES6.0开始,head插件支持使得node.js运行。
1、安装node.js

2、下载head并运行
git clone git://github.com/mobz/elasticsearch-head.git cd elasticsearch-head npm install npm run start open
HTTP://本地主机:9100 /

3、运行

输入http://localhost:9200/,点击连接,右侧会出现绿色

打开浏览器调试工具发现报错:Origin null is not allowed by Access-Control-Allow-Origin.

原因是:head插件作为客户端要连接ES服务(localhost:9200),此时存在跨域问题,elasticsearch默认不允许跨域访问。

解决方案:设置elasticsearch允许跨域访问。

在config/elasticsearch.yml 后面增加以下参数:

#开启cors跨域访问支持,默认为false 
http.cors.enabled: true 
#跨域访问允许的域名地址,(允许所有域名)以上使用正则 
http.cors.allow-origin: /.*/

注意:将config/elasticsearch.yml另存为utf-8编码格式。

成功连接ES

三、ES 快速入门

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

3.1、创建索引库

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

关于索引这个语:

索引(名词):ES是基于Lucene构建的一个搜索服务,它要从索引库搜索符合条件索引数据。

索引(动词):索引库刚创建起来是空的,将数据添加到索引库的过程称为索引。

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

1)、使用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.

如下是创建的例子,创建xc_course索引库,共1个分片,0个副本:

2)使用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

创建类型为xc_course的映射,共包括三个字段:name、description、studymondel

由于ES6.0版本还没有将type彻底删除,所以暂时把type起一个没有特殊意义的名字。

post 请求:http://localhost:9200/xc_course/doc/_mapping

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

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

发现报错如下:

{
    "error": {
        "root_cause": [
            {
                "type": "cluster_block_exception",
                "reason": "blocked by: [FORBIDDEN/12/index read-only / allow delete (api)];"
            }
        ],
        "type": "cluster_block_exception",
        "reason": "blocked by: [FORBIDDEN/12/index read-only / allow delete (api)];"
    },
    "status": 403
}

原因:主要是ES存储空间不足,es触发了自动保护机制,将索引设置为只读模式

再次发起请求,结果如下:

{
    "acknowledged": true
}

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

如果报错:Types cannot be provided in put mapping requests, unless the include_type_name parameter is set to true

原因分析:原因是由于写法是低版本的elasticsearch的,高版本要求传入一个include_type_name参数,值为true。所以加上一个参数即可。如下:

http://localhost:9200/es_test/_mapping/_doc?include_type_name=true

 

3.3、创建文档

ES中的文档相当于MySQL数据库表中的记录。

发送:put 或Post http://localhost:9200/xc_course/doc/id值,(如果不指定id值ES会自动生成ID)

http://localhost:9200/xc_course/doc/4028e58161bcf7f40161bcf8b77c0000

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

使用postman测试:

通过head查询数据:

更新文档

1)、完全替换

Post http://localhost:9200/xc_course1/doc/4028e58161bcf7f40161bcf8b77c0000

{
  "name":"spring cloud实战",
  "description":"本课程主要从四个章节进行讲解: 1.微服务架构入门 2.spring cloud 基础入门 3.实战SpringBoot 4.注册中心eureka。",
  "studymodel":"201001",
  "price":5.6
}

2)、局部更新

下边的例子是只更新price字段。

Post http://localhost:9200/xc_course1/doc/4028e58161bcf7f40161bcf8b77c0000/_update

{
    "doc": {
        "price":66.6
        }
}

3.4、搜索文档

1、根据课程id查询文档

发送:get http://localhost:9200/xc_course/doc/4028e58161bcf7f40161bcf8b77c0000

使用 postman测试:

2、查询所有记录

查询指定索引库指定类型下的文档,发送 get http://localhost:9200/xc_course/doc/_search

查询所有索引库的文档,发送:post http://localhost:9200/_search

3、查询名称中包括bootstrap 关键字的的记录

发送:get http://localhost:9200/xc_course/doc/_search?q=name:bootstrap

4、查询学习模式为201001的记录

发送 get http://localhost:9200/xc_course/doc/_search?q=studymodel:201001

3.4.1查询结果分析

分析上边查询结果:

{
    "took": 7,
    "timed_out": false,
    "_shards": {
        "total": 1,
        "successful": 1,
        "skipped": 0,
        "failed": 0
    },
    "hits": {
        "total": 1,
        "max_score": 0.2876821,
        "hits": [
            {
                "_index": "xc_course",
                "_type": "doc",
                "_id": "4028e58161bcf7f40161bcf8b77c0000",
                "_score": 0.2876821,
                "_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实战"}

结果如下:

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

4.2、安装IK分词器6.2.1

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

下载IK分词器:(Github地址:https://github.com/medcl/elasticsearch-analysis-ik),下载zip:

 解压,并将解压的文件拷贝到ES安装目录的plugins下的ik目录下

重启es,请求体中加入analyzer,

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

测试分词效果:

4.3、两种分词模式

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

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

测试两种分词模式:

发送: post localhost:9200/_analyze

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

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

4.4、自定义词库

如果要让分词器支持一些专有词语,可以自定义词库。

iK分词器自带一个main.dic的文件,此文件为词库文件。

 在上边的目录中新建一个my.dic文件(注意文件格式为utf-8(不要选择utf-8 BOM))

可以在其中自定义词汇:比如定义“传智播客”,由于不是一个词,故需要自定义词库。在config目录下创建my.dic文件,文件内放入“传智播客”,保存成utf-8格式

IKAnalyzer.cfg.xml配置文件中配置my.dic,

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE properties SYSTEM "http://java.sun.com/dtd/properties.dtd">
<properties>
    <comment>IK Analyzer 扩展配置</comment>
    <!--用户可以在这里配置自己的扩展字典 -->
    <entry key="ext_dict">my.dic</entry>
     <!--用户可以在这里配置自己的扩展停止词字典-->
    <entry key="ext_stopwords"></entry>
    <!--用户可以在这里配置远程扩展字典 -->
    <!-- <entry key="remote_ext_dict">words_location</entry> -->
    <!--用户可以在这里配置远程扩展停止词字典-->
    <!-- <entry key="remote_ext_stopwords">words_location</entry> -->
</properties>

重启ES,测试分词效果:

4.5、安装IK分词器7.6.2

IK分词器7.6.2下载:https://github.com/medcl/elasticsearch-analysis-ik/releases

注意:下载的文件里必须有plugin-descriptor.properties才能正常启动

五、映射

上边章节安装了ik分词器,如何在索引和搜索时去使用ik分词器呢?如何指定其它类型的field,比如日期类型、数值类型等。

本章节学习各种映射类型及映射维护方法。

5.1、映射维护方法

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

2、创建映射
post 请求:http://localhost:9200/xc_course/doc/_mapping,一个例子:

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

3、更新映射
映射创建成功可以添加新字段,已有字段不允许更新。

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

结果:

{
    "acknowledged": true
}

查询所有索引的映射

4、删除映射
通过删除索引来删除映射。可以在elasticsearch-head中删除。

5.2、常用映射类型

5.2.1 text文本字段

下图是ES6.2核心的字段类型如下:

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

1、text
1)analyzer
通过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去搜索,结果搜索不到数据

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

3)store

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

删除xc_course/doc下的映射

创建新映射:Post http://localhost:9200/xc_course/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"
           },
           "studymodel": {
              "type": "keyword"
           },
           "pic": {
              "type": "text",
              "index":false
           }
        }
}

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

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

查询测试:
Get http://localhost:9200/xc_course/_search?q=name:开发

Get http://localhost:9200/xc_course/_search?q=description:开发

Get http://localhost:9200/xc_course/_search?q=pic:group1/M00/00/01/wKhlQFqO4MmAOP53AAAcwDwm6SU490.jpg

Get http://localhost:9200/xc_course/_search?q=studymodel:201002

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

5.2.2 keyword关键字字段

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

5.2.2.1测试

更改映射:

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

插入文档:

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

根据studymodel查询文档,搜索:http://localhost:9200/xc_course1/_search?q=name:java

 

 

name 是keyword类型,所以查询方式是精确查询,故搜索java是搜索不到的。

5.2.3 date日期类型

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

1)format
通过format设置日期格式

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

{
  "properties": {   
           "name": {
              "type": "keyword"
           },
           "description": {
              "type": "text"
           },
           "studymodel": {
              "type": "keyword"
           },
           "pic": {
              "type": "text","index":false
           },
           "timestamp": {
              "type": "date",
              "format":"yyyy-MM-dd HH:mm:ss||yyyy-MM-dd"
           }
        }
}

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

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

5.2.4 数值类型

下边是ES支持的数值类型

 

 

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

{
  "properties": {   
           "name": {
              "type": "keyword"
           },
           "description": {
              "type": "text"
           },
           "studymodel": {
              "type": "keyword"
           },
           "pic": {
              "type": "text","index":false
           },
           "timestamp": {
              "type": "date",
              "format":"yyyy-MM-dd HH:mm:ss||yyyy-MM-dd"
           },
           "price": {
              "type": "scaled_float",
              "scaling_factor":100
           }
        }
}

由于比例因子为100,如果我们输入的价格是23.45则ES中会将23.45乘以100存储在ES中。如果输入的价格是23.456,ES会将23.456乘以100再取一个接近原始值的数,得出2346。使用比例因子的好处是整型比浮点型更易压缩,节省磁盘空间。如果比例因子不适合,则从下表选择范围小的去用:

更新已有映射,并插入文档:http://localhost:9200/xc_course/doc/3

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

5.2.5 综合例子

创建如下映射
post:http://localhost:9200/xc_course/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"
           },
           "studymodel": {
              "type": "keyword"
           },
           "pic": {
              "type": "text",
              "index":false
           },
           "timestamp": {
              "type": "date",
              "format":"yyyy-MM-dd HH:mm:ss||yyyy-MM-dd||epoch_millis"
           },
           "price": {
              "type": "float"
           }
        }
}

插入文档:Post: http://localhost:9200/xc_course/doc/1 

{
  "name":"Bootstrap 开发",
  "description":"Bootstrap是由Twitter推出的一个前台页面开发框架,是一个非常流行的开发框架,此框架集成了多种页面效果。此开发框架包含了大量的CSS、JS程序代码,可以帮助开发者(尤其是不擅长页面开发的程序人员)轻松的实现一个不受浏览器限制的精美界面效果。",
  "studymodel":"201002",
  "pic":"group1/M00/00/00/wKhlQFs6RCeAY0pHAAJx5ZjNDEM428.jpg",
  "timestamp":"2018-04-25 18:28:58",
  "price":38.6
}

六、索引管理(java代码实现)

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。

创建一个springboot工程
1)、添加依赖:

<parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.1.0.RELEASE</version>
    </parent>
    <properties>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.elasticsearch.client</groupId>
            <artifactId>elasticsearch-rest-high-level-client</artifactId>
            <version>6.2.1</version>
            <exclusions>
                <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-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>2.0.9</version>
        </dependency>
        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-io</artifactId>
            <version>1.3.2</version>
        </dependency>
        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-lang3</artifactId>
            <version>3.12.0</version>
        </dependency>
    </dependencies>

 

2)、配置文件application.yml

server:
  port: ${port:40100}
spring:
  application:
    name: xc‐search‐service
xuecheng:
  elasticsearch:
    hostlist: ${eshostlist:127.0.0.1:9200} #多个结点中间用逗号分隔

3)、配置类

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 ElasticsearchConfig {

    @Value("${xuecheng.elasticsearch.hostlist}")
    private String hostlist;

    @Bean
    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));
    }

    //项目主要使用RestHighLevelClient,对于低级的客户端暂时不用
    @Bean
    public RestClient restClient(){
        //解析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");
        }
        return RestClient.builder(httpHostArray).build();
    }

}

 

4)、启动类

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

5)、测试

创建索引库、添加文档、查询文档、更新文档

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

import org.elasticsearch.action.DocWriteResponse;
import org.elasticsearch.action.admin.indices.create.CreateIndexRequest;
import org.elasticsearch.action.admin.indices.create.CreateIndexResponse;
import org.elasticsearch.action.admin.indices.delete.DeleteIndexRequest;
import org.elasticsearch.action.admin.indices.delete.DeleteIndexResponse;
import org.elasticsearch.action.delete.DeleteRequest;
import org.elasticsearch.action.delete.DeleteResponse;
import org.elasticsearch.action.get.GetRequest;
import org.elasticsearch.action.get.GetResponse;
import org.elasticsearch.action.index.IndexRequest;
import org.elasticsearch.action.index.IndexResponse;
import org.elasticsearch.action.update.UpdateRequest;
import org.elasticsearch.action.update.UpdateResponse;
import org.elasticsearch.client.IndicesClient;
import org.elasticsearch.client.RestClient;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.xcontent.XContentType;
import org.elasticsearch.rest.RestStatus;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;

import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

@SpringBootTest
@RunWith(SpringRunner.class)
public class TestIndex {

    @Autowired
    RestHighLevelClient client;

    @Autowired
    RestClient restClient;

    //创建索引库
    @Test
    public void testCreateIndex() throws IOException {
        //创建索引对象
        CreateIndexRequest createIndexRequest = new CreateIndexRequest("xc_course");
        //设置参数
        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" +
                "              \"analyzer\":\"ik_max_word\",\n" +
                "              \"search_analyzer\":\"ik_smart\"\n" +
                "           },\n" +
                "           \"description\": {\n" +
                "              \"type\": \"text\",\n" +
                "              \"analyzer\":\"ik_max_word\",\n" +
                "              \"search_analyzer\":\"ik_smart\"\n" +
                "           },\n" +
                "           \"studymodel\": {\n" +
                "              \"type\": \"keyword\"\n" +
                "           },\n" +
                "           \"timestamp\": {\n" +
                "              \"type\": \"date\",\n" +
                "              \"format\":\"yyyy-MM-dd HH:mm:ss||yyyy-MM-dd||epoch_millis\"\n" +
                "           },\n" +
                "           \"price\": {\n" +
                "              \"type\": \"float\"\n" +
                "           }\n" +
                "        }\n" +
                "}", XContentType.JSON);
        //操作索引的客户端
        IndicesClient indices = client.indices();
        //执行创建索引库
        CreateIndexResponse createIndexResponse = indices.create(createIndexRequest);
        //得到响应
        boolean acknowledged = createIndexResponse.isAcknowledged();
        System.out.println(acknowledged); // true

    }

    //删除索引库
    @Test
    public void testDeleteIndex() throws IOException {
        //删除索引对象
        DeleteIndexRequest deleteIndexRequest = new DeleteIndexRequest("xc_course");
        //操作索引的客户端
        IndicesClient indices = client.indices();
        //执行删除索引
        DeleteIndexResponse delete = indices.delete(deleteIndexRequest);
        //得到响应
        boolean acknowledged = delete.isAcknowledged();
        System.out.println(acknowledged); // CREATED

    }

    //添加文档
    @Test
    public void testAddDoc() throws IOException {
        //文档内容
        //准备json数据
        Map<String, Object> jsonMap = new HashMap<>();
        jsonMap.put("name", "spring cloud实战");
        jsonMap.put("description", "本课程主要从四个章节进行讲解: 1.微服务架构入门 2.spring cloud 基础入门 3.实战Spring Boot 4.注册中心eureka。");
        jsonMap.put("studymodel", "201001");
        SimpleDateFormat dateFormat =new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        jsonMap.put("timestamp", dateFormat.format(new Date()));
        jsonMap.put("price", 5.6f);

        //创建索引创建对象
        IndexRequest indexRequest = new IndexRequest("xc_course","doc");
        //文档内容
        indexRequest.source(jsonMap);
        //通过client进行http的请求
        IndexResponse indexResponse = client.index(indexRequest);
        DocWriteResponse.Result result = indexResponse.getResult();
        System.out.println(result);

    }

    //查询文档
    @Test
    public void testGetDoc() throws IOException {
        //查询请求对象
        GetRequest getRequest = new GetRequest("xc_course","doc","JnyKlIUBtEM8ifzeQLxk");
        GetResponse getResponse = client.get(getRequest);
        //得到文档的内容
        Map<String, Object> sourceAsMap = getResponse.getSourceAsMap();
        System.out.println(sourceAsMap);
    }
    //更新文档
    @Test
    public void updateDoc() throws IOException {
        UpdateRequest updateRequest = new UpdateRequest("xc_course", "doc","JnyKlIUBtEM8ifzeQLxk");
        Map<String, String> map = new HashMap<>();
        map.put("name", "spring cloud实战2");
        updateRequest.doc(map);
        UpdateResponse update = client.update(updateRequest);
        RestStatus status = update.status();
        System.out.println(status); // OK
    }
    //根据id删除文档
    @Test
    public void testDelDoc() throws IOException {
        //删除文档id
        String id = "JnyKlIUBtEM8ifzeQLxk";
        //删除索引请求对象
        DeleteRequest deleteRequest = new DeleteRequest("xc_course","doc",id);
        //响应对象
        DeleteResponse deleteResponse = client.delete(deleteRequest);
        //获取响应结果
        DocWriteResponse.Result result = deleteResponse.getResult();
        System.out.println(result); // DELETED
    }
}

创建索引库之后

添加文档之后

 

根据id查询文档

控制台打印

{price=5.6, studymodel=201001, name=spring cloud实战, description=本课程主要从四个章节进行讲解: 1.微服务架构入门 2.spring cloud 基础入门 3.实战Spring Boot 4.注册中心eureka。, timestamp=2023-01-09 11:19:26}

根据id更新文档之后

 

根据id删除文档

七、搜索管理(java代码实现)

 数据准备

http://localhost:9200/xc_course/doc/1
{
  "name":"Bootstrap开发",
  "description":"Bootstrap是由Twitter推出的一个前台页面开发框架,是一个非常流行的开发框架,此框架集成了多种页面效果。此开发框架包含了大量的CSS、JS程序代码,可以帮助开发者(尤其是不擅长页面开发的程序人员)轻松的实现一个不受浏览器限制的精美界面效果。",
  "studymodel":"201002",
  "pic":"group1/M00/00/00/wKhlQFs6RCeAY0pHAAJx5ZjNDEM428.jpg",
  "timestamp":"2018-04-25 18:28:58",
  "price":38.6
}
http://localhost:9200/xc_course/doc/2
{
  "name":"java编程基础",
  "description":"java语言是世界第一编程语言,在软件开发领域使用人数最多。",
  "studymodel":"201001",
  "pic":"group1/M00/00/00/wKhlQFs6RCeAY0pHAAJx5ZjNDEM428.jpg",
  "timestamp":"2018-03-25 19:11:35",
  "price":68.6
}
http://localhost:9200/xc_course/doc/3
{
  
}

1、简单搜索

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

q:搜索字符串。例子:?q=name:spring 搜索name中包括spring的文档。

2、DSL 搜索

DSL(Domain Specific Language)是ES提出的基于json的搜索方式,在搜索时传入特定的json格式的数据来完成不同的搜索需求。
DSL比URI搜索方式功能强大,在项目中建议使用DSL方式来完成搜索

查询指定索引库指定类型下的文档。(通常使用此方法)

发送:post http://localhost:9200/xc_course/doc/_search

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

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

结果:

{
    "took": 7,
    "timed_out": false,
    "_shards": {
        "total": 1,
        "successful": 1,
        "skipped": 0,
        "failed": 0
    },
    "hits": {
        "total": 3,
        "max_score": 1.0,
        "hits": [
            {
                "_index": "xc_course",
                "_type": "doc",
                "_id": "1",
                "_score": 1.0,
                "_source": {
                    "studymodel": "201002",
                    "name": "Bootstrap开发"
                }
            },
            {
                "_index": "xc_course",
                "_type": "doc",
                "_id": "2",
                "_score": 1.0,
                "_source": {
                    "studymodel": "201001",
                    "name": "java编程基础"
                }
            },
            {
                "_index": "xc_course",
                "_type": "doc",
                "_id": "3",
                "_score": 1.0,
                "_source": {
                    "studymodel": "201001",
                    "name": "spring开发基础"
                }
            }
        ]
    }
}

java代码

import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.client.RestClient;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.SearchHits;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;

import java.io.IOException;
import java.util.Map;

@SpringBootTest
@RunWith(SpringRunner.class)
public class TestSearch {
    @Autowired
    RestHighLevelClient client;
    @Autowired
    RestClient restClient;

    //搜索type下的全部记录
    @Test
    public void testSearchAll() throws IOException {
        SearchRequest searchRequest = new SearchRequest("xc_course");
        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);
        }
    }
}

结果:

Bootstrap开发
201002
java编程基础
201001
spring开发基础
201001

7.3.2 分页查询

ES支持分页查询,传入两个参数:from和size。form:表示起始文档的下标,从0开始。size:查询的文档数量。

发送:post http://localhost:9200/xc_course/doc/_search

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

结果:

{
    "took": 5,
    "timed_out": false,
    "_shards": {
        "total": 1,
        "successful": 1,
        "skipped": 0,
        "failed": 0
    },
    "hits": {
        "total": 3,
        "max_score": 1.0,
        "hits": [
            {
                "_index": "xc_course",
                "_type": "doc",
                "_id": "1",
                "_score": 1.0,
                "_source": {
                    "studymodel": "201002",
                    "name": "Bootstrap开发"
                }
            }
        ]
    }
}

Java代码

//搜索type下的全部记录
    @Test
    public void testSearchPagingQuery() throws IOException {
        SearchRequest searchRequest = new SearchRequest("xc_course");
        searchRequest.types("doc");
        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
        searchSourceBuilder.query(QueryBuilders.matchAllQuery());
        //分页查询,设置起始下标,从0开始
        searchSourceBuilder.from(0);
        //每页显示个数
        searchSourceBuilder.size(2);
        //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);
        }
    }

结果:

Bootstrap开发
201002
java编程基础
201001

7.3.3 Term Query

Term Query为精确查询,在搜索时会整体匹配关键字,不再将关键字分词。

发送:post http://localhost:9200/xc_course/doc/_search

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

结果:

{
    "took": 2,
    "timed_out": false,
    "_shards": {
        "total": 1,
        "successful": 1,
        "skipped": 0,
        "failed": 0
    },
    "hits": {
        "total": 1,
        "max_score": 0.9331132,
        "hits": [
            {
                "_index": "xc_course",
                "_type": "doc",
                "_id": "3",
                "_score": 0.9331132,
                "_source": {
                    "studymodel": "201001",
                    "name": "spring开发基础"
                }
            }
        ]
    }
}

java代码

@Test
    public void testTermQuery() throws IOException {
        SearchRequest searchRequest = new SearchRequest("xc_course");
        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);
        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);
        }
    }

结果:

spring开发基础
201001

7.3.4 根据id精确匹配

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

测试:post : http://127.0.0.1:9200/xc_course/doc/_search

{
    "query": {
        "ids": {
            "type": "doc",
            "values": ["3","4","100"]
        }
    }
}

结果:

{
    "took": 5,
    "timed_out": false,
    "_shards": {
        "total": 1,
        "successful": 1,
        "skipped": 0,
        "failed": 0
    },
    "hits": {
        "total": 1,
        "max_score": 1.0,
        "hits": [
            {
                "_index": "xc_course",
                "_type": "doc",
                "_id": "3",
                "_score": 1.0,
                "_source": {
                    "name": "spring开发基础",
                    "description": "spring 在java领域非常流行,java程序员都在用。",
                    "studymodel": "201001",
                    "pic": "group1/M00/00/00/wKhlQFs6RCeAY0pHAAJx5ZjNDEM428.jpg",
                    "timestamp": "2018-02-24 19:11:35",
                    "price": 88.6
                }
            }
        ]
    }
}

Java代码

@Test
    public void testQueryByManyIds() throws IOException {
        SearchRequest searchRequest = new SearchRequest("xc_course");
        searchRequest.types("doc");
        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
        String[] split = new String[]{"1","2"};
        List<String> idList = Arrays.asList(split);
        searchSourceBuilder.query(QueryBuilders.termsQuery("_id", idList));
        //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);
        }
    }

结果:

Bootstrap开发
201002
java编程基础
201001

7.3.5 match Query

1、基本使用

match Query即全文检索,它的搜索方式是先将搜索字符串分词,再使用各各词条从索引中搜索。

match query与Term query区别是match query在搜索前先将搜索关键字分词,再拿各各词语去索引中搜索

发送:post http://localhost:9200/xc_course/doc/_search

{
    "query": {
        "match": {
            "description": {
                "query": "spring开发",
                "operator": "or"
            }
        }
    }
}

结果:

{
    "took": 3,
    "timed_out": false,
    "_shards": {
        "total": 1,
        "successful": 1,
        "skipped": 0,
        "failed": 0
    },
    "hits": {
        "total": 3,
        "max_score": 1.3339276,
        "hits": [
            {
                "_index": "xc_course",
                "_type": "doc",
                "_id": "3",
                "_score": 1.3339276,
                "_source": {
                    "name": "spring开发基础",
                    "description": "spring 在java领域非常流行,java程序员都在用。",
                    "studymodel": "201001",
                    "pic": "group1/M00/00/00/wKhlQFs6RCeAY0pHAAJx5ZjNDEM428.jpg",
                    "timestamp": "2018-02-24 19:11:35",
                    "price": 88.6
                }
            },
            {
                "_index": "xc_course",
                "_type": "doc",
                "_id": "1",
                "_score": 0.7281746,
                "_source": {
                    "name": "Bootstrap开发",
                    "description": "Bootstrap是由Twitter推出的一个前台页面开发框架,是一个非常流行的开发框架,此框架集成了多种页面效果。此开发框架包含了大量的CSS、JS程序代码,可以帮助开发者(尤其是不擅长页面开发的程序人员)轻松的实现一个不受浏览器限制的精美界面效果。",
                    "studymodel": "201002",
                    "pic": "group1/M00/00/00/wKhlQFs6RCeAY0pHAAJx5ZjNDEM428.jpg",
                    "timestamp": "2018-04-25 18:28:58",
                    "price": 38.6
                }
            },
            {
                "_index": "xc_course",
                "_type": "doc",
                "_id": "2",
                "_score": 0.5735117,
                "_source": {
                    "name": "java编程基础",
                    "description": "java语言是世界第一编程语言,在软件开发领域使用人数最多。",
                    "studymodel": "201001",
                    "pic": "group1/M00/00/00/wKhlQFs6RCeAY0pHAAJx5ZjNDEM428.jpg",
                    "timestamp": "2018-03-25 19:11:35",
                    "price": 68.6
                }
            }
        ]
    }
}

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

operator:or 表示 只要有一个词在文档中出现则就符合条件,and表示每个词都在文档中出现则才符合条件。

上边的搜索的执行过程是:

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

Java代码

//根据关键字搜索
    @Test
    public void testMatchQuery() throws IOException {
        SearchRequest searchRequest = new SearchRequest("xc_course");
        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);
        }
    }

结果:

spring开发基础
201001
Bootstrap开发
201002
java编程基础
201001

2、minimum_should_match

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

Post http://localhost:9200/xc_course/doc/_search

比如搜索语句如下:

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

结果:

{
    "took": 3,
    "timed_out": false,
    "_shards": {
        "total": 1,
        "successful": 1,
        "skipped": 0,
        "failed": 0
    },
    "hits": {
        "total": 1,
        "max_score": 2.1431413,
        "hits": [
            {
                "_index": "xc_course",
                "_type": "doc",
                "_id": "1",
                "_score": 2.1431413,
                "_source": {
                    "name": "Bootstrap开发",
                    "description": "Bootstrap是由Twitter推出的一个前台页面开发框架,是一个非常流行的开发框架,此框架集成了多种页面效果。此开发框架包含了大量的CSS、JS程序代码,可以帮助开发者(尤其是不擅长页面开发的程序人员)轻松的实现一个不受浏览器限制的精美界面效果。",
                    "studymodel": "201002",
                    "pic": "group1/M00/00/00/wKhlQFs6RCeAY0pHAAJx5ZjNDEM428.jpg",
                    "timestamp": "2018-04-25 18:28:58",
                    "price": 38.6
                }
            }
        ]
    }
}

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

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

Java代码

@Test
    public void testMinimumMatchQuery() throws IOException {
        SearchRequest searchRequest = new SearchRequest("xc_course");
        searchRequest.types("doc");
        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
        //source源字段过虑
        searchSourceBuilder.fetchSource(new String[]{"name","studymodel"}, new String[]{});
        //匹配关键字
        MatchQueryBuilder matchQueryBuilder = QueryBuilders.matchQuery("description", "spring开发框架").minimumShouldMatch("80%");//设置匹配占比
        searchSourceBuilder.query(matchQueryBuilder);
        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);
        }
    }

结果:

Bootstrap开发
201002

7.3.6 multi Query

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

1、基本使用

单项匹配是在一个field中去匹配,多项匹配是拿关键字去多个Field中匹配。

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

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

{
    "query": {
        "multi_match": {
            "query": "spring css",
            "minimum_should_match": "80%",
            "fields": ["name","description"]
        }
    }
}

结果:

{
    "took": 9,
    "timed_out": false,
    "_shards": {
        "total": 1,
        "successful": 1,
        "skipped": 0,
        "failed": 0
    },
    "hits": {
        "total": 2,
        "max_score": 1.3339276,
        "hits": [
            {
                "_index": "xc_course",
                "_type": "doc",
                "_id": "3",
                "_score": 1.3339276,
                "_source": {
                    "name": "spring开发基础",
                    "description": "spring 在java领域非常流行,java程序员都在用。",
                    "studymodel": "201001",
                    "pic": "group1/M00/00/00/wKhlQFs6RCeAY0pHAAJx5ZjNDEM428.jpg",
                    "timestamp": "2018-02-24 19:11:35",
                    "price": 88.6
                }
            },
            {
                "_index": "xc_course",
                "_type": "doc",
                "_id": "1",
                "_score": 0.69607234,
                "_source": {
                    "name": "Bootstrap开发",
                    "description": "Bootstrap是由Twitter推出的一个前台页面开发框架,是一个非常流行的开发框架,此框架集成了多种页面效果。此开发框架包含了大量的CSS、JS程序代码,可以帮助开发者(尤其是不擅长页面开发的程序人员)轻松的实现一个不受浏览器限制的精美界面效果。",
                    "studymodel": "201002",
                    "pic": "group1/M00/00/00/wKhlQFs6RCeAY0pHAAJx5ZjNDEM428.jpg",
                    "timestamp": "2018-04-25 18:28:58",
                    "price": 38.6
                }
            }
        ]
    }
}

2、提升boost

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

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

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

结果:

{
    "took": 1,
    "timed_out": false,
    "_shards": {
        "total": 1,
        "successful": 1,
        "skipped": 0,
        "failed": 0
    },
    "hits": {
        "total": 2,
        "max_score": 1.4149667,
        "hits": [
            {
                "_index": "xc_course",
                "_type": "doc",
                "_id": "1",
                "_score": 1.4149667,
                "_source": {
                    "name": "Bootstrap开发",
                    "description": "Bootstrap是由Twitter推出的一个前台页面开发框架,是一个非常流行的开发框架,此框架集成了多种页面效果。此开发框架包含了大量的CSS、JS程序代码,可以帮助开发者(尤其是不擅长页面开发的程序人员)轻松的实现一个不受浏览器限制的精美界面效果。",
                    "studymodel": "201002",
                    "pic": "group1/M00/00/00/wKhlQFs6RCeAY0pHAAJx5ZjNDEM428.jpg",
                    "timestamp": "2018-04-25 18:28:58",
                    "price": 38.6
                }
            },
            {
                "_index": "xc_course",
                "_type": "doc",
                "_id": "3",
                "_score": 1.3339276,
                "_source": {
                    "name": "spring开发基础",
                    "description": "spring 在java领域非常流行,java程序员都在用。",
                    "studymodel": "201001",
                    "pic": "group1/M00/00/00/wKhlQFs6RCeAY0pHAAJx5ZjNDEM428.jpg",
                    "timestamp": "2018-02-24 19:11:35",
                    "price": 88.6
                }
            }
        ]
    }
}

通过查询发现Bootstrap排在前边。提升boost,通常关键字匹配上name的权重要比匹配上description的权重高,这里可以对name的权重提升。

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

结果:

{
    "took": 1,
    "timed_out": false,
    "_shards": {
        "total": 1,
        "successful": 1,
        "skipped": 0,
        "failed": 0
    },
    "hits": {
        "total": 2,
        "max_score": 9.331132,
        "hits": [
            {
                "_index": "xc_course",
                "_type": "doc",
                "_id": "3",
                "_score": 9.331132,
                "_source": {
                    "name": "spring开发基础",
                    "description": "spring 在java领域非常流行,java程序员都在用。",
                    "studymodel": "201001",
                    "pic": "group1/M00/00/00/wKhlQFs6RCeAY0pHAAJx5ZjNDEM428.jpg",
                    "timestamp": "2018-02-24 19:11:35",
                    "price": 88.6
                }
            },
            {
                "_index": "xc_course",
                "_type": "doc",
                "_id": "1",
                "_score": 1.4149667,
                "_source": {
                    "name": "Bootstrap开发",
                    "description": "Bootstrap是由Twitter推出的一个前台页面开发框架,是一个非常流行的开发框架,此框架集成了多种页面效果。此开发框架包含了大量的CSS、JS程序代码,可以帮助开发者(尤其是不擅长页面开发的程序人员)轻松的实现一个不受浏览器限制的精美界面效果。",
                    "studymodel": "201002",
                    "pic": "group1/M00/00/00/wKhlQFs6RCeAY0pHAAJx5ZjNDEM428.jpg",
                    "timestamp": "2018-04-25 18:28:58",
                    "price": 38.6
                }
            }
        ]
    }
}

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

@Test
    public void testMultiQuery() throws IOException {
        SearchRequest searchRequest = new SearchRequest("xc_course");
        searchRequest.types("doc");
        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
        //source源字段过虑
        searchSourceBuilder.fetchSource(new String[]{"name","studymodel"}, new String[]{});
        MultiMatchQueryBuilder multiMatchQueryBuilder = QueryBuilders.multiMatchQuery("spring框架",
                "name", "description").minimumShouldMatch("50%");
        multiMatchQueryBuilder.field("name",10);//提升boost
        //匹配关键字
        searchSourceBuilder.query(multiMatchQueryBuilder);
        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);
        }
    }

结果:

spring开发基础
201001
Bootstrap开发
201002

7.3.7 布尔查询

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

三个参数:

must:文档必须匹配must所包括的查询条件,相当于 “AND”

should:文档应该匹配should所包括的查询条件其中的一个或多个,相当于 "OR"

must_not:文档不能匹配must_not所包括的该查询条件,相当于“NOT”

分别使用 must、should、must_not测试下边的查询:

发送:POST http://localhost:9200/xc_course/doc/_search

测试must

{
    "_source": ["name","studymodel","description"],
    "from": 0,
    "size": 3,
    "query": {
        "bool": {
            "must": [
                {
                    "multi_match": {
                        "query": "spring框架",
                        "minimum_should_match": "50%",
                        "fields": [ "name^10", "description" ]

                    }
                },
                {
                    "term": {
                        "studymodel": "201001"
                    }
                }
            ]
        }
    }
}

结果:

{
    "took": 6,
    "timed_out": false,
    "_shards": {
        "total": 1,
        "successful": 1,
        "skipped": 0,
        "failed": 0
    },
    "hits": {
        "total": 1,
        "max_score": 9.801136,
        "hits": [
            {
                "_index": "xc_course",
                "_type": "doc",
                "_id": "3",
                "_score": 9.801136,
                "_source": {
                    "studymodel": "201001",
                    "name": "spring开发基础",
                    "description": "spring 在java领域非常流行,java程序员都在用。"
                }
            }
        ]
    }
}

测试should

{
    "_source": ["name","studymodel","description"],
    "from": 0,
    "size": 3,
    "query": {
        "bool": {
            "should": [
                {
                    "multi_match": {
                        "query": "spring框架",
                        "minimum_should_match": "50%",
                        "fields": [ "name^10", "description" ]

                    }
                },
                {
                    "term": {
                        "studymodel": "201001"
                    }
                }
            ]
        }
    }
}

结果:

{
    "took": 2,
    "timed_out": false,
    "_shards": {
        "total": 1,
        "successful": 1,
        "skipped": 0,
        "failed": 0
    },
    "hits": {
        "total": 3,
        "max_score": 9.801136,
        "hits": [
            {
                "_index": "xc_course",
                "_type": "doc",
                "_id": "3",
                "_score": 9.801136,
                "_source": {
                    "studymodel": "201001",
                    "name": "spring开发基础",
                    "description": "spring 在java领域非常流行,java程序员都在用。"
                }
            },
            {
                "_index": "xc_course",
                "_type": "doc",
                "_id": "1",
                "_score": 1.4149667,
                "_source": {
                    "studymodel": "201002",
                    "name": "Bootstrap开发",
                    "description": "Bootstrap是由Twitter推出的一个前台页面开发框架,是一个非常流行的开发框架,此框架集成了多种页面效果。此开发框架包含了大量的CSS、JS程序代码,可以帮助开发者(尤其是不擅长页面开发的程序人员)轻松的实现一个不受浏览器限制的精美界面效果。"
                }
            },
            {
                "_index": "xc_course",
                "_type": "doc",
                "_id": "2",
                "_score": 0.47000363,
                "_source": {
                    "studymodel": "201001",
                    "name": "java编程基础",
                    "description": "java语言是世界第一编程语言,在软件开发领域使用人数最多。"
                }
            }
        ]
    }
}

测试must_not

{
    "_source": ["name","studymodel","description"],
    "from": 0,
    "size": 3,
    "query": {
        "bool": {
            "must_not": [
                {
                    "multi_match": {
                        "query": "spring框架",
                        "minimum_should_match": "50%",
                        "fields": [ "name^10", "description" ]

                    }
                },
                {
                    "term": {
                        "studymodel": "201001"
                    }
                }
            ]
        }
    }
}

结果:

{
    "took": 6,
    "timed_out": false,
    "_shards": {
        "total": 1,
        "successful": 1,
        "skipped": 0,
        "failed": 0
    },
    "hits": {
        "total": 0,
        "max_score": null,
        "hits": []
    }
}

 

Java代码

//BoolQuery ,将搜索关键字分词,拿分词去索引库搜索
    @Test
    public void testBoolQuery() throws IOException {
        //创建搜索请求对象
        SearchRequest searchRequest= new SearchRequest("xc_course");
        searchRequest.types("doc");
        //创建搜索源配置对象
        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
        searchSourceBuilder.fetchSource(new String[]{"name","description","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);
        }
    }

结果:{studymodel=201001, name=spring开发基础, description=spring 在java领域非常流行,java程序员都在用。}

7.3.8 过虑器

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

过虑器在布尔查询中使用,下边是在搜索结果的基础上进行过虑:

{
    "_source": ["name","studymodel","description","price"],
    "query": {
        "bool": {
            "must": [
                {
                    "multi_match": {
                        "query": "spring框架",
                        "minimum_should_match": "50%",
                        "fields": [ "name^10", "description" ]

                    }
                }
            ],
            "filter": [
                {
                    "term": {
                        "studymodel": "201001"
                    }
                },
                {
                    "range": {
                        "price": {"gte": 60,"lte": 100}
                    }
                }
            ]
             
        }
    }
}

结果:

{
    "took": 7,
    "timed_out": false,
    "_shards": {
        "total": 1,
        "successful": 1,
        "skipped": 0,
        "failed": 0
    },
    "hits": {
        "total": 1,
        "max_score": 9.331132,
        "hits": [
            {
                "_index": "xc_course",
                "_type": "doc",
                "_id": "3",
                "_score": 9.331132,
                "_source": {
                    "price": 88.6,
                    "studymodel": "201001",
                    "name": "spring开发基础",
                    "description": "spring 在java领域非常流行,java程序员都在用。"
                }
            }
        ]
    }
}

range:范围过虑,保留大于等于60 并且小于等于100的记录。

term :项匹配过虑,保留studymodel等于"201001"的记录。

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

Java代码

// 布尔查询使用过虑器
    @Test
    public void testFilter() throws IOException {
        SearchRequest searchRequest = new SearchRequest("xc_course");
        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));
        //设置布尔查询对象
        searchSourceBuilder.query(boolQueryBuilder);
        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");
//            double price = (double) sourceAsMap.get("price");
            System.out.println(name);
            System.out.println(studymodel);
            System.out.println(description);
//            System.out.println(price);
        }
    }

结果:

spring开发基础
201001
spring 在java领域非常流行,java程序员都在用。

7.3.9 排序

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

发送 POST http://localhost:9200/xc_course/doc/_search

过虑0--10元价格范围的文档,并且对结果进行排序,先按studymodel降序,再按价格升序

{
    "_source": ["name","studymodel","description","price"],
    "query": {
        "bool": {
            "filter": [
                {
                    "range": {
                        "price": {"gte": 0,"lte": 100}
                    }
                }
            ]
             
        }
    },
    "sort": [
        {
            "studymodel": "desc"
        },
        {
            "price": "asc"
        }
    ]
}

结果:

{
    "took": 18,
    "timed_out": false,
    "_shards": {
        "total": 1,
        "successful": 1,
        "skipped": 0,
        "failed": 0
    },
    "hits": {
        "total": 3,
        "max_score": null,
        "hits": [
            {
                "_index": "xc_course",
                "_type": "doc",
                "_id": "1",
                "_score": null,
                "_source": {
                    "price": 38.6,
                    "studymodel": "201002",
                    "name": "Bootstrap开发",
                    "description": "Bootstrap是由Twitter推出的一个前台页面开发框架,是一个非常流行的开发框架,此框架集成了多种页面效果。此开发框架包含了大量的CSS、JS程序代码,可以帮助开发者(尤其是不擅长页面开发的程序人员)轻松的实现一个不受浏览器限制的精美界面效果。"
                },
                "sort": [
                    "201002",
                    38.6
                ]
            },
            {
                "_index": "xc_course",
                "_type": "doc",
                "_id": "2",
                "_score": null,
                "_source": {
                    "price": 68.6,
                    "studymodel": "201001",
                    "name": "java编程基础",
                    "description": "java语言是世界第一编程语言,在软件开发领域使用人数最多。"
                },
                "sort": [
                    "201001",
                    68.6
                ]
            },
            {
                "_index": "xc_course",
                "_type": "doc",
                "_id": "3",
                "_score": null,
                "_source": {
                    "price": 88.6,
                    "studymodel": "201001",
                    "name": "spring开发基础",
                    "description": "spring 在java领域非常流行,java程序员都在用。"
                },
                "sort": [
                    "201001",
                    88.6
                ]
            }
        ]
    }
}

Java代码

@Test
    public void testSort() throws IOException {
        SearchRequest searchRequest = new SearchRequest("xc_course");
        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");
            double price = (double) sourceAsMap.get("price");
            System.out.println(name);
            System.out.println(studymodel);
            System.out.println(description);
            System.out.println(price);
        }
    }

结果:

Bootstrap开发
201002
Bootstrap是由Twitter推出的一个前台页面开发框架,是一个非常流行的开发框架,此框架集成了多种页面效果。此开发框架包含了大量的CSS、JS程序代码,可以帮助开发者(尤其是不擅长页面开发的程序人员)轻松的实现一个不受浏览器限制的精美界面效果。
38.6
java编程基础
201001
java语言是世界第一编程语言,在软件开发领域使用人数最多。
68.6
spring开发基础
201001
spring 在java领域非常流行,java程序员都在用。
88.6

7.3.10 高亮显示

高亮显示可以将搜索结果一个或多个字突出显示,以便向用户展示匹配关键字的位置。

在搜索语句中添加highlight即可实现,如下:

Post: http://127.0.0.1:9200/xc_course/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": [
        {
            "studymodel": "desc"
        },
        {
            "price": "asc"
        }
    ],
    "highlight": {
        "pre_tags": ["<tag1>"],
        "post_tags": ["</tag2>"],
        "fields": {
            "name": {},
            "description": {}
        }
    }
}

结果:

{
    "took": 5,
    "timed_out": false,
    "_shards": {
        "total": 1,
        "successful": 1,
        "skipped": 0,
        "failed": 0
    },
    "hits": {
        "total": 3,
        "max_score": null,
        "hits": [
            {
                "_index": "xc_course",
                "_type": "doc",
                "_id": "1",
                "_score": null,
                "_source": {
                    "price": 38.6,
                    "studymodel": "201002",
                    "name": "Bootstrap开发",
                    "description": "Bootstrap是由Twitter推出的一个前台页面开发框架,是一个非常流行的开发框架,此框架集成了多种页面效果。此开发框架包含了大量的CSS、JS程序代码,可以帮助开发者(尤其是不擅长页面开发的程序人员)轻松的实现一个不受浏览器限制的精美界面效果。"
                },
                "highlight": {
                    "name": [
                        "Bootstrap<tag1>开发</tag2>"
                    ],
                    "description": [
                        "Bootstrap是由Twitter推出的一个前台页面<tag1>开发</tag2><tag1>框架</tag2>,是一个非常流行的<tag1>开发</tag2><tag1>框架</tag2>,此<tag1>框架</tag2>集成了多种页面效果。",
                        "此<tag1>开发</tag2><tag1>框架</tag2>包含了大量的CSS、JS程序代码,可以帮助<tag1>开发</tag2>者(尤其是不擅长页面<tag1>开发</tag2>的程序人员)轻松的实现一个不受浏览器限制的精美界面效果。"
                    ]
                },
                "sort": [
                    "201002",
                    38.6
                ]
            },
            {
                "_index": "xc_course",
                "_type": "doc",
                "_id": "2",
                "_score": null,
                "_source": {
                    "price": 68.6,
                    "studymodel": "201001",
                    "name": "java编程基础",
                    "description": "java语言是世界第一编程语言,在软件开发领域使用人数最多。"
                },
                "highlight": {
                    "description": [
                        "java语言是世界第一编程语言,在软件<tag1>开发</tag2>领域使用人数最多。"
                    ]
                },
                "sort": [
                    "201001",
                    68.6
                ]
            },
            {
                "_index": "xc_course",
                "_type": "doc",
                "_id": "3",
                "_score": null,
                "_source": {
                    "price": 88.6,
                    "studymodel": "201001",
                    "name": "spring开发基础",
                    "description": "spring 在java领域非常流行,java程序员都在用。"
                },
                "highlight": {
                    "name": [
                        "spring<tag1>开发</tag2>基础"
                    ]
                },
                "sort": [
                    "201001",
                    88.6
                ]
            }
        ]
    }
}

Java代码

@Test
    public void testHighlight() throws IOException {
        SearchRequest searchRequest = new SearchRequest("xc_course");
        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);
        //匹配关键字
//        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("<tag1>");//设置前缀
        highlightBuilder.postTags("</tag2>");//设置后缀
        // 设置高亮字段
        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集群结构的示意图:

图中有三个集群,在服务器中启动一个elasticsearch就会有一个进程,启动两个进程就是两个节点,通常一个物理机就启动一个进程。

P开头的表示分片,R开头的表示副本,副本为分片的复制品。

从上图总结以下概念:

1、结点

ES集群由多个服务器组成,每个服务器即为一个Node结点(该服务只部署了一个ES进程)。

2、分片

  当我们的文档量很大时,由于内存和硬盘的限制,同时也为了提高ES的处理能力、容错能力及高可用能力,我们将索引分成若干分片,每个分片可以放在不同的服务器这样就实现了多个服务器共同对外提供索引及搜索服务一个搜索请求过来,会分别从各各分片去查询,最后将查询到的数据合并返回给用户

3、副本

  为了提高ES的高可用同时也为了提高搜索的吞吐量,我们将分片复制一份或多份存储在其它的服务器,这样即使当前的服务器挂掉了,拥有副本的服务器照常可以提供服务。
4、主结点

  一个集群中会有一个或多个主结点,主结点的作用是集群管理,比如增加节点,移除节点等,主结点挂掉后ES会重新选一个主结点。

5、结点转发

每个结点都知道其它结点的信息,我们可以对任意一个结点发起请求,接收请求的结点会转发给其它结点查询数据

8.2、搭建集群

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

8.2.1 结点的三个角色

主结点:master节点主要用于集群的管理及索引 比如新增结点、分片分配、索引的新增和删除等。

数据结点:data 节点上保存了数据分片,它负责索引和搜索操作。

客户端结点:client 节点仅作为请求客户端存在,client的作用也作为负载均衡器,client 节点不存数据,只是将请求均衡转发到其它结点

通过下边两项参数来配置结点的功能:
node.master: #是否允许为主结点
node.data: #允许存储数据作为数据结点
node.ingest: #是否允许成为协调节点,

四种组合方式:
master=true,data=true:即是主结点又是数据结点
master=false,data=true:仅是数据结点
master=true,data=false:仅是主结点,不存储数据
master=false,data=false:即不是主结点也不是数据结点,此时可设置ingest为true表示它是一个客户端。

下面我们来搭两个节点的集群。创建两个分片,每个分片一个副本。

8.2.2创建结点 1

解压elasticsearch-6.2.1.zip 到D:\install\ElasticSearch\cluster\elasticsearch-1

结点1对外服务的http端口是:9200,集群管理端口是9300

配置elasticsearch.yml

cluster.name: xuecheng
node.name: xc_node_1
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
node.ingest: true
bootstrap.memory_lock: false
node.max_local_storage_nodes: 2

path.data: D:\install\ElasticSearch\elasticsearch-1\data
path.logs: D:\install\ElasticSearch\elasticsearch-1\logs

http.cors.enabled: true
http.cors.allow-origin: /.*/

启动结点1

8.2.3创建结点 2

解压elasticsearch-6.2.1.zip 到D:\install\ElasticSearch\cluster\elasticsearch-2

结点1对外服务的http端口是:9201,集群管理端口是9302

elasticsearch.yml内容如下

cluster.name: xuecheng
node.name: xc_node_2
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
node.ingest: true
bootstrap.memory_lock: false
node.max_local_storage_nodes: 2

path.data: D:\install\ElasticSearch\elasticsearch-2\data
path.logs: D:\install\ElasticSearch\elasticsearch-2\logs

http.cors.enabled: true
http.cors.allow-origin: /.*/

注意:两个节点属于一个集群,集群的名称要一致。

将elasticsearch-1的ik分词器插件拷贝到elasticsearch-2中。

启动结点2,

启动head插件,浏览器访问

8.2.4 创建索引库

1)使用head连上其中一个结点

上图表示两个结点已经创建成功。xc_node_1前面有一个五角星,表示主节点。

2)下边创建索引库,共2个分片,每个分片一个副本。

副本数为1表示每片只有一个副本。

 创建成功,刷新head:

上图可以看到共有4个分片,其中两个分片是副本。0表示一片,1表示一片,

3)每个结点安装IK分词器

将plugins目录下的ik目录拷贝过去即可。

8.2.5 集群的健康

通过访问 GET /_cluster/health 来查看Elasticsearch 的集群健康情况。

用三种颜色来展示健康状态: green 、 yellow 或者 red 。

green:所有的主分片和副本分片都正常运行。

yellow:所有的主分片都正常运行,但有些副本分片运行不正常。

red:存在主分片运行不正常。

Get请求:http://localhost:9200/_cluster/health

响应结果:

{ 
    "cluster_name""xuecheng",
    "status""green",
    "timed_out"false,
    "number_of_nodes"2,
    "number_of_data_nodes"2,
    "active_primary_shards"2,
    "active_shards"4,
    "relocating_shards"0,
    "initializing_shards"0,
    "unassigned_shards"0,
    "delayed_unassigned_shards"0,
    "number_of_pending_tasks"0,
    "number_of_in_flight_fetch"0,
    "task_max_waiting_in_queue_millis"0, 
    "active_shards_percent_as_number"100
}

8.3、测试

1)创建映射并写入文档

{
  "properties": {   
           "name": {
              "type": "text",
              "analyzer":"ik_max_word",
              "search_analyzer":"ik_smart"
           },
           "description": {
              "type": "text",
              "analyzer":"ik_max_word",
              "search_analyzer":"ik_smart"
           },
           "studymodel": {
              "type": "keyword"
           },
           "pic": {
              "type": "text",
              "index":false
           },
           "timestamp": {
              "type": "date",
              "format":"yyyy-MM-dd HH:mm:ss||yyyy-MM-dd||epoch_millis"
           },
           "price": {
              "type": "float"
           }
        }
}

连接 其中任意一台结点,创建映射写入文档。

Post http://localhost:9200/xc_course/doc/3

{
  "name":"spring开发基础",
  "description":"spring 在java领域非常流行,java程序员都在用。",
  "studymodel":"201001",
  "pic":"group1/M00/00/00/wKhlQFs6RCeAY0pHAAJx5ZjNDEM428.jpg",
  "timestamp":"2018-02-24 19:11:35",
  "price":88.6
}

结果:

{
    "_index": "xc_course",
    "_type": "doc",
    "_id": "3",
    "_version": 1,
    "result": "created",
    "_shards": {
        "total": 2,
        "successful": 2,
        "failed": 0
    },
    "_seq_no": 0,
    "_primary_term": 1
}

从上边的提示可看出,两个分片都保存成功。

2)搜索
向其它一个结点发起搜索请求,查询全部数据。

Post http://localhost:9200/xc_course/doc/_search

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

结果:

{
    "took": 87,
    "timed_out": false,
    "_shards": {
        "total": 2,
        "successful": 2,
        "skipped": 0,
        "failed": 0
    },
    "hits": {
        "total": 1,
        "max_score": 1.0,
        "hits": [
            {
                "_index": "xc_course",
                "_type": "doc",
                "_id": "3",
                "_score": 1.0,
                "_source": {
                    "studymodel": "201001",
                    "name": "spring开发基础"
                }
            }
        ]
    }
}

3)关闭一个结点
关闭主节点xc_node_1,ES会重新选中一个主结点(前提在配置结点时允许它可以为主结点)

此时向活的结点发起搜索请求,仍然正常。

Post http://localhost:9201/xc_course/doc/_search

4)添加一个结点
添加结点3,端口设置为:
http端口是:9202,集群管理端口是9302

此结点的配置:

node.master: false
node.data: true

完整配置如下:

cluster.name: xuecheng
node.name: xc_node_3
network.host: 0.0.0.0
http.port: 9202
transport.tcp.port: 9302
node.master: false
node.data: true
discovery.zen.ping.unicast.hosts: ["0.0.0.0:9300", "0.0.0.0:9301"]
discovery.zen.minimum_master_nodes: 1
node.ingest: true
bootstrap.memory_lock: false
node.max_local_storage_nodes: 2

path.data: D:\install\ElasticSearch\cluster\elasticsearch-3\data
path.logs: D:\install\ElasticSearch\cluster\elasticsearch-3\logs

http.cors.enabled: true
http.cors.allow-origin: /.*/

如果报错:failed to send join request to master

原因是:节点是scp拷过去的,节点有了其他节点的data,删除后重启即可。

启动结点3,刷新head,下图显示ES将分片分在了3个结点

连接http://localhost:9202,发现数据仍然存在

向结点 3发起搜索请求:Get: http://127.0.0.1:9202/xc_course/doc/_search

全部数据可被正常搜索到。

九、Spring Data ElasticSearch 使用

9.1、Spring Data ElasticSearch简介

3.1.1 什么是Spring Data

Spring Data是一个用于简化数据库访问,并支持云服务的开源框架。其主要目标是使得对数据的访问变得方便快捷,并支持map-reduce框架和云计算数据服务。 Spring Data可以极大的简化JPA的写法,可以在几乎不用写实现的情况下,实现对数据的访问和操作。除了CRUD外,还包括如分页、排序等一些常用的功能。

Spring Data的官网:http://projects.spring.io/spring-data/

Spring Data常用的功能模块如下:

 

 

 

 

 

 3.1.2 什么是Spring Data ElasticSearch

Spring Data ElasticSearch 基于 spring data API 简化 elasticSearch操作,将原始操作elasticSearch的客户端API进行封装 。Spring Data为Elasticsearch项目提供集成搜索引擎。Spring Data Elasticsearch POJO的关键功能区域为中心的模型与Elastichsearch交互文档和轻松地编写一个存储库数据访问层。

官方网站:http://projects.spring.io/spring-data-elasticsearch/

3.3.2 常用查询命名规则

关键字命名规则解释示例
and findByField1AndField2 根据Field1和Field2获得数据 findByTitleAndContent
or findByField1OrField2 根据Field1或Field2获得数据 findByTitleOrContent
is findByField 根据Field获得数据 findByTitle
not findByFieldNot 根据Field获得补集数据 findByTitleNot
between findByFieldBetween 获得指定范围的数据 findByPriceBetween
lessThanEqual findByFieldLessThan 获得小于等于指定值的数据 findByPriceLessThan

 

十、springboot整合Spring Data ElasticSearch

由于我的es版本为6.2.1,故springboot版本选择2.1.4

1)、添加依赖

<parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.0.1.RELEASE</version>
    </parent>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-elasticsearch</artifactId>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>

 

2)、编写application.yml

server:
  port: 8082
spring:
  data:
    elasticsearch:
      cluster-name: xuecheng
      #配置es节点信息,逗号分隔,如果没有指定,则启动ClientNode(9200端口是http查询使用的。9300集群使用。这里使用9300.)
      cluster-nodes: localhost:9300,localhost:9301,localhost:9302

3)、编写实体Article

import lombok.Data;
import org.springframework.data.annotation.Id;
import org.springframework.data.elasticsearch.annotations.Document;
import org.springframework.data.elasticsearch.annotations.Field;
import org.springframework.data.elasticsearch.annotations.FieldType;

@Data
@Document(indexName="blog3", type="article",shards = 2, replicas = 1)
public class Article {
    //@Id 文档主键 唯一标识,id不要加@Field注解
    @Id
    private Integer id;
    @Field(index=true,analyzer="ik_smart",store=true,searchAnalyzer="ik_smart",type = FieldType.text)
    private String title;
    @Field(index=true,analyzer="ik_smart",store=true,searchAnalyzer="ik_smart",type = FieldType.text)
    private String content;
}

其中,注解解释如下:

@Document(indexName="blob3",type="article"):
    indexName:索引的名称(必填项)
    type:索引的类型
@Id:主键的唯一标识
@Field(index=true,analyzer="ik_smart",store=true,searchAnalyzer="ik_smart",type = FieldType.text)
    index:是否设置分词
    analyzer:存储时使用的分词器
    searchAnalyze:搜索时使用的分词器
    store:是否存储
    type: 数据类型

@Document注解里面的几个属性,类比mysql的话是这样:

index –> DB   
type –> Table   
Document –> row

加上@Id注解后,在Elasticsearch里对应的该列就是主键了,在查询时就可以直接用主键查询。其实和mysql非常类似,基本就是一个数据库。

@Persistent
@Inherited
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE})
public @interface Document {
   String indexName();//索引库的名称,个人建议以项目的名称命名
   String type() default "";//类型,个人建议以实体的名称命名
   short shards() default 5;//默认分区数
   short replicas() default 1;//每个分区默认的备份数
   String refreshInterval() default "1s";//刷新间隔
   String indexStoreType() default "fs";//索引文件存储类型
}

@Field

加上了@Document注解之后,默认情况下这个实体中所有的属性都会被建立索引、并且分词。通过@Field注解来进行详细的指定,如果没有特殊需求,那么只需要添加@Document即可。

@Field注解的定义如下:

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
@Documented
@Inherited
public @interface Field {

   FieldType type() default FieldType.Auto;#自动检测属性的类型
   FieldIndex index() default FieldIndex.analyzed;#默认情况下分词
   DateFormat format() default DateFormat.none;
   String pattern() default "";
   boolean store() default false;#默认情况下不存储原文
   String searchAnalyzer() default "";#指定字段搜索时使用的分词器
   String indexAnalyzer() default "";#指定字段建立索引时指定的分词器
   String[] ignoreFields() default {};#如果某个字段需要被忽略
   boolean includeInParent() default false;
}

 

4)、编写启动类

@SpringBootApplication(exclude = DataSourceAutoConfiguration.class)
//扫描Dao接口
@EnableElasticsearchRepositories(basePackages = {"com.zwh.dao"})
public class MyDataElasticsearchApplication {
    public static void main(String[] args) {
        SpringApplication.run(MyDataElasticsearchApplication.class,args);
    }
}

5)、编写Dao

public interface ArticleRepository extends ElasticsearchRepository<Article,Integer> {
}

6)、编写测试类

import com.zwh.dao.ArticleRepository;
import com.zwh.entity.Article;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.sort.SortBuilders;
import org.elasticsearch.search.sort.SortOrder;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.elasticsearch.core.ElasticsearchTemplate;
import org.springframework.data.elasticsearch.core.query.NativeSearchQueryBuilder;
import org.springframework.data.elasticsearch.core.query.SearchQuery;
import org.springframework.test.context.junit4.SpringRunner;

import java.util.List;

@RunWith(SpringRunner.class)
@SpringBootTest(classes = MyDataElasticsearchApplication.class)
public class IndexTest {
    @Autowired
    private ElasticsearchTemplate elasticsearchTemplate;  //注入ElasticsearchTemplate类
    @Autowired
    private ArticleRepository articleRepository;
    @Test
    public void testCreate(){
        // 创建索引,会根据Item类的@Document注解信息来创建
        elasticsearchTemplate.createIndex(Article.class);
        // 配置映射,会根据Item类中的id、Field等字段来自动完成映射
        elasticsearchTemplate.putMapping(Article.class);
    }
    // 删除索引
    @Test
    public void testDelete(){
        this.elasticsearchTemplate.deleteIndex(Article.class);
        this.elasticsearchTemplate.deleteIndex("blog3");
    }
    // 创建文档
    @Test
    public void testCreateDocument() {
        Article article = new Article();
        article.setId(1);
        article.setTitle("活着");
        article.setContent("讲述了在大时代背景下,随着内战、三反五反、大跃进、“文化大革命”等社会变革,徐福贵的人生和家庭不断经受着苦难,到了最后所有亲人都先后离他而去,仅剩下年老的他和一头老牛相依为命。小说以普通、平实的故事情节讲述了在急剧变革的时代中福贵的不幸遭遇和坎坷命运,在冷静的笔触中展现了生命的意义和存在的价值,揭示了命运的无奈,与生活的不可捉摸。");
        articleRepository.save(article);
        Article article1 = new Article();
        article1.setId(2);
        article1.setTitle("平凡的世界");
        article1.setContent("该书以中国70年代中期到80年代中期十年间为背景,通过复杂的矛盾纠葛,以孙少安和孙少平两兄弟为中心,刻画了当时社会各阶层众多普通人的形象;劳动与爱情、挫折与追求、痛苦与欢乐、日常生活与巨大社会冲突纷繁地交织在一起,深刻地展示了普通人在大时代历史进程中所走过的艰难曲折的道路。");
        articleRepository.save(article1);
        Article article2 = new Article();
        article2.setId(3);
        article2.setTitle("傲慢与偏见");
        article2.setContent("该片讲述了19世纪初期英国的乡绅之女伊丽莎白·班内特五姐妹的爱情与择偶的故事。");
        articleRepository.save(article2);
    }
    /**测试更新*/
    @Test
    public void update(){
        Article article = new Article();
        article.setId(3);
        article.setTitle("傲慢与偏见 更新");
        article.setContent("该片讲述了19世纪初期英国的乡绅之女伊丽莎白·班内特五姐妹的爱情与择偶的故事。");
        articleRepository.save(article);
    }

    /**测试删除*/
    @Test
    public void delete(){
        Article article = new Article();
        article.setId(3);
        articleRepository.delete(article);
    }
    /**批量插入*/
    @Test
    public void save100(){
        for(int i=10;i<=15;i++){
            Article article = new Article();
            article.setId(i);
            article.setTitle(i+"elasticSearch 3.0版本发布");
            article.setContent(i+"ElasticSearch是一个基于Lucene的搜索服务器。它提供了一个分布式多用户能力的全文搜索引擎,基于RESTful web接口");
            articleRepository.save(article);
        }
    }
    @Test
    public void findAll(){
        for (Article article : articleRepository.findAll()) {
            System.out.println(article.toString());
        }
    }
    /**分页查询
     * 注意:page从0开始
     * */
    @Test
    public void findAllPage(){
        Pageable pageable = PageRequest.of(0,10);
        Page<Article> page = articleRepository.findAll(pageable);
        for(Article article:page.getContent()){
            System.out.println(article.toString());
        }
    }
    /**条件查询*/
    @Test
    public void findByTitle(){
        String condition = "活着";
        List<Article> articleList = articleRepository.findByTitle(condition);
        for(Article article:articleList){
            System.out.println(article);
        }
    }
    @Test
    public void findByTitleAndContent(){
        List<Article> articleList = articleRepository.findByTitleAndContent("版本发布","服务器");
        for(Article article:articleList){
            System.out.println(article.toString());
        }
    }
    @Test
    public void findByTitleOrContent(){
        List<Article> articleList = articleRepository.findByTitleOrContent("活着","英国");
        for(Article article:articleList){
            System.out.println(article.toString());
        }
    }
    @Test
    public void findByTitleNot(){
        List<Article> articleList = articleRepository.findByTitleNot("活着");
        for(Article article:articleList){
            System.out.println(article.toString());
        }
    }
    /**条件分页查询*/
    @Test
    public void findByTitlePage(){
        String condition = "活着";
        Pageable pageable = PageRequest.of(0,2);
        Page<Article> page = articleRepository.findByTitle(condition,pageable);
        for(Article article:page.getContent()){
            System.out.println(article);
        }
    }

    // 使用Elasticsearch的原生查询对象进行查询。
    @Test
    public void findByNativeQuery() {
        //创建一个SearchQuery对象
        SearchQuery searchQuery = new NativeSearchQueryBuilder()
                //设置查询条件,此处可以使用QueryBuilders创建多种查询
                .withQuery(QueryBuilders.queryStringQuery("活着").defaultField("title"))
                //还可以设置分页信息
                .withPageable(PageRequest.of(0, 5))
                //创建SearchQuery对象
                .build();

        //使用模板对象执行查询
        elasticsearchTemplate.queryForList(searchQuery, Article.class).forEach(a-> System.out.println(a));
    }

    @Test
    public void findByNativeQuery1() {
        NativeSearchQueryBuilder builder = new NativeSearchQueryBuilder();
        builder.withQuery(QueryBuilders.boolQuery().must(QueryBuilders.matchQuery("title", "版本发布"))
                .must(QueryBuilders.matchQuery("content", "搜索服务器")));
        builder.withSort(SortBuilders.fieldSort("id").order(SortOrder.DESC));

        SearchQuery searchQuery = builder.build();
        //使用模板对象执行查询
        elasticsearchTemplate.queryForList(searchQuery, Article.class).forEach(a-> System.out.println(a));
    }

}

 

posted on 2023-01-11 11:21  周文豪  阅读(143)  评论(0编辑  收藏  举报