SpringData ElasticSearch - 简化开发,完美适配 Spring 生态

目录

一、SpringData ElasticSearch

1.1、环境配置

1.2、创建实体类

1.3、ElasticsearchRestTemplate 的使用

1.3.1、创建索引 设置映射

1.3.2、简单的增删改查

1.3.3、搜索

1.4、ElasticsearchRepository

1.4.1、使用方式

1.4.2、简单的增删改查

1.4.3、分页排序查询


一、SpringData ElasticSearch


1.1、环境配置

a)依赖如下:

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

b)配置文件如下:

spring:elasticsearch:uris: env-base:9200

1.2、创建实体类

a)简单结构如下(后续实例,围绕此结构展开):

import org.springframework.data.annotation.Id
import org.springframework.data.elasticsearch.annotations.Document
import org.springframework.data.elasticsearch.annotations.Field
import org.springframework.data.elasticsearch.annotations.FieldType@Document(indexName = "album_info", shards = 1, replicas = 0)
data class AlbumInfoDo(/*** @Id: 表示文档中的主键,并且会在保存在 ElasticSearch 数据结构中 {"id": "", "userId": "", "title": ""}*/@Id@Field(type = FieldType.Keyword)val id: Long? = null,/*** @Field: 描述 Java 类型中的属性映射*      - name: 对应 ES 索引中的字段名. 默认和属性同名*      - type: 对应字段类型,默认是 FieldType.Auto (会根据我们数据类型自动进行定义),但是建议主动定义,避免导致错误映射*      - index: 是否创建索引. text 类型创建倒排索引,其他类型创建正排索引.  默认是 true*      - analyzer: 分词器名称.  中文我们一般都使用 ik 分词器(ik分词器有 ik_smart 和 ik_max_word)*/@Field(name = "user_id", type = FieldType.Long)val userId: Long,@Field(type = FieldType.Text, analyzer = "ik_max_word")val title: String,@Field(type = FieldType.Text, analyzer = "ik_smart")val content: String,
)

b)复杂嵌套结构如下:

import org.springframework.data.annotation.Id
import org.springframework.data.elasticsearch.annotations.Document
import org.springframework.data.elasticsearch.annotations.Field
import org.springframework.data.elasticsearch.annotations.FieldType@Document(indexName = "album_list")
data class AlbumListDo(@Id@Field(type = FieldType.Keyword)var id: Long,@Field(type = FieldType.Nested) // 表示一个嵌套结构var userinfo: UserInfoSimp,@Field(type = FieldType.Text, analyzer = "ik_max_word")var title: String,@Field(type = FieldType.Text, analyzer = "ik_smart")var content: String,@Field(type = FieldType.Nested) // 表示一个嵌套结构var photos: List<AlbumPhotoSimp>,
)data class UserInfoSimp(@Field(type = FieldType.Long)val userId: Long,@Field(type = FieldType.Text, analyzer = "ik_max_word")val username: String,@Field(type = FieldType.Keyword, index = false)val avatar: String,
)data class AlbumPhotoSimp(@Field(type = FieldType.Integer, index = false)val sort: Int,@Field(type = FieldType.Keyword, index = false)val photo: String,
)

对于一个小型系统来说,一般也不会创建这种复杂程度的文档,因为会涉及到很多一致性问题, 需要通过大量的 mq 进行同步,给系统带来一定的开销. 

因此,一般会将需要进行模糊查询的字段存 Document 中(es 就擅长这个),而其他数据则可以在 Document 中以 id 的形式进行存储.   这样就既可以借助 es 高效的模糊查询能力,也能减少为保证一致性而带来的系统开销.  从 es 中查到数据后,再通过其他表的 id 从数据库中拿数据即可(这点开销,相对于从大量数据的数据库中进行 like 查询,几乎可以忽略).

1.3、ElasticsearchRestTemplate 的使用

1.3.1、创建索引 设置映射

@SpringBootTest(classes = [DataEsApplication::class])
class DataEsApplicationTests {@Resource private lateinit var elasticsearchTemplate: ElasticsearchRestTemplate@Testfun test1() {//创建索引elasticsearchTemplate.indexOps(AlbumInfoDo::class.java).create()//设置映射elasticsearchTemplate.indexOps(AlbumInfoDo::class.java).putMapping(elasticsearchTemplate.indexOps(AlbumInfoDo::class.java).createMapping())}}

效果如下:

1.3.2、创建索引映射注意事项(必看)

a)在没有创建索引库和映射的情况下,也可以直接向 es 库中插入数据,如下代码:

