# Redis OM Spring
了解如何使用 Redis Stack 和 Spring 进行构建
Redis Stack 提供了一种无缝且直接的方式来使用来自 Redis 的不同数据模型和功能,包括文档存储、图形数据库、时间序列数据数据库、概率数据结构和全文搜索引擎。
Redis Stack 由多个客户端库支持,包括 Node.js、Java 和 Python,以便开发人员可以使用他们喜欢的语言。我们将使用 Redis Stack 支持库之一;Redis OM 春天。Redis OM Spring 提供了一个强大的存储库和基于强大的 Spring Data Redis (SDR) 框架构建的自定义对象映射抽象。
# 你需要什么:
- Redis 堆栈:请参阅https://redis.io/docs/stack/get-started/install/
- RedisInsight:见
https://redis.io/docs/stack/insight
- 你最喜欢的浏览器
- Java 11 或更高版本
# 带有 Spring Initializer 的 Spring Boot 脚手架
我们将首先使用 Spring Initializer
创建一个骨架应用程序,打开浏览器到 https://start.spring.io
并按如下方式配置我们的骨架应用程序:
- 我们将使用基于 Maven 的构建(选中 Maven 复选框)
- **
2.6.4
**以及Redis OM Spring 支持的当前版本的 Spring Boot版本 - 团体:
com.redis.om
- 人工制品:
skeleton
- 姓名:
skeleton
- 描述:Redis OM Spring 的 Skeleton App
- 包裹名字:
com.redis.om.skeleton
- 包装:罐装
- 爪哇:
11
- 依赖关系**
web
:devtools
和lombok
**。
( web
Spring Web) 使我们能够使用 Spring MVC 构建 RESTful 应用程序。随着devtools
我们获得快速的应用程序重启和重新加载。并lombok
减少像 getter 和 setter 这样的样板代码。
单击Generate
并下载 ZIP 文件,解压缩并将 Maven 项目加载到您选择的 IDE 中。
# 添加 Redis OM Spring
打开 Maven pom.xml
,在<dependencies>
和<build>
部分之间,我们将添加快照存储库,以便我们可以获取 redis-om-spring 的最新 SNAPSHOT 版本:
<repositories>
<repository>
<id>snapshots-repo</id>
<url>https://s01.oss.sonatype.org/content/repositories/snapshots/</url>
</repository>
</repositories>
然后在<dependencies>
部分添加0.3.0
Redis OM Spring 的版本:
<dependency>
<groupId>com.redis.om</groupId>
<artifactId>redis-om-spring</artifactId>
<version>0.3.0-SNAPSHOT</version>
</dependency>
# 添加招摇
我们将使用 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 {
// ...
}
# 创建域
我们的域名将相当简单;Person
s 有Address
es。让我们从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
:String
使用ULID自动生成的
firstName
: AString
代表他们的名字或名字。lastName
: AString
代表他们的姓氏或姓氏。age
:Integer
代表他们的年龄。personalStatement
:String
代表包含事实或其他传记信息的个人文本陈述。homeLoc
: Aorg.springframework.data.geo.Point
代表地理坐标。address
:Address
表示个人邮政地址的类型实体。skills
:一个Set<String>
代表字符串的集合,代表人拥有的技能。
# @文档
Person`类 ( )使用( `com.redis.om.skeleton.models.Person`) 进行注释,它将对象标记为 Redis 实体,以由适当类型的存储库作为 JSON 文档持久化。`@Document``com.redis.om.spring.annotations.Document
# @Indexed 和 @Searchable
字段id
, firstName
, lastName
, age
, homeLoc
,address
和skills
都用@Indexed
( com.redis.om.spring.annotations.Indexed
) 注释。在使用 Redis OM 注释的实体上,@Document
Spring 将扫描字段并将适当的搜索索引字段添加到实体的架构中。例如,对于类,将在应用程序启动时创建Person
一个名为的索引。com.redis.om.skeleton.models.PersonIdx
在索引模式中,将为每个带@Indexed
注释的属性添加一个搜索字段。RediSearch 是支持搜索的底层搜索引擎,支持 Text(全文搜索)、Tag(精确匹配搜索)、Numeric(范围查询)、Geo(地理范围查询)和 Vector(向量相似性查询)字段。为了@Indexed
字段,则根据属性的数据类型选择适当的搜索字段(标记、数字或地理)。
标记为@Searchable
( com.redis.om.spring.annotations.Searchable
) 的字段(例如personalStatement
in )Person
反映为搜索索引架构中的全文搜索字段。
# 嵌套字段搜索功能
嵌入类Address
( ) 有几个用 和com.redis.om.skeleton.models.Address
注释的属性,它们将在 Redis 中生成搜索索引字段。这些字段的扫描是由类中属性上的注释触发的:@Indexed``@Searchable``@Indexed``address``Person
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 和分页/排序功能所需的全部内容。( ) extends RedisDocumentRepository
( )扩展 CrudRepository 以提供额外的方法来使用分页和排序检索实体。com.redis.om.spring.repository.RedisDocumentRepository``PagingAndSortingRepository``org.springframework.data.repository.PagingAndSortingRepository
# @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
方法,这将确保我们在每次应用程序重新加载时都有干净的数据。
我们Person
使用 Lombok 生成的构建器方法创建一个对象,然后使用 repo 的save
方法保存它。
# 使用 Redis Insight 密切关注
让我们启动 RedisInsight 并连接到 localhost 的 6379 端口。通过干净的 Redis Stack 安装,我们可以使用内置 CLI 检查系统中的密钥:
对于少量数据,您可以使用keys
命令(对于任何大量数据,使用scan
):
keys *
如果您想密切关注针对服务器发出的命令,RedisInsight 提供了一个分析器。如果您单击屏幕底部的“配置文件”按钮,它应该会显示分析器窗口,您可以通过单击“启动分析器”箭头来启动分析器。
让我们使用 Maven 命令启动我们的 Spring Boot 应用程序:
./mvnw spring-boot:run
在 RedisInsight 上,如果应用程序正确启动,您应该会在分析器上看到一连串命令:
现在我们可以通过简单地刷新“Keys”视图来检查新加载的数据:
您现在应该看到两个键;一个用于“Thor”的 JSON 文档,另一个用于 Spring Data Redis(和 Redis OM Spring)用于维护实体主键列表的 Redis Set。
您可以选择键列表中的任何键以在详细信息面板上显示其内容。对于 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 调用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 中使用 devtools,所以应用程序应该已经重新加载,并且数据库应该重新填充新数据。在 RedisInsight 中按 enter 键模式输入框刷新视图。请注意,我们使用存储库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:
我们可以/all
从 people-controller-v-1 中看到该方法,扩展您应该看到的内容:
如果您选择“试用”,然后选择“执行”,您应该会看到生成的 JSON 数组,其中包含数据库中的所有 People 文档:
我们还可以使用 repo 的 findById 方法添加通过 id 检索 Person 的功能:
@GetMapping("{id}")
Optional<Person> byId(@PathVariable String id) {
return repo.findById(id);
}
刷新 Swagger UI,我们应该会看到新添加的端点。我们可以使用 SRANDMEMBER
RedisInsight CLI 上的命令来获取一个 id,如下所示:
SRANDMEMBER com.redis.om.skeleton.models.Person
在 Swagger UI 中插入生成的 ID,我们可以得到对应的 JSON 文档:
# 自定义存储库查找器
现在我们已经测试了相当多的 CRUD 功能,让我们将一些自定义查找器添加到我们的存储库中。我们将从一个数值范围内的查找器开始,它的age
属性为Person
:
public interface PeopleRepository extends RedisDocumentRepository<Person,String> {
// Find people by age range
Iterable<Person> findByAgeBetween(int minAge, int maxAge);
}
在运行时,repository 方法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);
}
刷新 UI,我们可以看到新的端点。让我们用一些数据来试试:
30
使用formin
和37
for的值调用端点,max
我们得到两次命中;“斯嘉丽·约翰逊”和“伊丽莎白·奥尔森”是仅有的两个年龄在 30 到 37 岁之间的人。
如果我们查看 RedisInsight 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
使用 first name和 last name执行请求Downey
,我们得到:
以及对 RedisInsight 的查询结果:
现在让我们尝试地理空间查询。该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
端点。让我们看看哪个复仇者联盟住在距离澳大利亚南威尔士萨福克公园酒吧 10 英里的范围内……嗯。
执行请求,我们得到 Chris Hemsworth 的记录:
在 Redis Insight 中,我们可以看到支持的 RediSearch 查询:
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*”的查询字符串
# 嵌套对象搜索
您已经注意到其中的address
对象Person
被映射为 JSON 对象。如果我们想按地址字段搜索,我们使用下划线来访问嵌套字段。例如,如果我们想按城市查找 Person,方法签名将是:
// 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”测试端点:
搜索返回 Scarlett Johansson 和 Samuel L. Jackson 的记录:
我们可以使用标签搜索查看支持的 RediSearch 查询:
# 使用实体流进行流体搜索
Redis OM Spring Entity Streams 提供了一个 Java 8 Streams 接口来使用 RediSearch 查询 Redis JSON 文档。实体流允许您以类似于 SQL 语句的类型安全声明方式处理数据。流可用于将查询表示为操作链。
Redis OM Spring 中的实体流提供与 Java 8 流相同的语义。流可以由 Redis 映射实体 ( @Document
) 或实体的一个或多个属性组成。实体流逐步构建查询,直到调用终端操作(例如collect
)。每当终端操作应用于 Stream 时,Stream 不能接受对其管道的附加操作,这意味着 Stream 已启动。
让我们从一个简单的例子开始,一个@Service
包含EntityStream
查询映射类实例的Spring 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
注入到PeopleService
using 中@Autowired
。然后我们可以Person
使用entityStream.of(Person.class)
. 流表示SELECT * FROM Person
关系数据库上的 a 等价物。然后调用collect
将执行底层查询并返回Person
Redis 中所有对象的集合。
# 实体元模型
为您提供了一个生成的元模型以生成更精细的查询,一个与您的模型同名但以美元符号结尾的类。在下面的示例中,我们的实体模型是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());
}
在此示例中,我们还使用 Streamssorted
方法声明我们的流将按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 来利用 Redis Stack 的文档数据库的强大功能以及来自 Spring Boot 应用程序的搜索功能。我们将在以后的文章中通过 Redis OM Spring 探索其他 Redis Stack 功能