Stuffing maps into DynamoDB
posted 2020.10.02 by Clark Wilkins, Simplexable

The following is a simple way to (a) instantiate an object (map) in a document record if it's not in place yet, or (b) add a dependent object (map) to an existing object in the record. This gets around problems with updating

documentId.views.thisUser...
where documentId does not have a views.thisUser attribute yet.

// establish the route for Express.js

router.post("/log-view", async (req, res) => {
const { itemId, itemType, userId } = req.body;


// itemType is determining which DDB table I am working on here...

if (itemType === 3) {
params = {
TableName: "healthica-sales-discussions",
KeyConditionExpression: "#i = :i",
ExpressionAttributeNames: { "#i": "id", "#v": "views" },
ExpressionAttributeValues: { ":i": itemId },
ProjectionExpression: "#v",
};
}


// getData gets the document record using the parameters above...

var data = await getData(params, res, "returnData");
var { Items } = data;
var viewTime = new Date().toUTCString();


// If my main counter field is not there, the entire object
// (aka "map") is not valid, so define it

var views = Items[0].views || { total: 0 };
if (isNaN(views.total)) {
views = { total: 0 };
}


// This is a backup in case the total field has been corrupted

if (isNaN(views.total)) {
views = { total: 0 };
}


// If I have a view count for this user, increment by one.
// Otherwise, let's start at 1.

try {
var userViews = views[userId].total + 1;
} catch (err) {
var userViews = 1;
}


// Total views always increments by one.

views.total += 1;
views[userId] = { total: userViews, latest: viewTime };


// Now update where the entire views object is replaced.

params = {
TableName: "healthica-sales-discussions",
Key: { id: itemId },
ExpressionAttributeNames: { "#v": "views" },
ExpressionAttributeValues: { ":v": views },
UpdateExpression: "SET #v = :v",
};
updateRecord(params, res);
});