SpringBoot集成Elasticsearch实例

SpringBoot项目集成Elasticsearch实例

导包

<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-elasticsearch</artifactId>
</dependency>

配置es连接

spring:data:elasticsearch:cluster-name: elasticsearchcluster-nodes: 127.0.0.1:9300

准备query接收查询条件

@Data
public class CarSearchQuery extends BaseQuery {private Long carType;private Double maxPrice;private Double minPrice;//0 以下 1 以上private Integer carAgeType;private Integer carAge;//是否超值private Integer costEffective;//急售private Integer rushSale;//准新车private Integer quasiNewCar;//可迁全国private Integer transitiveCountry;//排序字段private String sortField;//排序类型 desc降序 asc升序号private String sortType;private Double longitude;private Double latitude;private Double distance;private Long shopId;
}

准备Controller

@RestController
@RequestMapping("/car/search")
public class CarSearchController {@Autowiredprivate ICarSearchService carSearchService;@PostMappingpublic AjaxResult search(@RequestBody CarSearchQuery query) {PageList<CarDoc> search = carSearchService.search(query);return AjaxResult.me().setData(search);}
}

定义Document文档类型

@Data
@AllArgsConstructor
@NoArgsConstructor
@Document(indexName = "example-car", type = "car")
public class CarDoc {@Idprivate Long id;@Field(type = FieldType.Text, analyzer = "ik_smart", searchAnalyzer = "ik_smart")private String title;@Field(type = FieldType.Keyword)private String cover;@Field(type = FieldType.Double)private BigDecimal salePrice;@Field(type = FieldType.Double)private BigDecimal costPrice;@Field(type = FieldType.Integer)private Integer isNew;@Field(type = FieldType.Date)private Date registerTime;@Field(type = FieldType.Double)private Double mileAge;@Field(type = FieldType.Long)private Long shopId;@MultiField(mainField = @Field(type = FieldType.Text, analyzer = "ik_smart", searchAnalyzer = "ik_smart"),otherFields = {@InnerField(type = FieldType.Keyword, suffix = "keyword")})private String shopName; // @MultiField对同一字段应用不同的分析器或存储策略,这里既可以将该字段按照text进行拆分,也可以作为keyword进行查询@Field(type = FieldType.Keyword)private String shopAddress;@Field(type = FieldType.Date)private Date onSaleTime;@Field(type = FieldType.Integer)private Integer costEffective;@Field(type = FieldType.Integer)private Integer rushSale;@Field(type = FieldType.Integer)private Integer quasiNewCar;@Field(type = FieldType.Integer)private Integer transitiveCountry;@Field(type = FieldType.Long)private Long typeId;@MultiField(mainField = @Field(type = FieldType.Text, analyzer = "ik_smart", searchAnalyzer = "ik_smart"),otherFields = {@InnerField(type = FieldType.Keyword, suffix = "keyword")})private String typeName; @Field(type = FieldType.Text, analyzer = "ik_smart", searchAnalyzer = "ik_smart")private String carInfo;@GeoPointField   // 表示坐标类型private GeoPoint shopPoint;  
}

准备一个Repository接口

@Repository
public interface CarDocRepository extends ElasticsearchRepository<CarDoc, Long> {
}// 接口中的泛型,一个是文档对象的类型,一个是文档对象id的类型

使用spring-boot-starter-data-elasticsearch对es进行操作时也要按照es请求的格式进行操作

{"query": {"bool": {"must": {"match_all": {}},"filter": {"term": {"username": "Steven King"}}}}
}

查询

