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) 框架构建的强大的存储库和自定义对象映射抽象。

您需要什么

使用 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
  • 依赖项:webdevtoolslombok

web(Spring Web)使我们能够使用 Spring MVC 构建 RESTful 应用程序。使用 devtools,我们可以获得快速应用程序重启和重新加载。而 lombok 则减少了样板代码,例如 getter 和 setter。

Spring Initializer

点击 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> 部分添加 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 {
  // ...
}

创建领域

我们的领域将非常简单:拥有 Addresses 的 Persons。让我们从 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:表示 String 集合的 Set<String>,表示此人拥有的技能。

@Document

Person 类(com.redis.om.skeleton.models.Person)使用 @Documentcom.redis.om.spring.annotations.Document)进行注解,这将标记该对象为 Redis 实体,并由适当类型的存储库作为 JSON 文档进行持久化。

@Indexed 和 @Searchable

idfirstNamelastNameagehomeLocaddressskills 字段都使用 @Indexedcom.redis.om.spring.annotations.Indexed)进行注解。在使用 @Document 进行注解的实体上,Redis OM Spring 将扫描这些字段,并将相应的搜索索引字段添加到该实体的架构中。例如,对于 Person 类,将在应用程序启动时创建名为 com.redis.om.skeleton.models.PersonIdx 的索引。在索引架构中,将为每个使用 @Indexed 进行注解的属性添加一个搜索字段。RediSearch 是驱动搜索的底层搜索引擎,它支持文本(全文搜索)、标签(精确匹配搜索)、数值(范围查询)、地理(地理范围查询)和向量(向量查询)字段。对于使用 @Indexed 进行注解的字段,将根据属性的数据类型选择相应的搜索字段(标签、数值或地理)。

标记为 @Searchablecom.redis.om.spring.annotations.Searchable)的字段(例如 Person 中的 personalStatement)将反映为搜索索引架构中的全文搜索字段。

嵌套字段搜索功能

嵌入式类 Addresscom.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 Data 存储库

现在,模型已经到位,我们需要创建模型和 Redis 之间的桥梁,即 Spring Data 存储库。与其他 Spring Data 存储库一样,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 和分页/排序功能所需的一切。RedisDocumentRepositorycom.redis.om.spring.repository.RedisDocumentRepository)扩展 PagingAndSortingRepositoryorg.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 表达式中,我们将首先调用存储库的 deleteAll 方法,这将确保每次应用程序重新加载时都拥有干净的数据。

我们使用 Lombok 生成的构建器方法创建一个 Person 对象,然后使用存储库的 save 方法进行保存。

使用 Redis Insight 跟踪信息

让我们启动 Redis Insight 并连接到端口 6379 上的 localhost。使用干净的 Redis Stack 安装,我们可以使用内置的 CLI 检查系统中的键。

Redis Insight

对于少量数据,可以使用 keys 命令(对于大量数据,请使用 scan)。

keys *

如果您想关注针对服务器发出的命令,Redis Insight 提供了一个探查器。如果您点击屏幕底部的“profile”按钮,应该会显示探查器窗口,您可以在其中点击“Start Profiler”箭头来启动探查器。

让我们使用 Maven 命令启动 Spring Boot 应用程序。

./mvnw spring-boot:run

在 Redis Insight 中,如果应用程序成功启动,您应该会在探查器中看到大量命令飞过。

Redis Insight

现在,我们可以通过简单地刷新“Keys”视图来检查新加载的数据。

Redis Insight

您现在应该看到两个键:一个用于“Thor”的 JSON 文档,另一个用于 Spring Data Redis(以及 Redis OM Spring)用来维护实体主密钥列表的 Redis 集合。

您可以选择键列表中的任何键,并在详细信息面板上显示其内容。对于 JSON 文档,我们将获得一个不错的树状视图。

Redis Insight

应用程序启动时执行了多个 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"

清理 Person 存储库

下一组命令是由调用 repo.deleteAll() 生成的。

