ElasticSearch快速入门和实战

ElasticSearch快速入门和实战

1、什么是全文搜索 lucene

全文搜索是计算机通过扫描文章的每一个词,对每一个词建立一个索引,指明改词咋子文章中出现的次数和位置,当用户查询时根据索引查找,类似字典通过检索字表查询的字过程

​ 检索:检(建立索引) 索(搜索索引)

​ 全文检索以文本为检索对象,找出含有指定词汇的文本。全面、准确和快速是衡量全文检索系统的关键指标

​ 关于全文检索我们要知道

1. 只处理文本
2. 不处理语义
3. 搜索时英文不区分大小写
4. 结果列表有相关度排序

2、什么是Elastic Search

​ ElasticSearch简称ES,是基于lucene构建的一个准实时的高扩展的分布式搜索引擎。目前在企业级的搜索引擎中是非常流行的。Elasticsearch使用Java开发,提供了简单的RESTful API从而让全文搜索变得简单,避免了lucene的复杂性

3、Elasticsearch的特点

(1)可以作为一个大型分布式集群(数百台服务器)技术,处理PB级数据,服务大公司;也可以运行在单机上,服务小公司

(2)Elasticsearch不是什么新技术,主要是将全文检索、数据分析以及分布式技术,合并在了一起,才形成了独一无二的ES;lucene(全文检索),商用的数据分析软件(也是有的),分布式数据库(mycat)

(3)对用户而言,是开箱即用的,非常简单,作为中小型的应用,直接3分钟部署一下ES,就可以作为生产环**境的系统来使用了,数据量不大,操作不是太复杂

4、Elasticsearch的应用场景

国外

​ 维基百科、Stack Overflow(国外的程序异常讨论论坛)、GitHub…

国内

百度阿里巴巴腾讯、携程、滴滴、今日头条、饿了么、360安全、小米、vivo均有对ES的使用

5、Elasticsearch的相关概念关系图

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-nbla5JFB-1601288686101)(ElasticSearch%E5%BF%AB%E9%80%9F%E5%85%A5%E9%97%A8%E5%92%8C%E5%AE%9E%E6%88%98.assets/image-20200922232827960.png)]

6、kibana的基本操作

6.1、索引的基本操作

# 1.创建索引(不能有大写)
PUT /ems
PUT /wadwa

# 2.查看索引
GET /_cat/indices?v

