es模糊查询
前言
es是一个优秀的搜索框架,最近项目上有用到,但是csdn能找到的资料有限,因此出个文章仅供参考,不对的地方大家可以指出,共同进步。 本文针对初学es使用es新建索引到查询教程,本文基于es版本为:elasticsearch-7.14.1-linux-x86_64.tar.gz, kibana版本为:kibana-7.14.1-linux-x86_64.tar.gz 安装过程就不介绍了,同学们可以自行百度安装。提示:以下是本篇文章正文内容,下面案例可供参考
一、es 索引建立以及kibana的相关操作
1.1 kibana操作
我们常见的搜索类型有
1.time 可以有很多格式的时间类型,我这里使用了最常用的格式, 2.keyword,使用在已经确定的枚举类型的,例如男、女
3.text,使用在模糊查找的字段,例如文章内容等
4.list,使用在1对多的关系中,例如一个人的标签,可爱、善良
这里我都新建了这几种类型,可以参考如何使用。
1.1.1 新建索引
新建索引名称为test,属性有name,nameType,time 和一个tag标签list
PUT /test
{
"mappings": {
"properties": {
"name": { "type": "text" },
"nameType": { "type": "text" },
"time": {
"type": "date",
"format": "yyyy-MM-dd HH:mm:ss || yyyy-MM-dd || yyyy/MM/dd HH:mm:ss|| yyyy/MM/dd ||epoch_millis"
},
"tags": {
"properties": {
"id": {"type":"keyword"},
"tagName": {
"type": "text"
}
}
}
}
}
}
1.1.2 在kibana上新建索引,看到以下提示就是新建成功
1.1.3 删除索引
DELETE /test
1.1.4 查询所有数据
GET /test/_search
{
"query": {"match_all": {
}}
}
1.1.5 查看索引的映射情况
GET /test
二、使用步骤
1.本文基于springboot,要先引入maven依赖
代码如下(示例):
<properties>
<java.version>1.8</java.version>
<elasticsearch.version>7.14.1</elasticsearch.version>
</properties>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.16.20</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-elasticsearch</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.9</version>
</dependency>
<dependency>
<groupId>apache-beanutils</groupId>
<artifactId>commons-beanutils</artifactId>
<version>1.7.0</version>
</dependency>
主要是spring-boot-starter-data-elasticsearch,其他有用到就一起粘贴下来了。使用的springboot版本为
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.4.4</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
2.建立连接
建立如下的配置,在control层就可以直接注入使用连接
一般es的端口为9200,如果有账号密码的也可以使用账号密码连接
这里就不展示了,可以看看api就知道了。
代码如下(示例):
package com.example.springdemo.demo.conifg;
import org.apache.http.HttpHost;
import org.elasticsearch.client.RestClient;
import org.elasticsearch.client.RestHighLevelClient;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class EsConfig {
@Bean("restHighLevelClient")
public RestHighLevelClient esRestClient(){
RestHighLevelClient client = new RestHighLevelClient(
RestClient.builder(
//在这里配置你的elasticsearch的地址
new HttpHost("123.xx.xx.xxx", 9200, "http")
)
);
return client;
}
}
3 es插入数据
3.1注入client
@Autowired
private RestHighLevelClient restHighLevelClient;
3.2 插入数据
这里只介绍了单个插入,批量插入使用client.bulk方法即可
private String insertData(EsModel esModel) throws IOException {
//要插入的索引名称test
IndexRequest request = new IndexRequest("test");
request.id(UUID.randomUUID().toString());
request.timeout(TimeValue.timeValueSeconds(1));
request.timeout("1s");
//将我们的数据放入请求,json
request.source(JSON.toJSONString(esModel),XContentType.JSON);
//request.source(describe);
System.out.println("json:"+JSON.toJSONString(esModel));
//客服端发送请求
IndexResponse index = restHighLevelClient.index(request, RequestOptions.DEFAULT);
request.source(JSON.toJSONString(esModel),XContentType.JSON);
String result = index.toString()+"/n"+index.toString();
System.out.println(result);
return result;
}
这里的插入格式es支持两种格式,一种是json格式的,如上面的写法,一种是map格式的,
JSON.parseObject(JSON.toJSONString(user), Map.class);
map格式的可以使用json的工具类直接转换即可。
3.2.1 插入数据control层如下
@RequestMapping("/testInsert")
String testInsert() throws IOException, IllegalAccessException, NoSuchMethodException, InvocationTargetException {
StringBuilder sb = new StringBuilder();
EsModel user = new EsModel();
user.setName("王小明");
user.setNameType("男");
user.setTime(new Date());
List<Tag> tags = new ArrayList<>();
Tag tag = new Tag();
tag.setId(UUID.randomUUID().toString());
tag.setTagName("活泼");
Tag tag1 = new Tag();
tag1.setId(UUID.randomUUID().toString());
tag1.setTagName("开朗");
Tag tag11= new Tag();
tag11.setId(UUID.randomUUID().toString());
tag11.setTagName("活泼2");
tags.add(tag);
tags.add(tag1);
tags.add(tag11);
user.setTags(tags);
String s1 = insertData(user);
sb.append(s1).append("/n");
EsModel user2 = new EsModel();
user2.setName("王小二");
user2.setNameType("男");
user2.setTime(new Date());
List<Tag> tags4 = new ArrayList<>();
Tag tag4 = new Tag();
tag4.setId(UUID.randomUUID().toString());
tag4.setTagName("高富活泼");
Tag tag5 = new Tag();
tag5.setId(UUID.randomUUID().toString());
tag5.setTagName("英俊");
tags4.add(tag4);
tags4.add(tag5);
user2.setTags(tags4);
String s3 = insertData(user2);
sb.append(s3).append("/n");
EsModel user1 = new EsModel();
user1.setName("王小红");
user1.setNameType("女");
user1.setTime(new Date());
List<Tag> tags1 = new ArrayList<>();
Tag tag2 = new Tag();
tag2.setId(UUID.randomUUID().toString());
tag2.setTagName("漂亮");
Tag tag3 = new Tag();
tag3.setId(UUID.randomUUID().toString());
tag3.setTagName("可爱");
tags1.add(tag3);
tags1.add(tag2);
user1.setTags(tags1);
String s = insertData(user1);
sb.append(s).append("/n");
return sb.toString();
}
3.2.2 注意点
这里需要注意的是,如果格式是时间格式,我们需要在实体上加个jsonfield注解否则转换成string的时候会报错。实体类如下
package com.example.springdemo.demo.model;
import com.alibaba.fastjson.annotation.JSONField;
import com.fasterxml.jackson.annotation.JsonFormat;
import lombok.Data;
import org.springframework.data.elasticsearch.annotations.DateFormat;
import org.springframework.data.elasticsearch.annotations.Field;
import org.springframework.data.elasticsearch.annotations.FieldType;
import java.io.Serializable;
import java.text.Format;
import java.util.Date;
import java.util.List;
@Data
public class EsModel implements Serializable {
private String name;
private String nameType;
// @Field(type = FieldType.Date, format= DateFormat.custom,pattern ="yyyy-MM-dd HH:mm:ss")
// @JsonFormat(shape = JsonFormat.Shape.STRING, pattern ="yyyy-MM-dd HH:mm:ss",timezone="GMT+8")
@JSONField(format="yyyy-MM-dd HH:mm:ss")
private Date time;
private List<Tag> tags;
}
package com.example.springdemo.demo.model;
import lombok.Data;
@Data
public class Tag {
private String id;
private String tagName;
}
执行插入指令显示如下
http://localhost:8008/search/testInsert
去kibana查询下数据是否插入
执行上面的查询所有数据指令可以看到数据已经成功插入
3.查询
接下来我们可以进行查询了,这里我们主要用的是中文查询,中文搜索的时候,和查询的时候都可以指定分词器,分词器的作用其实就是可以智能的把语句分成不同的词语索引,例如我爱中国,可以分解成,我,爱,中国,如果不加分词器,则会分解成我,爱,中,国,和更多的组合,一般会使用分词器,我这里就不使用了。
这样在搜索的时候就可以更精确的查找到用户想要的结果。
我们可以在kibana测试一下
GET _analyze
{"text":"我爱中国"}
使用分词器分词
GET _analyze
{"analyzer":"ik_max_word",
"text":"我爱中国"}
3.1 查询代码如下
实际项目使用的时候往往不会像一般简单教程里面的这么简单,分页,模糊查询,高亮,排序,是基本的要求,所以我就构造了下面的查询
这里的查询条件构造如下:
查询一个boolquery,也就是一个是否的条件查询
这里查询为:并且(must) nametype 含有男
并且(代码里面用must表示)
(
或者(代码用shuld) tags.tagName 中含有 活字
)
list里面的字段用list名字.属性表示。
@RequestMapping("/testSearch")
String contextLoads() throws IOException {
//1、创建查询请求,规定查询的索引
SearchRequest request = new SearchRequest("test");
//2、创建条件构造
SearchSourceBuilder builder = new SearchSourceBuilder();
//3、构造条件
BoolQueryBuilder boolQueryBuilder = new BoolQueryBuilder();
boolQueryBuilder.must(QueryBuilders.matchPhraseQuery("nameType","男"));
BoolQueryBuilder condition = new BoolQueryBuilder();
condition.should(QueryBuilders.matchPhraseQuery("tags.tagName","活"));
boolQueryBuilder.must(condition);
builder.query(boolQueryBuilder);
//高亮查询
HighlightBuilder highlightBuilder = new HighlightBuilder();
HighlightBuilder.Field field = new HighlightBuilder.Field("tags.tagName");
highlightBuilder.field(field);
highlightBuilder.requireFieldMatch(false);
// highlightBuilder.preTags("<em>");
// highlightBuilder.postTags("</em>");
highlightBuilder.preTags("<span style='color:red'>");
highlightBuilder.postTags("</span>");
builder.highlighter(highlightBuilder);
//指定返回的字段
String[] fetch ={"name","nameType","tags.tagName"};
builder.fetchSource(fetch,null);
builder.from(0);
builder.size(10);
//根据匹配度排序
builder.sort("_score",SortOrder.DESC);
//根据时间排序,asc升序
builder.sort("time", SortOrder.DESC);
//4、将构造好的条件放入请求中
request.source(builder);
//5、开始执行发送request请求
SearchResponse searchResponse = restHighLevelClient.search(request, RequestOptions.DEFAULT);
//6、开始处理返回的数据
SearchHit[] hits = searchResponse.getHits().getHits();
StringBuilder sb = new StringBuilder();
for (SearchHit hit : hits) {
Map<String, HighlightField> highlightFields = hit.getHighlightFields();
System.out.println("highlight"+highlightFields);
HighlightField content = highlightFields.get("tags.tagName");
//原始数据
Map<String, Object> sourceAsMap = hit.getSourceAsMap();
System.out.println("sourcemap"+sourceAsMap);
//高亮的字段替换
if (content!=null){
Text[] fragments = content.fragments();
sourceAsMap.put("tags",Arrays.asList(fragments));
}
sb.append(sourceAsMap);
}
System.out.println("sb"+ sb.toString());
return sb.toString();
}
这里的高亮显示,但是页面上不显示出来,这里换成了页面可以显示出来的格式
查询的结果如图所示
总结
例如:以上就是今天要讲的内容,本文仅仅简单介绍了es的基本使用,更多的查询例如termsquery精确查找,和fuzzyquery近似查找,例如国中,可以帮你搜索出中国,还有范围查询等等高级使用,可以查询es官方文档,这里仅作为一个入门介绍,如果该文章对你有用,可以点赞一下呀,比较写文章不容易,还是要花些时间的。