@Service
public class CarSearchServiceImpl implements ICarSearchService {@Autowiredprivate CarDocRepository repository;@Autowiredprivate HighlightResultMapper highlightResultMapper;@Autowiredprivate ElasticsearchTemplate elasticsearchTemplate;@Overridepublic PageList<CarDoc> search(CarSearchQuery query) {NativeSearchQueryBuilder builder = new NativeSearchQueryBuilder();BoolQueryBuilder boole = QueryBuilders.boolQuery(); // 外层的boolList<QueryBuilder> filter = boole.filter();		// 内层的fitlerList<QueryBuilder> must = boole.must();		// 内层的must// 查询"title", "typeName", "shopName", "carInfo"中有查询关键字的结果if (StringUtils.isNotBlank(query.getSearch())) {must.add(QueryBuilders.multiMatchQuery(query.getSearch(), "title", "typeName", "shopName", "carInfo"));}if (Objects.nonNull(query.getShopId())) {filter.add(QueryBuilders.termQuery("shopId", query.getShopId()));}// 进行范围查询if (Objects.nonNull(query.getMinPrice())) {filter.add(QueryBuilders.rangeQuery("costPrice").gte(query.getMinPrice()));}if (Objects.nonNull(query.getMaxPrice())) {filter.add(QueryBuilders.rangeQuery("costPrice").lte(query.getMaxPrice()));}// 按时间进行查找if (Objects.nonNull(query.getCarAge())) {Date date = DateUtils.addYears(new Date(), (-query.getCarAge()));if (query.getCarAgeType() == 1) {filter.add(QueryBuilders.rangeQuery("registerTime").lt(date.getTime()));}if (query.getCarAgeType() == 0) {filter.add(QueryBuilders.rangeQuery("registerTime").gte(date.getTime()));}}// 经纬度Double lon = query.getLongitude();Double lat = query.getLatitude();// 按照经纬度计算距离并按照距离进行查询if (Objects.nonNull(lon) && Objects.nonNull(lat) && Objects.nonNull(query.getDistance())) {GeoDistanceQueryBuilder point = QueryBuilders.geoDistanceQuery("shopPoint").point(lat, lon).distance(query.getDistance(), DistanceUnit.KILOMETERS);filter.add(point);}// 将bool加入到最外层query的构造器builder.withQuery(boole);SortOrder order = SortOrder.ASC;if (!"asc".equals(query.getSortType())) {order = SortOrder.DESC;}// 通过指定字段进行排序if (Objects.nonNull(query.getSortField())) {FieldSortBuilder sort = SortBuilders.fieldSort(query.getSortField()).order(order);builder.withSort(sort);} else {// 没传则默认按照距离排序if (Objects.nonNull(lat) && Objects.nonNull(lon)) {GeoDistanceSortBuilder point = new GeoDistanceSortBuilder("shopPoint", lat, lon);GeoDistanceSortBuilder sort = point.order(order);builder.withSort(sort);}}// 高亮展示,通过给字段前后加html标签的方式实现高亮等效果,需要一个高亮的工具类HighlightBuilder.Field title = new HighlightBuilder.Field("title").preTags("<span style='color:red'>").postTags("</span>");builder.withHighlightFields(title);// 进行分页展示builder.withPageable(PageRequest.of(query.getCurrentPage() - 1, query.getPageSize()));TermsAggregationBuilder aggBuilders1 = AggregationBuilders.terms("typeIdGroup").field("typeId").order(BucketOrder.count(true)).subAggregation(AggregationBuilders.terms("typeNameGroup").field("typeName.keyword").order(BucketOrder.count(true)));// 聚合查询builder.addAggregation(aggBuilders1);AggregatedPage<CarDoc> carDocs = elasticsearchTemplate.queryForPage(builder.build(), CarDoc.class, highlightResultMapper);Map<String, Object> map = SearchUtil.handleTermsAggsData(carDocs.getAggregations());Long counts = carDocs.getTotalElements();List<CarDoc> content = carDocs.getContent();for (CarDoc doc : content) {String s = doc.getShopAddress();if (StringUtils.isNotBlank(s)) {String string = s.split("市")[0] + "市";}doc.setShopAddress(s);}return new PageList<>(counts, content, map);}
}

PageList

@Data
public class PageList<T> {private Long count;private List<T> data; // 用于存放聚合查询的数据private Map<String,Object> map;public PageList() {}public PageList(Long count, List<T> data, Map<String, Object> map) {this.count = count;this.data = data;this.map = map;}public PageList(Long count, List<T> data) {this.count = count;this.data = data;}
}

高亮结果映射器

@Component
public class HighlightResultMapper implements SearchResultMapper {@Overridepublic <T> AggregatedPage<T> mapResults(SearchResponse response, Class<T> aClass, Pageable pageable) {// 记录总条数long totalHits = response.getHits().getTotalHits();// 记录列表(泛型) - 构建Aggregate使用List<T> list = new ArrayList<>();// 获取搜索结果(真正的的记录)SearchHits hits = response.getHits();for (SearchHit hit : hits) {if(hits.getHits().length <= 0){return null;}// 将原本的JSON对象转换成Map对象Map<String, Object> map = hit.getSourceAsMap();// 获取高亮的字段MapMap<String, HighlightField> highlightFields = hit.getHighlightFields();for (Map.Entry<String, HighlightField> highlightField : highlightFields.entrySet()) {// 获取高亮的KeyString key = highlightField.getKey();// 获取高亮的ValueHighlightField value = highlightField.getValue();// 实际fragments[0]就是高亮的结果,无需遍历拼接Text[] fragments = value.getFragments();StringBuilder sb = new StringBuilder();for (Text text : fragments) {sb.append(text);}// 因为高亮的字段必然存在于Map中,就是key值// 可能有一种情况,就是高亮的字段是嵌套Map,也就是说在Map里面还有Map的这种情况,这里没有考虑map.put(key, sb.toString());}// 把Map转换成对象T item = JSONObject.parseObject(JSONObject.toJSONString(map),aClass);list.add(item);}// 返回的是带分页的结果return new AggregatedPageImpl<>(list, pageable, totalHits,response.getAggregations()); //获取聚合结果}@Overridepublic <T> T mapSearchHit(SearchHit searchHit, Class<T> aClass) {return null;}
}

搜索工具类,用于处理聚合查询结果

public class SearchUtil {/*** 处理terms聚合   id  name* @param aggregations* @return*/public static Map<String, Object> handleTermsAggsData(Aggregations aggregations) {// 获取聚合查询结果Map<String, Aggregation> aggregationsMap = aggregations.getAsMap();Set<Map.Entry<String, Aggregation>> entries = aggregationsMap.entrySet();Iterator<Map.Entry<String, Aggregation>> iterator = entries.iterator();//6.1有多少聚合就要返回多少个key-List<IdName>Map<String, Object> aggsData = new HashMap<>();while (iterator.hasNext()) {Map.Entry<String, Aggregation> entry = iterator.next();String key = entry.getKey();System.out.println(key);Aggregation aggsId = entry.getValue();if (aggsId instanceof LongTerms) {   //6.2 拿到id聚合,并且必须是LongTermsLongTerms aggsIdLong = (LongTerms) aggsId;List<LongTerms.Bucket> buckets = aggsIdLong.getBuckets();//6.3 List<IdName<Long,String>List<IdName> list = new ArrayList<>();buckets.forEach(bucket -> {String idStr = bucket.getKeyAsString();//6.4 通过子聚合获取nameMap<String, Aggregation> subAggs = bucket.getAggregations().getAsMap();Set<Map.Entry<String, Aggregation>> entries1 = subAggs.entrySet();//直接获取第一个Map.Entry<String, Aggregation> nameAggEntry = entries1.iterator().next();Aggregation nameAgg = nameAggEntry.getValue();if (nameAgg instanceof StringTerms) {StringTerms nameAggStringTerms = (StringTerms) nameAgg;String nameStr = nameAggStringTerms.getBuckets().get(0).getKeyAsString();IdName idName = new IdName();idName.setId(Long.valueOf(idStr));idName.setName(nameStr);list.add(idName);}});aggsData.put(key, list);}}return aggsData;}
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/bicheng/27920.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

flask部署mtcnn

目录 打印人脸检测信息 输出结果 保存检测结果 浏览器查看nginx&#xff08;nginx配置这里就不多介绍了&#xff09; url图片检测人脸 输出结果 Flask hello-world Flaskmtcnn python调flaskmtcnn 打印人脸检测信息 import cv2 from mtcnn.mtcnn import MTCNNimg cv2.c…

[C++][数据结构][二叉搜索树]详细讲解

目录 1.概念2.二叉搜索树操作1.查找2.插入3.删除 3.二叉搜索树的实现4.二叉搜索树的应用1.K模型2.KV模型 5.二叉搜索树的性能分析 1.概念 二叉搜索树又称二叉排序树&#xff0c;具有以下性质 若它的左子树不为空&#xff0c;则左子树上所有节点的值都小于根节点的值若它的右子…

高考志愿填报,选专业应该考虑哪些因素?

这是一个复杂的社会&#xff0c;各种影响就业的因素层出不穷&#xff0c;也从未断绝。对于高考生而言&#xff0c;高考刚结束&#xff0c;短暂的放松后&#xff0c;就必须考虑自身以后应该就读什么专业&#xff0c;如果不对就读专业进行评估&#xff0c;仔细挑选&#xff0c;毕…

C#传值参数 -1值类型 -2引用类型

传值参数 -1值类型 -2引用类型 using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; //传值参数-1、值类型 2、引用类型 namespace PamatetersExample {class Program{static void Main(string[] args){St…

代码随想录算法训练营day22|701.二叉搜索树中的插入操作、 450.删除二叉搜索树中的节点、 235. 二叉搜索树的最近公共祖先

701.二叉搜索树中的插入操作 这道题较为简单&#xff0c;只需要通过递归找到符合要求的叶子节点&#xff0c;并将节点插入即可。 /*** Definition for a binary tree node.* public class TreeNode {* int val;* TreeNode left;* TreeNode right;* TreeNode(…

A45 STM32_HAL库函数 之 SMARTCARD通用驱动 -- A -- 所有函数的介绍及使用

A45 STM32_HAL库函数 之 SMARTCARD通用驱动 -- A -- 所有函数的介绍及使用 1 该驱动函数预览1.1 HAL_SMARTCARD_Init1.2 HAL_SMARTCARD_DeInit1.3 HAL_SMARTCARD_MspInit1.4 HAL_SMARTCARD_MspDeInit1.5 HAL_SMARTCARD_ReInit1.6 HAL_SMARTCARD_Transmit1.7 HAL_SMARTCARD_Rece…

mysql 8 创建用户,并对用户授权

创建用户&#xff1a; 对MySQL创建新用户。命令如下&#xff1a; create user devuser% identified by 123456; 授予权限 grant all privileges on joolun_ry.* to devuser% with grant option; 参数说明&#xff1a; joolun_ry&#xff1a;表明对那个库进行授权&#xf…

BFD(简单配置实验)

实验拓扑 配置接口IP地址 正常互通 配置静态BFD 查看状态&#xff1a;为UP 与静态路由联动 查看静态路由状态为active 将交换机的接口down掉 BFD的状态为down 再次查看静态路由的状态为Inactive

Qt事件的详细介绍和原理

介绍 事件类型&#xff1a; 1、窗口系统事件spontaneous。 2、异步事件PostEvent。 3、同步事件SendEvent 窗体系统事件spontaneous 代表事件有&#xff1a;各类外设事件&#xff0c;如鼠标单双击、滚轮、移动&#xff0c;键盘按键等事件。 使用场景&#xff1a;该类事件都是…

RawChat:优化AI对话体验,全面兼容GPT功能平台

文章目录 一、Rawchat简介1.1 RawChat的主要特性1.2 RawChat的技术原理简述 二、使用教程三、案例应用3.1 图片内容分析3.2 生图演示3.3 文档解析3.4 探索更多 四、小结 一、Rawchat简介 RawChat平台的诞生&#xff0c;其核心理念是降低用户访问类似ChatGPT这类先进AI服务的门…

Git使用-gitlab上面的项目如何整到本地的idea中

场景 一般我们在开发项目或者接手某个项目时&#xff0c;基本都要接触Git&#xff0c;比如上传项目代码&#xff0c;下载同事给你的交接代码等等。 这是一个基本功&#xff0c;小小整理一下日常操作中的使用。 第一步&#xff1a;在 GitLab 上找到你要克隆的项目&#xff0c;复…

干部管理软件有哪些

随着信息技术的飞速发展&#xff0c;干部管理软件在各级党政机关、国企事业单位中扮演着越来越重要的角色。这些软件通过整合干部管理的各项业务流程&#xff0c;实现了干部信息的系统化、规范化和高效化管理。以下是几款主流的干部管理软件及其特点&#xff1a; 一、干部信息…

QT小技巧

QT小技巧 滑条的美化 美化前 代码如下 //滑条的美化ui->horizontalSlider->setStyleSheet("QSlider::groove:horizontal {""border:1px solid skyblue;""background-color:skyblue;""height:10px;""border-radius:5px…

NFS网络文件存储入门

一、NFS介绍 NFS全称Network Files System&#xff0c;中文叫网络文件系统&#xff0c;是1980年由SUN发展出来在UNIX&Linux系统间实现磁盘文件共享的一种方法。NFS是当前互联网系统架构中最常用的服务之一&#xff0c;特别是中小公司应用频率很高。大公司可能用MFS&#x…

Nextjs 集成富文本编辑器react-quill

目录 一、组件代码 二、参考文档 由于Next与react有些差别&#xff0c;直接调用组件会报无法找到文档的错误&#xff0c;于是我们只有考虑动态导入了解决问题。因为富文本编辑器一般作用与form页面对SEO意义不大&#xff0c;所以这里可以考虑暂时关闭SSR。 一、组件代码 /*…

【系统架构】REST风格

系列文章目录 第一章 系统架构的演进 第二章 REST风格架构 文章目录 系列文章目录前言一、进程间的通信普通管道&#xff08;Pipe&#xff09;或者具名管道&#xff08;Named Pipe&#xff09;信号&#xff08;Signal&#xff09;信号量&#xff08;Semaphore&#xff09;消息…

C++ 字符串处理5-手机号邮箱如何脱敏处理

1. 关键词2. strutil.h3. strutil.cpp4. 测试代码5. 运行结果6. 源码地址 1. 关键词 关键词&#xff1a; C 字符串处理 分割字符串 连接字符串 跨平台 应用场景&#xff1a; 有些重要信息需要保密&#xff0c;比如手机号、邮箱等&#xff0c;如何在不影响用户阅读的情况下…

Hadoop之HDFS分布式文件系统

HDFS简介 Hadoop Distributed File System (HDFS): HDFS 是 Hadoop 的分布式文件系统,它设计用于存储大量数据,并提供 高吞吐率的数据访问,通过将数据分块存储在多个节点上,实现数据的冗余存储和容错。 HDFS重要概念 HDFS 通过统一的命名空间目录树来定位文件; 另外,它…

zookeeper、kakfa添加用户加密

背景 zookeeper无权限访问到根目录 步骤 在kafka/config 目录中创建 vi config/zookeeper_jaas.conf在zookeeper_jaas.conf中添加 Server { org.apache.kafka.common.security.plain.PlainLoginModule required username"admin" password"12345" user_ad…

温泉镇旅游微信小程序的设计与实现(论文+源码)_kaic

摘要 旅游业随着经济的快速发展呈现出一派欣欣向荣的景象&#xff0c;尤其是近两年来&#xff0c;各个行业运用科技以及因特网来促进旅游迅速发展&#xff0c;逐渐都显示出了的问题&#xff0c;特别突出的是在线上推广&#xff0c;其缺点也是特别明显。尽管在新冠肺炎的冲击下&…