Cosmos CreateDocumentQuery在where条件下执行linq perfromance



我有一个宇宙集合,大约有28000个文档,我在DocumentClient上使用CreateDocumentQuery,对类型为"T"的属性使用where条件。对于下面提到的不同类型的使用,我在获得结果时得到了非常显著的时间延迟差异。

情况1:

var docs2 = 
_documentClient.CreateDocumentQuery<HeartRateDayRecordIdentifierData>(collectionUri).Where(x =>
x.SubjectDeviceInformation.StudyId == "TestStudy"
&& x.SubjectDeviceInformation.SiteId == "Site_._Street_23"
&& x.SubjectDeviceInformation.SubjectId == "Subject3"
&& x.SubjectDeviceInformation.DeviceId == "Device1"
&& x.DaySplit == "20181112").AsEnumerable().FirstOrDefault(); 

情况2:这是相同的代码和条件,但这次,我使用函数变量来标记where条件。

Func<HeartRateDayRecordIdentifierData, bool> searchOptions = x =>
x.SubjectDeviceInformation.StudyId == "TestStudy"
&& x.SubjectDeviceInformation.SiteId == "Site_._Street_23"
&& x.SubjectDeviceInformation.SubjectId == "Subject3"
&& x.SubjectDeviceInformation.DeviceId == "Device1"
&& x.DaySplit == "20181112";
var docs1 = _documentClient.CreateDocumentQuery<HeartRateDayRecordIdentifierData>(collectionUri)
.Where(searchOptions).AsEnumerable().FirstOrDefault();

案例1具有内联where条件,在不到一秒的时间跨度内返回结果,而在实例2中,结果大约需要20-30秒,这似乎有点奇怪。我不明白内联where条件和传递可变where条件有什么区别。

如果有人对样本宇宙文件感兴趣:

{
"id": "TestStudy_Site_._Street_21_Subject1_Device1_20181217",
"AssemblyVersion": "1.2.3.0",
"DataItemId": "20181217/TestStudy_Site_._Street_21_Subject1_Device1_20181217",
"MessageType": "HeartRateDayDocumentIdentifier",
"TimeStamp": "2018-12-14T00:00:00",
"DaySplit": "20181217",
"SubjectDeviceInformation": {
"SubjectId": "Subject1",
"DeviceId": "Device1",
"StudyId": "TestStudy",
"SiteId": "Site_._Street_21"
}   
}

以下是用于反序列化文档的模型:内部类HeartRateDayRecordIdentifierData{公共字符串id{get;set;}

public string AssemblyVersion { get; set; }
public string DataItemId { get; set; }
public string MessageType { get; set; }
public DateTime TimeStamp { get; set; }
public string DaySplit { get; set; }
public SubjectDeviceInformation SubjectDeviceInformation { get; set; }
}
internal class SubjectDeviceInformation
{
public string SubjectId { get; set; }
public string DeviceId { get; set; }
public string StudyId { get; set; }
public string SiteId { get; set; }
}

对我在这里做的错事有什么建议吗。

在这两种情况下,您都是以一种非最佳的方式来执行此操作的。

如果没有匹配项,则只需要first或null。

但是,您正在通过调用AsEnumerable().FirstOrDefault()来执行同步跨分区查询调用。

此外,where子句应该是Expression<Func<HeartRateDayRecordIdentifierData, bool>>,而不是Func

在这两种情况下,首先返回CosmosDB中的所有数据,然后LINQ在内存中进行过滤以返回数据。

相反,您应该使用while(query.HasMoreResults)query.ExecuteNextAsync()方法返回数据。

以下是您的查询方式:

public async Task<HeartRateDayRecordIdentifierData> GetSomethingAsync()
{
var query = 
_documentClient.CreateDocumentQuery<HeartRateDayRecordIdentifierData>(collectionUri).Where(x =>
x.SubjectDeviceInformation.StudyId == "TestStudy"
&& x.SubjectDeviceInformation.SiteId == "Site_._Street_23"
&& x.SubjectDeviceInformation.SubjectId == "Subject3"
&& x.SubjectDeviceInformation.DeviceId == "Device1"
&& x.DaySplit == "20181112").AsDocumentQuery();
while(query.HasMoreResults)
{
var results = await query.ExecuteNextAsync();
if(results.Any())
return results.First();     
}          
return null;
}

这样一来,SDK只需进行最少的调用即可匹配数据,并且不会在所有可能的文档中进行查询。

如果你需要进一步的解释,请告诉我,因为这很棘手,而且样品对这件事没有真正的帮助。

如果您使用Cosmonaut,您也可以抽象所有这些,只需使用您的对象和.FirstOrDefaultAsync方法。这样,您的整个代码可以更改为:

public async Task<HeartRateDayRecordIdentifierData> GetSomethingAsync()
{
return await cosmosStore.Query().Where(x =>
x.SubjectDeviceInformation.StudyId == "TestStudy"
&& x.SubjectDeviceInformation.SiteId == "Site_._Street_23"
&& x.SubjectDeviceInformation.SubjectId == "Subject3"
&& x.SubjectDeviceInformation.DeviceId == "Device1"
&& x.DaySplit == "20181112").FirstOrDefaultAsync();
}

你可以自己选择适合你的路。免责声明,我是宇宙人的创造者

最新更新