# 3.删除索引
DELETE /ems
DELETE /*

6.2、类型的操作

创建类型
# 类型操作 index/type(mapping) 一个索引只能存在一个类型 ems/emp(id name age bir)
PUT /ems
{
  "mappings": {
    "emp" : {
      "properties": {
         "id" : {"type":"keyword"},
         "name" : {"type":"text"},
         "age" : {"type":"integer"},
         "bir" : {"type":"date"}
    }
    }
  }
}
Mapping Type : text,keyword,date,integer,long,double ,boolean ,ip
查看类型
# 查看创建索引以及索引中的映射类型
# 语法: GET/索引名/mapping/类型名

GET /ems/_mapping

6.3、文档的基本操作

添加文档
PUT /ems/emp/1
{
  "id":1,
  "name":"zhangsan",
  "age" : 23,
  "bir" : "2012-12-12"
}

#注意:如果需要自动生成id要以post方式,即POST /ems/emp

# 结果
{
  "_index" : "ems",
  "_type" : "emp",
  "_id" : "1",
  "_version" : 1,
  "result" : "created",  #创建
  "_shards" : {
    "total" : 2,
    "successful" : 1,
    "failed" : 0
  },
  "_seq_no" : 2,
  "_primary_term" : 1
}
查询文档
GET /ems/emp/1

# 结果
{
  "_index" : "ems",
  "_type" : "emp",
  "_id" : "1",
  "_version" : 1,
  "_seq_no" : 0,
  "_primary_term" : 1,
  "found" : true,
  "_source" : {  #查询结果
    "id" : 1,
    "name" : "zhangsan",
    "age" : 23,
    "bir" : "2012-12-12"
  }
}

删除文档
DELETE /ems/emp/1

# 结果
{
  "_index" : "ems",
  "_type" : "emp",
  "_id" : "1",
  "_version" : 2,
  "result" : "deleted", #删除成功
  "_shards" : {
    "total" : 2,
    "successful" : 1,
    "failed" : 0
  },
  "_seq_no" : 1,
  "_primary_term" : 1
}


更新文档
不保留原始数据更新
GET /ems/emp/1
{
  "name":"lisi"
}

# 插入之后的结果
{
  "_index" : "ems",
  "_type" : "emp",
  "_id" : "1",
  "_version" : 2,
  "_seq_no" : 3,
  "_primary_term" : 2,
  "found" : true,
  "_source" : {
    "name" : "lisi"
  }
}
保留原始数据更新(更新时添加新字段)
GET /ems/emp/1/_update
{
  "doc":{
    "name":"wangwu",
    "age":18
  }
}

#结果
{
  "_index" : "ems",
  "_type" : "emp",
  "_id" : "1",
  "_version" : 5,
  "_seq_no" : 6,
  "_primary_term" : 2,
  "found" : true,
  "_source" : {
    "id" : 1,
    "name" : "wangwu",
    "age" : 18,
    "bir" : "2012-12-12"
  }
}

文档的批量操作
# 文档的批量操作: _bulk
PUT /ems/emp/_bulk
{"index":{"_id":5}}
{"name":"小明","age":23,"bir":"2013-10-10","intr":"小妹妹"}
{"index":{"_id":4}}
{"name":"小黑","age":24,"bir":"2013-10-10","intr":"小弟弟"}
{"delete":{"_id":4}}
{"update":{"id":1}}
{"doc":{"name":"小霸王"}}

7、Elasticsearch的高级搜索(Query)

7.1、检索方式

  • 使用语法:

    URL查询: GET/索引/类型/_search?参数

    DSL查询: GET/索引/类型/_search{}

官方推荐使用DSL方式

7.2、URL检索

GET /ems/emp/_search?q=*&sort=age:desc&size=5&form=0&source=name,age,bir

_search 搜索的api

q=* 匹配所有文档

sort 以结果中指定字段排序,默认是asc

size 每页显示条数

from 从第几条开始

source : 指定显示的字段

7.3、DSL检索

普通查询
GET /ems/emp/_search
{
  "query": {"match_all": {}},
  "sort": [
    {
      "age": {
        "order": "desc"
      }
    },
    {
      "bir": {
        "order": "desc"
      }
    }
  ],
  "size": 3,
  "from": 1,
  "_source": "{name,age,bir}"
}

"query": {"match_all": {}} : 查询所有

"sort": [ { "age": {"order": "desc"}}, {"bir": { "order": "desc"} }] : 排序,可多个字段联合排序,

注意: 不能以text类型排序

"size": 3, "from": 1: 显示下标1开始的3条记录

"_source": "{name,age,bir}" : 指定要显示的字段

关键字查询(term)
GET /ems/emp/_search
{
  "query": {
    "term": {
      "FIELD": {
        "value": "VALUE"
      }
    }
  }
}

FIELD : 字段名

VALUE : 要查找的关键字

重点:

  • type : text 类型分词,除此之外的类型都不分词
  • ES默认使用的分词器为标准分词器
  • standard 中文 : 单字分词 英文 : 单词分词
索引库原理

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ibcWrUpk-1601288686104)(ElasticSearch%E5%BF%AB%E9%80%9F%E5%85%A5%E9%97%A8%E5%92%8C%E5%AE%9E%E6%88%98.assets/image-20200923235137484.png)]

范围查询(range)
GET /ems/emp/_search
{
  "query": {"range": {
    "age": {
      "gte": 1,
      "lte": 10
    }
  }}
}

gte :大于等于

lte : 小于等于

前缀查询(prefix)

检索含有指定前缀的关键词的相关文档

# 前缀查询基于关键词前缀查询 prefix 
GET /ems/emp/_search
{
  "query": {
    "prefix": {
      "name": {
        "value": "李"
      }
    }
  }
}
通配符查询 (wildcard)
GET /ems/emp/_search
{
  "query": {
    "wildcard": {
      "name": {
        "value": "李*"
      }
    }
  }
}

? : 匹配单个字符

* : 匹配任意个字符

多个id查询 (ids)
GET /ems/emp/_search
{
  "query": {
    "ids": {
      "name":["id1","id2","idn"]
    }
  }
}
模糊查询(fuzzy)

1、最大模糊错误在0~2之间

2、搜索关键词为2不允许存在模糊

3、搜索长度在3~5允许一次模糊查询

4、搜索长度大于5,允许两次模糊

布尔查询(bool)

bool: 组合多个条件实现复杂查询

must: 相当于&& 同时成立

should: 相当于||成立一个就行

must_not:相当于!不能满足任何一个

高亮查询(highlight)
GET /ems/emp/_search
{
  "query": {
    "term": {
      "content": {
        "value": "redis"
      }
    }
  },
  "highlight": {
    "pre_tags": ["<span style='color:red'"],
    "post_tags": ["</span>"],
    "fields": {
      "content" : {}
    }
  }
}

将查询结果带redis的内容进行高亮,默认是em标签为斜体

pre_tags : 进行高亮的前缀

post_tags : 后缀

多字段查询 (multi_match)
GET /ems/emp/_search
{
  "query": {
    "multi_match": {
      "query": "Redis小",
      "fields": ["content","name"]
    }
  }
}

如果搜索的字段分词,先分词在搜索

如果不分词,则整体搜索

多字段分词查询
GET /ems/emp/_search
{
  "query": {
    "query_string": {
      "default_field": "content",
      "query": "开源框架"
    }
  }
}

先对query这句话分词,在去content搜索

8、ik分词器

8.1 、ik分词器的在线安装

使用该命令在线安装

./bin/elasticsearch-plugin install https://github.com/medcl/elasticsearch-analysis-ik/releases/download/v6.3.0/elasticsearch-analysis-ik-6.3.0.zip

注意:需要和es版本一致

8.2、测试ik分词器安装成功

GET /_analyze
{
  "text" : "中华人民共和国国歌",
  "analyzer": "ik_max_word" #ik_smart
}

# 结果
{
  "tokens" : [
    {
      "token" : "中华人民共和国",
      "start_offset" : 0,
      "end_offset" : 7,
      "type" : "CN_WORD",
      "position" : 0
    },
    {
      "token" : "中华人民",
      "start_offset" : 0,
      "end_offset" : 4,
      "type" : "CN_WORD",
      "position" : 1
    },
    {
      "token" : "中华",
      "start_offset" : 0,
      "end_offset" : 2,
      "type" : "CN_WORD",
      "position" : 2
    },
    {
      "token" : "华人",
      "start_offset" : 1,
      "end_offset" : 3,
      "type" : "CN_WORD",
      "position" : 3
    },....}

ik_max_word: 表示最细分词方式

ik_smart : 表示最粗分词方式,即中华人民共和国国歌拆分为 “中华人民共和国” 和 “国歌”

8.3、创建索引时指定分词类型

PUT /ems
{
  "mappings": {
    "emp" : {
      "properties": {
         "id" : {"type":"keyword"},
         "name" : {
           "type":"text",
           "analyzer" : "ik_max_word"
         },
         "content" : {
           "type":"text",
           "analyzer" : "ik_max_word"
         },
         "bir" : {"type":"date"}
    }
    }
  }
}

8.4、配置扩展字典

添加一条数据文档

# 添加文档
PUT /ems/emp/2
{
  "id":2,
  "name":"张三啊",
  "content":"小明开车到大街上,被碰瓷了!",
  "bir":"2018-11-11"
}

# 查询
GET /ems/emp/_search
{
  "query": {
    "term": {
      "content": {
        "value": "碰瓷"
      }
    }
  }
}

发现根据关键字碰瓷搜索查不到,因为碰瓷是比较新的词,es并没有

这时就需要使用扩展词了

# 新建一个文件exts.dic,在里面添加"碰瓷"
vim /usr/local/elasticsearch-6.8.0/config/analysis-ik/exts.dic

打开配置文件IKAnalyzer.cfg.xml

<?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">exts.dic </entry>
         <!--用户可以在这里配置自己的扩展停止词字典-->
        <entry key="ext_stopwords"></entry>
        <!--用户可以在这里配置远程扩展字典 -->
        <!-- <entry key="remote_ext_dict">words_location</entry> -->
        <!--用户可以在这里配置远程扩展停止词字典-->
        <!-- <entry key="remote_ext_stopwords">words_location</entry> -->
</properties>

根据需要进行配置,这里我把exts.dic文件添加到扩展字典

重启elasticsearch和kibana,再次搜索

GET /ems/emp/_search
{
  "query": {
    "term": {
      "content": {
        "value": "碰瓷"
      }
    }
  }
}

发现可以搜到结果了

8.5、配置远程扩展字典

1、新建一个springboot项目,在mian下面添加webapp/ext.txt

2、在ext.txt文件添加内容"杠精"

3、将该springboot项目配置修改如下:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-bOcvsVzI-1601288686107)(ElasticSearch%E5%BF%AB%E9%80%9F%E5%85%A5%E9%97%A8%E5%92%8C%E5%AE%9E%E6%88%98.assets/image-20200925193847285.png)]

4、修改配置文件IKAnalyzer.cfg.xml

<entry key="remote_ext_dict">http://localhost/es/ext.txt</entry>

localhost改成你的主机的ip,如果是虚拟机那就是192.168…

如果是云服务器要和自己电脑连接可使用端口映射

5、重新启动ES

看到这个表示扩展成功

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-pgcqTNSc-1601288686110)(ElasticSearch%E5%BF%AB%E9%80%9F%E5%85%A5%E9%97%A8%E5%92%8C%E5%AE%9E%E6%88%98.assets/image-20200925204209193.png)]

6、我们再在ext.txt随便添加点东西,重新部署springboot,发现es可以实时刷新

9、过滤查询(Filter Query)

9.1、过滤查询

  		过滤查询(Filter queries)只是**简单的检查包含或者排除**,筛选出符合的文档,**并不计算、得分、排序、并且可以缓存文档**,因此性能比单查询快,**过滤适用于大数据,因此一般先进行过滤,再在过滤的基础上查询**

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-FI87qrgi-1601288686111)(ElasticSearch%E5%BF%AB%E9%80%9F%E5%85%A5%E9%97%A8%E5%92%8C%E5%AE%9E%E6%88%98.assets/image-20200925235832712.png)]

9.2、过滤查询举例

GET /ems1/emp/2
{
  "query": {
    "bool": {
      "filter": {
          "exists": {
            "field": "name"
        }
      }
    }
  }
}

一般使用过滤都会加上bool查询

exists:表示过滤存在属性name的文档,即如果文档没有name,就不查询

filter后面也可以根据term…查询

10、java操作ElasticSearch

10.1、服务说明

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-IywlyDNo-1601288686113)(ElasticSearch%E5%BF%AB%E9%80%9F%E5%85%A5%E9%97%A8%E5%92%8C%E5%AE%9E%E6%88%98.assets/image-20200926101419368.png)]

将需要进行搜索的内容存放到es中,也就是说es存放的是大量可以进行搜索的索引库

某些内容的详细信息还是需要从数据库查

为什么不都存放在es中呢?

因为es的事务做的不好

10.2、java操作ES

1、导入依赖

es的相关依赖
注意:版本需和安装的es版本一致

<dependency>
    <groupId>org.elasticsearch</groupId>
    <artifactId>elasticsearch</artifactId>
    <version>6.8.0</version>
</dependency>

<dependency>
    <groupId>org.elasticsearch.client</groupId>
    <artifactId>transport</artifactId>
    <version>6.8.0</version>
</dependency>

<dependency>
    <groupId>org.elasticsearch.plugin</groupId>
    <artifactId>transport-netty4-client</artifactId>
    <version>6.8.0</version>
</dependency>
2、索引的操作
package com.huidu;

import org.elasticsearch.action.admin.indices.create.CreateIndexRequest;
import org.elasticsearch.action.admin.indices.create.CreateIndexResponse;
import org.elasticsearch.action.admin.indices.delete.DeleteIndexRequestBuilder;
import org.elasticsearch.action.support.master.AcknowledgedResponse;
import org.elasticsearch.client.transport.TransportClient;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.transport.TransportAddress;
import org.elasticsearch.common.xcontent.XContentType;
import org.elasticsearch.transport.client.PreBuiltTransportClient;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;

import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.concurrent.ExecutionException;

public class TestIndexAndTypeMapping {

    private TransportClient transportClient;

    @Before
    public void before() throws UnknownHostException {

        //创建客户端
        this.transportClient=new PreBuiltTransportClient(Settings.EMPTY);
        //设置es服务地址
        transportClient.addTransportAddress(new TransportAddress(InetAddress.getByName("123.56.226.106"),9300));
    }

    @After
    public void after(){
        transportClient.close();
    }

    //创建索引
    @Test
    public void testCreateIndex(){
        //创建索引  需保证索引不存在
        CreateIndexResponse dangdang = transportClient.admin().indices().prepareCreate("dangdang2").get();
        //获取信息
        System.out.println(dangdang.isAcknowledged());
    }

    //删除索引
    @Test
    public void testDeleteIndex(){
        //删除索引
        AcknowledgedResponse dangdang = transportClient.admin().indices().prepareDelete("dangdang2").get();
        //获取信息
        System.out.println(dangdang.isAcknowledged());
    }

    //创建索引,创建类型,创建mapping
    @Test
    public void testCreateIndexAndTypeMapping() throws ExecutionException, InterruptedException {
        //创建索引对象
        CreateIndexRequest dangdang = new CreateIndexRequest("dangdang");
        //索引类型映射
        //参数1:类型名 参数2:映射的json格式  参数3:映射格式类型
        dangdang.mapping("book","{\"properties\":{\"id\":{\"type\":\"keyword\"},\"name\":{\"type\":\"text\",\"analyzer\":\"ik_max_word\"},\"content\":{\"type\":\"text\",\"analyzer\":\"ik_max_word\"},\"bir\":{\"type\":\"date\"}}}", XContentType.JSON);

        //创建索引
        CreateIndexResponse createIndexResponse = transportClient.admin().indices().create(dangdang).get();
        System.out.println(createIndexResponse.isAcknowledged());
    }
}
3、文档的操作
3.1、添加文档
  • 创建一个实体类Book,属性类型和映射类型相同
@Data
@NoArgsConstructor
@AllArgsConstructor
@Accessors(chain = true)
public class Book {

    private String id;
    private String content;
    private Date bir;
    private String name;
}
  • 添加一条指定id的文档
@Test
public void createDoc(){
    Book book=new Book(null,"我觉得java是世界上最好的语言",new Date(),"java入坟");
    String bookJson = JSONObject.toJSONStringWithDateFormat(book,"yyyy-MM-dd");
    IndexResponse indexResponse = transportClient.prepareIndex("dangdang", "book", "1").setSource(bookJson, XContentType.JSON).get();
    System.out.println("插入是否成功"+indexResponse.status());
}
  • 添加一条文档 自动添加id
@Test
public void createDocAuto(){
    Book book=new Book(null,"我觉得java是世界上最好的语言-1",new Date(),"java入坟-1");
    String bookJson = JSONObject.toJSONStringWithDateFormat(book,"yyyy-MM-dd");
    IndexResponse indexResponse = transportClient.prepareIndex("dangdang", "book").setSource(bookJson, XContentType.JSON).get();
    System.out.println("插入是否成功"+indexResponse.status());

3.2、更新一条文档
//更新一条文档
@Test
public void updateDoc(){
    Book book=new Book();
    book.setName("java入门").setContent("java从入门到入坟aaaa");
    String bookJson = JSONObject.toJSONString(book);

    UpdateResponse updateResponse = transportClient.prepareUpdate("dangdang", "book", "1").setDoc(bookJson, XContentType.JSON).get();
    System.out.println(updateResponse.status());
}
3.3、删除一条文档
//删除一条文档
@Test
public void deleteDoc(){
    DeleteResponse deleteResponse = transportClient.prepareDelete("dangdang", "book", "kMQLynQBebkfspAecwNI").get();
    System.out.println(deleteResponse.status());
}
3.4、查询文档
查询一条文档
//查询一条文档
@Test
public void findOneDoc(){
    GetResponse documentFields = transportClient.prepareGet("dangdang", "book", "1").get();
    System.out.println(documentFields.getSourceAsString());
}
各种查询,查询所有
@Test
public void testQuery(){
    //查询所有
    MatchAllQueryBuilder matchAllQueryBuilder=QueryBuilders.matchAllQuery();
    //termQuery
    TermQueryBuilder termQueryBuilder = QueryBuilders.termQuery("content", "最好");
    //rangeQuery...
    //调用方法
    testResult(termQueryBuilder);
}


public void testResult(QueryBuilder queryBuilder){
    SearchResponse searchResponse = transportClient.prepareSearch("dangdang")//索引
        .setTypes("book")//类型
        .setQuery(queryBuilder)//查询条件
        .setFrom(2) //起始条数 默认是0,(当前页-1)*size
        .setSize(5) //每页展示记录数
        .addSort("bir", SortOrder.DESC) //年龄升序排序
        .get();

    System.out.println("总条数:"+ searchResponse.getHits().getTotalHits());
    System.out.println("最大得分:"+searchResponse.getHits().getMaxScore());
    SearchHit[] hits = searchResponse.getHits().getHits();
    for (SearchHit hit : hits) {
        System.out.println("每一条记录:"+hit.getSourceAsString());
    }
}
高亮查询
//高亮查询
@Test
public void highlight(){
    //创建highlightBuilder对象
    HighlightBuilder highlightBuilder = new HighlightBuilder();
    highlightBuilder.field("*")
                    .requireFieldMatch(false)
                    .preTags("<span style='color:red'>;")
                    .postTags("</span>");

    SearchResponse searchResponse = transportClient.prepareSearch("dangdang")
            .setTypes("book")
            .setQuery(QueryBuilders.multiMatchQuery("java", "content", "name"))
            .highlighter(highlightBuilder)
            .get();

    SearchHit[] hits = searchResponse.getHits().getHits();
    for (SearchHit hit : hits) {
        Book book=new Book();
        //封装原始结果
         Map<String, Object> sourceAsMap = hit.getSourceAsMap();
         book.setId(hit.getId());
         book.setName(sourceAsMap.get("name").toString());
         book.setContent(sourceAsMap.get("content").toString());
         book.setBir(null);

         //高亮处理
        Map<String, HighlightField> highlightFieldMap=hit.getHighlightFields();
        if(highlightFieldMap.containsKey("name")){
            String highName = highlightFieldMap.get("name").fragments()[0].toString();
            System.out.println(highName);
            book.setName(highName);
        }
        if(highlightFieldMap.containsKey("content")){
            String highContent = highlightFieldMap.get("content").fragments()[0].toString();
            System.out.println(highContent);
            book.setContent(highContent);
        }
    }
}
过滤查询
//过滤查询
@Test
public void FilterQuery(){
    SearchResponse searchResponse = transportClient.prepareSearch("dangdang")//索引
            .setTypes("book")//类型
            .setPostFilter(QueryBuilders.termQuery("content", "java"))//查询过滤
            .setQuery(QueryBuilders.matchAllQuery())//查询
            .get();

    SearchHit[] hits = searchResponse.getHits().getHits();

    for (SearchHit hit : hits) {
        System.out.println(hit.getSourceAsString());
    }
}
3.5、批量操作
//批量操作
@Test
public void Bulk(){

    //添加
    Book book=new Book("3","python入门是一本好书",new Date(),"python入门");
    IndexRequest source = new IndexRequest("dangdang", "book")
            .source(JSONObject.toJSONStringWithDateFormat(book, "yyyy-MM-dd"), XContentType.JSON);

    //删除
    DeleteRequest deleteRequest = new DeleteRequest("dangdang","book","2");

    //修改
    Book book1=new Book();
    book1.setContent("是真的很不错");
    UpdateRequest updateRequest = new UpdateRequest("dangdang","book","3").doc(JSONObject.toJSONString(book1));

    //返回批量更新对象
    BulkRequestBuilder bulkRequestBuilder = transportClient.prepareBulk();
    BulkResponse bulkItemResponses = bulkRequestBuilder
            .add(source)
            .add(deleteRequest)
            .add(updateRequest)
            .get();

    BulkItemResponse[] items = bulkItemResponses.getItems();
    for (BulkItemResponse item : items) {
        System.out.println(item.status());
    }

}

11、SpringBoot操作ElasticSearch

11.1、引入依赖

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-elasticsearch</artifactId>
</dependency>

11.2、去spring data官网将该配置复制下来

@Configuration
public class ElasticSearchRestClientConfig extends AbstractElasticsearchConfiguration {

    //这个client用来替换 transportClient(9300)-->tcp对象
    @Override
    @Bean
    public RestHighLevelClient elasticsearchClient() {
        final ClientConfiguration clientConfiguration = ClientConfiguration.builder()
                .connectedTo("123.56.226.106:9200")  //http
                .build();

        return RestClients.create(clientConfiguration).rest();
    }
}

11.3、使用RestHighLevelClient对象操作es

文档删除
@Test
public  void test() throws IOException {
    DeleteRequest deleteRequest = new DeleteRequest("dangdang","book","1");
    DeleteResponse delete = restHighLevelClient.delete(deleteRequest, RequestOptions.DEFAULT);
    System.out.println(delete.status());
}
文档添加
@Test
public void test1() throws IOException {
    IndexRequest indexRequest = new IndexRequest("dangdang","book","6");
    indexRequest.source("{\"name\":\"小黑\",\"content\":\"小黑黑\"}", XContentType.JSON);
    IndexResponse index = restHighLevelClient.index(indexRequest, RequestOptions.DEFAULT);
    System.out.println(index.status());
}
高级搜索
@Test
public void test2() throws IOException {
    SearchRequest searchRequest = new SearchRequest("dangdang");

    //搜索构建对象
    SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
    searchSourceBuilder.query(QueryBuilders.matchAllQuery())
            .from(0)
            .size(5);
            //.sort("");
            //...

    //创建搜索请求
    searchRequest.types("book").source(searchSourceBuilder);
    SearchResponse searchResponse = restHighLevelClient.search(searchRequest,RequestOptions.DEFAULT);

    System.out.println("总条数:"+ searchResponse.getHits().getTotalHits());
    System.out.println("最大得分:"+searchResponse.getHits().getMaxScore());
    SearchHit[] hits = searchResponse.getHits().getHits();
    for (SearchHit hit : hits) {
        System.out.println("每一条记录:"+hit.getSourceAsString());
    }
}

11.4、使用Repository继承ElasticsearchRepository接口的方式

优点:可自动将对象转换成json,或者将查询出来的json封装成对象

缺点:只能进行较为简单的操作,复杂操作还是需要使用RestHighLevelClient

创建要创建的索引和类型对象的实体类
@Data
/**
 * 用在类上  作用:将emp的对象映射成Es的一条json格式的文档
 *      indexName:用来指定这个对象的转为json文档存在索引
 *      type :这个索引下创建的类型名称
 */