"DEL" "com.redis.om.skeleton.models.Person"
"KEYS" "com.redis.om.skeleton.models.Person:*"

第一个调用清除 Spring Data Redis(以及 Redis OM Spring)维护的主密钥集合,第二个调用收集所有要删除的密钥,但在第一次加载数据时,没有要删除的密钥。

保存 Person 实体

下一个存储库调用是 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? Im 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, Im 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 个 People;由于我们使用的是 Spring 中的 devtools,因此应用程序应该已经重新加载,数据库也重新填充了新数据。按 Enter 键进入 Redis Insight 中的键模式输入框以刷新视图。请注意,我们使用了存储库的 saveAll 方法以批量保存多个对象。

Redis Insight

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。

SwaggerUI

我们可以看到来自 people-controller-v-1/all 方法,展开后,您应该会看到。

SwaggerUI

如果您选择“Try it out”,然后选择“Execute”,您应该会看到包含数据库中所有 People 文档的 JSON 数组结果。

SwaggerUI

让我们还添加通过其 ID 检索 Person 的功能,方法是使用存储库的 findById 方法。

@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

在 Swagger UI 中插入结果 ID 后,我们可以获取相应的 JSON 文档。

SwaggerUI

自定义存储库查找器

现在,我们已经测试了相当多的 CRUD 功能,让我们为存储库添加一些自定义查找器。我们将从 Personage 属性上的数值范围查找器开始。

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);
}

刷新 UI 后,我们可以看到新的端点。让我们使用一些数据来尝试一下。

SwaggerUI

使用 min 的值为 30 以及 max 的值为 37 调用该端点,我们将获得两个匹配项:“Scarlett Johansson”和“Elizabeth Olsen”是唯一两个年龄介于 30 到 37 之间的人。

SwaggerUI

如果我们查看 Redis Insight 探查器,我们可以看到结果查询,它是在索引数值字段 age 上进行的范围查询。

Redis Insight

我们还可以创建具有多个属性的查询方法。例如,如果我们想根据姓和名进行查询,我们可以声明一个类似于以下的存储库方法。

// 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 并测试新创建的端点

SwaggerUI

使用名字为 Robert,姓氏为 Downey 执行请求,我们得到

SwaggerUI

以及 Redis Insight 上的查询结果

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 UI,我们现在应该看到 byHomeLoc 端点。让我们看看哪些复仇者住在澳大利亚南威尔士的萨福克公园酒吧 10 英里范围内... 嗯。

SwaggerUI

执行请求,我们得到了 Chris Hemsworth 的记录

SwaggerUI

在 Redis Insight 中,我们可以看到支持查询

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);
}

再次,我们可以使用文本 "mother" 在 Swagger UI 上尝试它

SwaggerUI

这将导致一个命中,Robert Downey Jr. 的记录。

SwaggerUI

请注意,如果需要,您可以传递类似 "moth*" 的带有通配符的查询字符串

SwaggerUI

嵌套对象搜索

您已经注意到 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 端点

SwaggerUI

正如预期的那样,我们应该得到两个命中:Scarlett Johansson 和 Elizabeth Olsen,她们都在纽约有地址

SwaggerUI

技能集被索引为标签搜索。要查找具有提供列表中的任何技能的人,我们可以添加一个类似于以下的存储库方法

// 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" 测试端点

SwaggerUI

搜索返回了 Scarlett Johansson 和 Samuel L. Jackson 的记录

SwaggerUI

我们可以看到使用标签搜索的支持查询

Redis Insight

使用实体流进行流畅搜索

Redis OM Spring 实体流提供了一个 Java 8 流接口,用于使用 Redis Stack 查询 Redis JSON 文档。实体流允许您以类似于 SQL 语句的类型安全声明方式处理数据。流可用于将查询表达为操作链。

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());
}

在这个示例中,我们还使用流的 sorted 方法来声明我们的流将按 Person$.AGEASC 升序排序。

要 "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 功能

RATE THIS PAGE
Back to top ↑