dot Redis 8 来了——而且是开源的

了解更多

Redis OM .NET 更新

嵌入式文档、索引数组以及 Redis OM .NET 的其他强大新增功能!

2021 年 11 月,我们发布了 Redis OM .NET(Redis 的对象映射库)的 v0.1.0 版本。 当然,与任何早期版本软件一样,还有很多工作要做。 从那时起,我们解决了该库的一些怪癖。 我们还在 .NET 中添加了几个令人兴奋的新功能,以使开发人员能够快速提高生产力。

在查看新内容之前,如果您需要 Redis OM 的一般概述,可以查看公告帖子或观看下面的视频。

https://www.youtube.com/embed/DFNKmbGKa5w

现在,让我们看看添加到 Redis OM .NET 的新功能!

嵌入式对象索引和查询

Redis OM .NET 现在允许您索引模型中嵌入式对象中的字段。 例如,假设您有一个带有嵌入式 Address 模型的 Customer 模型

[Document(StorageType = StorageType.Json)]
public class Customer
{
   [Indexed] public string FirstName { get; set; }
   [Indexed] public string LastName { get; set; }
   public string Email { get; set; }
   public Address Address {get; set;}
}

public class Address
{
    public string StreetName { get; set; }
    public string ZipCode { get; set; }
    public string City { get; set; }
    public string State { get; set; }
    public GeoLoc Location { get; set; }
    public int HouseNumber { get; set; }
}

以前,您将无法索引 Customer 中的嵌入式 Address。 现在,Redis OM .NET 提供了两种方法来执行此操作。

直接 JSON 路径

假设您想在其他模型之间共享嵌入式模型,并且不想过度索引嵌入式模型中的字段。

您可以将嵌入式模型单独保留,并使用相对于模型根目录的 JSON 路径从您的模型中对其进行索引。 要索引 Customer 的 Address 中的 City 和 State

[Document(StorageType = StorageType.Json)]
public class Customer
{
   [Indexed] public string FirstName { get; set; }
   [Indexed] public string LastName { get; set; }
   public string Email { get; set; }
   [Indexed(JsonPath = "$.City")]
   [Indexed(JsonPath = "$.State")]
   public Address Address {get; set;}
}

级联到嵌入式文档中

完成此操作的另一种方法是声明要在 Address 类中索引的内容,并使用 IndexedAttribute 的“CascadeDepth”属性级联到其中。 所以使用以下 Address 模型

[Document(StorageType = StorageType.Json)]
public class Address
{
    public string StreetName { get; set; }
    public string ZipCode { get; set; }
    [Indexed] public string City { get; set; }
    [Indexed] public string State { get; set; }
    [Indexed] public GeoLoc Location { get; set; }
    [Indexed] public int HouseNumber { get; set; }
}

您可以像这样从您的客户模型中索引

[Document(StorageType = StorageType.Json)]
public class Customer
{
   [Indexed] public string FirstName { get; set; }
   [Indexed] public string LastName { get; set; }
   public string Email { get; set; }
   [Indexed(CascadeDepth=1)]
   public Address Address {get; set;}
}

CascadeDepth 指的是您要索引到对象图中的深度。 设置 Cascade 深度将导致 Redis OM 遵循所有 IndexedAttributeSearchableAttribute 来查找要在模型对象图中索引的字段。

使用这两种模式之一,当您创建索引时,嵌入式文档将按您期望的方式进行索引,然后当您运行像这样的 LINQ 查询时

customers.Where(c=>c.Address.ZipCode == "10001");

… Redis OM 知道如何根据提供的模型正确构建查询。

可查询数组

自 v0.1.0 以来,我们添加到 Redis OM 的另一个很棒的功能是能够索引和查询 JSON 对象中的字符串数组。 使用 IndexedAttribute.装饰数组。

[Document(StorageType = StorageType.Json, Prefixes = new []{"Person"})]
public class Person
{

    [RedisIdField] [Indexed]public string? Id { get; set; }
    [Indexed] public string[] Skills { get; set; } = Array.Empty<string>();    
    [Indexed] public string? FirstName { get; set; }
    [Indexed] public string? LastName { get; set; }
}

完成此操作后,您可以使用数组的 Contains 方法查询这些数组

people.Where(x => x.Skills.Contains(skill));

索引枚举、ULID、GUID 和布尔值

Redis OM 之前仅支持索引数值、字符串和 GeoLocs。 从 0.2.0 版本开始,您还可以索引枚举、ULID、GUID 和布尔值。 为此,请使用 IndexedAttribute 来装饰模型中的这些字段。

聚合未标记为 Aggregatable 的字段

对于聚合用户来说,一个痛点是并非所有字段都可以标记为或必须标记为可聚合。 因此,这些字段以前没有资格在 Redis OM .NET 中的聚合中使用。 借助 Redis OM 中 AggregationSet 中的新 Load 和 LoadAll 方法,现在可以加载未标记为可聚合的字段。 

