DynamoDb Nested Map Update - amazon-dynamodb

After working a bit with DynamoDb I’ve run into an issue that from what I’ve read so far is not really ideal for DynamoDb. So before I make the switch to RDS, I’d like to see if there’s anyway I can achieve what I need with DynamoDb. I’ve also thought about breaking this out into multiple tables for DynamoDb
Below of my Data schema. There is a list nested inside the item. I need to be able to append strings to the list.
{
“server-id”: “123345678”,
“server-name”: “my-server”
“topics”: [
{
“name”: “my-topic”,
“subscribers”: [] //This is what I need to append
}
]
}

Yes, this is possible.
var AWS = require('aws-sdk');
AWS.config.update({region: 'us-east-1'});
var ddb = new AWS.DynamoDB({apiVersion: '2012-08-10'});
var params = {
ExpressionAttributeNames: {
"#T": "topics",
"#S": "subscribers"
},
ExpressionAttributeValues: {
":vals": {
L: [
{ N: "123" },
{ N: "456" }
]
}
},
Key: {
'server-id': { S: '123345678' }
},
ReturnValues: "ALL_NEW",
TableName: 'dummy-table',
UpdateExpression: "SET #T[0].#S = list_append(#T[0].#S, :vals)"
};
ddb.updateItem(params, function(err, data) {
if (err) {
console.log("Error", err);
} else {
console.log("Success", data);
}
});

Related

Problems structuring and querying a table properly - DynamoDB

I am new to dynamoDB. I am having difficulty developing a table structure. I have data that can best be thought of as a folder structure. There are folders which are nested in parent folders. Most of the time, I will be querying for all folders with a given parent folder, however, there are times when I will be querying individual folders.
If I use the parent_id (parent folder) as the partition key and the id of the individual folder as the sort key, I believe that this creates a table where all related files are stored together and I can query them efficiently. However, I have questions.
First, the query "works" in that it returns the data, but is it written so that it queries the data correctly and is not merely scrolling through the whole table?
router.get("/api/children_folders/:parent_id", (req, res, next) => {
let parent_id = req.params.parent_id;
let params = {
TableName: tableName,
KeyConditionExpression: "parent_id = :pid",
ExpressionAttributeValues: {
":pid": parent_id,
},
ScanIndexForward: false,
};
docClient.query(params, (err, data) => {
if (err) {
console.log(err);
return res.status(err.statusCode).send({
message: err.message,
status: err.statusCode,
});
} else {
return res.status(200).send(data);
}
});
});
Second, if I want to query for individual tags, do I need to pass in a combination of the parent folder ID and the actual ID, or is this OK?
router.get("/api/folder/:folder_id", (req, res, next) => {
let tag_id = req.params.folder_id;
let params = {
TableName: tableName,
KeyConditionExpression: "folder_id = :fid",
ExpressionAttributeValues: {
":fid": folder_id,
},
Limit: 1,
};
docClient.query(params, (err, data) => {
if (err) {
console.log(err);
return res.status(err.statusCode).send({
message: err.message,
status: err.statusCode,
});
} else {
if (!_.isEmpty(data.Items)) {
return res.status(200).send(data.Items[0]);
} else {
return res.status(404).send();
}
}
});
});
I just feel like I am missing some thing here and I want to make sure that I am grabbing the data correctly.
The PK, should be something that would divide the load equally (ideally). I don't the fully picture of your problem but assuming you can chose a good parent folder as a partition key, then you can insert every file/dir with a sort key representing its full path
For example:
PK SK
/home /username/pictures/cat.jpg
This way if you want to get a specific item you can use the get item request
var params = {
Key: {
"PK": { "S": "/home" },
"SK": { "S": "/username/pictures/cat.jpg" }
},
TableName: tableName
};
var result = await dynamodb.getItem(params).promise()
Now if you want to list all the files in "/home/username/pictures" you can use begins with query
const params = {
TableName: 'tablenName',
KeyConditionExpression: '#PK = :root_path and begins_with(#SK, :sub_path)',
ExpressionAttributeNames:{
"#user_id": "root_path",
"#user_relation": 'sub_path'
},
ExpressionAttributeValues: {
":root_path": "/home",
":sub_path": "/username/pictures"
}
}