@Document(indexName = "ems",type = "emp")
public class Emp {

    @Id //使对象的id和文档的_id 一一对应
    private String id;
    //@Field:用在属性上 代表mapping 的一个字段   type:指定字段类型  analyzer:指定分词器
    @Field(type = FieldType.Text,analyzer = "ik_max-word")
    private String name;
    @Field(type = FieldType.Integer)
    private Integer age;
    @Field(type = FieldType.Date)
    @JsonFormat(pattern = "yyyy-MM-dd")
    private Date bir;
    @Field(type = FieldType.Text,analyzer = "ik_max-word")
    private String content;
    @Field(type = FieldType.Keyword)
    private String address;
}
自定义EmpRepository接口继承ElasticsearchRepository接口
//自定义EmpRepository
public interface EmpRepository extends ElasticsearchRepository<Emp,String> {
}
使用该接口进行基本的增删改查
@SpringBootTest
public class TestEmpRepository {

    @Autowired
    private EmpRepository empRepository;

    //保存|更新一条文档(setId为已经存在的id就是更新)
    @Test
    public void testSave(){
        Emp emp = new Emp();
        emp.setId(UUID.randomUUID().toString());
        emp.setName("张三丰");
        emp.setBir(new Date());
        emp.setAddress("武当山学院");
        emp.setAge(23);
        emp.setContent("武当大师,一生创建多种武功,如太极,武当剑法");
        empRepository.save(emp);
    }

