dot Redis 8 发布了——而且是开源的

了解更多

Redis OM Spring 的新功能

Redis OM 项目进展顺利。经过六个月的努力,该团队为 Redis Stack 创建了一套可用且稳定的 API。我们希望 Redis OM Spring 中的这 10 个新功能能够简化和优化由 Redis 驱动的 Spring 应用程序。

Redis Stack2022 年 3 月 的发布标志着 Redis 发行版的一个分水岭时刻。 Redis Stack 是第一个公开可用的 Redis 发行版,它集成了几个流行的、经过实战考验的 Redis 模块。 Redis Stack 包含 JSON 文档数据库、一个成熟的搜索引擎、一个时间序列数据库、一个图形数据库和概率数据结构

Redis OM

Redis 对象映射 (OM) 系列库最初是为了为 Redis 支持的几个模块提供高级 API。随着 Redis Stack 的发布,我们的工作重点已转移到 Stack 提供的模块和功能上。第一波功能集中在 JSON 文档数据库 (RedisJSON) 及其与搜索引擎 (RediSearch) 的集成上。

Redis OM Spring 中的最新功能

您可能知道,Redis OM Spring 是一个客户端库,可帮助您在 Spring 应用程序中对您的域进行建模并将数据持久化到 Redis。最新版本的 ROMS(版本 0.6.0)反映了我们对文档和搜索的关注(尽管我们以预览形式偷偷加入了一些其他功能)。

那么自我们在 2021 年底宣布该项目并发布了Redis OM Spring 教程以来,有哪些新功能?以下是重点。

嵌套对象映射

Redis OM Spring 作为 Spring Data Redis 的扩展,主要用于处理“持久实体”:即经过注解的 Java 对象,以便它们可以被序列化并存储在数据存储中。 Redis OM Spring 是首批提供 Java 实体多模型持久化的 Spring Data 项目之一,例如 Redis 哈希和 Redis JSON 文档。在 GA 版本中,您现在可以使用 @Indexed 标记嵌套对象,并基于嵌套对象属性进行搜索

@Document
public class Person {
  @Id private String id;
  //...
  @Indexed @NonNull private Address address;
}

通过这样的映射,您可以例如,在存储库中声明一个方法,以根据 PersonAddress 中的城市搜索 Person

// Performing a tag search on city
Iterable<Person> findByAddress_City(String city);

或者,如果您想按 CityState 搜索,您可以声明一个类似的方法

// Performing a full-search on street
Iterable<Person> findByAddress_CityAndAddress_State(String city, String state);

“Catch-All”搜索方法

如果您在实体中有任何全文搜索方法,您可以通过简单地声明一个名为“search”的方法来一次搜索所有字段

 // Performing a text search on all text fields:
 Iterable<Person> search(String text);

对文本索引的更精细控制

IndexedSearchable 注解提供对底层搜索索引生成的完全控制

 @Searchable(sortable = true, nostem = true, weight = 20.0)
 private String buildingType;

将微调参数传递到底层 RediSearch 引擎的能力为更强大的查询打开了可能性。在此示例中,buildingType 字段的定义包括 sortablenostemweight 参数。sortable 参数通过以额外的内存为代价来加速按特定字段进行排序; nostem 参数可以通过禁用字段的词干提取来加速索引;最后,weight 参数是一个乘数,它会影响计算结果准确性时字段的重要性。

自定义 GSON 转换器集成

添加了几个实用程序类来控制对象的序列化方式。例如,假设您想在 JSON 文档中将 StringsSet 存储为逗号分隔的字符串。您将使用 @JsonAdapter GSON 注解和 Redis OM SetToStringAdapter

 @Indexed(separator = ",")
 @JsonAdapter(SetToStringAdapter.class)
 private Set<String> workType;

排序和分页查询

CrudRepository 之上,PagingAndSortingRepository 抽象添加了额外的方法,以简化对实体的分页访问。RedisDocumentRepository 实现了此接口,并允许将 Pageable 对象传递给任何声明的方法以返回结果的“页面”。 findAllByTitleStartingWith 方法检索标题以给定 prefix 开头的 MyDoc 实体的所有实例

Page<MyDoc> findAllByTitleStartingWith(String prefix, Pageable pageable);

这样的查询可能会返回数百万个结果。在一个典型的 Web 应用程序中,我们可能更喜欢最多显示几百个结果的页面。现在,使用页面请求,我们可以只请求返回包含 100 条记录的第一页结果

Pageable pageRequest = PageRequest.of(0, 100);
Page<MyDoc> result = repository.findAllByTitleStartingWith("hel", pageRequest);

分页检索实体 ID

应用程序经常需要获取一种或另一种 ID 列表:用户 ID、客户编号等等。 Redis OM Spring RedisDocumentRepository 提供了一个内置的 getIds 方法,该方法采用页面对象来有效地检索实体的 ID

Pageable pageRequest = PageRequest.of(0, 1);
Page<String> ids = repository.getIds(pageRequest);
ids = repository.getIds(pageRequest.next());