Add a number to a list item with DynamoDB

This is the DynamoDB table structure I'm working on:
{
"userId": "99999999-9999-9999-9999-999999999999",
"userProfile": {
"email": "myemail#gmail.com",
"firstName": "1234124",
"lastName": "123423",
},
"masterCards": [
{
"cardId": 101000000000001,
"cardImage": "logo.png",
"cardName": "VipCard1",
"cardWallet": "0xFDB17d12057b6Fe8c8c425D2DB88d8475674567"
},
{
"cardId": 102000000000002,
"cardImage": "logo.png",
"cardName": "VipCard2",
"cardWallet": "0xFDB17d12057b6Fe8c8c425D2DB88d8183454345"
},
{
"cardId": 103000000000003,
"cardImage": "logo.png",
"cardName": "VipCard3",
"cardWallet": "0xFDB17d12057b6Fe8c8c425D2DB88d8184345345"
}
],
}
I'm trying to increase the cardId field by one for the first list item with this Lambda function:
const dynamoDB = new AWS.DynamoDB({region: 'eu-central-1', apiVersion:'2012-08-10'});
const counterId="99999999-9999-9999-9999-999999999999"
const params = {
TableName:"FidelityCardsUsers",
Key: {"userId":{"S":counterId}},
UpdateExpression:"ADD #masterCards[0].#cardId :increment",
ExpressionAttributeNames:{
"#masterCards": "masterCards",
"#cardId": "cardId"
},
ExpressionAttributeValues:{":increment": {"N": "1"}}
}
dynamoDB.updateItem(params, function(err, data) {
if (err) {
console.log('error getting counter from DynamDB: ',err)
callback(err);
} else {
callback(null,data)
}
})
In return I get only a new top-level attribute named "mastercards[0].cardId[0]" with a value number set to 1.
I have tried to increment In an array and its work fine with AWS.DynamoDB.DocumentClient()
Example :
var AWS = require("aws-sdk");
var docClient = new AWS.DynamoDB.DocumentClient();
let params = {
TableName:'tableName',
Key: {
'venueId': 'VENUE_002'
},
UpdateExpression: "ADD #walk.#coordinates[0] :increment",
ExpressionAttributeNames: {
'#walk': 'walk',
'#coordinates': 'coordinates'
},
ExpressionAttributeValues: {
':increment': 1 // This is from the client
},
ReturnValues: 'UPDATED_NEW'
};
docClient.update(params, function (err, data) {
if (err) {
console.log('failure:updateShuttleDirection:failed');
console.log(err);
} else {
console.log('success:updateShuttleDirection:complete');
console.log(data);
}
});
Sample Data:
"walk": {
"coordinates": [
10,
20
],
"type": "Point"
},
I have tried to increment 10 to 11 and its work fine
Reading the doc here, it seems that:
the ADD action can only be used on top-level attributes, not nested
attributes.

How do i convert a single json record retrieved from firebase into an array

I am trying to take a single record from firebase to use in vuejs but I cant find out how to convert it to an array, if thats even what i should be doing.
my mutation
GET_CASE(state, caseId) {
state.caseId = caseId;
},
My action
getCase ({ commit, context }, data) {
return axios.get('http' + data + '.json')
.then(res => {
const convertcase = []
convertcase.push({ data: res.data })
//result below of what is returned from the res.data
console.log(convertcase)
// commit('GET_CASE', convertcase)
})
.catch(e => context.error(e));
},
I now get the following returned to {{ myCase }}
[ { "data": { case_name: "Broken laptop", case_status: "live", case_summary: "This is some summary content", contact: "", createdBy: "Paul", createdDate: "2018-06-21T15:20:22.932Z", assessor: "Gould", updates: "" } } ]
when all i want to display is Broken Laptop
Thanks
Example let obj = {a: 1, b: 'a'); let arr = Object.values(obj) // arr = [1, 'a']
async getCase ({ commit, context }, url) {
try {
let { data } = await axios.get(`http${url}.json`)
commit('myMutation', Object.values(data))
} catch (error) {
context.error(error)
}
}
But as I'm reading your post again, I think you don't want array from object. You want array with one object. So, maybe this is what you want:
async getCase ({ commit, context }, url) {
try {
let { data } = await axios.get(`http${url}.json`)
commit('myMutation', [data])
} catch (error) {
context.error(error)
}
}
Put this inside / after your .then
Object.keys(data).forEach(function(k, i) {
console.log(k, i);
});
With a response from Axios, you can get your data as:
res.data.case_name
res.data.case_number
....
Just build JavaScript object holding these properties and pass this object to your mutation. I think it is better than using an array.
const obj = {};
Object.assign(obj, res.data);
commit('GET_CASE', obj)
And in your mutation you do as follows:
mutations: {
GET_CASE (state, payload) {
for (var k in payload) {
if (payload.hasOwnProperty(k)) {
state[k] = payload[k]
}
}
}
}
Alternatively you can code your store as follows:
state: {
case: {},
...
},
getters: {
getCase: state => {
return state.case
},
....
},
mutations: {
GET_CASE (state, payload) {
state.case = payload
}
}
and you call the value of a case field form a component as follows:
const case = this.$store.getters.getCase
..... = case.case_name

