ElasticSearch基础知识
一、核心概念
基础概念 | 描述 |
---|---|
索引(index) | 索引是具有相似结构的文档的集合, 可以把索引当作是数据库中的表, 比如可以有一个商品分类索引, 订单索引.每个索引都要有唯一的名称, 名称要小写, 通过索引名称来执行索引、搜索、更新和删除等操作.可以有任意多个索引, 只要保证名称不同即可. |
类型(type) | type是index的逻辑分类, 在ES 6.x版本之前, 每个索引中可以定义一个或多个type, 而在6.X版本之后, 一个index中只能定义一个type.一种type一般被定义为具有一组公共field的document, 比如对博客系统中的数据建立索引, 可以定义用户数据type, 博客数据type, 评论数据type, 也就是每个document都必须属于某一个具体的type, 也就是说每个document都有_type属性. |
文档(document) | t文档是存储在ES中的一个个JSON格式的字符串, 是ES索引中的最小数据单元, 由field(字段)构成.一个document可以是一条商品分类数据, 一条订单数据, 例如: #book document{“book_id”: “1”,“book_name”: “Thinking in Java(Java 编程思想)”,“book_desc”: “Java学习者不得不看的经典书籍”,“book_price”: 108.00,“category_id”: “5”} |
映射(mapping) | 类似于关系数据库中的Table结构, 每个index都有一个映射: 定义索引中每个字段的类型.所有文档在写进索引之前都会先进行分析, 如何对文本进行分词、哪些词条又会被过滤, 这类行为叫做映射(mapping).映射可以提前定义, 也可以在第一次存储文档时自动识别. 一般由用户自己定义规则. |
字段(filed) | 字段可以是一个简单的值(如字符串、数字、日期), 也可以是一个数组, 还可以嵌套一个对象或多个对象.字段类似于关系数据库中表数据的列, 每个字段都对应一个类型.可以指定如何分析某一字段的值, 即对field指定分词器. |
ES的索引中, 各概念的关系为: Field --> Document --> Type --> Index, 索引结构图如下:
更多内容参考
官方帮助文档 :
https://www.elastic.co/guide/index.html
牛人博客:
https://www.cnblogs.com/shoufeng/p/9887327.html 上述表格文字和图片截取自此篇博客。
https://www.cnblogs.com/qdhxhz/p/11448451.html 可以了解倒排索引和分片的知识
二、准备工作
版本环境:ElastciSearch 7.12.1 Ik分词器 7.12.1 Kibana 7.12.1
在SpringBoot中操作elasticsearch的准备工作
(1).引入es的RestHighLevelClient依赖:
<dependency>
<groupId>org.elasticsearch.client</groupId>
<artifactId>elasticsearch-rest-high-level-client</artifactId>
</dependency>
(2).SpringBoot默认的ES版本有可能并不是7.12.1,我们需要覆盖默认的ES版本,一定要保证版本之间保持一致:
<properties>
<elasticsearch.version>7.12.1</elasticsearch.version>
</properties>
(3).初始化RestHighLevelClient:
RestHighLevelClient client = new RestHighLevelClient(RestClient.builder(
HttpHost.create("http://xxx.xxx.xxx.xxx:9200"),HttpHost.create("服务器地址"), . . . //可以添加多个做集群设置,以逗号分割
));
三、索引库操作
1、增
DSL语句:
PUT /索引库名称
{
"mappings": {
"properties": {
"字段名":{
"type": "text",
"analyzer": "ik_max_word"
},
"字段名2":{
"type": "keyword",
"index": "false"
},
"字段名3":{
"properties": {
"子字段": {
"type": "keyword"
}
}
},
// ...略
}
}
}
Java代码:
// 1.创建Request对象
CreateIndexRequest request = new CreateIndexRequest("索引库名称");
// 2.准备请求的参数:DSL语句
request.source(MAPPING_TEMPLATE, XContentType.JSON); //MAPPING_TEMPLATE 创建索引库的DSL语句,一般很长,所以配置为静态常量字符串
// 3.发送请求
client.indices().create(request, RequestOptions.DEFAULT);
2、删
DSL语句:DELETE /索引库名
Java代码:
// 1.创建Request对象
DeleteIndexRequest request = new DeleteIndexRequest("索引库名称");
// 2.发送请求
client.indices().delete(request, RequestOptions.DEFAULT);
3、判断索引库是否存在
DSL语句获取索引库:GET /hotel
Java代码:
// 1.创建Request对象
GetIndexRequest request = new GetIndexRequest("索引库名称");
// 2.发送请求
boolean exists = client.indices().exists(request, RequestOptions.DEFAULT);
// 3.输出
System.err.println(exists ? "索引库已经存在!" : "索引库不存在!");
四、文档操作
1、增
DSL语句:
POST /{索引库名}/_doc/1
{
"name": "Chen",
"age": 22
}
Java代码:
// 1.创建Request对象
IndexRequest request = new IndexRequest("索引库名称").id("文档Id");
// 2.准备参数:JSON格式的数据
request.source(“JSON数据”, XContentType.JSON);
// 3.发送请求
client.index(request, RequestOptions.DEFAULT);
批量导入文档
// 批量查询数据
List<Person> persons= personService.list();
// 1.创建Request
BulkRequest request = new BulkRequest();
// 2.准备参数,添加多个新增的Request
for (Person person: persons) {
request.add(new IndexRequest("person")
.id(person.getId().toString())
.source(JSON.toJSONString(person), XContentType.JSON));
}
// 3.发送请求
client.bulk(request, RequestOptions.DEFAULT);
2、删
DSL语句:DELETE /索引库名称/_doc/{id}
Java代码:
// 1.准备Request
DeleteRequest request = new DeleteRequest("索引库名称", "文档ID");
// 2.发送请求
client.delete(request, RequestOptions.DEFAULT);
3、改
在ES中有两种修改方式:
(1)全量修改:本质是先根据id删除,再新增
(2)增量修改:修改文档中的指定字段值
在RestClient的API中,全量修改与新增的API完全一致,判断依据是ID:
(1)如果新增时,ID已经存在,则修改
(2)如果新增时,ID不存在,则新增
1.准备Request
UpdateRequest request = new UpdateRequest ("索引库名称", "文档ID");
// 2.准备请求参数
request.doc(
"name", "King",
"age", "666"
);
// 3.发送请求
client.update(request, RequestOptions.DEFAULT);
4、查
DSL语句:GET /索引库名称/_doc/{id}
Java代码:
// 1.准备Request
GetRequest request = new GetRequest("索引库名称", "文档Id");
// 2.发送请求,得到响应
GetResponse response = client.get(request, RequestOptions.DEFAULT);
// 3.解析响应结果
String json = response.getSourceAsString();
Person person= JSON.parseObject(json, Person.class);
System.out.println(person);
5、全文检索
match查询:单字段查询
//match查询DSL语句:
GET /索引库名称/_search
{
"query": {
"match": {
"FIELD": "TEXT" //需要匹配查询的字段
}
}
}
multi_match查询:多字段查询,任意一个字段符合条件就算符合查询条件
//mulit_match查询DSL语句:
GET /索引库名称/_search
{
"query": {
"multi_match": {
"query": "TEXT",
"fields": ["FIELD1", " FIELD12"] //可以匹配多个查询条件
}
}
}
Java代码:
// 1.准备Request
SearchRequest request = new SearchRequest("索引库名称");
// 2.准备DSL
request.source()
.query(QueryBuilders.matchQuery("查询字段", "字段值"));
//.query(QueryBuilders.multiMatchQuery("字段值", "Filed1","Filed2",...));
// 3.发送请求
SearchResponse response = client.search(request, RequestOptions.DEFAULT);
// 4.解析响应
handleResponse(response);
6、排序
DSL语句:
GET /索引库名称/_search
{
"query": {
"match_all": {}
},
"sort": [
{
"FIELD": "desc" // 排序字段、排序方式ASC、DESC
}
]
}
Java代码:
// 页码,每页大小
int page = 1, size = 5;
// 1.准备Request
SearchRequest request = new SearchRequest("索引库名称");
// 2.准备DSL
// 2.1.query
request.source().query(QueryBuilders.matchAllQuery());
// 2.2.排序 sort
request.source().sort("字段名", SortOrder.ASC);
// 2.3.分页 from、size
request.source().from((page - 1) * size).size(5);
// 3.发送请求
SearchResponse response = client.search(request, RequestOptions.DEFAULT);
// 4.解析响应
handleResponse(response);
7、分页
DSL语句:
GET /索引库名称/_search
{
"query": {
"match_all": {}
},
"from": 0, // 分页开始的位置,默认为0
"size": 10, // 期望获取的文档总数
"sort": [
{"FIELD": "asc"}
]
}
java代码见上一条。
8、精确查询
term:根据词条精确值查询
// term查询
GET /索引库名称/_search
{
"query": {
"term": {
"FIELD": {
"value": "VALUE"
}
}
}
}
range:根据值的范围查询
// range查询
GET /索引库名称/_search
{
"query": {
"range": {
"FIELD": {
"gte": 10, // 这里的gte代表大于等于,gt则代表大于
"lte": 20 // lte代表小于等于,lt则代表小于
}
}
}
}
Java代码:
// 1.准备Request
SearchRequest request = new SearchRequest("索引库名称");
// 2.准备DSL
// 2.1.准备BooleanQuery,布尔查询是用must、must_not、filter等方式组合其它查询
BoolQueryBuilder boolQuery = QueryBuilders.boolQuery();
// 2.2.添加term
boolQuery.must(QueryBuilders.termQuery("字段名", "字段值"));
// 2.3.添加range
boolQuery.filter(QueryBuilders.rangeQuery("字段名").lte(字段值));
request.source().query(boolQuery);
// 3.发送请求
SearchResponse response = client.search(request, RequestOptions.DEFAULT);
// 4.解析响应
handleResponse(response);
9、聚合查询
DSL语句:
#聚合查询
GET /索引库名称/_search
{
"size":0,
"aggs": {
"随便起一个聚合名称": {
"terms": {
"field": "字段名",
"size": 10
}
}
}
}
#聚合查询,自定义排序规则
GET /索引库名称/_search
{
"query": {
"range": {
"字段名": {
"lte": 200
}
}
},
"size": 0,
"aggs": {
"随便起一个聚合名称": {
"terms": {
"field": "字段名",
"size": 10,
"order": {
"_count": "asc"
}
}
}
}
}
#Metric聚合语法(avg,max,min,stat等)
GET /索引库名称/_search
{
"size": 0,
"aggs": {
"随便起一个聚合名称": {
"terms": {
"field": "字段名",
"size": 10,
"order": {
"随便起一个子聚合名称.avg": "asc"
}
},
"aggs": {
"随便起的子聚合名称": {
"stats": {
"field": "score"
}
}
}
}
}
}
Java代码:
// 聚合查询
@Test
public void testAggregation() throws IOException{
// 准备request
SearchRequest request = new SearchRequest("索引库名称");
//DSL语句
request.source().size(0);
request.source().aggregation(
AggregationBuilders.terms("聚合名称")
.size(显示结果数).field("字段名"));
// 发送请求
SearchResponse response = client.search(request, RequestOptions.DEFAULT);
//获取聚合结果
Aggregations aggregations = response.getAggregations();
//获取terms
Terms terms = aggregations.get("聚合名称");
//获取buckets
List<? extends Terms.Bucket> buckets = terms.getBuckets();
//遍历取出key值
for (Terms.Bucket bucket : buckets) {
String key = bucket.getKeyAsString();
System.out.println(key);
}
}
10、查询结果中关键字高亮显示
#高亮显示
GET /索引库名称/_search
{
"query": {
"match": {
"字段名": "字段值"
}
},
"highlight": {
"fields": {
"字段名": {
}
}
}
}
Java代码:
// 高亮显示
@Test
public void testHighlight() throws IOException {
// 1.准备Request
SearchRequest request = new SearchRequest("索引库名称");
// 2.准备DSL
// 2.1.query
request.source().query(QueryBuilders.matchQuery("字段名", "字段值"));
// 2.2.高亮
request.source().highlighter(new HighlightBuilder().field("字段名").requireFieldMatch(false));
// 3.发送请求
SearchResponse response = client.search(request, RequestOptions.DEFAULT);
// 4.解析响应
handleResponse(response);
}
private void handleResponse(SearchResponse response) {
// 4.解析响应
SearchHits searchHits = response.getHits();
// 4.1.获取总条数
long total = searchHits.getTotalHits().value;
System.out.println("共搜索到" + total + "条数据");
// 4.2.文档数组
SearchHit[] hits = searchHits.getHits();
// 4.3.遍历
for (SearchHit hit : hits) {
// 获取文档source
String json = hit.getSourceAsString();
// 反序列化
Person person= JSON.parseObject(json, Person.class);
Map<String, HighlightField> highlightFields = hit.getHighlightFields();
if (!CollectionUtils.isEmpty(highlightFields)) {
// 根据字段名获取高亮结果
HighlightField highlightField = highlightFields.get("name");
if (highlightField != null) {
// 获取高亮值
String name = highlightField.getFragments()[0].string();
// 覆盖非高亮结果
person.setName(name);
}
}
System.out.println("person= " + person);
}
}
五、数据存储、备份和恢复
后续补充。。。。
PS:自学完黑马关于ES的教学做的简单总结,不喜勿喷,谢谢。传送门