Azure 用于更新或插入属于多个分区键的文档的存储过程

Azure 用于更新或插入属于多个分区键的文档的存储过程,azure,azure-cosmosdb,Azure,Azure Cosmosdb,我有一个属于分区集合的文档列表。我想我可以使用存储过程来完成这项工作,而不是从.NET客户端查询每个文档并进行更新或插入 我最初没有意识到的是,存储过程是在单个分区键的事务范围内执行的。因此,我得到了必须为此操作提供的分区键值 问题是文档(我正试图插入)可能属于不同的分区。如何在存储过程中实现这一点?在我的例子中,SP是无用的,除非它可以在多个分区上运行 这就是我构建SP的方式: function upsertEcertAssignments(ecerts) { var collecti

我有一个属于分区集合的文档列表。我想我可以使用存储过程来完成这项工作,而不是从.NET客户端查询每个文档并进行更新或插入

我最初没有意识到的是,存储过程是在单个分区键的事务范围内执行的。因此,我得到了必须为此操作提供的分区键值

问题是文档(我正试图插入)可能属于不同的分区。如何在存储过程中实现这一点?在我的例子中,SP是无用的,除非它可以在多个分区上运行

这就是我构建SP的方式:

function upsertEcertAssignments(ecerts) {
    var collection = getContext().getCollection();
    var collectionLink = collection.getSelfLink();
    var response = getContext().getResponse();

    // Validate input
    if (!ecerts) throw new Error("The ecerts is null or undefined");
    if (ecerts.length == 0) throw new Error("The ecerts list size is 0");

    // Recursively call the 'process' function
    processEcerts(ecerts, 0);

    function processEcerts(ecerts, index) {
        if (index >= ecerts.length) {
            response.setBody(index);
            return; 
        }               

        var query = {query: "SELECT * FROM DigitalEcerts c WHERE c.code = @code AND c.collectionType = @type", parameters: [{name: "@code", value: ecerts[index].code}, {name: "@type", value: 0}]};
        var isQueryAccepted = collection.queryDocuments(collectionLink, query, {partitionKey: ecerts[index].code}, function(err, foundDocuments, foundOptions) {
            if (err) throw err;

            if (foundDocuments.length > 0) {
                var existingEcert = foundDocuments[0];
                ecerts[index].id = existingEcert.id;
                var isAccepted = __.replaceDocument(existingEcert._self, ecerts[index], function(err, updatedEcert, replacedOptions) {
                    if (err) throw err;

                    processEcerts(ecerts, index + 1);        
                });
                if (!isAccepted) {
                    response.setBody(index);                
                }
            } else {
                var isAccepted = __.createDocument(__.getSelfLink(), ecerts[index], function(err, insertedEcert, insertedOptions) {
                    if (err) throw err;

                    processEcerts(ecerts, index + 1);        
                });
                if (!isAccepted) {
                    response.setBody(index);                
                }
            }
        });

        if (!isQueryAccepted)
            response.setBody(index);                
    }
}
从.NET中,如果我这样称呼它,我会遇到partitionKey值问题:

var continuationIndex = await _docDbClient.ExecuteStoredProcedureAsync<int>(UriFactory.CreateStoredProcedureUri(_docDbDatabaseName, _docDbDigitalEcertsCollectionName, "UpsertDigitalMembershipEcertAssignments"), digitalEcerts);
var continuationIndex=wait _docDbClient.ExecuteStoredProcedureAsync(UriFactory.CreateStoredProcedureUri(_docDbDatabaseName,_docDbDigitalEcertsCollectionName,“UpsertDigitalMembershipRecertAssignments”),digitalEcerts);
如果我用分区键调用它,它会工作…但它是无用的:

var continuationIndex = await _docDbClient.ExecuteStoredProcedureAsync<int>(UriFactory.CreateStoredProcedureUri(_docDbDatabaseName, _docDbDigitalEcertsCollectionName, "UpsertDigitalMembershipEcertAssignments"), new RequestOptions { PartitionKey = new PartitionKey(digitalEcerts[0].Code) }, digitalEcerts.Take(1).ToList());
var continuationIndex=await\u docDbClient.ExecuteStoredProcedureAsync(UriFactory.CreateStoredProcedureUri(\u docDbDatabaseName,\u docDbDigitalEcertsCollectionName,“UpsertDigitalMembershipRecertAssignments”),新请求选项{PartitionKey=new PartitionKey(digitalEcerts[0].code)},digitalEcerts.Take(1).ToList();
我感谢你的指点

谢谢

如果针对存储过程注册的集合是 单分区集合,则事务的作用域为所有 集合中的文档。如果集合已分区, 然后,存储过程在 单分区密钥。然后必须执行每个存储过程 包括与作用域对应的分区键值 事务必须在以下条件下运行

你可以参考上面提到的描述。我们可以通过在
FeedOptions
参数中将
EnableCrossPartitionQuery
设置为
true
来跨分区查询文档。但是,
RequestOptions
对于执行存储过程没有这样的属性

所以,在执行sp时,似乎必须提供分区键。当然,它可以被upsert函数替换。从业务逻辑的角度来看,这是无用的,但是如果批量操作,SP可以释放一些性能压力,因为SP在服务器端运行


希望它能帮助您。

听上去,您的唯一id是
code
类型的组合。我建议将您的
id
属性设置为两者的组合


这保证了您的
id
是唯一的,但也消除了查询它的需要。

真正的问题是,为什么使用存储过程而不是SDK提供的Upsert方法?这是因为我需要文档
id
来执行Upsert!除非我完全错了,
id
是Cosmos知道文档是相同的。我所拥有的文档不是从Cosmos数据库中提取的……但是它们具有相同的属性。因此,我最终使用PK和type查询每个文档。如果该文档存在,我将使用该id向上插入。这需要很长时间,尤其是我有大约100个文档需要部分更新..id在CosmosDB中不是唯一的属性。它只有在自己的分区内才是唯一的。好的…谢谢你…但我不确定我是否理解。假设有一个实体具有以下3个属性:id=''、code='123'和type=0。
id
未知。如果我执行
upsert
,它将始终插入文档,因为
id
与任何现有文档都不匹配…对吗?除非我首先使用
code
type
进行查询,否则我无法知道如何更新或插入。这是我的问题。我希望我可以使用SP在服务器上执行查询并替换或插入,以避免往返。查询和键入是否足以为您提供唯一的
id
?如果是,那么为什么不将
id
设置为
code
type
的组合?是的……谢谢……这是我在问题中提到的文档。实际上,我很惊讶为服务器端提供的所有示例代码都使用未分区的集合?这是不现实的
Upsert
对我不起作用…我必须首先查询以找到
id
…这是因为源文档不是从Cosmos中提取的…因此它们的
id
未知。虽然这解决了我的问题,但它当然不能解决我在存储过程中遇到的问题。