DocumentDB 更新多个文档失败
DocumentDB updating multiple documents fails
我编写了一个存储过程,用于将类型 属性 添加到 DocumentDB 集合中的所有文档。不幸的是,存储过程在仅更新一个文档后就失败了。该集合包含大约 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();
}
}}
根据返回的响应,我可以看到查询 returns 100 个文档。 tryUpdate 被调用两次,但不接受对 replaceDocument 的第二次调用。为什么要更新的文档很多,还是不接受?
根据我对同一问题的回答MSDN
是的,每个插入 700RUs +(估计)20RUs,在每秒只允许 250RUs 的集合上将是一个问题。查询是 700RU,因为您正在执行 NOT 操作,这实际上是一次扫描,因为它无法被索引。
所以有些事情要尝试;
1) 更改逻辑以排除 NOT is_defined 检查并可能按 _ts DESC 排序以获取最后更新的文档。这可能比做 NOT 检查便宜。然后你可以检查你得到的每个文档是否已经有 Type 属性,如果没有则添加一个和 ReplaceDocument
2) 您也可以尝试在执行此操作时将集合扩展到 S3,然后再次将其缩小到 S1。这会给你 2500 RUs 玩。
3) 即使使用 S3,您可能仍然 运行 参与其中,它可能只是发生在比第二个文档更多的文档之后。
所以,为了解决这个问题,我会在应用程序中执行查询 return 只是没有定义 属性 的记录的 ID,
SELECT 值 c.id 来自 c 而不是 is_defined(c.Type)
将这些 id 粘贴到某种列表/数组中,然后从列表中获取 () 项目并将其作为数组传递给 sproc。现在让 sproc 循环通过传递的数组,通过 id 执行 ReadDocument,更新和替换以及递增计数器。
当 isAccepted return 为 false 时,将响应主体设置为计数器的值,并将 return 设置为调用代码。现在调用代码可以 Skip(counter).Take(x) 并再次调用存储过程。
查看 this sample 中的示例,了解如何通过存储过程进行批量插入。这显示了如何批处理记录、执行存储过程,以及如何从响应正文中获取存储过程在 isAccepted == false 之前在该批处理中到达的当前位置。
我编写了一个存储过程,用于将类型 属性 添加到 DocumentDB 集合中的所有文档。不幸的是,存储过程在仅更新一个文档后就失败了。该集合包含大约 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();
}
}}
根据返回的响应,我可以看到查询 returns 100 个文档。 tryUpdate 被调用两次,但不接受对 replaceDocument 的第二次调用。为什么要更新的文档很多,还是不接受?
根据我对同一问题的回答MSDN
是的,每个插入 700RUs +(估计)20RUs,在每秒只允许 250RUs 的集合上将是一个问题。查询是 700RU,因为您正在执行 NOT 操作,这实际上是一次扫描,因为它无法被索引。
所以有些事情要尝试;
1) 更改逻辑以排除 NOT is_defined 检查并可能按 _ts DESC 排序以获取最后更新的文档。这可能比做 NOT 检查便宜。然后你可以检查你得到的每个文档是否已经有 Type 属性,如果没有则添加一个和 ReplaceDocument
2) 您也可以尝试在执行此操作时将集合扩展到 S3,然后再次将其缩小到 S1。这会给你 2500 RUs 玩。
3) 即使使用 S3,您可能仍然 运行 参与其中,它可能只是发生在比第二个文档更多的文档之后。
所以,为了解决这个问题,我会在应用程序中执行查询 return 只是没有定义 属性 的记录的 ID,
SELECT 值 c.id 来自 c 而不是 is_defined(c.Type)
将这些 id 粘贴到某种列表/数组中,然后从列表中获取 () 项目并将其作为数组传递给 sproc。现在让 sproc 循环通过传递的数组,通过 id 执行 ReadDocument,更新和替换以及递增计数器。
当 isAccepted return 为 false 时,将响应主体设置为计数器的值,并将 return 设置为调用代码。现在调用代码可以 Skip(counter).Take(x) 并再次调用存储过程。
查看 this sample 中的示例,了解如何通过存储过程进行批量插入。这显示了如何批处理记录、执行存储过程,以及如何从响应正文中获取存储过程在 isAccepted == false 之前在该批处理中到达的当前位置。