SpringData+ElasticSearch实现简单的站内搜索 (模糊查询、高亮显示)

SpringData+ElasticSearch实现简单的站内搜索 (模糊查询、高亮显示)1 环境搭建 1 1 引入依赖核心依赖如下 其他依赖自行引入 注意 springboot 版本 parentgroupI springframew boot gro

大家好,欢迎来到IT知识分享网。

1.  环境搭建

1.1  引入依赖

核心依赖如下(其他依赖自行引入)

<!--注意:springboot版本--> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.0.6.RELEASE</version> </parent> <!--springboot web --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <!--通过spring data 操作Es--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-elasticsearch</artifactId> </dependency> <!--springboot 继承test--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> <!--引入lombook--> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version>1.16.20</version> <scope>provided</scope> </dependency> 

注意:低版本的springboot 不支持springboot data

1.2  编写yml配置信息

 spring: data: elasticsearch: cluster-nodes: 172.16.251.142:9300 指定连接ip地址和端口号 

 

2.  代码实现

2.1  创建实体类Product

@Document(indexName = "shopping",type = "product") @Data @AllArgsConstructor @NoArgsConstructor public class Product { @Id private String id; @Field(type = FieldType.Text,analyzer = "ik_max_word", searchAnalyzer = "ik_max_word") private String name; @Field(type = FieldType.Double) private Double marketPrice; @Field(type = FieldType.Double) private Double shopPrice; @Field(type = FieldType.Text) private String image; @Field(type = FieldType.Text,analyzer = "ik_max_word", searchAnalyzer = "ik_max_word") private String description; @Field(type = FieldType.Integer) private Integer isHot; @Field(type = FieldType.Integer) private Integer cId; } @Document: 代表一个文档记录 ​ indexName: 用来指定索引名称 ​ type: 用来指定索引类型 @Id: 用来将对象中id和ES中_id映射 @Field: 用来指定ES中的字段对应Mapping ​ type: 用来指定ES中存储类型 ​ analyzer: 用来指定使用哪种分词器

2.2  编写简单Repository接口类  接口中可以什么都不写使用一些基础的方法,或者根据命名规则自定义方法并实现,或者如2.3下的自定义复杂接口

/ * 根据需要自定义接口方法 * 定义方法方法时需注意命名规范 */ public interface ProductRepository extends ElasticsearchRepository<Product,String> { }

2.3  自定义复杂方法接口CustomProductRepository及实现CustomProductRepositoryImpl

