springboot集成Elasticsearch各类搜索功能实现

  • springboot集成Elasticsearch使用completion suggest实现自动关键字补全
  • 建立学生的索引和映射:
  • 开始测试:
  • 接下来是实现获取suggest的代码:
  • 前端代码
  • 实现Fuzzy模糊查询
  • es中的Fuzzy查询语句示例
  • Java实现Fuzzy和高亮返回
  • 数组元素查询
  • 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"
      }
    }
  }
}

结果正确:

springboot mongo全文索引检索 springboot 搜索功能_Elasticsearch

接下来是实现获取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;

	}

输出结果正确:

springboot mongo全文索引检索 springboot 搜索功能_completion_02


封装的工具方法

/**
     * 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="请搜索..">

springboot mongo全文索引检索 springboot 搜索功能_Elasticsearch_03


实现Fuzzy模糊查询

Fuzzy有纠错功能
应用Fuzzy查询的字段需要是 text 类型的。

es中的Fuzzy查询语句示例

GET /indextest/testmapping/_search/
{
  "query": {
    "fuzzy": {
      "namePinyin": {
        "value": "zhonghuarenmingonghegu"
      } 
    } 
  } ,
  "highlight" :{
    "fields": {
      "namePinyin" :{}
    }
  }
}

springboot mongo全文索引检索 springboot 搜索功能_关键字自动补全_04

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;
    }

数组元素查询

这个可以用作标签查询、定位。

springboot mongo全文索引检索 springboot 搜索功能_suggest_05

Elasticsearch许可证过期

elasticsearch报日志:

org.elasticsearch.ElasticsearchSecurityException: current license is
non-compliant for [security]

替换许可证

到elastic**官网**https://register.elastic.co/注册一下账号,注册邮箱会收到许可证文件

springboot mongo全文索引检索 springboot 搜索功能_关键字自动补全_06


将许可证放到服务器下,用的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

springboot mongo全文索引检索 springboot 搜索功能_completion_07


这样就ok了。