Cosmos CreateDocumentQuery linq Производительность в зависимости от состояния
У меня есть коллекция космоса, имеющая где-то около 28000 документов, и я использую CreateDocumentQuery на DocumentClient с условием where для свойств типа 'T'. С различными типами использования, упомянутыми ниже, я получаю очень резкую разницу во времени при получении результатов.
Случай 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, который имеет встроенный метод, где условие возвращает результаты в промежуток времени менее секунды, где, как и в случае 2, результат занимает около 20-30 секунд, что кажется немного странным. Я не понимаю, в чём разница между наличием встроенного условия и передачей условия в качестве допустимого.
Если кто-то заинтересовался образцом космического документа:
{
"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 { public string id { get; задавать; }
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; }
}
Любые предложения по поводу чего-то плохого я делаю здесь.
1 ответ
В обоих случаях вы делаете это неоптимальным образом.
Вы хотите сначала или ноль, если нет совпадения.
Однако вы делаете синхронный перекрестный запрос запроса, вызывая 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, который выполняет минимально необходимое количество вызовов для сопоставления данных и не будет запрашивать все возможные документы.
Дайте мне знать, если вам нужно какое-либо дальнейшее объяснение, потому что это довольно сложно, и примеры не очень помогают в этом.
Вы также можете абстрагировать все это и просто использовать ваши объекты и .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();
}
Вы можете сами выбрать, какой путь выбрать для вас.Отказ от ответственности, я создатель космонавта.