JSON

Updating Documents in DocumentDb

字号+ 作者:H5之家 来源:H5之家 2017-02-14 14:00 我要评论( )

Updating Documents in DocumentDb,Inmylastfewposts,I’vebeenworkingonanAzureMobileAppsreplacementservice.ItwillruninAzureFunctionsanduseDocumentDbasaba

In my last few posts, I’ve been working on an Azure Mobile Apps replacement service. It will run in Azure Functions and use DocumentDb as a backing store. Neither of these requirements are possible in the Azure Mobile Apps server SDK today. Thus far, I’ve created a CRUD HTTP API , initialized the DocumentDb store andhandled inserts. Today is all about fetching, but more importantly it is about replacing documents and handling conflict resolution.

The DocumentDb Driver

Before I get started with the code for the endpoint, I need to add some more functionality to my DocumentDb promisified driver. In the document.js file, I’ve added the following:

module.exports = {
createDocument: function (client, collectionRef, docObject, callback) {
client.createDocument(collectionRef._self, docObject, callback);
},
fetchDocument: function (client, collectionRef, docId, callback) {
var querySpec = {query: 'SELECT * FROM root r WHERE [email protected]',parameters: [{ name: [email protected]', value: docId}]
};
client.queryDocuments(collectionRef._self, querySpec).current(callback);
},
readDocument: function (client, docLink, options, callback) {
client.readDocument(docLink, options, callback);
},
replaceDocument: function(client, docLink, docObject, callback) {
client.replaceDocument(docLink, docObject, callback);
}
};

My first attempt at reading a document used the readDocument() method. I would construct a docLink using the following:

var docLink = `${refs.table._self}${refs.table._docs}${docId}`;

However, this always resulted in a 400 Bad Request response from DocumentDb. The reason is likely that the _self link uses the shorted (and obfuscated) URI, whereas the Document Id I am using is a GUID and is not obfuscated. If you take a look at the response from DocumentDb, there is an id field and a _rid field. The _rid field is used in the document links. Thus, instead of using readDocument() , I’m using a queryDocuments() call on the driver to search for the Id. I’ve also promisified these calls in the normal manner using the Bluebird library.

Fetching a Record

The Azure Mobile Apps SDK allows me to GET /tables/todoitem/id where id is the GUID. With the driver complete, I can do the following in the Azure Function table controller:

function getOneItem(req, res, id) {
driver.fetchDocument(refs.client, refs.table, id)
.then((document) => {
if (typeof document === 'undefined')res.status(404).json({ 'error': 'Not Found' });
elseres.status(200).json(convertItem(document));
})
.catch((error) => {
res.status(error.code).json(convertError(error));
});
}

When doing this, I did notice that some semantics seem to have changed in the Azure Functions SDK. I can no longer use context.bindings.id and has to switch to using req.params.id . Aside from this small change in the router code, this code is relatively straight forward. I established the convertItem() and convertError() methods in my last article.

Replacing a Record

The more complex case is replacing a record. There is a little bit of logic around conflict resolution:

If there is an If-Match header, then ensure the version of the current record matches the If-Match header, otherwise return a 412 response. If there is no If-match header, but the new record contains a version, return a 409 response. Otherwise update the record

Because we want the version and updatedAt fields to be controlled as well, we need to ensure the new object does not contain those values when it is submitted to DocumentDb:

function replaceItem(req, res, id) {
driver.fetchDocument(refs.client, refs.table, id)
.then((document) => {
if (typeof document === 'undefined') {res.status(404).json({ 'error': 'Not Found' });return;
}
var item = req.body, version = new Buffer(document._etag).toString('base64')
if (item.id !== id) {res.status(400).json({ 'error': 'Id does not match' });return;
}
if (req.headers.hasOwnProperty('if-match') && req.header['if-match'] !== version) {res.status(412).json({ 'current': version, 'new': item.version, 'error': 'Version Mismatch' })return;
}
if (item.hasOwnProperty('version') && item.version !== version) {res.status(409).json({ 'current': version, 'new': item.version, 'error': 'Version Mismatch' });return;
}
// Delete the version and updatedAt fields from the doc before submitting
delete item.updatedAt;
delete item.version;
driver.replaceDocument(refs.client, document._self, item)
.then((updatedDocument) => {res.status(200).json(convertItem(updatedDocument));return;
});
})
.catch((error) => {
res.status(error.code).json(convertError(error));
});
}

I’m using the same Base64 encoding for the etag in the current document to ensure I can do a proper match. I could get DocumentDb to do all this work for me the options value in the driver replaceDocument() method allows me to specify an If-Match. However, to do that, I would need to still fetch the record (since I need the document link), so I may as well do the checks myself. This also keeps some load off the DocumentDb, which is helpful.

 

1.本站遵循行业规范,任何转载的稿件都会明确标注作者和来源;2.本站的原创文章,请转载时务必注明文章作者和来源,不尊重原创的行为我们将追究责任;3.作者投稿可能会经我们编辑修改或补充。

相关文章
  • MongoDb学习笔记

    MongoDb学习笔记

    2016-10-30 12:00

  • NuclearAtk 网络安全研究中心

    NuclearAtk 网络安全研究中心

    2015-11-24 10:53

  • 最严肃的安全原创平台

    最严肃的安全原创平台

    2015-10-14 15:41

网友点评