安装

  1. docker pull elasticsearch:7.4.2
  2. docker pull kibana:7.4.2
mkdir -p /mydata/elasticsearch/config
mkdir -p /mydata/elasticsearch/data

echo "http.host: 0.0.0.0" >/mydata/elasticsearch/config/elasticsearch.yml

chmod -R 777 /mydata/elasticsearch/

docker run --name elasticsearch -p 9200:9200 -p 9300:9300 \
-e  "discovery.type=single-node" \
-e ES_JAVA_OPTS="-Xms64m -Xmx512m" \
-v /mydata/elasticsearch/config/elasticsearch.yml:/usr/share/elasticsearch/config/elasticsearch.yml \
-v /mydata/elasticsearch/data:/usr/share/elasticsearch/data \
-v  /mydata/elasticsearch/plugins:/usr/share/elasticsearch/plugins \
-d elasticsearch:7.4.2 

docker update elasticsearch --restart=always

docker run --name kibana -e ELASTICSEARCH_HOSTS=http://182.92.121.133:9200 -p 5601:5601 -d kibana:7.4.2

docker update kibana  --restart=always

安装ik分词器
https://github.com/medcl/elasticsearch-analysis-ik/releases,下载解压到/mydate/elasticsearch/plugins/ik

chmod -R 777 plugins/ik
docker restart elasticsearch
rm -rf elasticsearch-analysis-ik-7.6.2.zip

理解倒排索引

保存的记录

  • 红海行动
  • 探索红海行动
  • 红海特别行动
  • 红海记录片
  • 特工红海特别探索

将内容分词就记录到索引中

记录
红海 1,2,3,4,5
行动 1,2,3
探索 2,5
特别 3,5
纪录片 4,
特工 5

检索红海特工行动:
查出后计算相关性得分:3号记录命中了2次,5号记录也命中了两次,且3号本身才有3个单词,2/3比2/5得分高,所以3号最匹配

关系型数据库中两个数据表示是独立的,即使他们里面有相同名称的列也不影响使用,但ES中不是这样的。elasticsearch是基于Lucene开发的搜索引擎,而ES中不同type下名称相同的filed最终在Lucene中的处理方式是一样的。

两个不同type下的两个user_name,在ES同一个索引下其实被认为是同一个filed,你必须在两个不同的type中定义相同的filed映射。否则,不同type中的相同字段名称就会在处理中出现冲突的情况,导致Lucene处理效率下降。
去掉type就是为了提高ES处理数据的效率。
Elasticsearch 7.xURL中的type参数为可选。比如,索引一个文档不再要求提供文档类型。
Elasticsearch 8.x不再支持URL中的type参数。
解决:将索引从多类型迁移到单类型,每种类型文档一个独立索引

命令

PUT和POST

新增文档
POST新增。如果不指定id,会自动生成id。指定id就会修改这个数据,并新增版本号;

  • 可以不指定id,不指定id时永远为创建
  • 指定不存在的id为创建;指定存在的id为更新,而版本号会根据内容变没变而觉得版本号递增与否

PUT可以新增也可以修改。PUT必须指定id;由于PUT需要指定id,我们一般用来做修改操作,不指定id会报错。

  • 必须指定id
  • 版本号总会增加

更新文档

POST customer/externel/1/_update
{
    "doc":{
        "name":"111"
    }
}
或者
POST customer/externel/1
{
    "doc":{
        "name":"222"
    }
}
或者
PUT customer/externel/1
{
    "doc":{
        "name":"222"
    }
}

POST更新文档,带有_update

  • POST操作会对比源文档数据,如果相同不会有什么操作,version和_seq_no都不变。
  • PUT操作总会重新保存并增加version版本

POST更新文档,不带_update

  • 在更新过程中,重复执行更新操作,数据也能够更新成功,不会和原来的数据进行对比

整合

    <properties>
        <java.version>8</java.version>
        <elasticsearch.version>7.4.2</elasticsearch.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-elasticsearch</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
            <scope>runtime</scope>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-configuration-processor</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </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>1.2.62</version>
        </dependency>
    </dependencies>
@Configuration
public class ElasticSearchClientConfig {

    @Bean
    public RestHighLevelClient restHighLevelClient() {
        RestHighLevelClient client = new RestHighLevelClient(
                RestClient.builder(
                        new HttpHost("182.92.121.133", 9200, "http")));
        return client;
    }

}
package com.bang;

import com.alibaba.fastjson.JSON;
import com.bang.pojo.User;
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.bulk.BulkRequest;
import org.elasticsearch.action.bulk.BulkResponse;
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.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.action.support.master.AcknowledgedResponse;
import org.elasticsearch.action.update.UpdateRequest;
import org.elasticsearch.action.update.UpdateResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.client.indices.GetIndexRequest;
import org.elasticsearch.common.unit.TimeValue;
import org.elasticsearch.common.xcontent.XContentType;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.index.query.TermQueryBuilder;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.SearchHits;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.search.fetch.subphase.FetchSourceContext;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.test.context.SpringBootTest;

import java.io.IOException;
import java.util.ArrayList;
import java.util.concurrent.TimeUnit;

@SpringBootTest
class SpringbootElasticsearchApplicationTests {

    @Autowired
    @Qualifier("restHighLevelClient")
    public RestHighLevelClient client;

    //索引的创建、获取、删除
    //测试索引的创建
    @Test
    public void testCreateIndex() throws IOException {
        //创建索引请求
        CreateIndexRequest request = new CreateIndexRequest("bang_index");
        //执行创建请求
        CreateIndexResponse response = client.indices().create(request, RequestOptions.DEFAULT);
        System.out.println(response);
    }