    //删除一条文档deleteById
    //删除所有deleteAll

    //检索一条记录(自动封装为对象)
    @Test
    public void testFindOne(){
        Optional<Emp> byId = empRepository.findById("c8e403cb-cb21-4c1f-9a74-2947c3470901");
        System.out.println(byId.get());
    }

    //查询所有(排序)
    @Test
    public void testFindAll(){
        Iterable<Emp> empRepositoryAll = empRepository.findAll(Sort.by(Sort.Order.asc("age")));
        empRepositoryAll.forEach(System.out::println);
    }

    //分页
    @Test
    public void testPage(){
        Page<Emp> search = empRepository.search(QueryBuilders.matchAllQuery(), PageRequest.of(0, 3));
        search.forEach(System.out::println);
    }
}
根据姓名 姓名和年龄 地址查询

在自定义的接口添加相应的方法,如果根据名字就findByName,根据名字和年龄findByNameAndAge…

//自定义EmpRepository
public interface EmpRepository extends ElasticsearchRepository<Emp,String> {

    List<Emp> findByName(String name);

    List<Emp> findByNameAndAge(String name,Integer age);
}
KeywordSampleElasticsearch Query String
AndfindByNameAndPrice{ "query" : { "bool" : { "must" : [ { "query_string" : { "query" : "?", "fields" : [ "name" ] } }, { "query_string" : { "query" : "?", "fields" : [ "price" ] } } ] } }}
OrfindByNameOrPrice{ "query" : { "bool" : { "should" : [ { "query_string" : { "query" : "?", "fields" : [ "name" ] } }, { "query_string" : { "query" : "?", "fields" : [ "price" ] } } ] } }}
IsfindByName{ "query" : { "bool" : { "must" : [ { "query_string" : { "query" : "?", "fields" : [ "name" ] } } ] } }}
NotfindByNameNot{ "query" : { "bool" : { "must_not" : [ { "query_string" : { "query" : "?", "fields" : [ "name" ] } } ] } }}
BetweenfindByPriceBetween{ "query" : { "bool" : { "must" : [ {"range" : {"price" : {"from" : ?, "to" : ?, "include_lower" : true, "include_upper" : true } } } ] } }}
LessThanfindByPriceLessThan{ "query" : { "bool" : { "must" : [ {"range" : {"price" : {"from" : null, "to" : ?, "include_lower" : true, "include_upper" : false } } } ] } }}
LessThanEqualfindByPriceLessThanEqual{ "query" : { "bool" : { "must" : [ {"range" : {"price" : {"from" : null, "to" : ?, "include_lower" : true, "include_upper" : true } } } ] } }}
GreaterThanfindByPriceGreaterThan{ "query" : { "bool" : { "must" : [ {"range" : {"price" : {"from" : ?, "to" : null, "include_lower" : false, "include_upper" : true } } } ] } }}
GreaterThanEqualfindByPriceGreaterThan{ "query" : { "bool" : { "must" : [ {"range" : {"price" : {"from" : ?, "to" : null, "include_lower" : true, "include_upper" : true } } } ] } }}
BeforefindByPriceBefore{ "query" : { "bool" : { "must" : [ {"range" : {"price" : {"from" : null, "to" : ?, "include_lower" : true, "include_upper" : true } } } ] } }}
AfterfindByPriceAfter{ "query" : { "bool" : { "must" : [ {"range" : {"price" : {"from" : ?, "to" : null, "include_lower" : true, "include_upper" : true } } } ] } }}
LikefindByNameLike{ "query" : { "bool" : { "must" : [ { "query_string" : { "query" : "?*", "fields" : [ "name" ] }, "analyze_wildcard": true } ] } }}
StartingWithfindByNameStartingWith{ "query" : { "bool" : { "must" : [ { "query_string" : { "query" : "?*", "fields" : [ "name" ] }, "analyze_wildcard": true } ] } }}
EndingWithfindByNameEndingWith{ "query" : { "bool" : { "must" : [ { "query_string" : { "query" : "*?", "fields" : [ "name" ] }, "analyze_wildcard": true } ] } }}
Contains/ContainingfindByNameContaining{ "query" : { "bool" : { "must" : [ { "query_string" : { "query" : "*?*", "fields" : [ "name" ] }, "analyze_wildcard": true } ] } }}
InfindByNameIn(Collection<String>names){ "query" : { "bool" : { "must" : [ {"bool" : {"must" : [ {"terms" : {"name" : ["?","?"]}} ] } } ] } }}
NotInfindByNameNotIn(Collection<String>names){ "query" : { "bool" : { "must" : [ {"bool" : {"must_not" : [ {"terms" : {"name" : ["?","?"]}} ] } } ] } }}
NearfindByStoreNearNot Supported Yet !
TruefindByAvailableTrue{ "query" : { "bool" : { "must" : [ { "query_string" : { "query" : "true", "fields" : [ "available" ] } } ] } }}
FalsefindByAvailableFalse{ "query" : { "bool" : { "must" : [ { "query_string" : { "query" : "false", "fields" : [ "available" ] } } ] } }}
OrderByfindByAvailableTrueOrderByNameDesc{ "query" : { "bool" : { "must" : [ { "query_string" : { "query" : "true", "fields" : [ "available" ] } } ] } }, "sort":[{"name":{"order":"desc"}}] }

