RedisOM for Java
学习如何使用Redis Stack和Spring进行构建
Redis Stack 提供了一种无缝且直接的方式来使用 Redis 的不同数据模型和功能,包括文档存储、时间序列数据库、概率数据结构和全文搜索引擎。
Redis Stack 由多个客户端库支持,包括 Node.js、Java 和 Python,因此开发者可以使用他们偏好的语言。我们将使用其中一个支持 Redis Stack 的库;Redis OM Spring。 Redis OM Spring 提供了强大的仓库和自定义对象映射抽象,这些功能建立在强大的 Spring Data Redis (SDR) 框架之上。
你需要准备什么:
- Redis Stack: 参见 /docs/latest/operate/oss_and_stack/install/install-stack/
- Redis Insight
- 您最喜欢的浏览器
- Java 11 或更高版本
使用Spring Initializer的Spring Boot脚手架
我们将从使用Spring Initializer创建一个骨架应用开始,打开你的浏览器访问https://start.spring.io,并按照以下方式配置我们的骨架应用:
- 我们将使用基于Maven的构建(勾选Maven复选框)
- 以及版本
2.6.4
的 Spring Boot,这是 Redis OM Spring 当前支持的版本 - 组:
com.redis.om
- 工件:
skeleton
- 名称:
skeleton
- 描述:Redis OM Spring 的骨架应用
- 包名:
com.redis.om.skeleton
- 打包方式:JAR
- Java:
11
- 依赖项:
web
、devtools
和lombok
。
web
(Spring Web) 使我们能够使用 Spring MVC 构建 RESTful 应用程序。使用 devtools
我们可以实现快速的应用程序重启和重新加载。而 lombok
则减少了像 getters 和 setters 这样的样板代码。
点击Generate
并下载ZIP文件,解压后将Maven项目加载到您选择的IDE中。
添加 Redis OM Spring
打开 Maven 的 pom.xml
文件,在
和
部分之间,我们将添加快照仓库,以便获取 redis-om-spring 的最新 SNAPSHOT 版本:
<repositories>
<repository>
<id>snapshots-repo</id>
<url>https://s01.oss.sonatype.org/content/repositories/snapshots/</url>
</repository>
</repositories>
然后在
部分添加Redis OM Spring的版本0.3.0
:
<dependency>
<groupId>com.redis.om</groupId>
<artifactId>redis-om-spring</artifactId>
<version>0.3.0-SNAPSHOT</version>
</dependency>
添加Swagger
我们将使用Swagger UI来测试我们的Web服务端点。要将Swagger 2添加到Spring REST Web服务中,使用Springfox实现,请将以下依赖项添加到POM中:
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-boot-starter</artifactId>
<version>3.0.0</version>
</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger-ui</artifactId>
<version>3.0.0</version>
</dependency>
让我们将Swagger Docker Bean添加到Spring App类中:
@Bean
public Docket api() {
return new Docket(DocumentationType.SWAGGER_2)
.select()
.apis(RequestHandlerSelectors.any())
.paths(PathSelectors.any())
.build();
}
这将捕获我们应用程序暴露的任何HTTP端点。添加到你的应用程序的属性文件中(src/main/resources/application.properties):
spring.mvc.pathmatch.matching-strategy=ANT_PATH_MATCHER
最后,为了在应用程序中启用Swagger,我们需要使用EnableSwagger2
注解,通过注解主应用程序类来实现:
@EnableSwagger2
@SpringBootApplication
public class SkeletonApplication {
// ...
}
创建领域
我们的领域将非常简单;拥有Address
es的Person
s。让我们从Person
实体开始:
package com.redis.om.skeleton.models;
import java.util.Set;
import org.springframework.data.annotation.Id;
import org.springframework.data.geo.Point;
import com.redis.om.spring.annotations.Document;
import com.redis.om.spring.annotations.Indexed;
import com.redis.om.spring.annotations.Searchable;
import lombok.AccessLevel;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NonNull;
import lombok.RequiredArgsConstructor;
@RequiredArgsConstructor(staticName = "of")
@AllArgsConstructor(access = AccessLevel.PROTECTED)
@Data
@Document
public class Person {
// Id Field, also indexed
@Id
@Indexed
private String id;
// Indexed for exact text matching
@Indexed @NonNull
private String firstName;
@Indexed @NonNull
private String lastName;
//Indexed for numeric matches
@Indexed @NonNull
private Integer age;
//Indexed for Full Text matches
@Searchable @NonNull
private String personalStatement;
//Indexed for Geo Filtering
@Indexed @NonNull
private Point homeLoc;
// Nest indexed object
@Indexed @NonNull
private Address address;
@Indexed @NonNull
private Set<String> skills;
}
Person
类具有以下属性:
id
: 使用ULIDs自动生成的String
firstName
: 一个String
,表示他们的名字或名字。lastName
: 一个String
,表示他们的姓氏。age
: 一个Integer
,表示他们的年龄,单位为年。personalStatement
: 一个String
,表示包含事实或其他传记信息的个人文本声明。homeLoc
: 一个表示地理坐标的org.springframework.data.geo.Point
。address
: 一个类型为Address
的实体,表示该人的邮政地址。skills
: 一个Set
,表示该人所拥有的技能的字符串集合。
@Document
Person
类 (com.redis.om.skeleton.models.Person
) 被注解为 @Document
(com.redis.om.spring.annotations.Document
),这标志着该对象是一个 Redis 实体,将由适当类型的存储库持久化为 JSON 文档。
@Indexed 和 @Searchable
字段 id
, firstName
, lastName
, age
, homeLoc
, address
, 和 skills
都被注解为 @Indexed
(com.redis.om.spring.annotations.Indexed
)。在使用 @Document
注解的实体上,Redis OM Spring 将扫描字段并为实体的模式添加适当的搜索索引字段。例如,对于 Person
类,在应用程序启动时将创建一个名为 com.redis.om.skeleton.models.PersonIdx
的索引。在索引模式中,将为每个使用 @Indexed
注解的属性添加一个搜索字段。RediSearch,作为支持搜索的底层搜索引擎,支持文本(全文搜索)、标签(精确匹配搜索)、数字(范围查询)、地理(地理范围查询)和向量(向量查询)字段。对于 @Indexed
字段,根据属性的数据类型选择适当的搜索字段(标签、数字或地理)。
标记为@Searchable
(com.redis.om.spring.annotations.Searchable
)的字段,例如Person
中的personalStatement
,在搜索索引模式中被反映为全文搜索字段。
嵌套字段搜索功能
嵌入类 Address
(com.redis.om.skeleton.models.Address
) 有几个用 @Indexed
和 @Searchable
注解的属性,这些属性将在 Redis 中生成搜索索引字段。这些字段的扫描是由 Person
类中 address
属性上的 @Indexed
注解触发的:
package com.redis.om.skeleton.models;
import com.redis.om.spring.annotations.Indexed;
import com.redis.om.spring.annotations.Searchable;
import lombok.Data;
import lombok.NonNull;
import lombok.RequiredArgsConstructor;
@Data
@RequiredArgsConstructor(staticName = "of")
public class Address {
@NonNull
@Indexed
private String houseNumber;
@NonNull
@Searchable(nostem = true)
private String street;
@NonNull
@Indexed
private String city;
@NonNull
@Indexed
private String state;
@NonNull
@Indexed
private String postalCode;
@NonNull
@Indexed
private String country;
}
Spring 数据仓库
有了模型之后,我们需要在模型和Redis之间创建一个桥梁,即Spring Data Repository。与其他Spring Data Repositories一样,Redis OM Spring数据存储库的目标是显著减少实现数据访问所需的样板代码。创建一个Java接口如下:
package com.redis.om.skeleton.models.repositories;
import com.redis.om.skeleton.models.Person;
import com.redis.om.spring.repository.RedisDocumentRepository;
public interface PeopleRepository extends RedisDocumentRepository<Person,String> {
}
这就是我们获取所有CRUD和分页/排序功能所需的全部内容。RedisDocumentRepository
(com.redis.om.spring.repository.RedisDocumentRepository
) 继承自 PagingAndSortingRepository
(org.springframework.data.repository.PagingAndSortingRepository
),而后者又继承自CrudRepository,以提供使用分页和排序检索实体的额外方法。
@EnableRedisDocumentRepositories
在我们启动应用程序之前,我们需要启用我们的Redis文档存储库。像大多数Spring Data项目一样,Redis OM Spring提供了一个注解来实现这一点;即@EnableRedisDocumentRepositories
。我们在主应用程序类上添加注解:
@EnableRedisDocumentRepositories(basePackages = "com.redis.om.skeleton.*")
@EnableSwagger2
@SpringBootApplication
public class SkeletonApplication {
使用存储库进行CRUD操作
启用存储库后,我们可以使用我们的存储库;让我们放入一些数据以查看对象映射的实际效果。让我们创建一个CommandLineRunner
,它将在应用程序启动时执行:
public class SkeletonApplication {
@Bean
CommandLineRunner loadTestData(PeopleRepository repo) {
return args -> {
repo.deleteAll();
String thorSays = “The Rabbit Is Correct, And Clearly The Smartest One Among You.”;
// Serendipity, 248 Seven Mile Beach Rd, Broken Head NSW 2481, Australia
Address thorsAddress = Address.of("248", "Seven Mile Beach Rd", "Broken Head", "NSW", "2481", "Australia");
Person thor = Person.of("Chris", "Hemsworth", 38, thorSays, new Point(153.616667, -28.716667), thorsAddress, Set.of("hammer", "biceps", "hair", "heart"));
repo.save(thor);
};
}
在loadTestData
方法中,我们将使用PeopleRepository
的一个实例(感谢Spring的依赖注入!)。在返回的lambda中,我们将首先调用repo的deleteAll
方法,这将确保每次应用程序重新加载时都有干净的数据。
我们使用Lombok生成的构建器方法创建一个Person
对象,然后使用repo的save
方法保存它。
使用Redis Insight进行监控
让我们启动Redis Insight并连接到本地主机的6379端口。通过一个干净的Redis Stack安装,我们可以使用内置的CLI来检查系统中的键:
对于少量数据,您可以使用keys
命令(对于任何大量数据,请使用scan
):
keys *
如果你想密切关注针对服务器发出的命令,Redis Insight 提供了一个分析器。如果你点击屏幕底部的“profile”按钮,它应该会显示分析器窗口,在那里你可以通过点击“Start Profiler”箭头来启动分析器。
让我们通过使用Maven命令来启动我们的Spring Boot应用程序:
./mvnw spring-boot:run
在 Redis Insight 上,如果应用程序正确启动,您应该会在分析器上看到一系列命令飞过:
现在我们可以通过简单地刷新“Keys”视图来检查新加载的数据:
你现在应该看到两个键;一个用于“Thor”的JSON文档,另一个用于Spring Data Redis(和Redis OM Spring)用来维护实体主键列表的Redis集合。
您可以选择键列表上的任何键以在详细信息面板上显示其内容。对于JSON文档,我们获得了一个很好的树状视图:
在应用程序启动时执行了几个Redis命令。让我们分解它们,以便理解发生了什么。
索引创建
第一个是对FT.CREATE
的调用,这是在Redis OM Spring扫描了@Document
注解之后发生的。正如你所看到的,由于它在Person
上遇到了注解,它创建了PersonIdx
索引。
"FT.CREATE"
"com.redis.om.skeleton.models.PersonIdx" "ON" "JSON"
"PREFIX" "1" "com.redis.om.skeleton.models.Person:"
"SCHEMA"
"$.id" "AS" "id" "TAG"
"$.firstName" "AS" "firstName" "TAG"
"$.lastName" "AS" "lastName" "TAG"
"$.age" "AS" "age" "NUMERIC"
"$.personalStatement" "AS" "personalStatement" "TEXT"
"$.homeLoc" "AS" "homeLoc" "GEO"
"$.address.houseNumber" "AS" "address_houseNumber" "TAG"
"$.address.street" "AS" "address_street" "TEXT" "NOSTEM"
"$.address.city" "AS" "address_city" "TAG"
"$.address.state" "AS" "address_state" "TAG"
"$.address.postalCode" "AS" "address_postalCode" "TAG"
"$.address.country" "AS" "address_country" "TAG"
"$.skills[*]" "AS" "skills"
清理人员存储库
下一组命令是通过调用 repo.deleteAll()
生成的:
"DEL" "com.redis.om.skeleton.models.Person"
"KEYS" "com.redis.om.skeleton.models.Person:*"
第一次调用清除了Spring Data Redis维护的主键集合(因此也包括Redis OM Spring),第二次调用收集了所有要删除的键,但在首次加载数据时没有需要删除的键。
保存人员实体
下一个仓库调用是 repo.save(thor)
,这会触发以下序列:
"SISMEMBER" "com.redis.om.skeleton.models.Person" "01FYANFH68J6WKX2PBPX21RD9H"
"EXISTS" "com.redis.om.skeleton.models.Person:01FYANFH68J6WKX2PBPX21RD9H"
"JSON.SET" "com.redis.om.skeleton.models.Person:01FYANFH68J6WKX2PBPX21RD9H" "." "{"id":"01FYANFH68J6WKX2PBPX21RD9H","firstName":"Chris","lastName":"Hemsworth","age":38,"personalStatement":"The Rabbit Is Correct, And Clearly The Smartest One Among You.","homeLoc":"153.616667,-28.716667","address":{"houseNumber":"248","street":"Seven Mile Beach Rd","city":"Broken Head","state":"NSW","postalCode":"2481","country":"Australia"},"skills":["biceps","hair","heart","hammer"]}
"SADD" "com.redis.om.skeleton.models.Person" "01FYANFH68J6WKX2PBPX21RD9H"
让我们来分解一下:
- 第一次调用使用生成的ULID来检查ID是否在主键集合中(如果是,它将被移除)
- 第二次调用检查JSON文档是否存在(如果存在,它将被移除)
- 第三次调用使用
JSON.SET
命令来保存JSON负载 - 最后一次调用将保存文档的主键添加到主键集合中
现在我们已经通过.save
方法看到了存储库的实际操作,我们知道从Java到Redis的旅程是可行的。现在让我们添加一些更多的数据,使交互更加有趣:
@Bean
CommandLineRunner loadTestData(PeopleRepository repo) {
return args -> {
repo.deleteAll();
String thorSays = “The Rabbit Is Correct, And Clearly The Smartest One Among You.”;
String ironmanSays = “Doth mother know you weareth her drapes?”;
String blackWidowSays = “Hey, fellas. Either one of you know where the Smithsonian is? I’m here to pick up a fossil.”;
String wandaMaximoffSays = “You Guys Know I Can Move Things With My Mind, Right?”;
String gamoraSays = “I Am Going To Die Surrounded By The Biggest Idiots In The Galaxy.”;
String nickFurySays = “Sir, I’m Gonna Have To Ask You To Exit The Donut”;
// Serendipity, 248 Seven Mile Beach Rd, Broken Head NSW 2481, Australia
Address thorsAddress = Address.of("248", "Seven Mile Beach Rd", "Broken Head", "NSW", "2481", "Australia");
// 11 Commerce Dr, Riverhead, NY 11901
Address ironmansAddress = Address.of("11", "Commerce Dr", "Riverhead", "NY", "11901", "US");
// 605 W 48th St, New York, NY 10019
Address blackWidowAddress = Address.of("605", "48th St", "New York", "NY", "10019", "US");
// 20 W 34th St, New York, NY 10001
Address wandaMaximoffsAddress = Address.of("20", "W 34th St", "New York", "NY", "10001", "US");
// 107 S Beverly Glen Blvd, Los Angeles, CA 90024
Address gamorasAddress = Address.of("107", "S Beverly Glen Blvd", "Los Angeles", "CA", "90024", "US");
// 11461 Sunset Blvd, Los Angeles, CA 90049
Address nickFuryAddress = Address.of("11461", "Sunset Blvd", "Los Angeles", "CA", "90049", "US");
Person thor = Person.of("Chris", "Hemsworth", 38, thorSays, new Point(153.616667, -28.716667), thorsAddress, Set.of("hammer", "biceps", "hair", "heart"));
Person ironman = Person.of("Robert", "Downey", 56, ironmanSays, new Point(40.9190747, -72.5371874), ironmansAddress, Set.of("tech", "money", "one-liners", "intelligence", "resources"));
Person blackWidow = Person.of("Scarlett", "Johansson", 37, blackWidowSays, new Point(40.7215259, -74.0129994), blackWidowAddress, Set.of("deception", "martial_arts"));
Person wandaMaximoff = Person.of("Elizabeth", "Olsen", 32, wandaMaximoffSays, new Point(40.6976701, -74.2598641), wandaMaximoffsAddress, Set.of("magic", "loyalty"));
Person gamora = Person.of("Zoe", "Saldana", 43, gamoraSays, new Point(-118.399968, 34.073087), gamorasAddress, Set.of("skills", "martial_arts"));
Person nickFury = Person.of("Samuel L.", "Jackson", 73, nickFurySays, new Point(-118.4345534, 34.082615), nickFuryAddress, Set.of("planning", "deception", "resources"));
repo.saveAll(List.of(thor, ironman, blackWidow, wandaMaximoff, gamora, nickFury));
};
}
我们现在数据库中有6个人;由于我们正在使用Spring中的开发工具,应用程序应该已经重新加载,并且数据库已经用新数据重新填充。在Redis Insight中按下键模式输入框以刷新视图。注意,我们使用了存储库的saveAll
来批量保存多个对象。
Web服务端点
在我们用更多有趣的查询充实仓库之前,让我们创建一个控制器,以便我们可以使用Swagger UI测试我们的查询:
package com.redis.om.skeleton.controllers;
import com.redis.om.skeleton.models.Person;
import com.redis.om.skeleton.models.repositories.PeopleRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/api/v1/people")
public class PeopleControllerV1 {
@Autowired
PeopleRepository repo;
@GetMapping("all")
Iterable<Person> all() {
return repo.findAll();
}
}
在这个控制器中,我们注入了一个仓库并使用了一个CRUD方法,findAll()
,来返回数据库中所有的Person
文档。
如果我们导航到 http://localhost:8080/swagger-ui/,你应该会看到 Swagger UI:
我们可以看到来自people-controller-v-1的/all
方法,展开后你应该看到:
如果你选择“试一试”然后“执行”,你应该会看到包含数据库中所有People文档的JSON数组结果:
我们还添加了通过使用 repo 的 findById 方法按 id 检索 Person 的功能:
@GetMapping("{id}")
Optional<Person> byId(@PathVariable String id) {
return repo.findById(id);
}
刷新Swagger UI,我们应该能看到新添加的端点。我们可以使用Redis Insight CLI上的SRANDMEMBER
命令来获取一个id,如下所示:
SRANDMEMBER com.redis.om.skeleton.models.Person
将生成的ID插入Swagger UI中,我们可以获取相应的JSON文档:
自定义仓库查找器
既然我们已经测试了大量的CRUD功能,现在让我们在存储库中添加一些自定义查找器。我们将从在Person
的age
属性上进行数字范围的查找器开始:
public interface PeopleRepository extends RedisDocumentRepository<Person,String> {
// Find people by age range
Iterable<Person> findByAgeBetween(int minAge, int maxAge);
}
在运行时,存储库方法 findByAgeBetween
由框架实现,因此您只需声明它,Redis OM Spring 将处理查询和结果的映射。要使用的属性在关键词 "findBy" 之后选择。"Between" 关键字是告诉查询构建器使用什么操作的谓词。
要在Swagger UI上测试它,让我们向控制器添加一个相应的方法:
@GetMapping("age_between")
Iterable<Person> byAgeBetween( //
@RequestParam("min") int min, //
@RequestParam("max") int max) {
return repo.findByAgeBetween(min, max);
}
刷新用户界面,我们可以看到新的端点。让我们用一些数据来尝试一下:
使用值30
作为min
和37
作为max
调用端点,我们得到两个匹配项;
“斯嘉丽·约翰逊”和“伊丽莎白·奥尔森”是仅有的两个年龄在30到37岁之间的人。
如果我们查看Redis Insight Profiler,我们可以看到生成的查询,这是一个对索引数字字段age
的范围查询:
我们还可以创建具有多个属性的查询方法。例如,如果我们想通过名字和姓氏进行查询,我们会声明一个像这样的仓库方法:
// Find people by their first and last name
Iterable<Person> findByFirstNameAndLastName(String firstName, String lastName);
让我们添加一个相应的控制器方法:
@GetMapping("name")
Iterable<Person> byFirstNameAndLastName(@RequestParam("first") String firstName, //
@RequestParam("last") String lastName) {
return repo.findByFirstNameAndLastName(firstName, lastName);
}
再次,我们可以刷新swagger UI并测试新创建的端点:
使用名字Robert
和姓氏Downey
执行请求,我们得到:
以及在 Redis Insight 上的查询结果:
现在让我们尝试一个地理空间查询。homeLoc
属性是一个地理点,通过在方法声明中使用“Near”谓词,我们可以得到一个查找器,该查找器接受一个点和该点周围的半径进行搜索:
// Draws a circular geofilter around a spot and returns all people in that
// radius
Iterable<Person> findByHomeLocNear(Point point, Distance distance);
And the corresponding controller method:
@GetMapping("homeloc")
Iterable<Person> byHomeLoc(//
@RequestParam("lat") double lat, //
@RequestParam("lon") double lon, //
@RequestParam("d") double distance) {
return repo.findByHomeLocNear(new Point(lon, lat), new Distance(distance, Metrics.MILES));
}
刷新Swagger US后,我们现在应该能看到byHomeLoc
端点。让我们看看哪些复仇者住在澳大利亚南威尔士Suffolk Park Pub的10英里范围内... 嗯。
执行请求后,我们得到了克里斯·海姆斯沃斯的记录:
在 Redis Insight 中,我们可以看到后台查询:
让我们尝试对personalStatement
属性进行全文搜索查询。为此,我们在查询方法前加上search
这个词,如下所示:
// Performs full-text search on a person’s personal Statement
Iterable<Person> searchByPersonalStatement(String text);
以及相应的控制器方法:
@GetMapping("statement")
Iterable<Person> byPersonalStatement(@RequestParam("q") String q) {
return repo.searchByPersonalStatement(q);
}
再次,我们可以在Swagger UI上尝试使用文本“mother”:
这导致了一个单一的结果,即小罗伯特·唐尼的记录:
请注意,如果需要,您可以传递带有通配符的查询字符串,如“moth*”
嵌套对象搜索
你已经注意到Person
中的address
对象被映射为一个JSON对象。如果我们想通过地址字段进行搜索,我们使用下划线来访问嵌套字段。例如,如果我们想通过城市查找一个人,方法签名将是:
// Performing a tag search on city
Iterable<Person> findByAddress_City(String city);
让我们添加匹配的控制器方法,以便我们可以测试它:
@GetMapping("city")
Iterable<Person> byCity(@RequestParam("city") String city) {
return repo.findByAddress_City(city);
}
让我们测试一下byCity端点:
正如预期的那样,我们应该得到两个匹配项;斯嘉丽·约翰逊和伊丽莎白·奥尔森,两人都在纽约有地址:
技能集被索引为标签搜索。要找到具有提供列表中任何技能的人,我们可以添加一个存储库方法,如下所示:
// Search Persons that have one of multiple skills (OR condition)
Iterable<Person> findBySkills(Set<String> skills);
以及相应的控制器方法:
@GetMapping("skills")
Iterable<Person> byAnySkills(@RequestParam("skills") Set<String> skills) {
return repo.findBySkills(skills);
}
让我们用值“deception”测试端点:
搜索返回了斯嘉丽·约翰逊和塞缪尔·杰克逊的记录:
我们可以通过标签搜索查看支持查询:
使用实体流进行流畅搜索
Redis OM Spring Entity Streams 提供了一个 Java 8 Streams 接口,用于使用 Redis Stack 查询 Redis JSON 文档。Entity Streams 允许您以类似于 SQL 语句的类型安全声明方式处理数据。Streams 可以用于将查询表达为一系列操作链。
Redis OM Spring 中的实体流提供了与 Java 8 流相同的语义。流可以由 Redis 映射实体(@Document
)或实体的一个或多个属性组成。实体流逐步构建查询,直到调用终端操作(如 collect
)。每当对流应用终端操作时,流不能再接受其管道中的其他操作,这意味着流已启动。
让我们从一个简单的例子开始,一个Spring @Service
,它包含EntityStream
来查询映射类Person
的实例:
package com.redis.om.skeleton.services;
import java.util.stream.Collectors;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.redis.om.skeleton.models.Person;
import com.redis.om.skeleton.models.Person$;
import com.redis.om.spring.search.stream.EntityStream;
@Service
public class PeopleService {
@Autowired
EntityStream entityStream;
// Find all people
public Iterable<Person> findAllPeople(int minAge, int maxAge) {
return entityStream //
.of(Person.class) //
.collect(Collectors.toList());
}
}
EntityStream
通过 @Autowired
注入到 PeopleService
中。然后我们可以通过使用 entityStream.of(Person.class)
获取 Person
对象的流。这个流相当于在关系型数据库上执行 SELECT * FROM Person
。调用 collect
将执行底层查询并返回 Redis 中所有 Person
对象的集合。
实体元模型
您将获得一个生成的元模型,用于生成更复杂的查询,该类的名称与您的模型相同,但以美元符号结尾。在下面的示例中,我们的实体模型是Person
;因此,我们得到一个名为Person$
的元模型。通过元模型,您可以访问底层搜索引擎字段操作。例如,我们有一个age
属性,它是一个整数。因此,我们的元模型有一个AGE
属性,其中包含我们可以与流的filter
方法一起使用的数字操作,例如between
。
// Find people by age range
public Iterable<Person> findByAgeBetween(int minAge, int maxAge) {
return entityStream //
.of(Person.class) //
.filter(Person$.AGE.between(minAge, maxAge)) //
.sorted(Person$.AGE, SortOrder.ASC) //
.collect(Collectors.toList());
}
在这个例子中,我们还使用了Streams的sorted
方法来声明我们的流将按照Person$.AGE
以ASC
升序排列。
要对属性表达式进行“AND”操作,我们可以链接多个.filter
语句。例如,要通过名字和姓氏重新创建查找器,我们可以以下列方式使用实体流:
// Find people by their first and last name
public Iterable<Person> findByFirstNameAndLastName(String firstName, String lastName) {
return entityStream //
.of(Person.class) //
.filter(Person$.FIRST_NAME.eq(firstName)) //
.filter(Person$.LAST_NAME.eq(lastName)) //
.collect(Collectors.toList());
}
在本文中,我们探讨了Redis OM Spring如何提供一些API,以便从Spring Boot应用程序中利用Redis Stack的文档数据库和搜索功能。在未来的文章中,我们将通过Redis OM Spring探索其他Redis Stack功能。