我的Cosmos项目有以下顶级简化设计。一些介绍然后跟进一些问题。提前感谢您阅读本文。
我有3个集合:Memberships
,Discounts
和Vouchers
:
每个集合存储不同的实体。每个集合中的实体不同,但属于同一个分区并具有继承的属性:
id
- 唯一身份证(宇宙要求......我叮叮当当)upsertDate
- 在upsert时更新,因此我可以订购文件(见下文)每个集合实体都有:
code
- 分区键type
- 集合中不同实体的鉴别器。 有会员,优惠券和折扣的thousdans。例如,当系统收到会员代码时,它可以快速查找文档,因为它是分区键。
如果这样做,它将返回属于同一code
的所有实体文档:
SELECT * FROM c WHERE c.code = 'YTR3IO'
如果有人这样做,它会返回属于同一code
和type
的所有实体文档:
SELECT * FROM c WHERE c.code = 'YTR3IO' and c.type = 10
type
通常是可以存储在特定集合中的不同实体的枚举。例如,对于成员资格,类型可以是notication
,comment
,request
,transaction
等
我在每个文档中都有upsertDate
的原因,所以我可以这样做:
SELECT * FROM c WHERE c.code = 'YTR3IO' AND c.type = 10 ORDER BY c.upsertDate DESC
对于索引政策,我已将政策覆盖到我认为最好的方式:
DocumentCollection collectionDefinition = new DocumentCollection();
collectionDefinition.Id = _docDbMembershipsCollectionName;
collectionDefinition.PartitionKey.Paths.Add("/code");
collectionDefinition.DefaultTimeToLive = -1;
// If queries are known upfront, index just the properties we need
collectionDefinition.IndexingPolicy.Automatic = true;
collectionDefinition.IndexingPolicy.IndexingMode = IndexingMode.Consistent;
collectionDefinition.IndexingPolicy.IncludedPaths.Clear();
IncludedPath path = new IncludedPath();
path.Path = "/code/?";
path.Indexes.Add(new RangeIndex(DataType.String) { Precision = -1 });
collectionDefinition.IndexingPolicy.IncludedPaths.Add(path);
path = new IncludedPath();
path.Path = "/upsertDate/?";
path.Indexes.Add(new RangeIndex(DataType.String) { Precision = -1 });
collectionDefinition.IndexingPolicy.IncludedPaths.Add(path);
path = new IncludedPath();
path.Path = "/type/?";
path.Indexes.Add(new RangeIndex(DataType.Number) { Precision = -1 });
collectionDefinition.IndexingPolicy.IncludedPaths.Add(path);
collectionDefinition.IndexingPolicy.ExcludedPaths.Clear();
collectionDefinition.IndexingPolicy.ExcludedPaths.Add(new ExcludedPath { Path = "/*" });
DocumentCollection ttlEnabledCollection = await
_docDbClient.CreateDocumentCollectionAsync(
UriFactory.CreateDatabaseUri(_docDbDatabaseName), collectionDefinition, new RequestOptions { OfferThroughput = 1000 });
问题:
问题1:如果我有upsertDate
的查询,则成本会上升:
FeedOptions queryOptions = new FeedOptions { MaxItemCount = 100, PartitionKey = new Microsoft.Azure.Documents.PartitionKey(code.ToUpper()) };
var query = this._docDbClient.CreateDocumentQuery<MembershipDeviceRegistrationItem>(
UriFactory.CreateDocumentCollectionUri(_docDbDatabaseName, _docDbMembershipsCollectionName), queryOptions)
.Where(e => e.CollectionType == MembershipCollectionTypes.DeviceRegistrationItem && e.Code.ToUpper() == code.ToUpper())
.OrderByDescending(e => e.UpsertDate)
.AsDocumentQuery();
List<MembershipDeviceRegistrationItem> allDocuments = new List<MembershipDeviceRegistrationItem>();
while (query.HasMoreResults)
{
var queryResult = await query.ExecuteNextAsync<MembershipDeviceRegistrationItem>();
cost += queryResult.RequestCharge;
allDocuments.AddRange(queryResult.ToList());
}
return allDocuments.ToList();
如何使用upsertDate
订购我的文档并选择最新的文档?
问题2:如何计算具有聚合的查询的成本?
FeedOptions queryOptions = new FeedOptions { MaxItemCount = 1, PartitionKey = new Microsoft.Azure.Documents.PartitionKey(code.ToUpper()) };
return _docDbClient.CreateDocumentQuery<int>(
UriFactory.CreateDocumentCollectionUri(_docDbDatabaseName, _docDbMembershipsCollectionName),
new SqlQuerySpec()
{
QueryText = $"SELECT value count(m.id) FROM {_docDbMembershipsCollectionName} m WHERE m.code = @code AND m.collectionType = @type",
Parameters = new SqlParameterCollection()
{
new SqlParameter("@code", code.ToUpper()),
new SqlParameter("@type", MembershipCollectionTypes.DeviceRegistrationItem)
}
}, queryOptions).AsEnumerable().First();
问题3:我需要制作一个Omni文档,其中包含有关特定成员资格的所有内容并返回给某个客户,以便他们可以显示其不同的部分。所以我创建了这样的东西(简化):
FeedOptions queryOptions = new FeedOptions { MaxItemCount = -1, PartitionKey = new Microsoft.Azure.Documents.PartitionKey(code.ToUpper()) };
var query = _docDbClient.CreateDocumentQuery<dynamic>(
UriFactory.CreateDocumentCollectionUri(_docDbDatabaseName, _docDbMembershipsCollectionName),
new SqlQuerySpec()
{
QueryText = $"SELECT * FROM {_docDbMembershipsCollectionName} m WHERE m.code = @code",
Parameters = new SqlParameterCollection()
{
new SqlParameter("@code", code.ToUpper())
}
}, queryOptions).AsDocumentQuery();
List<dynamic> items = new List<dynamic>();
while (query.HasMoreResults)
{
var queryResult = await query.ExecuteNextAsync();
cost += queryResult.RequestCharge;
items.AddRange(queryResult.ToList());
}
var omni = new DigitalMembershipOmni();
foreach (var item in items)
{
var collectionType = (MembershipCollectionTypes)item.collectionType;
if (collectionType == MembershipCollectionTypes.Membership)
{
omni.Clone((DigitalMembership)item);
}
else if (collectionType == MembershipCollectionTypes.RefreshItem)
{
omni.RefreshItems.Add((DigitalMembershipRefreshItem)item);
}
else if (collectionType == MembershipCollectionTypes.OfferItem)
{
omni.OfferItems.Add((DigitalMembershipOfferItem)item);
}
else if (collectionType == MembershipCollectionTypes.TrackingTransactionItem)
{
omni.TrackingTransactionItems.Add((DigitalMembershipTrackingTransactionItem)item);
}
else if (collectionType == MembershipCollectionTypes.BookingTransactionItem)
{
omni.BookingTransactionItems.Add((DigitalMembershipBookingTransactionItem)item);
}
else if (collectionType == MembershipCollectionTypes.MembershipTransactionItem)
{
omni.MembershipTransactionItems.Add((DigitalMembershipTransactionItem)item);
}
else if (collectionType == DigitalMembershipCollectionTypes.NotificationItem)
{
omni.NotificationItems.Add((DigitalMembershipNotificationItem)item);
}
else if (collectionType == DigitalMembershipCollectionTypes.CommentItem)
{
omni.CommentItems.Add((DigitalMembershipCommentItem)item);
}
else if (collectionType == DigitalMembershipCollectionTypes.ContactUsItem)
{
omni.ContactUsItems.Add((DigitalMembershipContactUsItem)item);
}
else if (collectionType == DigitalMembershipCollectionTypes.ReferralItem)
{
omni.ReferralItems.Add((DigitalMembershipReferralItem)item);
}
else if (collectionType == DigitalMembershipCollectionTypes.EcertPinItem)
{
omni.EcertPinItems.Add((DigitalMembershipEcertPinItem)item);
}
else if (collectionType == DigitalMembershipCollectionTypes.ProfileUpdateItem)
{
omni.ProfileUpdateItems.Add((DigitalMembershipProfileUpdateItem)item);
}
}
return omni;
上述工作....但它的成本很高......而且......它不能很好地扩展......所以如果一个成员会积累更多的数据,成本将会显着上升。我尝试对每种类型执行查询,指定要生成的顶级项目,例如前100个通知...但upsertDate
的订单(如上所述)会使查询非常昂贵....因此总成本甚至是最糟糕的。如何以合理的成本和性能完成这样的事情。
问题4:membershipCode
和vouchers
集合中有discounts
属性,如何进行JOIN查询以获取属于特定成员资格的凭证耗尽我的RU并杀死我的表现?我认为JOIN查询最终将使用不同的分区扫描整个vouchers
集合,并且可能会产生显着的成本和吞吐量问题。我能够使其合理运行的唯一方法是使用表DB将vouchers
链接到memberships
。 Table DB查询很快......但这给我的小项目增加了一个重要的复杂性。
我很欣赏任何指针。
问候