易扩展
高性能(基于倒排索引)
docker network create es-net #创建网络
ElasticSearch官网并且下载kibana依赖包
或者
采用镜像tar包
docker load -i tar包
docker run -d \
--name es \
-e "ES_JAVA_OPTS=-Xms512m -Xmx512m" \
-e "discovery.type=single-node" \
-v es-data:/usr/share/elasticsearch/data \
-v es-plugins:/usr/share/elasticsearch/plugins \
--privileged \
--network es-net \
-p 9200:9200 \
-p 9300:9300 \
elasticsearch:7.12.1
-e "cluster.name=es-docker-cluster"
:设置集群名称-e "http.host=0.0.0.0"
:监听的地址,可以外网访问-e "ES_JAVA_OPTS=-Xms512m -Xmx512m"
:内存大小-e "discovery.type=single-node"
:非集群模式-v es-data:/usr/share/elasticsearch/data
:挂载逻辑卷,绑定es的数据目录-v es-logs:/usr/share/elasticsearch/logs
:挂载逻辑卷,绑定es的日志目录-v es-plugins:/usr/share/elasticsearch/plugins
:挂载逻辑卷,绑定es的插件目录--privileged
:授予逻辑卷访问权--network es-net
:加入一个名为es-net的网络中-p 9200:9200
:端口映射配置在浏览器输入 ip:9200 即可看见一下消息说明 elasticsearch安装成功
docker run -d \
--name kibana \
-e ELASTICSEARCH_HOSTS=http://es:9200 \
--network=es-net \
-p 5601:5601 \
kibana:7.12.1
部署完成后 浏览器访问 ip:5601
--network es-net
:加入一个名为es-net的网络中,与elasticsearch在同一个网络中-e ELASTICSEARCH_HOSTS=http://es:9200"
:设置elasticsearch的地址,因为kibana已经与elasticsearch在一个网络,因此可以用容器名直接访问elasticsearch-p 5601:5601
:端口映射配置docker volume inspect es-plugins
[
{
"CreatedAt": "2022-05-06T10:06:34+08:00",
"Driver": "local",
"Labels": null,
"Mountpoint": "/var/lib/docker/volumes/es-plugins/_data",
"Name": "es-plugins",
"Options": null,
"Scope": "local"
}
]
/var/lib/docker/volumes/es-plugins/_data
中即可docker restart es
ik_smart
:最少切分ik_max_word
:最细切分随着互联网的发展,“造词运动”也越发的频繁。出现了很多新的词语,在原有的词汇列表中并不存在。比如:“奥力给”,“绝绝子” 等。我们需要把一些新词扩展到IK分词器中
在这个目录下新建一个 ext.dic
,可以参考config目录下复制一个配置文件进行修改,然后输入新词保存即可。
重启elasticsearch
docker restart es
stopword.dic
文件打开后把我们想停用的词输入 保存即可。docker restart es
索引库类似于数据库中的建表操作
type | 数据类型 |
---|---|
type | 数据类型 |
index | 是否索引 |
analyzer | 分词器 |
properties | 子字段 |
type常见有哪些 | |
字符串 | text,keyword |
数字 | long/integet/short/byte/double/float |
布尔 | boolean |
日期 | date |
对象 | object |
PUT /索引库名称
{
"mappings": {
"properties": {
"字段名":{
"type": "text",
"analyzer": "ik_smart"
},
"字段名2":{
"type": "keyword",
"index": "false"
},
"字段名3":{
"properties": {
"子字段": {
"type": "keyword"
}
}
},
// ...略
}
}
}
PUT /索引库名
DELETE /索引库名
#更新索引库 只能添加新字段,不能修改已有字段
PUT /索引库名/_mapping
{
"properties": {
"新字段名":{
"type": "integer"
}
}
}
GET /索引库名/_doc/文档id
POST /索引库名/_doc/文档id
{
"字段1": "值1",
"字段2": "值2",
"字段3": {
"子属性1": "值3",
"子属性2": "值4"
},
// ...
}
DELETE /索引库名/_doc/文档id
1.全量修改,会删除旧文档,添加新文档
PUT /索引库名/_doc/文档id
{
"字段1": "值1",
"字段2": "值2",
// ... 略
}
2.增量修改,修改指定字段值
POST /索引库名/_update/文档id
{
"doc": {
"字段名": "新的值",
}
}
pom依赖
引入es的RestHighLevelClient依赖:
org.elasticsearch.client
elasticsearch-rest-high-level-client
创建测试类初始化客户端
@BeforeEach//先运行这个方法进行绑定客户端
void setUp(){
this.client=new RestHighLevelClient(RestClient.builder(HttpHost.create("http://192.168.36.129:9200")));
}
公共部分
private RestHighLevelClient client;
创建索引库
//创建索引库
@Test
void t2() throws IOException {
//1. 创建索引库对象
CreateIndexRequest request = new CreateIndexRequest("hotel");
//2. 请求参数,HotelIndex.INDEX_MAPPING是创建索引库的字符串模板,内容是创建索引库的DSL语句
request.source(HotelIndex.INDEX_MAPPING, XContentType.JSON);
//3. 发送请求
client.indices().create(request, RequestOptions.DEFAULT);
}
删除索引库
//删除索引库
@Test
void t3() throws IOException {
//1.创建删除索引库请求对象
DeleteIndexRequest request = new DeleteIndexRequest("hotel");
//发送删除请求
client.indices().delete(request,RequestOptions.DEFAULT);
}
查询索引库
//查询索引库是否存在
@Test
void t4() throws IOException {
//1.创建查询索引库请求对象
GetIndexRequest request = new GetIndexRequest("hotel");
//发送请求
boolean exists = client.indices().exists(request, RequestOptions.DEFAULT);
System.out.println(exists==true?"索引库已经存在":"索引库不存在");
}
新增文档
//新增文档
@Test
void t5() throws IOException {
Hotel data = hotelService.getById(61083L);
HotelDoc hotelDoc = new HotelDoc(data);
//1.准备Request对象
IndexRequest request = new IndexRequest("hotel").id(hotelDoc.getId().toString());
//2.准备JSON文档(索引库中的数据)
request.source(JSON.toJSONString(hotelDoc),XContentType.JSON);
//3.发送请求
client.index(request,RequestOptions.DEFAULT);
}
删除文档
//删除文档
@Test
void t8() throws Exception{
DeleteRequest request = new DeleteRequest("hotel","61083");
client.delete(request,RequestOptions.DEFAULT);
}
//更新文档
@Test
void t7() throws Exception{
//1.准备Request
UpdateRequest request = new UpdateRequest("hotel","61083");
//2.准备请求参数
request.doc(
//部分更新 key value的形式
"price","666",
"name","李四"
);
client.update(request,RequestOptions.DEFAULT);
}
查询文档
//查询文档
@Test
void t6() throws IOException {
//1.查询请求对象
GetRequest request = new GetRequest("hotel","61083");
//2.发送请求,得到文档响应数据
GetResponse documentFields = client.get(request, RequestOptions.DEFAULT);
//3.拿到文档数据中的source
String data = documentFields.getSourceAsString();
//4.将source反序列化为对象
HotelDoc hotelDoc = JSON.parseObject(data, HotelDoc.class);
System.out.println("文档数据===>"+hotelDoc);
}
批量新增
//批量新增
@Test
void t9() throws Exception{
BulkRequest request = new BulkRequest();
List<Hotel> lists = hotelService.list();
for (Hotel list : lists) {
HotelDoc hotelDoc = new HotelDoc(list);
request.add(new IndexRequest("hotel").id(hotelDoc.getId().toString()).source(JSON.toJSONString(hotelDoc),XContentType.JSON));
}
client.bulk(request,RequestOptions.DEFAULT);
}
match_all
match_query 和 multi_match_query
ids range term
geo_distance 和 geo_bounding_box
GET /indexName/_search
{
"query": {
"查询类型": {
"查询条件": "条件值"
}
}
}
例:
GET /hotel/_search
{
"query": {
"match_all": {}
}
}
全文检索查询的一种,会对用户输入内容分词,然后去倒排索引库检索
GET /索引库/_search
{
"query": {
"match": {
"FIELD": "TEXT"
}
}
}
例:
GET /hotel/_search
{
"query": {
"match": {
"all": "外滩五钻"
}
}
}
GET /索引库/_search
{
"query": {
"multi_match": {
"query": "TEXT",
"fields": ["FIELD1", " FIELD12"]
}
}
}
根据一个字段查询
根据多个字段查询,参与查询字段越多,查询性能越差
term精确查询
#精确查询
GET /hotel/_search
{
"query": {
"term": {
"FIELD": {
"value": "VALUE"
}
}
}
}
例:
#精确查询
GET /hotel/_search
{
"query": {
"term": {
"city": {
"value": "上海"
}
}
}
}
range范围查询
GET /hotel/_search
{
"query": {
"range": {
"FILED": {
"gte": value,
"lte": value
}
}
}
}
例:
GET /hotel/_search
{
"query": {
"range": {
"price": {
"gte": 100,
"lte": 200
}
}
}
}
根据geo-distance位置范围查询
GET /hotel/_search
{
"query": {
"geo_distance":{
# distance: 可以设定查询距离
"distance":"2km",
# location:当前地址位置,此字段必须是 geo_point类型
"location":"31.21,121.5"
}
}
}
在原来查询的条件下进行加权算分
GET /hotel/_search
{
"query": {
"function_score": {
#第一次进行条件查询匹配
"query": {"match": {
"all": "外滩"
}},
#算分函数
"functions": [
{
"filter": {
"term": {
"brand": "如家"
}
},
#算分权重为 10
"weight": 10
}
],
#加权模式 boost_mode:乘法
"boost_mode": "multiply"
}
}
}
布尔查询是一个或多个查询子句的组合。子查询的组合方式有:
must
:必须匹配每个子查询,类似“与”should
:选择性匹配子查询,类似“或”must_not
:必须不匹配,不参与算分,类似“非”filter
:必须匹配,不参与算分需求:搜索名字包含“如家”,价格不高于400,在坐标31.21,121.5周围10km范围内的酒店。
GET /hotel/_search
{
"query": {
"bool": {
"must": [
{
"match": {
"name": "如家"
}
}
],
"must_not": [
{
"range": {
"FIELD": {
"lte": 500
}
}
}
],
"filter": [
{
"geo_distance": {
"distance": "10km",
"location": {
"lat": 31.21,
"lon": 121.5
}
}
}
]
}
}
}
语法
GET /索引库名称/_search
{
"query": {
"match_all": {}
},
"sort": [
{
"FIELD": "desc" // 排序字段和排序方式ASC、DESC
}
]
}
语法
GET /索引库名称/_search
{
"query": {
"match_all": {}
},
"from": 从第几条数据开始查询, // 分页开始的位置,默认为0
"size": 显示查询几条, // 期望获取的文档总数
"sort": [
{"按照哪个字段排序": "asc"}
]
}
语法:高亮处理,默认情况搜索字段必须与高亮字段保持一致
GET /索引库名称/_search
{
"query": {
"match": {
"FIELD": "TEXT"
}
},
"highlight": {
"fields": { // 指定要高亮的字段
"FIELD": {
"require_field_match":"false" #搜索字段与高亮字段匹配为false
"pre_tags": "", // 用来标记高亮字段的前置标签
"post_tags": "" // 用来标记高亮字段的后置标签
}
}
}
}
@Test
void t1() throws IOException {
//1.创建搜索请求对象
SearchRequest request = new SearchRequest("索引库名称");
//2.准备DSL参数
request.source().query(QueryBuilders.matchAllQuery()).from(0).size(10);
//3.发送请求 得到数据集合
SearchResponse search = client.search(request, RequestOptions.DEFAULT);
//4.解析响应数据
SearchHits hits = search.getHits();
//拿到数据集合中Hits数据的条数
long value = hits.getTotalHits().value;
//拿到数据集合
SearchHit[] hits1 = hits.getHits();
//5.遍历数据
for (SearchHit hit : hits1) {
System.out.println(hit.getSourceAsString());
}
}
@Test
void t2() throws IOException {
//1.创建搜索请求对象
SearchRequest request = new SearchRequest("索引库名");
//2.准备DSL参数
request.source().query(QueryBuilders.matchQuery("字段","值")).from(0).size(10);
//3.发送请求 得到数据集合
SearchResponse search = client.search(request, RequestOptions.DEFAULT);
//4.解析响应数据
SearchHits hits = search.getHits();
//拿到数据集合中Hits数据的条数
long value = hits.getTotalHits().value;
//拿到数据集合
SearchHit[] hits1 = hits.getHits();
//5.遍历数据
for (SearchHit hit : hits1) {
System.out.println(hit.getSourceAsString());
}
}
@Test
void t3() throws IOException {
//1.创建搜索请求对象
SearchRequest request = new SearchRequest("hotel");
//2.准备DSL参数
request.source().query(QueryBuilders.multiMatchQuery("查询的值","查询字段","查询字段",...)).from(0).size(10);
//3.发送请求 得到数据集合
SearchResponse search = client.search(request, RequestOptions.DEFAULT);
//4.解析响应数据
SearchHits hits = search.getHits();
//拿到数据集合中Hits数据的条数
long value = hits.getTotalHits().value;
//拿到数据集合
SearchHit[] hits1 = hits.getHits();
//5.遍历数据
for (SearchHit hit : hits1) {
System.out.println(hit.getSourceAsString());
}
}
@Test
void t4() throws IOException {
//1.创建搜索请求对象
SearchRequest request = new SearchRequest("hotel");
//2.准备DSL参数
BoolQueryBuilder boolQueryBuilder = new BoolQueryBuilder();
//精确查询
boolQueryBuilder.must(QueryBuilders.termQuery("字段名","查询的值"));
//范围查询
boolQueryBuilder.filter(QueryBuilders.rangeQuery("price").lte(300));
request.source().query(boolQueryBuilder);
//3.发送请求 得到数据集合
SearchResponse search = client.search(request, RequestOptions.DEFAULT);
//4.解析响应数据
SearchHits hits = search.getHits();
//拿到数据集合中Hits数据的条数
long value = hits.getTotalHits().value;
//拿到数据集合
SearchHit[] hits1 = hits.getHits();
//5.遍历数据
for (SearchHit hit : hits1) {
System.out.println(hit.getSourceAsString());
}
}
@Test
void t5() throws Exception{
//1.创建搜索请求对象
SearchRequest request = new SearchRequest("索引库名");
//2.准备DSL参数
request.source().sort("price", SortOrder.ASC).from(0).size(5);
//3.发送请求 得到数据集合
SearchResponse search = client.search(request, RequestOptions.DEFAULT);
handleResponse(search);
}
@Test
void t6()throws Exception{
//1.创建搜索请求对象
SearchRequest searchRequest = new SearchRequest("hotel");
//2.准备DSL参数
searchRequest.source().query(QueryBuilders.matchQuery("查询字段","value值"));
searchRequest.source().highlighter(new HighlightBuilder().field("高亮的字段").requireFieldMatch(false));
//3.发送请求 得到数据集合
SearchResponse search = client.search(searchRequest,RequestOptions.DEFAULT);
//4.解析响应数据
SearchHits hits = search.getHits();
//拿到数据集合中Hits数据的条数
long value = hits.getTotalHits().value;
//拿到数据集合
SearchHit[] hits1 = hits.getHits();
//5.遍历数据
for (SearchHit hit : hits1) {
//拿到高亮字段
Map<String, HighlightField> highlightFields = hit.getHighlightFields();
if (!CollectionUtils.isEmpty(highlightFields)) {
//拿到高亮字段的key
HighlightField name = highlightFields.get("name");
if(name!=null){
String highlightName = name.getFragments()[0].string();
System.out.println(highlightName);
}
}
}
}
聚合(aggregations):
可以实现对文档数据的统计、分析、运算。聚合常见的有三类:
Bucket:
对文档数据分组,并统计每组数量Metric:
对文档数据做计算,例如avgPipeline:
基于其它聚合结果再做聚合聚合(aggregations):
的字段类型必须是
keyword
数值
布尔
日期
聚合(aggregations)聚合三要素:聚合名称、聚合类型、聚合字段
GET /索引库名/_search
{
"size": 0, // 设置size为0,结果中不包含文档,只包含聚合结果
"aggs(固定名称)": { // 定义聚合
"brandAgg(自定义聚合名称)": { //给聚合起个名字
"terms": { // 聚合的类型,按照品牌值聚合,所以选择term
"field": "参与聚合的字段名", // 参与聚合的字段
"order": {
"排序字段": "asc" // 按照升序排列
},
"size": 20 // 希望获取的聚合结果数量
}
}
}
}
GET /索引库名/_search
{
#先进行查询,然后再将查询后的数据进行聚合
"query": {
"match": {
"name": "酒店"
}
},
"size": 0,
"aggs": {
"brandAgg": {
"terms": {
"field": "brand",
"size": 20
}
}
}
}
聚合可配置的属性 | |
---|---|
size | 指定聚合结果数量 |
order | 指定聚合结果排序方式 |
field | 指定聚合字段 |
GET /索引库名/_search
{
"size": 0,
"aggs": {
"brandAgg(自定义聚合名)": {
"terms(添加聚合类型)": {
"field": "字段",
"size": 20
},
"aggs": { // 是上面聚合的子聚合,也就是分组后对每组分别计算
"score_stats": { // 聚合名称
"stats": { // 聚合类型,这里stats可以计算min、max、avg等
"field": "score" // 聚合字段,这里是score
}
}
}
}
}
}
@Test
void t7()throws Exception{
//1.准备搜索请求对象
SearchRequest request = new SearchRequest("hotel");
//2.准备DSL
request.source().size(0);
request.source().aggregation(AggregationBuilders
//准备聚合:值为自定义聚合名称
.terms("brandAgg")
//聚合的字段
.field("brand")
//展示几条
.size(10));
//3.发出请求得到解析对象
SearchResponse search = client.search(request, RequestOptions.DEFAULT);
//4.拿到聚合对象
Aggregations aggregations = search.getAggregations();
//5.通过聚合对象.get(自定义聚合名称) 拿到具体的聚合类型的结果 对象
Terms brandAgg = aggregations.get("brandAgg");
//6.拿到桶Buckets
List<? extends Terms.Bucket> buckets = brandAgg.getBuckets();
//7.遍历每一条数据
for (Terms.Bucket bucket : buckets) {
String key = bucket.getKeyAsString();
System.out.println(key);
}
}
自定义分词器中过滤器的参数文档:
参数文档PUT /test
{
"settings": {
"analysis": {
"analyzer": { // 自定义分词器
"my_analyzer(自定义分词器名称)": { // 分词器名称
"tokenizer": "ik_max_word",
"filter": "py" #对应下面的过滤器名称
}
},
"filter": { // 自定义tokenizer filter
"py": { // 过滤器名称
"type": "pinyin", // 过滤器类型,这里是pinyin
"keep_full_pinyin": false,
"keep_joined_full_pinyin": true,
"keep_original": true,
"limit_first_letter_length": 16,
"remove_duplicated_term": true,
"none_chinese_pinyin_tokenize": false
}
}
}
},
"mappings": {
"properties": {
"name":{
"type": "text",
"analyzer": "my_analyzer", #当我插入文档数据时,会根据自定义分词规则进行过滤
"search_analyzer": "ik_smart" #搜索时使用ik分词器 可以避免搜到同音字
}
}
}
}
参与补全查询的字段必须是completion类型。
字段的内容一般是用来补全的多个词条形成的数组。
创建语法:
// 创建索引库
PUT test
{
"mappings": {
"properties": {
"title":{
"type": "completion"
}
}
}
}
// 示例数据
POST test/_doc
{
"title": ["Sony","switch","LOL", "WH-1000XM3"]
}
POST test/_doc
{
"title": ["switch", "WH-1000XM4"]
}
查询语法:
// 自动补全查询
GET /test2/_search
{
"suggest": {
"title_suggest": {
"text": "s", // 关键字
"completion": {
"field": "title", // 补全查询的字段
"skip_duplicates": true, // 跳过重复的
"size": 10 // 获取前10条结果
}
}
}
}
@Test
void t8()throws Exception{
//1.准备搜索请求对象
SearchRequest request = new SearchRequest("test2");
//2.准备DSL语句
request.source().suggest(new SuggestBuilder().addSuggestion(
// 自定义suggestion名称
"mySuggest",
//补全功能搜索的字段
SuggestBuilders.completionSuggestion("title")
//跳过重复的数据
.skipDuplicates(true)
//根据前缀来搜索补全的值,这里的值将来会通过前端传过来
.prefix("s")
//显示几条数据
.size(10)));
//3.发送请求得到数据结果
SearchResponse search = client.search(request, RequestOptions.DEFAULT);
//4.拿到suggest所有数据
Suggest suggest = search.getSuggest();
//4.1 拿到真正的suggestion补全数据的json串儿
Suggest.Suggestion<? extends Suggest.Suggestion.Entry<? extends Suggest.Suggestion.Entry.Option>> mySuggest = suggest.getSuggestion("mySuggest");
//5.遍历补全数据json串儿
for (Suggest.Suggestion.Entry<? extends Suggest.Suggestion.Entry.Option> options : mySuggest) {
//拿到自动补全数据
String data = options.getText().string();
System.out.println(data);
}
System.out.println(search);
}
提示:学习视频来自B站