Cosmos CreateDocumentQuery linq 在 where 条件下的性能
Cosmos CreateDocumentQuery linq perfromance on where condition
我有一个包含大约 28000 个文档的 cosmos 集合,我在 DocumentClient 上使用 CreateDocumentQuery 并在 'T' 类型的属性上使用 where 条件。通过下面提到的不同类型的使用,我在获得结果时的时间延迟差异很大。
案例一:
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 条件进行 decalre。
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 具有内联条件,其中条件在不到一秒的时间跨度内返回结果,而在 案例 2 中结果大约需要 20-30 秒,这看起来有点奇怪。我不明白内联 where 条件和将 where 条件作为变量传递之间有什么区别。
如果有人对示例 cosmos 文档感兴趣:
{
"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"
}
}
这是用于反序列化文档的模型:
内部 class HeartRateDayRecordIdentifierData
{
public 字符串 ID { 得到;放; }
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
。
在这两种情况下发生的情况是,首先您 return CosmosDB 中的所有数据,然后 LINQ 进行内存过滤以返回数据。
您应该改为使用 while(query.HasMoreResults)
和 query.ExecuteNextAsync()
方法来 return 您的数据。
您的查询应该是这样的:
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();
}
您可以自行选择适合您的方式。
免责声明,我是宇航员的创造者。
我有一个包含大约 28000 个文档的 cosmos 集合,我在 DocumentClient 上使用 CreateDocumentQuery 并在 'T' 类型的属性上使用 where 条件。通过下面提到的不同类型的使用,我在获得结果时的时间延迟差异很大。
案例一:
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 条件进行 decalre。
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 具有内联条件,其中条件在不到一秒的时间跨度内返回结果,而在 案例 2 中结果大约需要 20-30 秒,这看起来有点奇怪。我不明白内联 where 条件和将 where 条件作为变量传递之间有什么区别。
如果有人对示例 cosmos 文档感兴趣:
{
"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"
}
}
这是用于反序列化文档的模型: 内部 class HeartRateDayRecordIdentifierData { public 字符串 ID { 得到;放; }
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
。
在这两种情况下发生的情况是,首先您 return CosmosDB 中的所有数据,然后 LINQ 进行内存过滤以返回数据。
您应该改为使用 while(query.HasMoreResults)
和 query.ExecuteNextAsync()
方法来 return 您的数据。
您的查询应该是这样的:
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();
}
您可以自行选择适合您的方式。 免责声明,我是宇航员的创造者。