测试:

//根据姓名 姓名和年龄 地址查询
@Test
public void testFindByName(){
    List<Emp> search =  empRepository.findByName("张三丰");
    search.forEach(System.out::println);
}

@Test
public void testFindByNameAndAge(){
    List<Emp> search =  empRepository.findByNameAndAge("张三丰",23);
    search.forEach(System.out::println);
}

高亮、分页、排序等查询还是需要RestHighLevelClient

12、es中的集群

12.1、相关概念

集群 cluster

一个集群就是由一个或多个节点组织在一起,它们共同持有整个的数据,并一起提供索引和搜索功能。

一个集群由一个唯一的名字标识,这个名字默认就是“elasticsearch”。一个节点只能通过指定某个集群的名字,来加入这个集群。

节点 node

一个节点是集群中的一个服务器,作为集群的一部分,它存储数据,参与集群的索引和搜索功能。和集群类似,一个节点也是由一个名字来标识的,

在一个集群里,可以拥有任意多个节点。而且,如果当前你的网络中没有运行任何Elasticsearch节点,这时启动一个节点,会默认创建并加入一个叫做“elasticsearch”的集群。

分片和复制 shards&replicas

每个分片本身也是一个功能完善并且独立的“索引”,这个“索引”可以被放置到集群中的任何节点上。

分片重要的原因:

1)允许你水平分割/扩展你的内容容量。

2)允许你在分片(潜在地,位于多个节点上)之上进行分布式的、并行的操作,进而提高性能/吞吐量。

Elasticsearch允许你创建分片的一份或多份拷贝,这些拷贝叫做复制分片,或者直接叫复制。

复制重要的原因:

1)在分片/节点失败的情况下,提供了高可用性。因为这个原因,注意到复制分片从不与原/主要(original/primary)分片置于同一节点上。

2)扩展你的搜索量/吞吐量,因为搜索可以在所有的复制上并行运行。(为了保证在分片/节点失败的情况下,数据不会查询不准确

本笔记资料学习来自该视频

https://www.bilibili.com/video/BV1FK4y1x7Dr/?p=39

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值