How to access server aggregate on the client side within meteor

Bit of a noob question. I'm using meteor-native-mongo on the server to access the aggregate function in MongoDB, however, I'm not sure how I return and access the results on the client side. In the past subscribing and then accessing the collections on the client was pretty straightforward using the collection.find({}) function, however, I don't understand how to do it with the aggregate function. Can someone please explain.
Meteor.publish('companies', function(limit) {
db.collection('companies').aggregate([{ $group: { _id: { location: "$google_maps.geometry_location" }, companies: { $addToSet: { name: "$company_name" } }, count: { $sum: 1} } }, { $match: { count: { $gt: 1 } } }]).toArray((err, result) => {
console.log(result);
return result;
});
});
Use this.added, this.changed, this.removed from https://docs.meteor.com/api/pubsub.html#Subscription-added ...
Meteor.publish('companies', function(limit) {
var subscription = this;
db.collection('companies').aggregate([{ $group: { _id: { location: "$google_maps.geometry_location" }, companies: { $addToSet: { name: "$company_name" } }, count: { $sum: 1} } }, { $match: { count: { $gt: 1 } } }]).toArray((err, result) => {
subscription.added('companies-aggregate', 'geometry-grouping', {result: result});
});
});
// On the client:
var CompaniesAggregate = new Mongo.Collection('companies-aggregate');
// Inside a reactive context, like a helper
var result = CompaniesAggregate.findOne('geometry-grouping').result;
Naturally, to make it reactive, you'd have to know when the results of the aggregations would change. There is no automatic way to do that--you would have to resolve that logically, with your own code.
The best way to do that is to save the subscription variable in an array somewhere in a higher scope, and called changed on all the subscriptions for 'companies' for the geometry-grouping document, computing an updated result.
The commenter's solution won't be realtime; in other words, if one user makes a change to the e.g. company name or location, another user won't see those changes.

How to create and populate lists in DynamoDB

I wish to create an Item in DynamoDB that is a list. This is my code:
var list_update_params = {
TableName: "table01",
Key: {
"MachineID": {
"S": MachineID
},
"Hour": {
"S": Hour
}
},
UpdateExpression: "set var01_list = list_append(var01_list, :ot)",
ExpressionAttributeValues: {
":ot": {"L": [{"N": var01}]}
},
ReturnValues: "NONE"
};
dynamodb.updateItem(list_update_params, function(err, data) {
if (err) console.log(err, err.stack);
else console.log("Updated List to DynamoDB");
});
The problem is list_append expects the attribute var01_list to already be present, but I wouldn't know at the first insert. Is there a technique where it'll let me create an insert a List attribute if one doesn't exist and append to it in later calls?
Got the answer from a similar post here.
UpdateExpression: "set var01_list= list_append(if_not_exists(var01_list, :empty_list), :h)",
ExpressionAttributeValues: {
":h": {"L": [{"N":var01}]},
":empty_list": {"L": []}
},
The key was using if_not_exists with list_append. Didn't know that could be done in this matter

Resources