为什么我的Azure Cosmos查询应该返回许多结果时会返回空结果?

时间:2020-08-28 21:06:24

标签: c# azure-cosmosdb

我正在对我的Cosmos数据库实例运行查询,当我知道我应该得到一些结果时,偶尔返回0结果。

        var options = new QueryRequestOptions()
        {
            MaxItemCount = 25
        };
        var query = @"
                    select c.id,c.callTime,c.direction,c.action,c.result,c.duration,c.hasR,c.hasV,c.callersIndexed,c.callers,c.files
                    from c
                    where
                    c.ownerId=@ownerId
                    and c.callTime>=@dateFrom
                    and c.callTime<=@dateTo
                    and (CONTAINS(c.phoneNums_s, @name)
                    or CONTAINS(c.names_s, @name)
                    or CONTAINS(c.xNums_s, @name))
                    order by c.callTime desc";
        var queryIterator = container.GetItemQueryIterator<CallIndex>(new QueryDefinition(query)
        .WithParameter("@ownerId", "62371255008")
        .WithParameter("@name", "harr")
        .WithParameter("@dateFrom", dateFrom) // 5/30/2020 5:00:00 AM +00:00
        .WithParameter("@dateTo", dateTo) // 8/29/2020 4:59:59 AM +00:00
        .WithParameter("@xnum", null), requestOptions: options, continuationToken: null);
        if (queryIterator.HasMoreResults)
        {
            var feed = queryIterator.ReadNextAsync().Result;
            model.calls = feed.ToList(); //feed.Resource is empty; feed.Count is 0;
            model.CosmosContinuationToken = feed.ContinuationToken; //feed.ContinuationToken is populated with a large token value, indicating that there are more results, even though this fetch returned 0 items.
            model.TotalRecords = feed.Count(); // 0
        }

如您所见,即使我收到0个结果,延续令牌也表明在第一个请求之后那里还有更多数据。并且,在直观地直接检查数据库(Azure门户中的数据浏览器)中的数据之后,我看到了应该匹配的记录,但是在此查询中找不到它们。为了进一步测试,几秒钟后我运行了相同的查询,并收到了结果:

        var query = @"
                    select c.id,c.callTime,c.direction,c.action,c.result,c.duration,c.hasR,c.hasV,c.callersIndexed,c.callers,c.files
                    from c
                    where
                    c.ownerId=@ownerId
                    and c.callTime>=@dateFrom
                    and c.callTime<=@dateTo
                    and (CONTAINS(c.phoneNums_s, @name)
                    or CONTAINS(c.names_s, @name)
                    or CONTAINS(c.xNums_s, @name))
                    order by c.callTime desc";
        var queryIterator = container.GetItemQueryIterator<CallIndex>(new QueryDefinition(query)
        .WithParameter("@ownerId", "62371255008")
        .WithParameter("@name", "harr")
        .WithParameter("@dateFrom", dateFrom) // 5/30/2020 5:00:00 AM +00:00
        .WithParameter("@dateTo", dateTo) // 8/29/2020 4:59:59 AM +00:00
        .WithParameter("@xnum", null), requestOptions: options, continuationToken: null);
        if (queryIterator.HasMoreResults)
        {
            var feed = queryIterator.ReadNextAsync().Result;
            model.calls = feed.ToList(); //feed.Resource has 25 items; feed.Count is 25;
            model.CosmosContinuationToken = feed.ContinuationToken; //feed.ContinuationToken is populated, but it is considerably smaller than the token I received from the first request.
            model.TotalRecords = feed.Count(); // 25
        }

这是和以前一样的确切查询,但是这次feed给了我预期的结果。这已经发生了不止一次,并且继续断断续续地发生。这有什么用呢?这是Azure Cosmos中的错误吗?如果是这样,这似乎是一个严重的错误,它破坏了Cosmos(和整个数据库)的核心功能。

或者,这是预期的吗?是否有可能在第一个查询中,我需要继续ReadNextAsync直到使用延续标记返回一些结果?

感谢您的帮助,因为这破坏了我应用程序中非常基本的功能。

此外,我想补充一下,从查询返回的数据在我第一次查询尝试和第二次查询尝试之间没有被新添加。这些数据已经存在了一段时间。

2 个答案:

答案 0 :(得分:2)

您的代码是正确的,您应该消耗查询检查pageProps(尽管我将HasMoreResults更改为.Result以避免可能的死锁)。跨分区查询中可能发生的情况是,如果检查结果的初始分区没有任何空白页,则会得到一些空白页。

有时,即使将来的页面上有结果,查询也可能有空白页。原因可能是:

  • 该SDK可能会进行多个网络调用。
  • 查询可能需要很长时间才能检索文档。

参考:https://docs.microsoft.com/azure/cosmos-db/troubleshoot-query-performance#common-sdk-issues

答案 1 :(得分:0)

尝试使用以下代码:

查询Cosmos DB方法:

public async Task<DocDbQueryResult> QueryCollectionBaseWithPagingInternalAsync(FeedOptions feedOptions, string queryString, IDictionary<string, object> queryParams, string collectionName)
        {
            string continuationToken = feedOptions.RequestContinuation;
            List<JObject> documents = new List<JObject>();
            IDictionary<string, object> properties = new Dictionary<string, object>();
            int executionCount = 0;
            double requestCharge = default(double);
            double totalRequestCharge = default(double);

            do
            {
                feedOptions.RequestContinuation = continuationToken;
                var query = this.documentDbClient.CreateDocumentQuery<JObject>(
                    UriFactory.CreateDocumentCollectionUri(this.databaseName, collectionName),
                    new SqlQuerySpec
                    {
                        QueryText = queryString,
                        Parameters = ToSqlQueryParamterCollection(queryParams),
                    },
                    feedOptions)
                .AsDocumentQuery();

                var response = await query.ExecuteNextAsync<JObject>().ConfigureAwait(false);
                documents.AddRange(response.AsEnumerable());
                executionCount++;
                requestCharge = executionCount == 1 ? response.RequestCharge : requestCharge;
                totalRequestCharge += response.RequestCharge;
                continuationToken = response.ResponseContinuation;
            }
            while (!string.IsNullOrWhiteSpace(continuationToken) && documents.Count < feedOptions.MaxItemCount);

            var pagedDocuments = documents.Take(feedOptions.MaxItemCount.Value);
            var result = new DocDbQueryResult
            {
                ResultSet = new JArray(pagedDocuments),
                TotalResults = Convert.ToInt32(pagedDocuments.Count()),
                ContinuationToken = continuationToken
            };

            // if query params are not null, use existing query params also to be passed as properties.
            if (queryParams != null)
            {
                properties = queryParams;
            }

            properties.Add("TotalRequestCharge", totalRequestCharge);
            properties.Add("ExecutionCount", executionCount);

            return result;
        }

ToSqlQueryParamterCollection方法:

private static SqlParameterCollection ToSqlQueryParamtereCollection(IDictionary<string, object> queryParams)
        {
            var coll = new SqlParameterCollection();
            if (queryParams != null)
            {
                foreach (var paramKey in queryParams.Keys)
                {
                    coll.Add(new SqlParameter(paramKey, queryParams[paramKey]));
                }
            }

            return coll;
        }
相关问题