用于过滤和排序嵌套 structure/collection 属性的 RavenDb 索引(扇出索引)

RavenDb index to filter&sort on properties of the nested structure/collection (fanout index)

我正在寻找一种方法来创建 static index 来为 filtering/sorting 查询嵌套结构(对象集合)中 属性 值的组合以及结构容器。由于以下原因,这似乎不是微不足道的:


给定以下持久模型:

public class Document
{
    public string Title { get; set; }

    public List<UserChange> RecentModifications { get; set; }
}

哪里

public class UserChange
{
    public string UserId { get; set; }
    public DateTime Timestamp { get; set; }
}

问题:如何通过所有字段的组合为Document到filter/sort建立索引:TitleUserIdTimestamp?

可能的用例:


P.S。我知道可以通过重构持久性模型来绕过索引限制 - 在 User 文档中存储最近修改的文档的结构,但它会强加一些其他限制,我想避免。

在索引定义中使用以下 RQL:

from doc in docs.Documents
from modification in doc.RecentModifications 
select new {
    modification.UserId,
    modification.Timestamp
}

注意:'UserId' & 'timestamp' 在基础索引条目中没有分开。

因此过滤组合 UserId='A' AND Timestamp='2018-01-01' WILL return 用户 'A' 在“2018-01-01”上修改的记录。

另见 Fanout Indexes

注2: 'Title' 也可以使用以下方式进行索引和搜索:

from doc in docs.Documents
from modification in doc.RecentModifications 
select new {
    doc.Title,
    modification.UserId,
    modification.Timestamp
}

所以每个结果 'index entry' 将像以前一样包含 'UserId' & 'Timestamp' -和-相关的 'Title'

使用Indexes with Dynamic Fields. It allows to keep logical data structure and avoids creating a fanout index即可解决问题。

解决方案

为上面的 Document 集合创建以下索引:

public class MyIndex : AbstractIndexCreationTask<Document, DocumentIndDto>
{
    public MyIndex()
    {
        // Add fields that are used for filtering and sorting
        Map = docs =>
            from e in docs
            select new
            {
                Title = e.Title, 
                _ = e.RecentModifications.Select( x => CreateField ($"{nameof(Document.RecentModifications)}_{x.UserId}", x.Timestamp))
            };
    }
}

public class DocumentIndDto
{
    public string Title { get; set; }
    public Dictionary<string,DateTime> RecentModifications { get; set; }
}

查询MyIndex喜欢

var q = s.Query<DocumentIndDto, MyIndex>()
                .Where(p => p.Title == "Super" && p. RecentModifications["User1"] < DateTime.Now);

说明

具有动态字段的指定索引将为每条记录生成以下格式的额外字段和术语:

RecentModifications_User1 = '2018-07-01';
RecentModifications_User2 = '2018-07-02';

格式很重要,因为当您在像 myDic[key] 这样的高级查询中使用字典时,它会在生成的 RQL 中转换为 myDic_key。因此,它将允许我们在查询中使用这些字段。

如果您使用通常的 Query 而不是 DocumentQuery(参见 docs)进行查询,那么您需要正确的数据类型才能让 LINQ 工作。为此,我创建了 DocumentIndDto class,其中我的 RecentModifications 已成为字典,因此我可以在高级查询中使用它并获得正确的 RQL,如

from index 'MyIndex' where Title = $p0 and RecentModifications_User1 = $p1

有关详细信息,请参阅我的 discussion 与 Oren Eini(又名 Ayende Rahien)的主题。