让我们采用我们之前使用的 Customer 模型并添加一个 age 字段来演示它的工作原理。

public class Customer
{
    [Indexed] public string FirstName { get; set; }
    [Indexed] public string LastName { get; set; }
    [Indexed] public int Age { get; set; }
    public string Email { get; set; }
    [Indexed(JsonPath = "$.City")]
    [Indexed(JsonPath = "$.State")]
    public Address Address {get; set;}
}

Age 已索引,但未标记为可聚合。 以前,我们无法在聚合管道中使用 Age。 如果我们运行类似这样的代码,我们会收到错误,因为 Age 没有加载到管道中

var aggregations = provider.AggregationSet<Customer>();
await aggregations.Apply(c=>c.RecordShell.Age + 5, "Age_plus_five")
    .ToArrayAsync();

现在,在 Redis OM .NET 中,您可以在 AggregationSet 上调用 Load,加载单个字段或多个字段(通过初始化一个匿名对象)

await aggregations.Load(x=>x.RecordShell.Age)
    .Apply(c=>c.RecordShell.Age + 5, "Age_plus_five")
    .ToArrayAsync();

仅加载 Age 或

await aggregations.LoadAll
    .Apply(c=>c.RecordShell.Age + 5, "Age_plus_five")
    .Apply(c=>string.Format("My Name is {0}",c.RecordShell.FirstName), "myNameIs")
    .ToArrayAsync();

要从模型中加载多个字段,请注意匿名类型。

新水合 API

聚合的另一个怪癖是它们不会返回格式良好的对象。 原因是聚合返回其管道的结果,这通常正是完成所请求的所有操作及其操作结果所需要的。 现在已经修复了。 

借助新的 Hydration API,您可以将这些位拼凑成更可用的格式,从而允许您从 RedisAggreagtionResult 中完全或部分水合您的模型。 

让我们回顾一下 Load 部分的示例。 如果我们想将整个客户模型以及 apply 函数的结果带回来怎么办? 我们可以通过调用 LoadAll 而不是 Load 来做到这一点。 然后,我们在枚举每个 RedisAggregationResult 时调用 Hydrate。 Hydrate 尝试将 AggregationResult 中的任何内容绑定到您的模型。 因此,可能存在结果中不存在的空字段。

var collection = new RedisAggregationSet<Person>(_connection);
await foreach (var result in aggregations.LoadAll
    .Apply(c=>c.RecordShell.Age + 5, "Age_plus_five")
    .Apply(c=>string.Format("My Name is {0}",c.RecordShell.FirstName), "myNameIs"))
{
    var person = result.Hydrate(); //now a fully hydrated Person object!
}

简化的对象更新

在 Redis OM 的先前迭代中,如果您想更新某个项目,则需要枚举它、更新它,然后保存整个 RedisCollection。 这可能很麻烦。 我们添加了一个新的 API,使您可以轻松地更新存储在 Redis 中的对象。

await collection.UpdateAsync(object);

更轻松的删除

同样,我们简化了删除插入到 Redis 中的对象的操作。 要删除对象,请执行

await customers.DeleteAsync(object);

自带 ConnectionMultiplexer

一个被请求的功能是传递 ConnectionMultiplexer 以初始化 RedisConnectionProvider。 此构造函数允许您将您已经注入到 Redis OM 的其他服务中的任何复用器一起使用。

可配置的块大小

Redis OM 现在支持可配置的块大小,用于在底层进行自动分页。 以前,固定的块大小可能会对巨大的查询结果产生性能影响。 较小且不可配置的块大小,加上较大的结果集,可能会导致 Redis OM 必须进行多次往返才能具体化整个结果集。 现在,您可以将所需的块大小传递给 Redis OM。

var customers = provider.RedisCollection<Customer>(10000);

请注意,这受 RediSearch 中的 MAXSEARCHRESULT 配置参数的限制。

总结

在过去的几个月中,我们做了很多工作,为 Redis OM .NET 添加了许多令人兴奋的新功能,以及任何软件都不可避免的错误修复。 您可以通过关注 GitHub 存储库 来关注新功能。 如果您有兴趣在库中看到某些功能,请在 GitHub 上打开一个问题。 

资源

  • 一个演示如何将 Redis OM .NET 集成到您的 ASP.NET Core 应用程序中的骨架应用程序可在 GitHub 上找到。
  • Redis Developer 站点上的一个教程提供了有关如何使用 Redis OM .NET 的一些详细信息。
  • Redis OM .NET 的参考文档可在存储库的 github-pages 站点上找到。
  • 如果您对该库有任何疑问,请在 GitHub 上打开一个问题,或在 Redis Discord 服务器上联系我们