更新单个 JSON 字段而无需检索整个文档

在实际应用程序中,JSON 文档可能会变得非常大。检索整个文档可能变得昂贵且耗时。

想象一下,您已经使用 RedisDocumentRepository 保存了一个对象。您现在可以使用 updateField 方法和实体元模型类有效地更新单个字段

Company redisInc = repository.save( //
   Company.of(
     "RedisInc", 
     2011, 
     new Point(-122.066540, 37.377690), 
     "[email protected]"
   )
 );
 repository.updateField(redisInc, Company$.NAME, "Redis");

审计字段

Redis OM Spring 提供完善的支持,以透明地跟踪实体何时发生更改。您必须为实体类配备审计元数据才能从该功能中受益。您可以使用注解 @CreatedAt 定义它们;以存储实体创建的时间和 @LastModifiedDate;以存储实体上次更改的时间

 @CreatedDate
 private Date createdDate;
 @LastModifiedDate
 private Date lastModifiedDate;

检索所有不同的集合值

如果您的实体中有一个索引集合,例如

 @Indexed
 private Set<String> colors = new HashSet<String>();

您可以在存储库中声明一个类似的方法

Iterable<String> getAllColors();

它可以有效地返回所有文档中该集合中的所有不同值。

自动完成

RediSearch 提供了一个建议/自动完成 API。此 API 通过 @AutoComplete 和 @AutoCompletePayload 注解在 Redis OM Spring 中公开。

例如,假设您有一个表示 Airports 的实体,其中包含机场名称、代码和状态

@Document
 public class Airport {
   @Id 
   private String id;

   @AutoComplete
   private String name;

   @AutoCompletePayload("name")
   private String code;

   @AutoCompletePayload("name")
   private String state;
}

您可以声明以 "autoComplete*” 开头的方法,如下面针对“name”属性所示

public interface AirportsRepository extends RedisDocumentRepository<Airport, String> {
 List<Suggestion> autoCompleteName(String query);
 List<Suggestion> autoCompleteName(String query, AutoCompleteOptions options);
}

并获得给定查询字符串的建议/自动完成列表:

@Test
 public void testGetAutocompleteSuggestions() {
   List<Suggestion> suggestions = repository.autoCompleteName("col");
   List<String> suggestionsString = suggestions.stream().map(Suggestion::getString).collect(Collectors.toList());
   assertThat(suggestionsString).containsAll(List.of("Columbia", "Columbus", "Colorado Springs"));
}

此外,如果您传递一个“AutoCompleteOptions”,您可以控制搜索的执行方式(模糊与否),并且还可以在有效负载中包含任何使用 @AutoCompletePayload 注解标记的字段

@Test
 public void testGetAutocompleteSuggestionsWithPayload() {
   String columbusPayload = "{\"code\":\"CMH\",\"state\":\"OH\"}";
   String columbiaPayload = "{\"code\":\"CAE\",\"state\":\"SC\"}";
   String coloradoSpringsPayload = "{\"code\":\"COS\",\"state\":\"CO\"}";

   List<Suggestion> suggestions = repository.autoCompleteName("col", AutoCompleteOptions.get().withPayload());

   List<String> suggestionsString = suggestions.stream().map(Suggestion::getString).collect(Collectors.toList());

   List<Object> payloads = suggestions.stream().map(Suggestion::getPayload).collect(Collectors.toList());

   assertThat(suggestionsString) //
     .containsAll(List.of("Columbia", "Columbus", "Colorado Springs"));

   assertThat(payloads) //
     .containsAll( //
       List.of(columbusPayload,columbiaPayload,coloradoSpringsPayload)
   );
 }

布隆过滤器

Redis Stack 的一个强大功能是 RedisBloom 模块,该模块提供了一系列概率数据结构。布隆过滤器是一种概率数据结构,它提供了一种有效的方法来验证条目肯定不在集合中。当您在难以访问的资源上搜索项目时,此功能非常理想,并且如果您有极其庞大的数据集合,则尤其有用。

假设您有一个 SaS 应用程序的用户列表,并且您达到了 FAANG 级别的用户,比如说超过 1 亿。(恭喜!)在早期,检查电子邮件地址是否已在系统中使用很容易,因为您只需要查询数千而不是数亿的数据集。但这种情况已经改变。

RedisBloom 功能使此过程更容易。使用 @Bloom 注解为电子邮件属性创建和维护布隆过滤器

 @Bloom(name = "bf_person_email", capacity = 100000, errorRate = 0.001)
 String email;

过滤器就位后,将“存在”查询添加到存储库

 boolean existsByEmail(String email);

查询以线性时间运行,确保为您的客户提供一致且响应迅速的 UI。

我们的方向

这只是一个旅程的开始,我们致力于使 Redis Stack 对多种语言和平台上的开发人员有用且愉快。此版本将在 JSON/搜索方面保持相当稳定;我们预计当前 API 会发生细微更改。我们已经开始研究其余的模块和 API,并且对接下来会发生的事情感到兴奋。