DocumentDB更新多个文档失败

时间:2016-03-15 11:25:24

标签: stored-procedures insert-update azure-cosmosdb

我编写了一个用于向DocumentDB集合中的所有文档添加Type属性的存储过程。遗憾的是,更新一个文档后存储过程失败。该集合包含大约5000个文档。

这是存储过程:

function updateSproc() {
var collection = getContext().getCollection();
var collectionLink = collection.getSelfLink();
var response = getContext().getResponse();
var responseBody = {
    updated: 0,
    continuation: true,
    error: "",
    log: ""
};

// Validate input.
tryQueryAndUpdate();

// Recursively queries for a document by id w/ support for continuation tokens.
// Calls tryUpdate(document) as soon as the query returns a document.
function tryQueryAndUpdate(continuation) {
    var query = { query: "SELECT * FROM root c WHERE NOT is_defined(c.Type)", parameters: []};
    var requestOptions = { continuation: continuation};

    var isAccepted = collection.queryDocuments(collectionLink, query, requestOptions, function(err, documents, responseOptions) {
        if (err) {
    responseBody.error = err;
    throw err;
        }

        if (documents.length > 0) {
            // If documents are found, update them.
            responseBody.log += "Found documents: " + documents.length;
            tryUpdate(documents);
        } else if (responseOptions.continuation) {
            responseBody.log += "Continue query";
            tryQueryAndUpdate(responseOptions.continuation);
        } else {
            responseBody.log += "No more documents";
            responseBody.continuation = false;
            response.setBody(responseBody);
        }

    });

    // If we hit execution bounds - throw an exception.
    if (!isAccepted) {
        responseBody.log += "Query not accepted";
        response.setBody(responseBody);
    }
}

// Updates the supplied document according to the update object passed in to the sproc.
function tryUpdate(documents)
{
    if (documents.length > 0) {
        responseBody.log += "Updating documents " + documents.length;

        var document = documents[0];

        // DocumentDB supports optimistic concurrency control via HTTP ETag.
        var requestOptions = { etag: document._etag};

        document.Type="Type value";

        // Update the document.
        var isAccepted = collection.replaceDocument(document._self, document, requestOptions, function(err, updatedDocument, responseOptions) {
           if (err) {
              responseBody.error = err;
              throw err;
           }

           responseBody.updated++;
           documents.shift();
           tryUpdate(documents);
        });

        // If we hit execution bounds - throw an exception.
        if (!isAccepted) {
            responseBody.log += "Update not accepted";
            response.setBody(responseBody);
        }
    } else {
        tryQueryAndUpdate();
    }
}}

根据返回的响应,我可以看到查询返回100个文档。 tryUpdate被调用两次,但不接受第二次调用replaceDocument。当有许多文件要更新时,为什么不接受?

1 个答案:

答案 0 :(得分:1)

根据我对同一问题的回答MSDN

是的,对于每秒仅允许250RU的集合,每插入700RU +(估计)20RU将成为问题。查询是700RU,因为您正在执行NOT操作,这实际上是一次扫描,因为无法对其进行索引。

所以要尝试一些事情;

1)更改逻辑以排除NOT is_defined检查以及可能通过_ts DESC排序以获取最后更新的文档。这可能比做NOT检查便宜。然后你可以检查你已经获得的每个文档,如果它已经有一个Type属性,如果没有添加一个和ReplaceDocument

2)您还可以在执行此操作时尝试将集合扩展到S3,然后再将其缩放回S1。这将为您提供2500 RU。

3)即使使用S3,你仍然可能会遇到这种情况,它可能只发生在比第二个更多的文档之后。

所以,为了解决这个问题,我会在app中执行一个查询,只返回没有定义属性的记录的id,

SELECT VALUE c.id FROM c WHERE NOT is_defined(c.Type)

将这些ID粘贴到某种列表/数组中,然后从列表中获取()项目并将其作为数组传递给sproc。现在让sproc循环遍历传递的数组,通过id执行ReadDocument,更新并替换和增加计数器。

当isAccepted返回false时,将响应主体设置为计数器的值并返回到调用代码。现在调用代码可以跳过(计数器)。取(x)并再次调用sproc。

查看this sample以获取如何通过存储过程进行批量插入的示例。这显示了如何批处理记录,执行sproc,并在响应正文中isAccepted == false之前获取sproc到达该批处理的当前位置。