    @Testfun test1() {val o = AlbumListDo(id = 1,userinfo = UserInfoSimp(userId = 1,username = "cyk",avatar = "http:photo1.com"),title = "天气很好的一天",content = "早上起来,我要好好学习,然去公园散步~",photos = listOf(AlbumPhotoSimp(1, "www.photo1"),AlbumPhotoSimp(2, "www.photo2")))val result = esTemplate.save(o)println(result)}

b)即使上述代码中 AlbumListDo 中有各种注解标记,但是不会生效!!! es 会根据插入的数据,自动转化数据结构(无视你的注解).

c)因此,一定要先创建索引库和映射,再进行数据插入!

1.3.3、简单的增删改查

    /*** 更新和添加都是这样* 更新的时候会根据 id 进行覆盖*/@Testfun testSave() {//保存单条数据val a1 = AlbumInfoDo(id = 1,userId = 10000,title = "今天天气真好",content = "学习完之后,我要出去好好玩")val result = elasticsearchTemplate.save(a1)println(result)//保存多条数据val list = listOf(AlbumInfoDo(2, 10000, "西安六号线避雷", "前俯后仰。他就一直在那前后动。他背后是我朋友,我让他不要挤了,他直接就急了,开始故意很大力的挤来挤去。"),AlbumInfoDo(3, 10000, "字节跳动快上车~", "#内推 #字节跳动内推 #互联网"),AlbumInfoDo(4, 10000, "连王思聪也变得低调老实了", "如今的王思聪,不仅交女友的质量下降,在网上也不再像以前那样随意喷这喷那。显然,资金的紧张让他低调了许多"))val resultList = elasticsearchTemplate.save(list)resultList.forEach(::println)}@Testfun testDelete() {//根据主键删除elasticsearchTemplate.delete("1", AlbumInfoDo::class.java)}@Testfun testGet() {val result = elasticsearchTemplate.get("1", AlbumInfoDo::class.java)println(result)}

1.3.4、搜索

import org.cyk.dataes.model.AlbumInfoDo
import org.elasticsearch.index.query.QueryBuilders
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder
import org.junit.jupiter.api.Test
import org.springframework.boot.test.context.SpringBootTest
import org.springframework.data.domain.PageRequest
import org.springframework.data.domain.Sort
import org.springframework.data.elasticsearch.core.ElasticsearchRestTemplate
import org.springframework.data.elasticsearch.core.query.NativeSearchQueryBuilder
import javax.annotation.Resource@SpringBootTest(classes = [DataEsApplication::class])
class TemplateTests {@Resource private lateinit var elasticsearchTemplate: ElasticsearchRestTemplate/*** 全文检索查询(match_all)*/@Testfun testMatchAllQuery() {val query = NativeSearchQueryBuilder().withQuery(QueryBuilders.matchAllQuery()).build()val hits = elasticsearchTemplate.search(query, AlbumInfoDo::class.java)println("总数为: ${hits.totalHits}")hits.forEach { println(it.content) }}/*** 全文检索查询(match)*/@Testfun testMatchQuery() {val query = NativeSearchQueryBuilder().withQuery(QueryBuilders.matchQuery("title", "天气")).build()val hits = elasticsearchTemplate.search(query, AlbumInfoDo::class.java)hits.forEach { println(it.content) }}/*** 精确查询(term)*/@Testfun testTerm() {val query = NativeSearchQueryBuilder().withQuery(QueryBuilders.termQuery("user_id", 10001)).build()val hits = elasticsearchTemplate.search(query, AlbumInfoDo::class.java)hits.forEach { println(it.content) }}/*** 范围查询*/@Testfun testRangeQuery() {val query = NativeSearchQueryBuilder().withQuery(QueryBuilders.rangeQuery("id").gte(1).lt(4)).build()val hits = elasticsearchTemplate.search(query, AlbumInfoDo::class.java)hits.forEach { println(it.content) }}/*** 复合查询(bool)*/@Testfun testBoolQuery() {val boolQuery = QueryBuilders.boolQuery()//必要条件: query.must 得到一个集合val mustList = boolQuery.must()mustList.add(QueryBuilders.rangeQuery("user_id").gte(10000).lt(10003))//其他的搜索条件集合的获取方式类似val mustNotList = boolQuery.mustNot()val should = boolQuery.should()//当然,还有一种简化的写法,如下,下述代码相当于 query.should().add(QueryBuilders.matchAllQuery())boolQuery.should(QueryBuilders.matchAllQuery())val query =  NativeSearchQueryBuilder().withQuery(boolQuery).build()val hits = elasticsearchTemplate.search(query, AlbumInfoDo::class.java)hits.forEach { println(it.content) }}/*** 排序和分页*/@Testfun testSortAndPage() {val query = NativeSearchQueryBuilder().withQuery(QueryBuilders.matchAllQuery()).withPageable(PageRequest.of(0, 3) //从下标 0 开始,向后查询 3 条数据.withSort(Sort.by(Sort.Order.desc("id"))) //根据 id 降序排序(这里也可以根据多个字段进行升序降序)).build()val hits = elasticsearchTemplate.search(query, AlbumInfoDo::class.java)hits.forEach{ println(it.content) }}/*** 高亮搜索*/@Testfun testHighLight() {//定义高亮字段val field = HighlightBuilder.Field("title")//a) 前缀标签field.preTags("<span style='color:red'>")//b) 后缀标签field.postTags("</span>")//c) 高亮的片段长度(多少个几个字需要高亮)field.fragmentSize(2)//d) 每个字段高亮片段的数量,例如 1 表示获取每个字段的一个高亮片段field.numOfFragments(1)// withHighlightFields(Field... 高亮字段数组)val query = NativeSearchQueryBuilder().withQuery(QueryBuilders.matchQuery("title", "天气")).withHighlightFields(field).build()val hits = elasticsearchTemplate.search(query, AlbumInfoDo::class.java)//注意,hit.content 中本身是没有高亮数据的,因此这里需要手工处理hits.forEach {val result = it.content//根据高亮字段名称,获取高亮数据集合,结果是 List<String>val hList = it.getHighlightField("title")if(hList.size > 0) {//有高亮数据result.title = hList.get(0)}println(result)}}}

1.4、ElasticsearchRepository

1.4.1、使用方式

这个东西就跟 JPA 的使用方式一样,只不过高版本的 SpringData Elasticsearch 没有给 ElasticsearchRepository 接口提供复杂搜索查询,建议还是使用 ElasticsearchTemplate

自定义一个接口, 继承  ElasticsearchRepository 接口,如下:

import org.cyk.dataes.model.AlbumInfoDo
import org.springframework.data.elasticsearch.repository.ElasticsearchRepositoryinterface AlbumInfoRepo: ElasticsearchRepository<AlbumInfoDo, Long> //<实体类,主键类型>

1.4.2、简单的增删改查

import org.cyk.dataes.model.AlbumInfoDo
import org.cyk.dataes.service.AlbumInfoESRepo
import org.junit.jupiter.api.Test
import org.springframework.boot.test.context.SpringBootTest
import javax.annotation.Resource@SpringBootTest(classes = [DataEsApplication::class])
class RepoTests {@Resource private lateinit var albumInfoESRepo: AlbumInfoESRepo@Testfun testSave() {//增加单个val a = AlbumInfoDo(1, 10000, "今天天气真好", "学习完之后,我要出去好好玩")val result = albumInfoESRepo.save(a)println(result)//批量新增val list = listOf(AlbumInfoDo(2, 10000, "西安六号线避雷", "前俯后仰。他就一直在那前后动。他背后是我朋友,我让他不要挤了,他直接就急了,开始故意很大力的挤来挤去。"),AlbumInfoDo(3, 10000, "字节跳动快上车~", "#内推 #字节跳动内推 #互联网"),AlbumInfoDo(4, 10000, "连王思聪也变得低调老实了", "如今的王思聪,不仅交女友的质量下降,在网上也不再像以前那样随意喷这喷那。显然,资金的紧张让他低调了许多"))val resultList = albumInfoESRepo.saveAll(list)resultList.forEach(::println)}@Testfun testDel() {//根据 id 删除albumInfoESRepo.deleteById(1)//删除所有albumInfoESRepo.deleteAll()}@Testfun testFind() {//查询所有val resultList = albumInfoESRepo.findAll()resultList.forEach(::println)//根据 id 查询val result = albumInfoESRepo.findById(1)println(result.get())}}

1.4.3、分页排序查询

import org.cyk.dataes.service.AlbumInfoESRepo
import org.junit.jupiter.api.Test
import org.springframework.boot.test.context.SpringBootTest
import org.springframework.data.domain.PageRequest
import org.springframework.data.domain.Sort
import javax.annotation.Resource@SpringBootTest(classes = [DataEsApplication::class])
class RepoTests2 {@Resourceprivate lateinit var albumInfoESRepo: AlbumInfoESRepo@Testfun testFindPageAndSort() {//从 0 下标开始向后获取 3 个,并根据 id 降序排序val result = albumInfoESRepo.findAll(PageRequest.of(0, 3,Sort.by(Sort.Direction.DESC, "id")))result.content.forEach(::println)}}

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

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

相关文章

软考高级架构师:系统性能设计-阿姆达尔定律概念和例题

一、AI 讲解 系统性能设计中的一个重要概念是阿姆达尔定律&#xff08;Amdahl’s Law&#xff09;。阿姆达尔定律是由吉恩阿姆达尔&#xff08;Gene Amdahl&#xff09;在1967年提出的&#xff0c;用于评估系统性能提升的理论上限&#xff0c;特别是在考虑并行计算时。该定律表…

Java获取IP地址以及MAC地址(附Demo)

目录 前言1. IP及MAC2. 特定适配器 前言 需要获取客户端的IP地址以及MAC地址 import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader;public class test {public static void main(String[] args) {try {// 执行命令Process process…

MYSQL-7.内存

内存 Mysql的内存结构 大体可分为四个板块&#xff1a;mysql工作组件、线程本地内存、mysql共享内存、存储引擎缓冲区&#xff1b; Mysql server工作组件 对应着mysql架构图中的组件层&#xff1a; Mysql在启动时&#xff0c;会将这些工作组件初始化到内存中&#xff1b; …

编译时提示存在多个默认构造函数的错误怎么解决呢?

c程序中&#xff0c;如果编译器提升存在多个默认构造函数怎么解决呢&#xff1f; class Date { public:Date(){_year 1900;_month 1;_day 1;}Date(int year 1900, int month 1, int day 1){_year year;_month month;_day day;} private:int _year;int _month;int _day…

redis 数据库的安装及使用方法

目录 一 关系数据库与非关系型数据库 &#xff08;一&#xff09;关系型数据库 1&#xff0c;关系型数据库是什么 2&#xff0c;主流的关系型数据库有哪些 3&#xff0c;关系型数据库注意事项 &#xff08;二&#xff09;非关系型数据库 1&#xff0c;非关系型数据库是…

Python快速入门系列-9(Python项目实战)

第九章:Python项目实战 9.1 开发一个简单的Web应用9.1.1 项目概述9.1.2 环境准备9.1.3 项目结构9.1.4 代码实现9.1.4.1 创建数据库模型9.1.4.2 创建视图9.1.4.3 实用工具函数9.1.4.4 运行应用9.1.5 模板设计9.2 数据分析与可视化项目9.2.1 项目概述9.2.2 环境准备9.2.3 数据分…

超详细工具Navicat安装教程

Navicat是一款功能强大的数据库管理工具&#xff0c;可用于管理多种类型的数据库&#xff0c;包括MySQL、MariaDB、SQL Server、SQLite、Oracle和PostgreSQL等。以下是Navicat工具的一些主要特点和功能&#xff1a; 一.功能介绍 跨平台支持 多种数据库支持 直观的用户界面 数据…

【AI数字人】根据音频生成带动画的数字人

这是一个从音频和蒙面手势生成全身人体手势的框架,包括面部、局部身体、手和整体动作。为了实现这一目标,我们首先引入 BEATX (BEAT-SMPLXFLAME),一个新的网格级整体协同语音数据集。 BEATX 将 MoShed SMPLX 身体与 FLAME 头部参数相结合,进一步细化头部、颈部和手指运动的…

idea使用docker将Java项目生成镜像并使用

1&#xff1a;开启docker 远程访问 使用 vim 编辑docker服务配置文件 vim /lib/systemd/system/docker.service [Service] Typenotify # the default is not to use systemd for cgroups because the delegate issues still # exists and systemd currently does not suppor…

全面剖析CSS盒子模型:概念理解、构成元素、布局影响与实战技巧

在CSS进行网页布局与样式设计的过程中&#xff0c;盒子模型&#xff08;Box Model&#xff09;扮演着无可替代的角色。这一关键概念是精准掌控页面元素布局与样式的基石。唯有深入理解和熟练运用盒子模型的原理及各项属性&#xff0c;开发者方能自如地塑造页面中各元素的最终形…

Mac下Docker Desktop starting的解决方法

记录下自己在新增了一个新的容器后&#xff0c;Disk Size过大导致启动Docker Desktop会一直卡在Docker Desktop starting&#xff0c;并且重启无效的解决方法。该方法无需重新卸载&#xff0c;并且能保留原有的镜像和容器。 一、确认问题 首先确认Docker.raw大小以确认是否和笔…

某音乐平台歌曲信息逆向之参数寻找

如何逆向加密参数&#xff1a;某音乐平台歌曲信息逆向之webpack扣取-CSDN博客 参数构建 {"comm": {"cv": 4747474,"ct": 24,"format": "json","inCharset": "utf-8","outCharset": "ut…