赞
踩
1、安装elasticSearch和ik分词器
2、新增实体类
package io.renren.modules.elasticsearch.entity; import com.fasterxml.jackson.annotation.JsonFormat; import lombok.Data; import org.springframework.data.annotation.Id; import org.springframework.data.elasticsearch.annotations.DateFormat; import org.springframework.data.elasticsearch.annotations.Document; import org.springframework.data.elasticsearch.annotations.Field; import org.springframework.data.elasticsearch.annotations.FieldType; import java.util.Date; /** * @Document * * 作用在类,标记实体类为文档对象,一般有两个属性 * * indexName:对应索引库名称 * type:对应在索引库中的类型 * shards:分片数量,默认5 * replicas:副本数量,默认1 * @Id 作用在成员变量,标记一个字段作为id主键 * * @Field * 作用在成员变量,标记为文档的字段,并指定字段映射属性: * * type:字段类型,是是枚举:FieldType * index:是否索引,布尔类型,默认是true * store:是否存储,布尔类型,默认是false * analyzer:分词器名称 */ @Data @Document(indexName = "blog",type = "doc", useServerConfiguration = true,createIndex =false) public class EsBlog { @Id private String id; @Field(type = FieldType.Text,analyzer = "ik_max_word") private String title; @Field(type = FieldType.Text,analyzer = "ik_max_word") private String author; @Field(type = FieldType.Text,analyzer = "ik_max_word") private String content; @Field(type = FieldType.Date,format = DateFormat.custom, pattern = "yyyy-MM-dd HH:mm:ss||yyyy-MM-dd||epoch_millis") @JsonFormat(pattern = "yyyy-MM-dd") private Date createDate; @Field(type = FieldType.Date,format = DateFormat.custom, pattern = "yyyy-MM-dd HH:mm:ss||yyyy-MM-dd||epoch_millis") @JsonFormat(pattern = "yyyy-MM-dd") private Date updateDate; }
创建elasticSearch的mapper类
package io.renren.common.utils; import com.fasterxml.jackson.core.JsonEncoding; import com.fasterxml.jackson.core.JsonFactory; import com.fasterxml.jackson.core.JsonGenerator; import org.apache.commons.beanutils.PropertyUtils; import org.elasticsearch.action.get.GetResponse; import org.elasticsearch.action.get.MultiGetItemResponse; import org.elasticsearch.action.get.MultiGetResponse; import org.elasticsearch.action.search.SearchResponse; import org.elasticsearch.common.document.DocumentField; import org.elasticsearch.common.text.Text; import org.elasticsearch.search.SearchHit; import org.elasticsearch.search.fetch.subphase.highlight.HighlightField; import org.springframework.data.domain.Pageable; import org.springframework.data.elasticsearch.ElasticsearchException; import org.springframework.data.elasticsearch.annotations.Document; import org.springframework.data.elasticsearch.annotations.ScriptedField; import org.springframework.data.elasticsearch.core.AbstractResultMapper; import org.springframework.data.elasticsearch.core.DefaultEntityMapper; import org.springframework.data.elasticsearch.core.EntityMapper; import org.springframework.data.elasticsearch.core.aggregation.AggregatedPage; import org.springframework.data.elasticsearch.core.aggregation.impl.AggregatedPageImpl; import org.springframework.data.elasticsearch.core.mapping.ElasticsearchPersistentEntity; import org.springframework.data.elasticsearch.core.mapping.ElasticsearchPersistentProperty; import org.springframework.data.elasticsearch.core.mapping.SimpleElasticsearchMappingContext; import org.springframework.data.mapping.context.MappingContext; import org.springframework.stereotype.Component; import org.springframework.util.Assert; import org.springframework.util.StringUtils; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.lang.reflect.InvocationTargetException; import java.nio.charset.Charset; import java.util.*; @Component public class MyElasticSearchMapper extends AbstractResultMapper { private final MappingContext<? extends ElasticsearchPersistentEntity<?>, ElasticsearchPersistentProperty> mappingContext; public MyElasticSearchMapper() { this(new SimpleElasticsearchMappingContext()); } public MyElasticSearchMapper(MappingContext<? extends ElasticsearchPersistentEntity<?>, ElasticsearchPersistentProperty> mappingContext) { super(new DefaultEntityMapper(mappingContext)); Assert.notNull(mappingContext, "MappingContext must not be null!"); this.mappingContext = mappingContext; } public MyElasticSearchMapper(EntityMapper entityMapper) { this(new SimpleElasticsearchMappingContext(), entityMapper); } public MyElasticSearchMapper( MappingContext<? extends ElasticsearchPersistentEntity<?>, ElasticsearchPersistentProperty> mappingContext, EntityMapper entityMapper) { super(entityMapper); Assert.notNull(mappingContext, "MappingContext must not be null!"); this.mappingContext = mappingContext; } @Override public <T> AggregatedPage<T> mapResults(SearchResponse response, Class<T> clazz, Pageable pageable) { long totalHits = response.getHits().getTotalHits(); float maxScore = response.getHits().getMaxScore(); List<T> results = new ArrayList<>(); for (SearchHit hit : response.getHits()) { if (hit != null) { T result = null; if (!StringUtils.isEmpty(hit.getSourceAsString())) { result = mapEntity(hit.getSourceAsString(), clazz); } else { result = mapEntity(hit.getFields().values(), clazz); } setPersistentEntityId(result, hit.getId(), clazz); setPersistentEntityVersion(result, hit.getVersion(), clazz); setPersistentEntityScore(result, hit.getScore(), clazz); populateScriptFields(result, hit); results.add(result); } } return new AggregatedPageImpl<T>(results, pageable, totalHits, response.getAggregations(), response.getScrollId(), maxScore); } private String concat(Text[] texts) { StringBuilder sb = new StringBuilder(); for (Text text : texts) { sb.append(text.toString()); } return sb.toString(); } private <T> void populateScriptFields(T result, SearchHit hit) { if (hit.getFields() != null && !hit.getFields().isEmpty() && result != null) { for (java.lang.reflect.Field field : result.getClass().getDeclaredFields()) { ScriptedField scriptedField = field.getAnnotation(ScriptedField.class); if (scriptedField != null) { String name = scriptedField.name().isEmpty() ? field.getName() : scriptedField.name(); DocumentField searchHitField = hit.getFields().get(name); if (searchHitField != null) { field.setAccessible(true); try { field.set(result, searchHitField.getValue()); } catch (IllegalArgumentException e) { throw new ElasticsearchException( "failed to set scripted field: " + name + " with value: " + searchHitField.getValue(), e); } catch (IllegalAccessException e) { throw new ElasticsearchException("failed to access scripted field: " + name, e); } } } } } for (HighlightField field : hit.getHighlightFields().values()) { try { PropertyUtils.setProperty(result, field.getName(), concat(field.fragments())); } catch (InvocationTargetException | IllegalAccessException | NoSuchMethodException e) { throw new ElasticsearchException("failed to set highlighted value for field: " + field.getName() + " with value: " + Arrays.toString(field.getFragments()), e); } } } private <T> T mapEntity(Collection<DocumentField> values, Class<T> clazz) { return mapEntity(buildJSONFromFields(values), clazz); } private String buildJSONFromFields(Collection<DocumentField> values) { JsonFactory nodeFactory = new JsonFactory(); try { ByteArrayOutputStream stream = new ByteArrayOutputStream(); JsonGenerator generator = nodeFactory.createGenerator(stream, JsonEncoding.UTF8); generator.writeStartObject(); for (DocumentField value : values) { if (value.getValues().size() > 1) { generator.writeArrayFieldStart(value.getName()); for (Object val : value.getValues()) { generator.writeObject(val); } generator.writeEndArray(); } else { generator.writeObjectField(value.getName(), value.getValue()); } } generator.writeEndObject(); generator.flush(); return new String(stream.toByteArray(), Charset.forName("UTF-8")); } catch (IOException e) { return null; } } @Override public <T> T mapResult(GetResponse response, Class<T> clazz) { T result = mapEntity(response.getSourceAsString(), clazz); if (result != null) { setPersistentEntityId(result, response.getId(), clazz); setPersistentEntityVersion(result, response.getVersion(), clazz); } return result; } @Override public <T> LinkedList<T> mapResults(MultiGetResponse responses, Class<T> clazz) { LinkedList<T> list = new LinkedList<>(); for (MultiGetItemResponse response : responses.getResponses()) { if (!response.isFailed() && response.getResponse().isExists()) { T result = mapEntity(response.getResponse().getSourceAsString(), clazz); setPersistentEntityId(result, response.getResponse().getId(), clazz); setPersistentEntityVersion(result, response.getResponse().getVersion(), clazz); list.add(result); } } return list; } private <T> void setPersistentEntityId(T result, String id, Class<T> clazz) { if (clazz.isAnnotationPresent(Document.class)) { ElasticsearchPersistentEntity<?> persistentEntity = mappingContext.getRequiredPersistentEntity(clazz); ElasticsearchPersistentProperty idProperty = persistentEntity.getIdProperty(); // Only deal with String because ES generated Ids are strings ! if (idProperty != null && idProperty.getType().isAssignableFrom(String.class)) { persistentEntity.getPropertyAccessor(result).setProperty(idProperty, id); } } } private <T> void setPersistentEntityVersion(T result, long version, Class<T> clazz) { if (clazz.isAnnotationPresent(Document.class)) { ElasticsearchPersistentEntity<?> persistentEntity = mappingContext.getPersistentEntity(clazz); ElasticsearchPersistentProperty versionProperty = persistentEntity.getVersionProperty(); // Only deal with Long because ES versions are longs ! if (versionProperty != null && versionProperty.getType().isAssignableFrom(Long.class)) { // check that a version was actually returned in the response, -1 would indicate that // a search didn't request the version ids in the response, which would be an issue Assert.isTrue(version != -1, "Version in response is -1"); persistentEntity.getPropertyAccessor(result).setProperty(versionProperty, version); } } } private <T> void setPersistentEntityScore(T result, float score, Class<T> clazz) { if (clazz.isAnnotationPresent(Document.class)) { ElasticsearchPersistentEntity<?> entity = mappingContext.getRequiredPersistentEntity(clazz); if (!entity.hasScoreProperty()) { return; } entity.getPropertyAccessor(result) // .setProperty(entity.getScoreProperty(), score); } } }
创建控制层
package io.renren.modules.elasticsearch.controller; import io.renren.common.constant.Constant; import io.renren.common.page.PageData; import io.renren.common.utils.Result; import io.renren.modules.elasticsearch.entity.EsBlog; import io.renren.modules.elasticsearch.serivce.ElasticSearchService; import io.swagger.annotations.Api; import io.swagger.annotations.ApiImplicitParam; import io.swagger.annotations.ApiImplicitParams; import io.swagger.annotations.ApiOperation; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.*; import springfox.documentation.annotations.ApiIgnore; import java.util.Map; @RestController @RequestMapping("elasticSearch") @Api(tags="elasticSearch测试") public class ElasticSearchController { @Autowired private ElasticSearchService elasticSearchService; /** * 单个字段匹配查询与高亮 * @param params * @return */ @GetMapping("pageForOnlyKey") @ApiOperation("分页(单个字段匹配查询与高亮)") @ApiImplicitParams({ @ApiImplicitParam(name = Constant.PAGE, value = "当前页码,从1开始", paramType = "query", required = true, dataType="int") , @ApiImplicitParam(name = Constant.LIMIT, value = "每页显示记录数", paramType = "query",required = true, dataType="int") , @ApiImplicitParam(name = "content", value = "content", paramType = "query", dataType="String") }) public Result<PageData<EsBlog>> pageForOnlyKey(@ApiIgnore @RequestParam Map<String, Object> params){ PageData<EsBlog> page = elasticSearchService.pageForOnlyKey(params); return new Result<PageData<EsBlog>>().ok(page); } @GetMapping("page") @ApiOperation("分页(多字段匹配查询与高亮)") @ApiImplicitParams({ @ApiImplicitParam(name = Constant.PAGE, value = "当前页码,从1开始", paramType = "query", required = true, dataType="int") , @ApiImplicitParam(name = Constant.LIMIT, value = "每页显示记录数", paramType = "query",required = true, dataType="int") , @ApiImplicitParam(name = "keyWord", value = "关键字", paramType = "query", dataType="String") }) public Result<PageData<EsBlog>> page(@ApiIgnore @RequestParam Map<String, Object> params){ PageData<EsBlog> page = elasticSearchService.page(params); return new Result<PageData<EsBlog>>().ok(page); } @GetMapping("searchForLike") @ApiOperation("模糊查询") public Result searchForLike(String name){ return elasticSearchService.searchForLike(name); } @PostMapping @ApiOperation("保存") public Result save(@RequestBody EsBlog esBlog){ return elasticSearchService.save(esBlog); } @PutMapping @ApiOperation("修改") public Result update(@RequestBody EsBlog esBlog){ return elasticSearchService.update(esBlog); } @DeleteMapping @ApiOperation("删除") public Result delete(String id){ return elasticSearchService.delete(id); } }
添加服务层接口
package io.renren.modules.elasticsearch.serivce; import com.alibaba.fastjson.JSONObject; import io.renren.common.page.PageData; import io.renren.common.utils.MyElasticSearchMapper; import io.renren.common.utils.Result; import io.renren.modules.elasticsearch.dao.ElasticeSearchDao; import io.renren.modules.elasticsearch.entity.EsBlog; import org.apache.commons.lang3.StringUtils; import org.elasticsearch.action.search.SearchResponse; import org.elasticsearch.index.query.BoolQueryBuilder; import org.elasticsearch.index.query.QueryBuilders; import org.elasticsearch.search.SearchHit; import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder; import org.elasticsearch.search.fetch.subphase.highlight.HighlightField; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.domain.Page; import org.springframework.data.domain.PageRequest; import org.springframework.data.domain.Pageable; import org.springframework.data.elasticsearch.core.ElasticsearchTemplate; import org.springframework.data.elasticsearch.core.SearchResultMapper; import org.springframework.data.elasticsearch.core.aggregation.AggregatedPage; import org.springframework.data.elasticsearch.core.aggregation.impl.AggregatedPageImpl; import org.springframework.data.elasticsearch.core.query.*; import org.springframework.stereotype.Service; import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.UUID; @Service public class ElasticSearchService { @Autowired private ElasticeSearchDao elasticeSearchDao; @Autowired private ElasticsearchTemplate elasticsearchTemplate; @Autowired private MyElasticSearchMapper myElasticSearchMapper; public Result save(EsBlog esBlog) { esBlog.setId(UUID.randomUUID().toString().replaceAll("-","")); //保存至mysql数据库 elasticeSearchDao.save(esBlog); //保存到es IndexQuery indexQuery = new IndexQueryBuilder() .withId(esBlog.getId()) .withObject(esBlog) .build(); elasticsearchTemplate.index(indexQuery); return new Result(); } public Result update(EsBlog esBlog) { //更新mysql数据库 elasticeSearchDao.update(esBlog); //更新es IndexQuery indexQuery = new IndexQueryBuilder() .withId(esBlog.getId()) .withObject(esBlog) .build(); elasticsearchTemplate.index(indexQuery); return new Result(); } public Result delete(String id) { DeleteQuery deleteQuery = new DeleteQuery(); deleteQuery.setIndex("blog"); deleteQuery.setType("doc"); deleteQuery.setQuery(new BoolQueryBuilder().must(QueryBuilders.matchQuery("id",id))); elasticsearchTemplate.delete(deleteQuery); elasticeSearchDao.delete(id); return new Result(); } public Result searchForLike(String name) { SearchQuery searchQuery; if (StringUtils.isNotBlank(name)) { searchQuery = new NativeSearchQueryBuilder() .withQuery(QueryBuilders.boolQuery().should(QueryBuilders.matchQuery("title",name)) .should(QueryBuilders.matchQuery("author",name))) .build(); }else { searchQuery = new NativeSearchQueryBuilder() .withQuery(QueryBuilders.matchAllQuery()) .build(); } List<EsBlog> esBlog = elasticsearchTemplate.queryForList(searchQuery,EsBlog.class); return new Result().ok(esBlog); } public PageData<EsBlog> pageForOnlyKey(Map<String, Object> params) { Pageable pageable = PageRequest.of(Integer.valueOf(params.get("page").toString())-1,Integer.valueOf(params.get("limit").toString())); SearchQuery searchQuery = new NativeSearchQueryBuilder() .withQuery(QueryBuilders.matchQuery("content",params.get("content").toString())) .withHighlightFields(new HighlightBuilder.Field("content").preTags("<span style=\"color:red\">").postTags("</span>")) .build(); searchQuery.setPageable(pageable); // 不需要高亮直接return ideas // AggregatedPage<EsBlog> esBlogss = elasticsearchTemplate.queryForPage(searchQuery, EsBlog.class); // return new PageData<>(esBlogs.getContent(),esBlogs.getTotalElements()); //高亮字段 AggregatedPage<EsBlog> esBlogs = elasticsearchTemplate.queryForPage(searchQuery, EsBlog.class, new SearchResultMapper() { @Override public <T> AggregatedPage<T> mapResults(SearchResponse searchResponse, Class<T> aClass, Pageable pageable) { List<EsBlog> chunk = new ArrayList<>(); for (SearchHit searchHit : searchResponse.getHits()) { if (searchResponse.getHits().getHits().length <= 0) { return null; } EsBlog esBlog = JSONObject.parseObject(searchHit.getSourceAsString(), EsBlog.class); Map<String, HighlightField> highlightFields = searchHit.getHighlightFields(); HighlightField content = highlightFields.get("content"); if (content != null) { esBlog.setContent(content.fragments()[0].toString()); } chunk.add(esBlog); } if (chunk.size() > 0) { return new AggregatedPageImpl<>((List<T>) chunk); } return null; } }); if(null != esBlogs){ return new PageData<>(esBlogs.getContent(), esBlogs.getTotalElements()); }else { return new PageData<>(new ArrayList<>(),0); } } public PageData<EsBlog> page(Map<String, Object> params) { Pageable pageable = PageRequest.of(Integer.valueOf(params.get("page").toString())-1,Integer.valueOf(params.get("limit").toString())); //设置需要高亮的字段 title和author 对应的是es中字段名称(key键) //preTags postTags 这两个是设置的标签内容,实现开发中可按照实际场景设置不同的标签 //requireFieldMatch 多字段高亮需设置成false HighlightBuilder.Field allHighLight = new HighlightBuilder.Field("title"). preTags("<span style='color:red'>").postTags("</span>").requireFieldMatch(false); HighlightBuilder.Field allHighLight1 = new HighlightBuilder.Field("author").requireFieldMatch(false). preTags("<span style='color:red'>").postTags("</span>"); HighlightBuilder.Field[] ary = new HighlightBuilder.Field[2]; ary[0] = allHighLight; ary[1] = allHighLight1; SearchQuery query = new NativeSearchQueryBuilder() .withQuery(QueryBuilders.boolQuery().should(QueryBuilders.matchQuery("title",params.get("keyWord").toString())) .should(QueryBuilders.matchQuery("author",params.get("keyWord").toString()))) .withHighlightFields(ary) .withPageable(pageable) .build(); //搜索 Page<EsBlog> search = elasticsearchTemplate.queryForPage(query,EsBlog.class, myElasticSearchMapper); if(null != search){ return new PageData<>(search.getContent(),search.getTotalPages()); }else { return new PageData<>(new ArrayList<>(),0); } } }
添加Dao层接口
package io.renren.modules.elasticsearch.dao; import io.renren.modules.elasticsearch.entity.EsBlog; import org.apache.ibatis.annotations.Mapper; import org.apache.ibatis.annotations.Param; @Mapper public interface ElasticeSearchDao{ EsBlog getInfo(String id); void save(EsBlog esBlog); void update(EsBlog esBlog); void delete(@Param("id") String id); }
接口测试
1、测试新增
mysql数据库中添加成功
es中添加成功
2、多添加几条数据,测试搜索
查询title和author中包含“啊”关键字的记录
查询title和author中包含“为什么”关键字的记录
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。