# 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
  • 依赖关系**webdevtoolslombok**。

( webSpring 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.0Redis 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 {
  // ...
}

# 创建域

我们的域名将相当简单;Persons 有Addresses。让我们从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代表他们的年龄。
  • personalStatementString代表包含事实或其他传记信息的个人文本陈述。
  • homeLoc: Aorg.springframework.data.geo.Point代表地理坐标。
  • addressAddress表示个人邮政地址的类型实体。
  • 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,addressskills都用@Indexed( com.redis.om.spring.annotations.Indexed) 注释。在使用 Redis OM 注释的实体上,@DocumentSpring 将扫描字段并将适当的搜索索引字段添加到实体的架构中。例如,对于类,将在应用程序启动时创建Person一个名为的索引。com.redis.om.skeleton.models.PersonIdx在索引模式中,将为每个带@Indexed注释的属性添加一个搜索字段。RediSearch 是支持搜索的底层搜索引擎,支持 Text(全文搜索)、Tag(精确匹配搜索)、Numeric(范围查询)、Geo(地理范围查询)和 Vector(向量相似性查询)字段。为了@Indexed字段,则根据属性的数据类型选择适当的搜索字段(标记、数字或地理)。

标记为@Searchable( com.redis.om.spring.annotations.Searchable) 的字段(例如personalStatementin )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 检查系统中的密钥:

RedisInsight

对于少量数据,您可以使用keys命令(对于任何大量数据,使用scan):

keys *

如果您想密切关注针对服务器发出的命令,RedisInsight 提供了一个分析器。如果您单击屏幕底部的“配置文件”按钮,它应该会显示分析器窗口,您可以通过单击“启动分析器”箭头来启动分析器。

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

./mvnw spring-boot:run

在 RedisInsight 上,如果应用程序正确启动,您应该会在分析器上看到一连串命令:

RedisInsight

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

RedisInsight

您现在应该看到两个键;一个用于“Thor”的 JSON 文档,另一个用于 Spring Data Redis(和 Redis OM Spring)用于维护实体主键列表的 Redis Set。

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

RedisInsight

在应用程序启动时执行了几个 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批量保存多个对象。

RedisInsight

# 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:

招摇UI

我们可以/all从 people-controller-v-1 中看到该方法,扩展您应该看到的内容:

招摇UI

如果您选择“试用”,然后选择“执行”,您应该会看到生成的 JSON 数组,其中包含数据库中的所有 People 文档:

招摇UI

我们还可以使用 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 文档:

招摇UI

# 自定义存储库查找器

现在我们已经测试了相当多的 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,我们可以看到新的端点。让我们用一些数据来试试:

招摇UI

30使用formin37for的值调用端点,max我们得到两次命中;“斯嘉丽·约翰逊”和“伊丽莎白·奥尔森”是仅有的两个年龄在 30 到 37 岁之间的人。

招摇UI

如果我们查看 RedisInsight Profiler,我们可以看到结果查询,这是对索引数字字段的范围查询age

RedisInsight

我们还可以创建具有多个属性的查询方法。例如,如果我们想通过名字和姓氏进行查询,我们将声明一个存储库方法,如:

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

招摇UI

Robert使用 first name和 last name执行请求Downey,我们得到:

招摇UI

以及对 RedisInsight 的查询结果:

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 英里的范围内……嗯。

招摇UI

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

招摇UI

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

RedisInsight

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”进行尝试:

招摇UI

这导致单次命中,小罗伯特唐尼的记录:

招摇UI

请注意,如果需要,您可以使用通配符传递类似“moth*”的查询字符串

招摇UI

# 嵌套对象搜索

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

招摇UI

正如预期的那样,我们应该得到两个命中;斯嘉丽约翰逊和伊丽莎白奥尔森,都在尼约克的地址:

招摇UI

技能集被索引为标签搜索。要在提供的列表中找到具有任何技能的人员,我们可以添加一个存储库方法,例如:

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

招摇UI

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

招摇UI

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

RedisInsight

# 使用实体流进行流体搜索

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注入到PeopleServiceusing 中@Autowired。然后我们可以Person使用entityStream.of(Person.class). 流表示SELECT * FROM Person关系数据库上的 a 等价物。然后调用collect将执行底层查询并返回PersonRedis 中所有对象的集合。

# 实体元模型

为您提供了一个生成的元模型以生成更精细的查询,一个与您的模型同名但以美元符号结尾的类。在下面的示例中,我们的实体模型是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 功能

Last Updated: 4/18/2023, 8:45:33 AM