//自定义复杂方法不需要继承ElasticsearchRepository public interface CustomProductRepository { //价格查询并分页 List<Product> findByPageable(Double marketprice, Integer page,Integer size); //价格查询结果的总数 Integer findByPrice(Double marketprice); //term查询高亮(name) List<Product> findByNameAndHighlightAdnPageable(String name,int page,int size); //term 查询结果的数量 Integer findCountByName(String name); //分页查询所有 List<Product> findAll(Integer page, Integer size); } 
@Component public class CustomProductRepositoryImpl implements CustomProductRepository { //实现类需要注入elasticsearchTemplates @Autowired private ElasticsearchTemplate elasticsearchTemplate; @Override //分页查所有 es默认从0页开始,JqGrid默认传的page是1,所以-1 public List<Product> findAll(Integer page, Integer size) { NativeSearchQuery searchQuery = new NativeSearchQueryBuilder() .withQuery(QueryBuilders.matchAllQuery()) .withPageable(PageRequest.of(page-1, size)) .build(); //默认只查出来10条,不知道怎么改 List<Product> products = elasticsearchTemplate.queryForList(searchQuery, Product.class); return products; } @Override //价格分页查询 public List<Product> findByPageable(Double marketprice, Integer page, Integer size) { System.out.println(marketprice); NativeSearchQuery queryBuilder = new NativeSearchQueryBuilder() .withQuery(QueryBuilders.termQuery("marketPrice", marketprice)) .withPageable(PageRequest.of(page - 1, size)).build(); List<Product> products = elasticsearchTemplate.queryForList(queryBuilder, Product.class); return products; } @Override //价格查询结果的总数 public Integer findByPrice(Double marketprice) { NativeSearchQuery queryBuilder = new NativeSearchQueryBuilder() .withQuery(QueryBuilders.termQuery("marketPrice", marketprice)) .build(); List<Product> products = elasticsearchTemplate.queryForList(queryBuilder, Product.class); return products.size(); } @Override //term查询高亮(多字段查询) public List<Product> findByNameAndHighlightAdnPageable(String name, int page,int size) { HighlightBuilder.Field nameField = new HighlightBuilder .Field("*") .preTags("<span style='color:red'>") .postTags("</span>").requireFieldMatch(false); //多字段查询,可同时在name和description查询 对应实体类中的属性名 NativeSearchQuery nativeSearchQuery = new NativeSearchQueryBuilder() .withQuery(QueryBuilders.multiMatchQuery(name, "name", "description")) .withPageable(PageRequest.of(page - 1, size)) .withHighlightFields(nameField) .build(); AggregatedPage<Product> products = elasticsearchTemplate. queryForPage(nativeSearchQuery, Product.class, new SearchResultMapper() { @Override public <T> AggregatedPage<T> mapResults(SearchResponse response, Class<T> clazz, Pageable pageable) { SearchHits searchHits = response.getHits(); SearchHit[] hits = searchHits.getHits(); ArrayList<Product> products = new ArrayList<Product>(); for (SearchHit hit : hits) { Product product = new Product(); //原始map Map<String, Object> sourceAsMap = hit.getSourceAsMap(); product.setId(sourceAsMap.get("id").toString()); product.setName(sourceAsMap.get("name").toString()); product.setMarketPrice(Double.parseDouble(sourceAsMap.get("marketPrice").toString())); product.setShopPrice(Double.parseDouble(sourceAsMap.get("shopPrice").toString())); product.setImage(sourceAsMap.get("image").toString()); product.setDescription(sourceAsMap.get("description").toString()); //高亮 Map<String, HighlightField> highlightFields = hit.getHighlightFields(); System.out.println(highlightFields); if (highlightFields.get("name") != null) { String nameHighlight = highlightFields.get("name").getFragments()[0].toString(); product.setName(nameHighlight); } if (highlightFields.get("description") != null) { String contentHighlight = highlightFields.get("description").getFragments()[0].toString(); product.setDescription(contentHighlight); } products.add(product); } return new AggregatedPageImpl<T>((List<T>) products); } }); return products.getContent(); } @Override //term 高亮查询结果的数量 public Integer findCountByName(String name) { NativeSearchQuery nativeSearchQuery = new NativeSearchQueryBuilder() .withQuery(QueryBuilders.multiMatchQuery(name, "name","description")) .build(); return elasticsearchTemplate.queryForList(nativeSearchQuery, Product.class).size(); } } 

2.4  Controller层实现 前提是相关数据已经存入es中,可以在添加内容的时候存进去

@RestController @RequestMapping("product") public class ProductController { @Autowired private ProductService productService; @Autowired private ProductRepository productRepository; @Autowired private CustomProductRepository customProductRepository; //查所有 @RequestMapping("findByPage") public Map<String,Object> findByPage(Integer rows,Integer page){ List<Product> products = productService.findByPage(rows, page); //总条数 List<Product> all = productService.findAll(); int records = all.size(); //总页数 Integer total = null; if(records%rows == 0){ total = records/rows; }else{ total = records/rows+1; } //存入Map集合 Map<String,Object> map = new HashMap<>(); map.put("rows",products); map.put("page",page); map.put("total",total); map.put("records",records); return map; } //使用es搜索 @RequestMapping("findBySearch") public Map<String,Object> findBySearch(Integer rows,Integer page,String opt,String content){ Map<String,Object> map = new HashMap<>(); //按商品描述搜索 if("description".equals(opt) && content != ""){ List<Product> products = customProductRepository.findByNameAndHighlightAdnPageable(content, page, rows); //总数量 Integer records = customProductRepository.findCountByName(content); //总页数 Integer total = null; if(records % rows == 0){ total = records/rows; }else{ total = records/rows+1; } //存入map集合中 map.put("rows",products); map.put("page",page); map.put("records",records); map.put("total",total); //按商品价格搜索 }else if("marketPrice".equals(opt) && content != ""){ double marketPrice = Double.parseDouble(content); List<Product> products = customProductRepository.findByPageable(marketPrice, page, rows); //总数量 Integer records = customProductRepository.findByPrice(marketPrice); //总页数 Integer total = null; if(records % rows == 0){ total = records/rows; }else{ total = records/rows+1; } //存入map集合中 map.put("rows",products); map.put("page",page); map.put("records",records); map.put("total",total); }else{ List<Product> products = customProductRepository.findAll(page, rows); //这样获取总数量 long count = productRepository.count(); int records = Math.toIntExact(count); //Integer records = products.size(); 这样获取的不是总数量, porducts是每次查询出来一页的数据, //总页数 Integer total = null; if(records % rows == 0){ total = records/rows; }else{ total = records/rows+1; } //存入map集合中 map.put("rows",products); map.put("page",page); map.put("records",records); map.put("total",total); } return map; } } 

2.5  部分前台代码

前台这里用的是BootStrap+JqGrid实现的表格

 $(function () { $("#sub").click(function () { //获取查询条件 var opt = $("#opt").val(); var content = $("#content").val(); //清空表中数据 $("#tt").jqGrid("clearGridData"); //重新接收表中数据 $("#tt").jqGrid("setGridParam",{ url:"${pageContext.request.contextPath}/product/findBySearch?opt="+opt+"&content="+content+"", dataType:"json", type:"post" }).trigger("reloadGrid"); }); });

注意: 搜索输入框<input type=”text”>标签,如果什么都不填,content默认的是空字符串而不是null

HTML-input文本框如果不输入任何内容提交过后是一个空字符串还是null ?

 text类型取得空字符串,radio,checkbox未选中提交则是null

 

3.效果展示

SpringData+ElasticSearch实现简单的站内搜索 (模糊查询、高亮显示)

 

注意:springboot的新版本对JSP热部署配置文件的格式有所改变

老版本格式:

server: port: 8081 jsp-servlet: init-parameters: development: true

新版本格式:

server: port: 8081 servlet: jsp: init-parameters: development: true

 

刚开始学,bug比较多。

 

  详细学习文档点击这里 ——->【ES详细文档】

免责声明:本站所有文章内容,图片,视频等均是来源于用户投稿和互联网及文摘转载整编而成,不代表本站观点,不承担相关法律责任。其著作权各归其原作者或其出版社所有。如发现本站有涉嫌抄袭侵权/违法违规的内容,侵犯到您的权益,请在线联系站长,一经查实,本站将立刻删除。 本文来自网络,若有侵权,请联系删除,如若转载,请注明出处:https://haidsoft.com/158605.html

(0)
上一篇 2025-01-19 14:45
下一篇 2025-01-19 15:00

相关推荐

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注微信