用于过滤和排序嵌套 structure/collection 属性的 RavenDb 索引(扇出索引)
RavenDb index to filter&sort on properties of the nested structure/collection (fanout index)
我正在寻找一种方法来创建 static index 来为 filtering/sorting 查询嵌套结构(对象集合)中 属性 值的组合以及结构容器。由于以下原因,这似乎不是微不足道的:
- 如果嵌套 structure/collection 的属性被分离到索引的各个字段(各个集合)中,那么在过滤嵌套的 2+ 个属性时就不可能使用
AND
条件structure/collection.
- fanout index complexity (see an example),这使得 运行 的任何解决方案都太慢了。
给定以下持久模型:
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建立索引:Title
,UserId
和 Timestamp
?
可能的用例:
- 获取特定用户和日期范围内包含单词 'contract' 的所有文档
- 按用户最后修改的顺序对包含单词 'contract' 的文档进行排序。
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”上修改的记录。
注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)的主题。
我正在寻找一种方法来创建 static index 来为 filtering/sorting 查询嵌套结构(对象集合)中 属性 值的组合以及结构容器。由于以下原因,这似乎不是微不足道的:
- 如果嵌套 structure/collection 的属性被分离到索引的各个字段(各个集合)中,那么在过滤嵌套的 2+ 个属性时就不可能使用
AND
条件structure/collection. - fanout index complexity (see an example),这使得 运行 的任何解决方案都太慢了。
给定以下持久模型:
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建立索引:Title
,UserId
和 Timestamp
?
可能的用例:
- 获取特定用户和日期范围内包含单词 'contract' 的所有文档
- 按用户最后修改的顺序对包含单词 'contract' 的文档进行排序。
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”上修改的记录。
注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)的主题。