Azure 如何在CosmosDB Javascript存储过程中执行批量字段重命名

Azure 如何在CosmosDB Javascript存储过程中执行批量字段重命名,azure,azure-cosmosdb,Azure,Azure Cosmosdb,我一直在关注所示的javascript存储过程示例 下面的代码试图编写更新存储过程示例的修改版本。以下是我想做的: 我希望执行以下操作,而不是对单个文档进行操作 更新所提供查询返回的文档集 (可选)返回响应正文中已更新文档的计数 代码如下: function updateSproc(query, update) { var collection = getContext().getCollection(); var collectionLink = collection.ge

我一直在关注所示的javascript存储过程示例

下面的代码试图编写更新存储过程示例的修改版本。以下是我想做的:

  • 我希望执行以下操作,而不是对单个文档进行操作 更新所提供查询返回的文档集
  • (可选)返回响应正文中已更新文档的计数
代码如下:

function updateSproc(query, update) {
    var collection = getContext().getCollection();
    var collectionLink = collection.getSelfLink();
    var response = getContext().getResponse();
    var responseBody = {
        updated: 0,
        continuation: false
    };

    // Validate input.
    if (!query) throw new Error("The query is undefined or null.");
    if (!update) throw new Error("The update is undefined or null.");

    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 requestOptions = {continuation: continuation};

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

            if (documents.length > 0) {
                tryUpdate(documents);
            } 
            else if (responseOptions.continuation) {
                // Else if the query came back empty, but with a continuation token; repeat the query w/ the token.
                tryQueryAndUpdate(responseOptions.continuation);
            } 
            else {
                // Else if there are no more documents and no continuation token - we are finished updating documents.
                responseBody.continuation = false;
                response.setBody(responseBody);
            }
        });

        // If we hit execution bounds - return continuation:true
        if (!isAccepted) {
            response.setBody(responseBody);
        }
    }

    // Updates the supplied document according to the update object passed in to the sproc.
    function tryUpdate(documents) {
        if (documents.length > 0) {
            var requestOptions = {etag: documents[0]._etag};

            // Rename!
            rename(documents[0], update);

            // Update the document.
            var isAccepted = collection.replaceDocument(
                documents[0]._self, 
                documents[0], 
                requestOptions, 
                function (err, updatedDocument, responseOptions) {
                    if (err) throw err;

                    responseBody.updated++;
                    documents.shift();
                    // Try updating the next document in the array.
                    tryUpdate(documents);
                }
            );

            if (!isAccepted) {
                response.setBody(responseBody);
            }
        } 
        else {
            tryQueryAndUpdate();
        }
    }

    // The $rename operator renames a field.
    function rename(document, update) {
        var fields, i, existingFieldName, newFieldName;

        if (update.$rename) {
            fields = Object.keys(update.$rename);
            for (i = 0; i < fields.length; i++) {
                existingFieldName = fields[i];
                newFieldName = update.$rename[fields[i]];

                if (existingFieldName == newFieldName) {
                    throw new Error("Bad $rename parameter: The new field name must differ from the existing field name.")
                } else if (document[existingFieldName]) {
                    // If the field exists, set/overwrite the new field name and unset the existing field name.
                    document[newFieldName] = document[existingFieldName];
                    delete document[existingFieldName];
                } else {
                    // Otherwise this is a noop.
                }
            }
        }
    }
}
字段重命名后,我希望它们如下所示:

{ id: someId, A: "ChangeThisField" }
{ id: someId, B: "ChangeThisField" }
我试图调试此代码的两个问题:

  • 更新后的计数非常不准确。我怀疑我在继续令牌上做了一些非常愚蠢的事情-部分问题是我不确定该如何使用它
  • 重命名本身没有发生
    console.log()
    调试表明我从未进入重命名函数中的
    if(update.$rename)

    我修改了您的存储过程代码,如下所示,它适用于我。我没有使用对象或数组作为
    $rename
    参数,而是使用
    oldKey
    newKey
    。如果您确实关心参数的构造,您可以将
    重命名
    方法更改回去,这不会影响其他逻辑。请参阅我的代码:

    function updateSproc(query, oldKey, newKey) {
        var collection = getContext().getCollection();
        var collectionLink = collection.getSelfLink();
        var response = getContext().getResponse();
        var responseBody = {
            updated: 0,
            continuation: ""
        };
    
        // Validate input.
        if (!query) throw new Error("The query is undefined or null.");
        if (!oldKey) throw new Error("The oldKey is undefined or null.");
        if (!newKey) throw new Error("The newKey is undefined or null.");
    
        tryQueryAndUpdate();
    
        function tryQueryAndUpdate(continuation) {
            var requestOptions = {
                continuation: continuation, 
                pageSize: 1
            };
    
            var isAccepted = collection.queryDocuments(collectionLink, query, requestOptions, function (err, documents, responseOptions) {
                if (err) throw err;
    
                if (documents.length > 0) {
                    tryUpdate(documents);
                    if(responseOptions.continuation){
                        tryQueryAndUpdate(responseOptions.continuation);
                    }else{
                        response.setBody(responseBody);
                    }
    
                }
            });
    
            if (!isAccepted) {
                response.setBody(responseBody);
            }
        }
    
        function tryUpdate(documents) {
            if (documents.length > 0) {
                var requestOptions = {etag: documents[0]._etag};
                // Rename!
                rename(documents[0]);
    
                // Update the document.
                var isAccepted = collection.replaceDocument(
                    documents[0]._self, 
                    documents[0], 
                    requestOptions, 
                    function (err, updatedDocument, responseOptions) {
                        if (err) throw err;
    
                        responseBody.updated++;
                        documents.shift();
                        // Try updating the next document in the array.
                        tryUpdate(documents);
                    }
                );
    
                if (!isAccepted) {
                    response.setBody(responseBody);
                }
            } 
        }
    
        // The $rename operator renames a field.
        function rename(document) {
            if (oldKey&&newKey) {
                if (oldKey == newKey) {
                    throw new Error("Bad $rename parameter: The new field name must differ from the existing field name.")
                } else if (document[oldKey]) {       
                    document[newKey] = document[oldKey];
                    delete document[oldKey];
                }
            }
        }
    }
    
    我只有3个测试文档,所以我将
    pagesize
    设置为
    1
    ,以测试continuation的用法

    测试文档:

    输出


    希望它能帮助你。有什么问题,请告诉我。

    嗨,现在有什么进展吗?是的,谢谢你,现在一切正常了-虽然我认为当前的存储过程实现比原始输入格式稍微不灵活,但我可以接受!只是好奇,update.js示例对您来说也坏了吗?在尝试将对象用作传入参数时,我似乎根本无法使其工作。此外,仅就我的理解而言,从web门户运行存储过程将我们限制为仅执行一次存储过程脚本(最长执行时间为5秒),对吗?是的,是的。它有5秒的限制。因此,如果存储过程超时,它将崩溃。您可以在客户端sdk上执行存储过程,并将延续令牌作为参数传递,以便可以多次运行存储过程。顺便说一句,是的,如果我将对象作为参数传递,我也无法使update.js工作。我假设web portal将其作为字符串,而不是对象或数组。因此,我必须使用oldKey和newKey作为解决方法。但是,如果使用客户端sdk,则不会出现这种情况。我认为门户网站上的存储过程确实存在一些缺陷,我们可以向Azure平台提交反馈以对其进行优化: