Cosmos CreateDocumentQuery Linq性能在何处

时间:2018-12-18 14:13:42

标签: performance linq azure-cosmosdb azure-cosmosdb-sqlapi

我有一个cosmos集合,周围有28000个文档,并且我在DocumentClient上使用CreateDocumentQuery,条件是“ 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();

第一种情况,它具有内联条件,条件返回时间少于一秒,而在第二种情况下,第二种情况,结果大约需要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 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 个答案:

答案 0 :(得分:1)

在两种情况下,您都以非最佳方式进行此操作。

您只想要第一个,如果没有匹配项,则为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();
}

您可以自行选择适合您的方式。 免责声明,我是宇航员的创造者。