springboot集成Elasticsearch各类搜索功能实现
环境准备:
Elasticsearch服务器
ik中文分词器
Kibana服务
一个集成了ES的springboot项目
博主环境:
es:6.2.2 Kibana:6.2.2 springboot:2.1.2
springboot集成Elasticsearch使用completion suggest实现自动关键字补全
定义成completion的字段无法应用highlight返回。
实现目标:建立学生数据索引,以学生姓名为搜索自动补全字段。
学生实体类:
@Document(indexName = "student_index",type="student")
public class Student implements Serializable {
@Id
@Field(type= FieldType.Auto)
private String studentId;
@Override
public String toString() {
return "Student{" +
"studentId='" + studentId + '\'' +
", name='" + name + '\'' +
", age=" + age +
", scores=" + scores +
'}';
}
public Student(String studentId, String name, Integer age, List<Double> scores) {
this.studentId = studentId;
this.name = name;
this.age = age;
this.scores = scores;
}
public Student() {
}
public String getStudentId() {
return studentId;
}
public void setStudentId(String studentId) {
this.studentId = studentId;
}
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 List<Double> getScores() {
return scores;
}
public void setScores(List<Double> scores) {
this.scores = scores;
}
@Field(type=FieldType.Auto)
private String name;
@Field(type=FieldType.Auto)
private Integer age;
@Field(type=FieldType.Auto)
private List<Double> scores;
}
建立学生的索引和映射:
待搜索的字段需要设置为Completion类型
PUT /student_index
{
"mappings": {
"student" : {
"properties" : {
"name" : {
"type" : "completion",
"analyzer": "ik_smart"
},
"age" : {
"type" : "integer"
},
"studentId" : {
"type" : "text"
},
"scores":{
"type" : "float"
}
}
}
}
}
整点数据测试一下:
学生索引Repository类:
@Repository
public interface StudentRepository extends ElasticsearchCrudRepository<Student,String>{
}
开始测试:
在程序里插入一些数据;
@Autowired
private ElasticsearchTemplate elasticsearchTemplate;
@Test
public void contextLoads() {
List scores= new ArrayList<>();
scores.add(67.2);
scores.add(27.2);
scores.add(56.2);
/* studentIndexRepository.save(new Student(UUID.randomUUID().toString(), "张三", 21, scores ));
studentIndexRepository.save(new Student(UUID.randomUUID().toString(), "李四", 35, scores ));
studentIndexRepository.save(new Student(UUID.randomUUID().toString(), "王二", 45, scores ));
studentIndexRepository.save(new Student(UUID.randomUUID().toString(), "张大林", 23, scores ));
studentIndexRepository.save(new Student(UUID.randomUUID().toString(), "王大力", 51, scores ));*/
studentIndexRepository.save(new Student(UUID.randomUUID().toString(), "刘伯", 21, scores ));
studentIndexRepository.save(new Student(UUID.randomUUID().toString(), "刘思想", 35, scores ));
studentIndexRepository.save(new Student(UUID.randomUUID().toString(), "王皮皮", 45, scores ));
studentIndexRepository.save(new Student(UUID.randomUUID().toString(), "王二丫", 23, scores ));
studentIndexRepository.save(new Student(UUID.randomUUID().toString(), "王铁蛋", 51, scores ));
}
在Kibana测试
POST /student_index/completion/_search
{ "size": 0,
"suggest": {
"name-suggest": {
"prefix": "王二",
"completion": {
"field": "name"
}
}
}
}
结果正确:
接下来是实现获取suggest的代码:
测试方法
@Test
public void testSuggestCompletionProc() {
String suggestField="name";//指定在哪个字段搜索
String suggestValue="王二";//输入的信息
Integer suggestMaxCount=10;//获得最大suggest条数
CompletionSuggestionBuilder suggestionBuilderDistrict = new CompletionSuggestionBuilder(suggestField).prefix(suggestValue).size(suggestMaxCount);
SuggestBuilder suggestBuilder = new SuggestBuilder();
suggestBuilder.addSuggestion("student_suggest", suggestionBuilderDistrict);//添加suggest
//设置查询builder的index,type,以及建议
SearchRequestBuilder requestBuilder = this.elasticsearchTemplate.getClient().prepareSearch("student_index").setTypes("student").suggest(suggestBuilder);
System.out.println(requestBuilder.toString());
SearchResponse response = requestBuilder.get();
Suggest suggest = response.getSuggest();//suggest实体
Set<String> suggestSet = new HashSet<>();//set
int maxSuggest = 0;
if (suggest != null) {
Suggest.Suggestion result = suggest.getSuggestion("student_suggest");//获取suggest,name任意string
for (Object term : result.getEntries()) {
if (term instanceof CompletionSuggestion.Entry) {
CompletionSuggestion.Entry item = (CompletionSuggestion.Entry) term;
if (!item.getOptions().isEmpty()) {
//若item的option不为空,循环遍历
for (CompletionSuggestion.Entry.Option option : item.getOptions()) {
String tip = option.getText().toString();
if (!suggestSet.contains(tip)) {
suggestSet.add(tip);
++maxSuggest;
}
}
}
}
if (maxSuggest >= suggestMaxCount) {
break;
}
}
}
List<String> suggests = Arrays.asList(suggestSet.toArray(new String[]{}));
suggests.forEach((s)->{
System.out.println(s);
});
// return suggests;
}
输出结果正确:
封装的工具方法
/**
* completion suggest
* @param suggestField
* @param suggestValue
* @param suggestMaxCount
* @param index_
* @param indexType_
* @param elasticsearchTemplate__
* @return
*/
public List<String> listSuggestCompletion(String suggestField, String suggestValue, Integer suggestMaxCount,String index_,String indexType_,ElasticsearchTemplate elasticsearchTemplate__) {
/* String suggestField="name";//指定在哪个字段搜索
String suggestValue="王二";//输入的信息
Integer suggestMaxCount=10;//获得最大suggest条数*/
CompletionSuggestionBuilder suggestionBuilderDistrict = new CompletionSuggestionBuilder(suggestField).prefix(suggestValue).size(suggestMaxCount);
SuggestBuilder suggestBuilder = new SuggestBuilder();
suggestBuilder.addSuggestion("student_suggest", suggestionBuilderDistrict);//添加suggest
//设置查询builder的index,type,以及建议
if(elasticsearchTemplate__==null)
System.out.println( "this is Template null ***************************************************");
SearchRequestBuilder requestBuilder = elasticsearchTemplate__.getClient().prepareSearch(index_).setTypes(indexType_).suggest(suggestBuilder);
System.out.println(requestBuilder.toString());
SearchResponse response = requestBuilder.get();
Suggest suggest = response.getSuggest();//suggest实体
Set<String> suggestSet = new HashSet<>();//set
int maxSuggest = 0;
if (suggest != null) {
Suggest.Suggestion result = suggest.getSuggestion("student_suggest");//获取suggest,name任意string
for (Object term : result.getEntries()) {
if (term instanceof CompletionSuggestion.Entry) {
CompletionSuggestion.Entry item = (CompletionSuggestion.Entry) term;
if (!item.getOptions().isEmpty()) {
//若item的option不为空,循环遍历
for (CompletionSuggestion.Entry.Option option : item.getOptions()) {
String tip = option.getText().toString();
if (!suggestSet.contains(tip)) {
suggestSet.add(tip);
++maxSuggest;
}
}
}
}
if (maxSuggest >= suggestMaxCount) {
break;
}
}
}
List<String> suggests = Arrays.asList(suggestSet.toArray(new String[]{}));
suggests.forEach((s)->{
System.out.println(s);
});
return suggests;
}
前端代码
前端自动补全用JqueryUI的autocomplete组件。
<script>
var availableTags = new Array();
$(function() {
$("#tags").autocomplete({
source: availableTags
});
});
$(document).ready(function() {
$("#tags").bind("input propertychange", function () {
$("#valuetext").html($(this).val());
$.get("searchSuggest?value="+$(this).val(), function (data, status) {
var results=data;
for(var i=0;i<results.length;i++){
availableTags[i]=results[i].texts;
}
$("#tags").autocomplete({
source: availableTags
});
});
});
})
</script>
<!-- 标签 -->
<input type="search" id="tags" name="searchvalue" class="form-control" style="border-radius: 50px 0 0 50px;" placeholder="请搜索..">
实现Fuzzy模糊查询
Fuzzy有纠错功能
应用Fuzzy查询的字段需要是 text 类型的。
es中的Fuzzy查询语句示例
GET /indextest/testmapping/_search/
{
"query": {
"fuzzy": {
"namePinyin": {
"value": "zhonghuarenmingonghegu"
}
}
} ,
"highlight" :{
"fields": {
"namePinyin" :{}
}
}
}
Java实现Fuzzy和高亮返回
/**
* 拼音Fuzzy查询
* @param field
* @param value
* @param highlightField
* @param preTag
* @param postTag
* @param index
* @param indexType
* @param elasticsearchTemplate
* @return
*/
public List<String> listSearchByPinyin(String field,String value,String highlightField ,String preTag,String postTag,String index, String indexType , ElasticsearchTemplate elasticsearchTemplate){
List<String> result = new LinkedList<>();
FuzzyQueryBuilder queryBuilder = QueryBuilders.fuzzyQuery(field, value)
.fuzziness(Fuzziness.TWO)
.prefixLength(0)
.maxExpansions(10);
HighlightBuilder highlightBuilder = new HighlightBuilder();
highlightBuilder.field(highlightField);
highlightBuilder.preTags(preTag);
highlightBuilder.postTags(postTag);
SearchRequestBuilder requestBuilder = elasticsearchTemplate.getClient().prepareSearch(index)
.setTypes(indexType)
.setQuery(queryBuilder)
.highlighter(highlightBuilder);
SearchResponse response = requestBuilder.get();
System.out.println(response.getClusters().toString());
System.out.println(requestBuilder.toString());
SearchHits searchHits = response.getHits();
searchHits.forEach(i->{
System.out.println(i.getIndex());
System.out.println(i.getId());
System.out.println(i.getSourceAsString());
Text highlightFragment = i.getHighlightFields().get(highlightField).getFragments()[0];
result.add(highlightFragment.toString());
System.out.println();
});
return result;
}
数组元素查询
这个可以用作标签查询、定位。
Elasticsearch许可证过期
elasticsearch报日志:
org.elasticsearch.ElasticsearchSecurityException: current license is
non-compliant for [security]
替换许可证
到elastic**官网**https://register.elastic.co/
注册一下账号,注册邮箱会收到许可证文件
将许可证放到服务器下,用的docker要进入到es容器内,重命名许可证文件名为:license.json
# curl -XPUT -u elastic:yourregistername 'http://133.3.269.211:9200/_xpack/license?acknowledge=true' -H "Content-Type: application/json" -d @license.json
这样就ok了。