    //测试获取索引,判断索引是否存在
    @Test
    public void testGetIndex() throws IOException {
        GetIndexRequest request = new GetIndexRequest("bang_index");
        boolean exists = client.indices().exists(request, RequestOptions.DEFAULT);
        System.out.println(exists);
    }

    //测试删除索引
    @Test
    public void testDelIndex() throws IOException {
        DeleteIndexRequest request = new DeleteIndexRequest("bang_index");
        AcknowledgedResponse delete = client.indices().delete(request, RequestOptions.DEFAULT);
        System.out.println(delete.isAcknowledged());
    }


    //文档的增删改
    //测试添加文档
    @Test
    public void testAddDoc() throws IOException {
        User user = new User("renhaoshuai", 5);
        IndexRequest request = new IndexRequest("bang_index");
        //PUT /bang_index/_doc/1
        request.id("1");
        //规则  超时时间1秒,即请求时间超过1s不再请求。也可以不设置
        request.timeout(TimeValue.timeValueSeconds(1));
        //request.timeout("1s");//与上同效
        //将我们的数据放入请求
        request.source(JSON.toJSONString(user), XContentType.JSON);
        //客户端发送请求,获取响应的结果
        IndexResponse response = client.index(request, RequestOptions.DEFAULT);
        System.out.println(response);//IndexResponse[index=hany_index,type=_doc,id=1,version=1,
        //result=created,seqNo=0,primaryTerm=1,shards={"total":2,"successful":1,"failed":0}]
        System.out.println(response.status());//CREATED
    }

    //获取文档,判断文档是否存在    get /hany_index/_doc/1
    @Test
    public void testGetDoc() throws IOException {
        GetRequest request = new GetRequest("bang_index", "1");
        //不获取返回的"_source"上下文。仅做演示,可以不写
        request.fetchSourceContext(new FetchSourceContext(false));
        //排序字段。仅做演示,可以不写
        request.storedFields("_none_");
        boolean exists = client.exists(request, RequestOptions.DEFAULT);
        System.out.println(exists);
    }

    //获得文档的信息
    @Test
    public void testGetDocInfo() throws IOException {
        GetRequest request = new GetRequest("bang_index", "1");
        GetResponse response = client.get(request, RequestOptions.DEFAULT);
        System.out.println(response.getSourceAsString());
        //{"age":5,"name":"hany"},可以看到是个map
        System.out.println(response);//这里返回的内容和命令是一样的
        //{"_index":"hany_index","_type":"_doc","_id":"1","_version":1,"_seq_no":0,
        //"_primary_term":1,"found":true,"_source":{"age":5,"name":"hany"}}
    }

    //更新文档信息
    @Test
    public void testUpdateDoc() throws IOException {
        UpdateRequest request = new UpdateRequest("bang_index", "1");
        request.timeout("1s");
        //request.timeout(TimeValue.timeValueSeconds(1000));
        User user = new User("renhaoshuai", 4);
        request.doc(JSON.toJSONString(user), XContentType.JSON);
        UpdateResponse response = client.update(request, RequestOptions.DEFAULT);
        System.out.println(response.status());
    }

    //删除文档记录
    @Test
    public void testDeleteDoc() throws IOException {
        DeleteRequest request = new DeleteRequest("bang_index", "1");
        request.timeout("1s");
        DeleteResponse response = client.delete(request, RequestOptions.DEFAULT);
        System.out.println(response.status());//OK
    }

    //真实项目会批量操作数据(增删改)
    @Test
    public void testBulkRequest() throws IOException {
        BulkRequest request = new BulkRequest();
        request.timeout("1s");

        ArrayList<User> list = new ArrayList<>();
        list.add(new User("red", 12));
        list.add(new User("blue1", 13));
        list.add(new User("blue2", 13));
        list.add(new User("blue3", 13));
        list.add(new User("blue4", 13));

        for (int i = 0; i < list.size(); i++) {
            //这里演示插入数据,要更新、删除,修改里面的请求即可
            request.add(new IndexRequest("bang_index")
                    .id("" + (i + 1))
                    .source(JSON.toJSONString(list.get(i)), XContentType.JSON));
        }
        BulkResponse response = client.bulk(request, RequestOptions.DEFAULT);
        System.out.println(response.hasFailures());//是否失败
    }

    /*
     * 查询
     * HighlightBuilder 高亮构建
     * TermQueryBuilder 精确查询
     * MatchAllQueryBuilder 匹配所有
     */
    @Test
    public void testSearch() throws IOException {
        SearchRequest request = new SearchRequest("bang_index");
        //构建搜索条件
        SearchSourceBuilder builder = new SearchSourceBuilder();
        /*
         * 查询条件,可以使用QueryBuilders工具来实现
         * QueryBuilders.termQuery() 精确查询
         * QueryBuilders.matchAllQuery() 匹配所有
         */
        TermQueryBuilder query = QueryBuilders.termQuery("name", "red");
        builder.query(query);
        builder.timeout(new TimeValue(60, TimeUnit.SECONDS));
        request.source(builder);
        SearchResponse response = client.search(request, RequestOptions.DEFAULT);
        SearchHits hits = response.getHits();//搜索结果封装在这个SearchHits中
        System.out.println(JSON.toJSONString(hits));
        System.out.println("==========================");
        for (SearchHit hit : hits.getHits()) {
            System.out.println(hit.getSourceAsMap());//{name=red, age=12}
        }
    }
}

实战