网页右边,向下滑有目录索引,可以根据标题跳转到你想看的内容 |
---|
如果右边没有就找找左边 |
此文是学习尚硅谷Elasticsearch课程的笔记 |
---|
Elasticsearch |
---|
- 全文检索引擎
- Lucene 是 Apache软件基金会Jakarta项目组的一个子项目,提供了简单却强大的应用程序接口,能够全文索引和搜索。Java开发环境中Lucene是成熟的免费开源工具,但Lucene只是一个提供全文搜索功能类库的核心工具包,而真正使用还需要完善的服务框架搭建起来进行应用
- 于是Elasticsearch 和 Solr出现了(两个都是用Lucene进行开发的,各有优点)
- 如果除了搜索文本外,还需要它来处理分析查询,Elasticsearch 是更好的选择,
- 需要分布式索引,良好的可伸缩性,Elasticsearch是更好的选择
- 如果需要监控和指标,Elasticsearch 暴露了更多关键指标,是更好的选择
- 其它方面,Slor是更好的选择,当然Solr对新手不太友好,相对较难
Elastic Stack |
---|
一、入门
1. 下载安装windows和Linux版本
- 进入官网https://www.elastic.co/cn/,下载
- 解压
- 启动ES
- cmd窗口中,会有两个端口号
9300
端口为ES集群间组件的通信端口,9200
端口是浏览器访问的http协议RESTful端口
- 浏览器访问9200端口,出现如下内容,表示访问成功
2. 数据格式
正排索引,倒排索引 |
---|
3. 通过HTTPRestful操作ES
- ES向外暴露了很多httpRestFul风格的API,我们通过这些来快速入门,大家看看就好,这些不常用的
1. 索引
1.创建索引 |
---|
2.获取索引相关信息 |
---|
获取所有索引信息 |
---|
http://127.0.0.1:9200/_cat/indices?v
删除指定索引 |
---|
2. 文档
1. 创建文档 |
---|
{
"title":"小米手机",
"catrgory":"小米",
"images":"http://www.gulixueyuan.com/xm.jpg",
"price":3999.00
}
2. 查询文档 |
---|
- 根据id查http://127.0.0.1:9200/shopping/_doc/1001
- 查询所有http://127.0.0.1:9200/shopping/_search
3. 修改文档内容 |
---|
删除指定文档 |
---|
3. 复杂查询(先多添加点文档)
1. 查询catrgory是小米的文档 |
---|
- 写在地址栏
- 使用json
{
"query":{
"match":{
"category":"小米"
}
}
}
2. 全部查询(后面都只给出json条件,因为url都一样),以及分页等参数的设置 |
---|
{
"query":{
"match_all":{
}
},
"from":0,//起始页码
"size":2,//每页记录数
"_source":[
"title",
"price"
],//只显示title和price两个字段
"sort":{//排序
"price":{//按价格排序
"order":"desc"//降序
}
}
}
3. 多条件查询,必须成立的条件,and |
---|
{
"query":{//查询条件
"bool":{//多条件
"must":[//必须成立
{//条件1
"match":{
"catrgory":"小米"
}
},
{//条件2
"match":{
"price":"3999.0"
}
}
]
}
}
}
4. 多条件查询,不必须成立 |
---|
{
"query":{//查询条件
"bool":{//多条件
"should":[//成立一个即可
{//条件1
"match":{
"catrgory":"小米"
}
},
{//条件2
"match":{
"catrgory":"华为"
}
}
]
}
}
}
5. 查询价格大于4000的 |
---|
{
"query":{//查询条件
"bool":{//多条件
//省略了must或should,可以一起写上
"filter":{//过滤
"range":{//范围
"price":{//price字段
"gt":4000
}
}
}
}
}
}
4. 条件删除
- 发 POST 请求 :http://127.0.0.1:9200/shopping/_delete_by_query
- 请求参数
{
"query":{
"match":{
"price":4000.00
}
}
}
4. 匹配规则
全文检索匹配,自动分词 |
---|
完全匹配 |
---|
{
"query":{//查询条件
"match_phrase":{//完全匹配
"catrgory":"小华"
}
}
}
5. 高亮
{
"query":{//查询条件
"match":{//完全匹配
"catrgory":"小华"
}
},
"highlight":{
"fields":{
"catrgory":{}
}
}
}
6. 聚合操作
分组 |
---|
{
"aggs":{//聚合操作
"price_group":{//聚合操作的名称,自己随便起
"terms":{//分组
"field":"price"//分组字段
}
}
},
//"size":0 //如果加上这个,就不显示文档,直接显示聚合统计结果
}
求平均值 |
---|
{
"aggs":{//聚合操作
"price_group":{//聚合操作的名称,自己随便起
"terms":{//分组
"field":"price"//分组字段
}
},
"price_avg":{//平均值,随便起的
"avg":{//平均值
"field":"price"//求平均值字段
}
}
},
"size":0
}
7. 映射
创建隐射 |
---|
- 先新建一个索引
- 建立隐射http://127.0.0.1:9200/user/_mapping
{
"properties":{//映射
"name":{//第一个字段,name
"type":"text",//类型为text,文本,可以分词
"index":true//可以被索引查询
},
"sex":{//第二个字段
"type":"keyword",//不可以分词,必须完整匹配
"index":true
},
"telphone":{//第三个字段
"type":"keyword",//不可以分词,必须完整匹配
"index":false //不可以被索引查询
}
}
}
- 创建文档
- 查询
二、javaAPI 操作Elasticsearch
1. 环境搭建
- 创建maven工程,引入依赖
<dependencies>
<dependency>
<groupId>org.elasticsearch</groupId>
<artifactId>elasticsearch</artifactId>
<version>7.8.0</version>
</dependency>
<dependency>
<groupId>org.elasticsearch.client</groupId>
<artifactId>elasticsearch-rest-high-level-client</artifactId>
<version>7.8.0</version>
</dependency>
<!-- elasticsearch 依赖 2.x 的 log4j -->
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-api</artifactId>
<version>2.8.2</version>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-core</artifactId>
<version>2.8.2</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.9.9</version>
</dependency>
<!-- junit 单元测试 -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
</dependencies>
- 创建实体类
package com.yzpnb.es.entity;
public class User {
private String name;
private Integer age;
private String sex;
public String getName() {
return name;
}
public void setName(String name){
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
}
2. 测试类
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.yzpnb.es.entity.User;
import org.apache.http.HttpHost;
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.RestClient;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.client.indices.CreateIndexRequest;
import org.elasticsearch.client.indices.CreateIndexResponse;
import org.elasticsearch.client.indices.GetIndexRequest;
import org.elasticsearch.client.indices.GetIndexResponse;
import org.elasticsearch.common.unit.Fuzziness;
import org.elasticsearch.common.xcontent.XContentType;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.index.query.RangeQueryBuilder;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.SearchHits;
import org.elasticsearch.search.aggregations.AggregationBuilders;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightField;
import org.elasticsearch.search.sort.sortOrder;
import java.io.IOException;
import java.util.Map;
public class Elasticsearch01_Client {
public static void main(String[] args) throws IOException {
// 创建客户端对象
RestHighLevelClient client = new RestHighLevelClient(
RestClient.builder(new HttpHost("localhost", 9200, "http"))
);
// creteIndex(client,"user1");//创建索引
// getIndex(client,"user1");//获取索引
// delIndex(client,"user1");//删除索引
// createDoc(client,"user1","1001");//创建文档
// getDocbyId(client,"user1","1001");//查询文档
// updateDoc(client,"user1","1001");//修改文档
// delDoc(client,"user1","1001");//删除文档
createDocList(client,"user1");//批量新增文档
client.close();
}
//删除指定索引
private static void delIndex(RestHighLevelClient client, String indexName) {
// 删除索引 - 请求对象
DeleteIndexRequest request = new DeleteIndexRequest(indexName);
// 发送请求,获取响应
AckNowledgedResponse response = null;
try {
response = client.indices().delete(request, RequestOptions.DEFAULT);
} catch (IOException e) {
e.printstacktrace();
}
// 操作结果
System.out.println("操作结果 : " + response.isAckNowledged());
}
//创建指定索引
public static void creteIndex(RestHighLevelClient client,String indexName){
// 创建索引 - 请求对象
CreateIndexRequest request = new CreateIndexRequest(indexName);
// 发送请求,获取响应
CreateIndexResponse response = null;
try {
response = client.indices().create(request, RequestOptions.DEFAULT);
} catch (IOException e) {
e.printstacktrace();
}
boolean ackNowledged = response.isAckNowledged();
// 响应状态
System.out.println("操作状态 = " + ackNowledged);
}
//查询指定索引
public static void getIndex(RestHighLevelClient client,String indexName){
// 查询索引 - 请求对象
GetIndexRequest request = new GetIndexRequest(indexName);
// 发送请求,获取响应
GetIndexResponse response = null;
try {
response = client.indices().get(request, RequestOptions.DEFAULT);
} catch (IOException e) {
e.printstacktrace();
}
System.out.println("aliases:"+response.getAliases());
System.out.println("mappings:"+response.getMappings());
System.out.println("settings:"+response.getSettings());
}
/**
* 创建文档
* @param client 客户端对象
* @param indexName 索引名
* @param docId //指定文档id
*/
public static void createDoc(RestHighLevelClient client,String indexName,String docId){
// 新增文档 - 请求对象
IndexRequest request = new IndexRequest();
// 设置索引及唯一性标识
request.index(indexName).id(docId);
// 创建数据对象
User user = new User();
user.setName("zhangsan");
user.setAge(30);
user.setSex("男");
//将对象,转成json字符串
ObjectMapper objectMapper = new ObjectMapper();
String productJson = null;
try {
productJson = objectMapper.writeValueAsstring(user);
} catch (JsonProcessingException e) {
e.printstacktrace();
}
// 添加文档数据,数据格式为 JSON 格式
request.source(productJson, XContentType.JSON);
// 客户端发送请求,获取响应对象
IndexResponse response = null;
try {
response = client.index(request, RequestOptions.DEFAULT);
} catch (IOException e) {
e.printstacktrace();
}
3.打印结果信息
System.out.println("_index:" + response.getIndex());
System.out.println("_id:" + response.getId());
System.out.println("_result:" + response.getResult());
}
/**
* 根据id查询文档
* @param client 客户端对象
* @param indexName 索引名
* @param docId //指定文档id
*/
public static void getDocbyId(RestHighLevelClient client,String indexName,String docId){
//1.创建请求对象
GetRequest request = new GetRequest().index(indexName).id(docId);
//2.客户端发送请求,获取响应对象
GetResponse response = null;
try {
response = client.get(request, RequestOptions.DEFAULT);
} catch (IOException e) {
e.printstacktrace();
}
3.打印结果信息
System.out.println("_index:" + response.getIndex());
System.out.println("_type:" + response.getType());
System.out.println("_id:" + response.getId());
System.out.println("source:" + response.getSourceAsstring());
}
/**
* 修改文档
* @param client 客户端对象
* @param indexName 索引名
* @param docId //指定文档id
*/
public static void updateDoc(RestHighLevelClient client,String indexName,String docId){
// 修改文档 - 请求对象
UpdateRequest request = new UpdateRequest();
// 配置修改参数
request.index(indexName).id(docId);
// 设置请求体,对数据进行修改
request.doc(XContentType.JSON, "sex", "女");
// 客户端发送请求,获取响应对象
UpdateResponse response = null;
try {
response = client.update(request, RequestOptions.DEFAULT);
} catch (IOException e) {
e.printstacktrace();
}
System.out.println("_index:" + response.getIndex());
System.out.println("_id:" + response.getId());
System.out.println("_result:" + response.getResult());
}
/**
* 删除文档
* @param client 客户端对象
* @param indexName 索引名
* @param docId //指定文档id
*/
public static void delDoc(RestHighLevelClient client,String indexName,String docId){
//创建请求对象
DeleteRequest request = new DeleteRequest().index(indexName).id(docId);
//客户端发送请求,获取响应对象
DeleteResponse response = null;
try {
response = client.delete(request, RequestOptions.DEFAULT);
} catch (IOException e) {
e.printstacktrace();
}
//打印信息
System.out.println(response.toString());
}
/**
* 批量新增文档
* @param client 客户端对象
* @param indexName 索引名
*/
public static void createDocList(RestHighLevelClient client,String indexName){
//创建批量新增请求对象
BulkRequest request = new BulkRequest();
request.add(new IndexRequest().index(indexName).id("1001").source(XContentType.JSON, "name", "zhangsan"));
request.add(new IndexRequest().index(indexName).id("1002").source(XContentType.JSON, "name", "lisi"));
request.add(new IndexRequest().index(indexName).id("1003").source(XContentType.JSON, "name", "wangwu"));
//客户端发送请求,获取响应对象
BulkResponse responses = null;
try {
responses = client.bulk(request, RequestOptions.DEFAULT);
} catch (IOException e) {
e.printstacktrace();
}
//打印结果信息
System.out.println("took:" + responses.getTook());
System.out.println("items:" + responses.getItems());
}
/**
* 批量删除
*/
public static void delDocList(RestHighLevelClient client,String indexName){
//创建批量删除请求对象
BulkRequest request = new BulkRequest();
request.add(new DeleteRequest().index(indexName).id("1001"));
request.add(new DeleteRequest().index(indexName).id("1002"));
request.add(new DeleteRequest().index(indexName).id("1003"));
//客户端发送请求,获取响应对象
BulkResponse responses = null;
try {
responses = client.bulk(request, RequestOptions.DEFAULT);
} catch (IOException e) {
e.printstacktrace();
}
//打印结果信息
System.out.println("took:" + responses.getTook());
System.out.println("items:" + responses.getItems());
}
/**
* 请求体查询,查询全部
*/
public static void queryBodyDocAll(RestHighLevelClient client){
// 创建搜索请求对象
SearchRequest request = new SearchRequest();
request.indices("student");
// 构建查询的请求体
SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
// 查询所有数据
sourceBuilder.query(QueryBuilders.matchAllQuery());
request.source(sourceBuilder);
SearchResponse response = null;
try {
response = client.search(request, RequestOptions.DEFAULT);
} catch (IOException e) {
e.printstacktrace();
}
// 查询匹配
SearchHits hits = response.getHits();
System.out.println("took:" + response.getTook());
System.out.println("timeout:" + response.isTimedOut());
System.out.println("total:" + hits.getTotalHits());
System.out.println("Maxscore:" + hits.getMaxscore());
System.out.println("hits========>>");
for (SearchHit hit : hits) {
//输出每条查询的结果信息
System.out.println(hit.getSourceAsstring());
}
System.out.println("<<========");
}
/**
* 请求体查询,term分组查询
*/
public static void queryBodyDocTerm(RestHighLevelClient client){
// 创建搜索请求对象
SearchRequest request = new SearchRequest();
request.indices("student");
// 构建查询的请求体
SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
sourceBuilder.query(QueryBuilders.termQuery("age", "30"));
request.source(sourceBuilder);
SearchResponse response = null;
try {
response = client.search(request, RequestOptions.DEFAULT);
} catch (IOException e) {
e.printstacktrace();
}
// 查询匹配
SearchHits hits = response.getHits();
System.out.println("took:" + response.getTook());
System.out.println("timeout:" + response.isTimedOut());
System.out.println("total:" + hits.getTotalHits());
System.out.println("Maxscore:" + hits.getMaxscore());
System.out.println("hits========>>");
for (SearchHit hit : hits) {
//输出每条查询的结果信息
System.out.println(hit.getSourceAsstring());
}
System.out.println("<<========");
}
/**
* 请求体查询,分页查询
*/
public static void queryBodyDoclimit(RestHighLevelClient client){
// 创建搜索请求对象
SearchRequest request = new SearchRequest();
request.indices("student");
// 构建查询的请求体
SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
sourceBuilder.query(QueryBuilders.matchAllQuery());
// 分页查询
// 当前页其实索引(第一条数据的顺序号),from
sourceBuilder.from(0);
// 每页显示多少条 size
sourceBuilder.size(2);
request.source(sourceBuilder);
SearchResponse response = null;
try {
response = client.search(request, RequestOptions.DEFAULT);
} catch (IOException e) {
e.printstacktrace();
}
// 查询匹配
SearchHits hits = response.getHits();
System.out.println("took:" + response.getTook());
System.out.println("timeout:" + response.isTimedOut());
System.out.println("total:" + hits.getTotalHits());
System.out.println("Maxscore:" + hits.getMaxscore());
System.out.println("hits========>>");
for (SearchHit hit : hits) {
//输出每条查询的结果信息
System.out.println(hit.getSourceAsstring());
}
System.out.println("<<========");
}
/**
* 请求体查询,排序
*/
public static void queryBodyDocSort(RestHighLevelClient client){
// 创建搜索请求对象
SearchRequest request = new SearchRequest();
request.indices("student");
// 构建查询的请求体
SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
sourceBuilder.query(QueryBuilders.matchAllQuery());
// 排序
sourceBuilder.sort("age", SortOrder.ASC);
request.source(sourceBuilder);
SearchResponse response = null;
try {
response = client.search(request, RequestOptions.DEFAULT);
} catch (IOException e) {
e.printstacktrace();
}
// 查询匹配
SearchHits hits = response.getHits();
System.out.println("took:" + response.getTook());
System.out.println("timeout:" + response.isTimedOut());
System.out.println("total:" + hits.getTotalHits());
System.out.println("Maxscore:" + hits.getMaxscore());
System.out.println("hits========>>");
for (SearchHit hit : hits) {
//输出每条查询的结果信息
System.out.println(hit.getSourceAsstring()); }
System.out.println("<<========");
}
/**
* 请求体查询,过滤
*/
public static void queryBodyDocFilter(RestHighLevelClient client){
// 创建搜索请求对象
SearchRequest request = new SearchRequest();
request.indices("student");
// 构建查询的请求体
SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
sourceBuilder.query(QueryBuilders.matchAllQuery());
//查询字段过滤
String[] excludes = {};
String[] includes = {"name", "age"};
sourceBuilder.fetchSource(includes, excludes);
request.source(sourceBuilder);
SearchResponse response = null;
try {
response = client.search(request, RequestOptions.DEFAULT);
} catch (IOException e) {
e.printstacktrace();
}
// 查询匹配
SearchHits hits = response.getHits();
System.out.println("took:" + response.getTook());
System.out.println("timeout:" + response.isTimedOut());
System.out.println("total:" + hits.getTotalHits());
System.out.println("Maxscore:" + hits.getMaxscore());
System.out.println("hits========>>");
for (SearchHit hit : hits) {
//输出每条查询的结果信息
System.out.println(hit.getSourceAsstring());
}
System.out.println("<<========");
}
/**
* 请求体查询,bool
*/
public static void queryBodyDocBool(RestHighLevelClient client){
// 创建搜索请求对象
SearchRequest request = new SearchRequest();
request.indices("student");
// 构建查询的请求体
SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
// 必须包含
boolQueryBuilder.must(QueryBuilders.matchQuery("age", "30"));
// 一定不含
boolQueryBuilder.mustNot(QueryBuilders.matchQuery("name", "zhangsan"));
// 可能包含
boolQueryBuilder.should(QueryBuilders.matchQuery("sex", "男"));
sourceBuilder.query(boolQueryBuilder);
request.source(sourceBuilder);
SearchResponse response = null;
try {
response = client.search(request, RequestOptions.DEFAULT);
} catch (IOException e) {
e.printstacktrace();
}
// 查询匹配
SearchHits hits = response.getHits();
System.out.println("took:" + response.getTook());
System.out.println("timeout:" + response.isTimedOut());
System.out.println("total:" + hits.getTotalHits());
System.out.println("Maxscore:" + hits.getMaxscore());
System.out.println("hits========>>");
for (SearchHit hit : hits) {
//输出每条查询的结果信息
System.out.println(hit.getSourceAsstring());
}
System.out.println("<<========");
}
/**
* 请求体查询,范围查询 range
*/
public static void queryBodyDocRange(RestHighLevelClient client){
// 创建搜索请求对象
SearchRequest request = new SearchRequest();
request.indices("student");
// 构建查询的请求体
SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
RangeQueryBuilder rangeQuery = QueryBuilders.rangeQuery("age");
// 大于等于
rangeQuery.gte("30");
// 小于等于
rangeQuery.lte("40");
sourceBuilder.query(rangeQuery);
request.source(sourceBuilder);
SearchResponse response = null;
try {
response = client.search(request, RequestOptions.DEFAULT);
} catch (IOException e) {
e.printstacktrace();
}
// 查询匹配
SearchHits hits = response.getHits();
System.out.println("took:" + response.getTook());
System.out.println("timeout:" + response.isTimedOut());
System.out.println("total:" + hits.getTotalHits());
System.out.println("Maxscore:" + hits.getMaxscore());
System.out.println("hits========>>");
for (SearchHit hit : hits) {
//输出每条查询的结果信息
System.out.println(hit.getSourceAsstring());
}
System.out.println("<<========");
}
/**
* 请求体查询,模糊查询 fuzzy
*/
public static void queryBodyDocFuzzy(RestHighLevelClient client){
// 创建搜索请求对象
SearchRequest request = new SearchRequest();
request.indices("student");
// 构建查询的请求体
SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
sourceBuilder.query(QueryBuilders.fuzzyQuery("name","zhangsan").fuzziness(Fuzziness.ONE));
request.source(sourceBuilder);
SearchResponse response = null;
try {
response = client.search(request, RequestOptions.DEFAULT);
} catch (IOException e) {
e.printstacktrace();
}
// 查询匹配
SearchHits hits = response.getHits();
System.out.println("took:" + response.getTook());
System.out.println("timeout:" + response.isTimedOut());
System.out.println("total:" + hits.getTotalHits());
System.out.println("Maxscore:" + hits.getMaxscore());
System.out.println("hits========>>");
for (SearchHit hit : hits) {
//输出每条查询的结果信息
System.out.println(hit.getSourceAsstring());
}
System.out.println("<<========");
}
/**
* 请求体查询,高亮查询 highlight
*/
public static void queryBodyDocHighlight(RestHighLevelClient client){
// 创建搜索请求对象
SearchRequest request = new SearchRequest();
request.indices("student");
//构建高亮字段
HighlightBuilder highlightBuilder = new HighlightBuilder();
highlightBuilder.preTags("<font color='red'>");//设置标签前缀
highlightBuilder.postTags("</font>");//设置标签后缀
highlightBuilder.field("name");//设置高亮字段
// 构建查询的请求体
SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
//设置高亮构建对象
sourceBuilder.Highlighter(highlightBuilder);
//设置请求体
request.source(sourceBuilder);
//3.客户端发送请求,获取响应对象
SearchResponse response = null;
try {
response = client.search(request, RequestOptions.DEFAULT);
} catch (IOException e) {
e.printstacktrace();
}
//4.打印响应结果
SearchHits hits = response.getHits();
System.out.println("took::"+response.getTook());
System.out.println("time_out::"+response.isTimedOut());
System.out.println("total::"+hits.getTotalHits());
System.out.println("max_score::"+hits.getMaxscore());
System.out.println("hits::::>>");
for (SearchHit hit : hits) {
String sourceAsstring = hit.getSourceAsstring();
System.out.println(sourceAsstring);
//打印高亮结果
Map<String, HighlightField> highlightFields = hit.getHighlightFields();
System.out.println(highlightFields);
}
System.out.println("<<::::");
}
/**
* 请求体查询,聚合查询
*/
public static void queryBodyDocAggregation(RestHighLevelClient client){
// 高亮查询
SearchRequest request = new SearchRequest().indices("student");
SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
sourceBuilder.aggregation(AggregationBuilders.max("maxAge").field("age"));
//设置请求体
request.source(sourceBuilder);
//3.客户端发送请求,获取响应对象
SearchResponse response = null;
try {
response = client.search(request, RequestOptions.DEFAULT);
} catch (IOException e) {
e.printstacktrace();
}
//4.打印响应结果
SearchHits hits = response.getHits();
System.out.println(response);
}
/**
* 请求体查询,分组统计
*/
public static void queryBodyDocgroup(RestHighLevelClient client){
// 高亮查询
SearchRequest request = new SearchRequest().indices("student");
SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
sourceBuilder.aggregation(AggregationBuilders.terms("age_groupby").field("age"));
//设置请求体
request.source(sourceBuilder);
//3.客户端发送请求,获取响应对象
SearchResponse response = null;
try {
response = client.search(request, RequestOptions.DEFAULT);
} catch (IOException e) {
e.printstacktrace();
}
//4.打印响应结果
SearchHits hits = response.getHits();
System.out.println(response);
}
}
三、Linux集群搭建
1. 单机环境
先搭建一个linux单机环境 |
---|
cluster.name: elasticsearch #服务名称
node.name: node-1 # 结点名称
network.host: 0.0.0.0 # 主机地址
http.port: 9200 # 端口
cluster.initial_master_nodes: ["node-1"] # 注册主节点名称
- 更改系统配置
vim /etc/security/limits.conf
#每个进程可以打开的文件数限制
* soft nofile 65536
* hard nofile 65536
vim /etc/security/limits.d/20-nproc.conf
vim /etc/sysctl.conf
# 一个进程可以拥有的VMA(虚拟内存区域)的数量,默认65536
vm.max_map_count=655360
- 重新加载
sysctl -p
chown -R hadoop100:hadoop100 /opt/module/elasticsearch-7.8.0
- 进入es安装目录,执行bin/elasticsearch
- 测试
2. 集群Todo
日后补充 |
---|
四、重点
1. 核心概念
1. 索引 |
---|
2. 文档 |
---|
- 文档就是一条数据,可以被索引的基础信息单元
- 一个索引中,可以存储多个文档
3. 字段 |
---|
- 对文档数据的不同数据,进行分类标识
4. 映射(Mapping) |
---|
- mapping 是处理数据的方式和规则方面做一些限制,如:某个字段的数据类型、默认值、分析器、是否被索引等等
5. 分片(Shards) |
---|
6. 副本(Replicas) |
---|
7. 分配(Allocation) |
---|
- 将分片分配给某个节点的过程,包括分配主分片或者副本。如果是副本,还包含从主分片复制数据的过程。这个过程是由 master 节点完成的
2. 单节点集群的不足,和多节点故障转移
我们创建users索引,并传递json,分配3个主分片和一个副本,就是每个主分片都有一个福本 |
---|
{
"settings" : {
"number_of_shards" : 3,
"number_of_replicas" : 1
}
}
分析一些单节点的问题 |
---|
百度Elasticsearch-head插件,安装到浏览器即可
多节点 |
---|
水平扩容 |
---|
- 怎样为我们的正在增长中的应用程序按需扩容呢?当启动了第三个节点,我们的集群将会拥有三个节点的集群 : 为了分散负载而对分片进行重新分配
但是如果我们想要扩容超过 6 个节点怎么办呢? |
---|
{
"number_of_replicas" : 2
}
应对故障 |
---|
为什么我们集群状态是 yellow 而不是 green 呢 |
---|
3. 路由计算和分片控制
路由计算 |
---|
- 当索引一个文档的时候,文档会被存储到一个主分片中。 Elasticsearch 如何知道一个文档应该存放到哪个分片中呢?当我们创建文档时,它如何决定这个文档应当被存储在分片1 还是分片 2 中呢?首先这肯定不会是随机的,否则将来要获取文档的时候我们就不知道从何处寻找了。
实际上,这个过程是根据下面这个公式决定的:
- 所有的文档 API( get 、 index 、 delete 、 bulk 、 update 以及 mget )都接受一个叫做 routing 的路由参数 ,通过这个参数我们可以自定义文档到分片的映射。一个自定义的路由参数可以用来确保所有相关的文档——例如所有属于同一个用户的文档——都被存储到同一个分片中。
分片控制 |
---|
- 我们可以发送请求到集群中的任一节点。 每个节点都有能力处理任意请求。 每个节点都知道集群中任一文档位置,所以可以直接将请求转发到需要的节点上。 在下面的例子中,将所有的请求发送到 Node 1,我们将其称为 协调节点(coordinating node)
- 当发送请求的时候, 为了扩展负载,更好的做法是轮询集群中所有的节点
4. 写流程和读流程,以及更新流程
写流程 |
---|
- 新建,索引和删除文档所需要的步骤顺序:
- 客户端向 Node 1 发送新建、索引或者删除请求。
- 节点使用文档的 _id 确定文档属于分片 0 。请求会被转发到 Node 3,因为分片 0 的主分片目前被分配在 Node 3 上。
- Node 3 在主分片上面执行请求。如果成功了,它将请求并行转发到 Node 1 和 Node 2 的副本分片上。一旦所有的副本分片都报告成功, Node 3 将向协调节点报告成功,协调节点向客户端报告成功
读流程 |
---|
@H_324_1404@
- 从主分片或者副本分片检索文档的步骤顺序
更新流程 |
---|
- 部分更新一个文档的步骤如下
- 当主分片把更改,转发到副本分片时, 它不会转发更新请求。 相反,它转发完整文档的新版本。请记住,这些更改将会异步转发到副本分片,并且不能保证它们以发送它们相同的顺序到达。 如果 Elasticsearch 仅转发更改请求,则可能以错误的顺序应用更改,导致得到损坏的文档。
5. 多文档的操作流程
- mget 和 bulk API 的模式类似于单文档模式。区别在于协调节点知道每个文档存在于哪个分片中。它将整个多文档请求分解成 每个分片 的多文档请求,并且将这些请求并行转发到每个参与节点。
- 用单个 mget 请求取回多个文档所需的步骤顺序(可以对 docs 数组中每个文档设置 routing 参数)
- 客户端向 Node 1 发送 mget 请求
- Node 1 为每个分片构建多文档获取请求,然后并行转发这些请求到托管在每个所需的主分片或者副本分片的节点上。一旦收到所有答复, Node 1 构建响应并将其返回给客户端
- bulk API, 允许在单个批量请求中执行多个创建、索引、删除和更新请求,按如下步骤顺序执行
6. 分片原理和倒排索引
分片 |
---|
倒排索引 |
---|
举个倒排索引的例子 |
---|
- 为了创建倒排索引,我们首先将每个文档的 content 域拆分成单独的 词(我们称它为 词条或 tokens ),创建一个包含所有不重复词条的排序列表,然后列出每个词条出现在哪个文档。结果如下所示
- 现在,如果我们想搜索 quick brown ,我们只需要查找包含每个词条的文档
- 为了解决搜索+Quick和+fox 这样的索引查找不到文档的问题(Quick和quick是我们都希望检索到的,但Q和q不同,会检索不到),我们需要将词条规范为标准模式,就是找到与用户搜索的词条不完全一致,但具有足够相关性的文档
- 此时我们索引的Quick就不复存在了(因为标准化成quick了),那么如果我们还索引+Quick,就找不到了,所有此时我们对搜索的字符串使用与 content 域相同的标准化规则,会变成查询+quick +fox,这样两个文档都会匹配!分词和标准化的过程称为
分析
- 你只能搜索在索引中出现的词条,所以索引文本和查询字符串必须标准化为相
同的格式
7. 文档搜索
- 早期的全文检索会为整个文档集合建立一个很大的倒排索引并将其写入到磁盘。 一旦新的索引就绪,旧的就会被其替换,这样最近的变化便可以被检索到。
- 倒排索引被写入磁盘后是 不可改变的:它永远不会修改
- 不变性有重要的价值
8.文档刷写和文档合并
动态更新索引 |
---|
- 如何在保留不变性的前提下实现倒排索引的更新?
- Elasticsearch 基于 Lucene, 这个 java 库引入了按段搜索的概念.每一 段本身都是一个倒排索引, 但索引在 Lucene 中除表示所有段的集合外,还增加了提交点的概念 — 一个列出了所有已知段的文件
- 按段搜索会以如下流程执行
- 新文档被收集到内存索引缓存
- 不时地, 缓存被提交
- 新的段被开启,让它包含的文档可见以被搜索
- 内存缓存被清空,等待接收新的文档
- 当一个查询被触发,所有已知的段按顺序被查询。词项统计会对所有段的结果进行聚合,以保证每个词和每个文档的关联都被准确计算。 这种方式可以用相对较低的成本将新文档添加到索引。
- 段是不可改变的,所以既不能从把文档从旧的段中移除,也不能修改旧的段来进行反映文档的更新。 取而代之的是,每个提交点会包含一个 .del 文件,文件中会列出这些被删除文档的段信息。当一个文档被 “删除” 时,它实际上只是在 .del 文件中被 标记 删除。一个被标记删除的文档仍然可以被查询匹配到, 但它会在最终结果被返回前从结果集中移除
- 文档更新也是类似的操作方式:当一个文档被更新时,旧版本文档被标记删除,文档的新版本被索引到一个新的段中。 可能两个版本的文档都会被一个查询匹配到,但被删除的那个旧版本文档在结果集返回前就已经被移除
近实时搜索 |
---|
refresh_interval 可以在既存索引上进行动态更新。 在生产环境中,当你正在建立一个大的
新索引时,可以先关闭自动刷新,待开始使用该索引时,再把它们调回来
# 关闭自动刷新
PUT /users/_settings
{ "refresh_interval": -1 }
# 每一秒刷新
PUT /users/_settings
{ "refresh_interval": "1s" }
持久化变更 |
---|
持久化流程 |
---|
9. 文档分析和IK分词器
内置分析器 |
---|
- 下面列出最重要的分析器我,我们看看每个分析器会从"Set the shape to semi-transparent by calling set_trans(5)"这个字符串得到哪些词条
- 标准分析器
- 简单分析器
- 简单分析器在任何不是字母的地方分隔文本,将词条小写。
- 它会产生:set, the, shape, to, semi, transparent, by, calling, set, trans
- 空格分析器
- 空格分析器在空格的地方划分文本。
- 它会产生:Set, the, shape, to, semi-transparent, by, calling, set_trans(5)
- 语言分析器
IK分词器 |
---|
//下面可以测试ik分词器的效果
# GET http://localhost:9200/_analyze
{
"text":"测试单词",
"analyzer":"ik_max_word"
}
ik_max_word:会将文本做最细粒度的拆分
ik_smart:会将文本做最粗粒度的拆分
自定义分词器 |
---|
# PUT http://localhost:9200/my_index
{
"settings": {
"analysis": {
"char_filter": {
"&_to_and": {//自定义字符过滤器
"type": "mapping",//类型为映射
"mappings": [ "&=> and "]//将&映射为and
}},
"filter": {//定义过滤器
"my_stopwords": {
"type": "stop",//类型为不匹配
"stopwords": [ "the", "a" ]//遇到the a 不作为分词处理
}},
"analyzer": {
"my_analyzer": {//分词器名字
"type": "custom",//类型为分词器
"char_filter": [ "html_strip", "&_to_and" ],//使用的字符过滤器
"tokenizer": "standard",//使用标准分词器
"filter": [ "lowercase", "my_stopwords" ]//使用的过滤器
}}
}}}
10 客户端管理工具
Kibana 是一个免费且开放的用户界面,能够让你对 Elasticsearch 数据进行可视化,并让你在 Elastic Stack 中进行导航。你可以进行各种操作,从跟踪查询负载,到理解请求如何流经你的整个应用,都能轻松完成。 |
---|
1.下载: https://artifacts.elastic.co/downloads/kibana/kibana-7.8.0-windows-x86_64.zip
2. 修改 config/kibana.yml 文件
# 默认端口
server.port: 5601
# ES 服务器的地址
elasticsearch.hosts: ["http://localhost:9200"]
# 索引名
kibana.index: ".kibana"
# 支持中文
i18n.locale: "zh-CN"
- Windows 环境下执行 bin/kibana.bat 文件
- 通过浏览器访问 : http://localhost:5601
五、使用集成框架操作Elasticsearch
Spring Data 是一个用于简化数据库、非关系型数据库、索引库访问,并支持云服务的开源框架。其主要目标是使得对数据的访问变得方便快捷,并支持 map-reduce 框架和云计算数据服务。 Spring Data 可以极大的简化 JPA(Elasticsearch„)的写法,可以在几乎不用写实现的情况下,实现对数据的访问和操作。除了 CRUD 外,还包括如分页、排序等一些常用的功能 |
---|
- spring data常用模块
- 官网查找版本对应
接下来只需要创建maven项目搭建环境即可 |
---|
- 创建项目,引入依赖
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.3.6.RELEASE</version>
<relativePath/>
</parent>
<dependencies>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-elasticsearch</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-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-test</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
</dependency>
<!-- junit 单元测试 -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
</dependency>
</dependencies>
elasticsearch:
host: 127.0.0.1
port: 9200
logging:
level:
com:
yzpnb:
es: debug # 配置com.yzpnb.es这个路径下的日志等级
- 配置类
import lombok.Data;
import org.apache.http.HttpHost;
import org.elasticsearch.client.RestClient;
import org.elasticsearch.client.RestClientBuilder;
import org.elasticsearch.client.RestHighLevelClient;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.elasticsearch.config.AbstractElasticsearchConfiguration;
@Configuration
@Data
public class ElasticsearchConfig extends AbstractElasticsearchConfiguration {
@Value("${elasticsearch.host}")
private String host ;
@Value("${elasticsearch.port}")
private Integer port ;
//重写父类方法
@Override
public RestHighLevelClient elasticsearchClient() {
RestClientBuilder builder = RestClient.builder(new HttpHost(host, port));
RestHighLevelClient restHighLevelClient = new RestHighLevelClient(builder);
return restHighLevelClient;
}
}
- 实体类,映射索引和文档
package com.yzpnb.es.entity;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.ToString;
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
@NoArgsConstructor
@AllArgsConstructor
@ToString//上面4个注解是lomback的,自动生成get、set、toString、构造方法
@Document(indexName = "product", shards = 3, replicas = 1)//映射到ES,表示此类是product索引的映射,主分片3个,副本1个,每个主片,都有一个副本
public class Product {
//必须有 id,这里的 id 是全局唯一的标识,等同于 es 中的"_id"
@Id
private Long id;//商品唯一标识
/**
* type : 字段数据类型
* analyzer : 分词器类型
* index : 是否索引(默认:true)
* Keyword : 短语,不进行分词
*/
@Field(type = FieldType.Text, analyzer = "ik_max_word")
private String title;//商品名称
@Field(type = FieldType.Keyword)
private String category;//分类名称
@Field(type = FieldType.Double)
private Double price;//商品价格
@Field(type = FieldType.Keyword, index = false)
private String images;//图片地址
}
- dao层
import com.yzpnb.es.entity.Product;
import org.springframework.data.elasticsearch.repository.ElasticsearchRepository;
import org.springframework.stereotype.Repository;
@Repository
public interface ProductDao extends ElasticsearchRepository<Product,Long> {
}
- 启动类
- 测试类
import com.yzpnb.es.Application;
import com.yzpnb.es.dao.ProductDao;
import com.yzpnb.es.entity.Product;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.index.query.TermQueryBuilder;
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.sort;
import org.springframework.data.elasticsearch.core.ElasticsearchRestTemplate;
import org.springframework.test.context.junit4.springrunner;
import java.util.ArrayList;
import java.util.List;
@RunWith(springrunner.class)
@SpringBoottest(classes = Application.class)
public class SpringDataESIndexTest {
//注入 ElasticsearchRestTemplate
@Autowired
private ElasticsearchRestTemplate elasticsearchRestTemplate;
/**
* 索引相关操作
*/
//创建索引并增加映射配置,如果ES中现在没有我们实体类隐射的Product索引,那么会自动创建
@Test
public void createIndex(){
//创建索引,系统初始化会自动创建索引
System.out.println("创建索引");
}
@Test
public void deleteIndex(){
//创建索引,系统初始化会自动创建索引
boolean flg = elasticsearchRestTemplate.deleteIndex(Product.class);
System.out.println("删除索引 = " + flg);
}
/**
* 文档相关操作
*/
@Autowired
private ProductDao productDao;
/**
* 新增
*/
@Test
public void save(){
Product product = new Product();
product.setId(2L);
product.setTitle("华为手机");
product.setCategory("手机");
product.setPrice(2999.0);
product.setimages("http://www.atguigu/hw.jpg");
productDao.save(product);
}
//修改
@Test
public void update(){
Product product = new Product();
product.setId(1L);
product.setTitle("小米 2 手机");
product.setCategory("手机");
product.setPrice(9999.0);
product.setimages("http://www.atguigu/xm.jpg");
productDao.save(product);
}
//根据 id 查询
@Test
public void findById(){
Product product = productDao.findById(1L).get();
System.out.println(product);
}
//查询所有
@Test
public void findAll(){
Iterable<Product> products = productDao.findAll();
for (Product product : products) {
System.out.println(product);
}
}
//删除
@Test
public void delete(){
Product product = new Product();
product.setId(1L);
productDao.delete(product);
}
//批量新增
@Test
public void saveAll(){
List<Product> productList = new ArrayList<>();
for (int i = 0; i < 10; i++) {
Product product = new Product();
product.setId(Long.valueOf(i));
product.setTitle("["+i+"]小米手机");
product.setCategory("手机");
product.setPrice(1999.0+i);
product.setimages("http://www.atguigu/xm.jpg");
productList.add(product);
}
productDao.saveAll(productList);
}
//分页查询
@Test
public void findByPageable(){
//设置排序(排序方式,正序还是倒序,排序的 id)
Sort sort = Sort.by(Sort.Direction.DESC,"id");
int currentPage=0;//当前页,第一页从 0 开始,1 表示第二页
int pageSize = 5;//每页显示多少条
//设置查询分页
PageRequest pageRequest = PageRequest.of(currentPage, pageSize,sort);
//分页查询
Page<Product> productPage = productDao.findAll(pageRequest);
for (Product Product : productPage.getContent()) {
System.out.println(Product);
}
}
/**
* 查询相关操作
*/
/**
* term 查询
* search(termQueryBuilder) 调用搜索方法,参数查询构建器对象
*/
@Test
public void termQuery(){
TermQueryBuilder termQueryBuilder = QueryBuilders.termQuery("title", "机");
Iterable<Product> products = productDao.search(termQueryBuilder);
for (Product product : products) {
System.out.println(product);
}
}
/**
* term 查询加分页
*/
@Test
public void termQueryByPage(){
int currentPage= 0 ;
int pageSize = 5;
//设置查询分页
PageRequest pageRequest = PageRequest.of(currentPage, pageSize);
TermQueryBuilder termQueryBuilder = QueryBuilders.termQuery("title", "小");
Iterable<Product> products = productDao.search(termQueryBuilder,pageRequest);
for (Product product : products) {
System.out.println(product);
}
}
}
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 [email protected] 举报,一经查实,本站将立刻删除。