安装
docker pull elasticsearch:7.4.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}
}
}
}