DynamoDB retrieve only attribute values - amazon-web-services

I have a table that has userId as the PK and a single attribute called userToken.
I have written a batchGet() function to return all the userTokens for specific userIds, however it returns it like this:
[ { userToken: '1234' },
{ userToken: '5678' } ]
I'd like it to just return the values since I already know what the attribute name will be:
['1234', '5678']
How would I go about doing so?
const params = {
RequestItems: {
UserTokens: {
Keys: userIds,
AttributesToGet: [
'userToken'
]
}
}
};
db.batchGet(params, function(err, data) {
if (err) {
console.log("Error", err);
} else {
console.log(data.Responses);
sendNotifications(data.Responses);
}
});

DynamoDB always returns the attribute name and value.
You can easily filter this on the client side.
val = [ { userToken: '1234' }, { userToken: '5678' } ];
reducer = (accumulator, currentVal) => {
accumulator.push(currentVal.userToken);
return accumulator;
}
console.log(val.reduce(reducer, []));

Related

How to use where in condition in dynamodb filterexpression

I am learning dynamodb and I am trying how to fetch items with status 0 and 1 but when i write the below query, it is throwing error "Error ValidationException: Invalid FilterExpression: Syntax error; token: ":user_status_val", near: "IN :user_status_val". Could any one please help in fixing this issue.
const checkUserExists = (req) => {
return new Promise((resolve,reject) =>{
var searchParams = {};
if(req.body.email != ""){
searchParams = {
FilterExpression : "#email = :e AND #user_status IN :user_status_val",
ExpressionAttributeNames: {
"#user_status": "status",
"#email" : "email",
},
ExpressionAttributeValues: {
':user_status_val' : req.body.status,
':e' : req.body.email,
},
}
}
var params = {
Select: "COUNT",
TableName: 'register'
};
var finalParams = {...searchParams, ...params}
DynamoDB.scan(finalParams, function(err, data) {
if (err) {
console.log("Error", err);
} else {
console.log(data);
//res.send(data);
return resolve(data);
}
});
});
}

BatchWriteItemCommand with AWS.DynamoDB class using AWS SDK V3 in Nodejs

I have been trying for hours to perform a DynamoDB DeleteRequest using BatchWriteItemCommand but I keep getting the following error:
Error ValidationException: 1 validation error detected: Value null at 'requestItems.td_notes_sdk.member.1.member.deleteRequest.key' failed to satisfy constraint: Member must not be null
This is what my table looks like:
Partition key: user_id (string)
Sort key: timestamp (number)
DynamoDB Screenshot
This is what my code looks like:
// Import required AWS SDK clients and commands for Node.js
import {
DynamoDBClient,
BatchWriteItemCommand,
} from "#aws-sdk/client-dynamodb";
// Set the parameters
export const params = {
RequestItems: {
"td_notes_sdk": [
{
DeleteRequest: {
Item: {
Key: {
user_id: { S : "bb" },
timestamp: { N : 2 },
},
},
},
},
],
},
};
export const run = async () => {
const ddbClient = new DynamoDBClient({ region: "us-east-2" });
try {
const data = await ddbClient.send(new BatchWriteItemCommand(params));
console.log("Success, items inserted", data);
return data;
} catch (err) {
console.log("Error", err);
}
};
run();
Here are some resources that I've been trying to follow along with:
Resource 1: Writing items in Batch Example
Resource 2: AWS Javascript SDK v3 Documentation
Update: BatchWrite PutRequest work with the code below, so I know that the structure of my keys/attributes is closer to being correct. Still does not work for DeleteRequest.
export const params = {
RequestItems: {
"td_notes_sdk": [
{
PutRequest: {
Item: {
user_id: { "S": "bb" },
timestamp: { "N": "5" },
},
},
},
],
},
};
You don't supply an Item when deleting an item. You supply a Key.
Here is a working example:
const params_delete = {
RequestItems: {
"td_notes_sdk": [
{
DeleteRequest: {
Key: {
user_id: { S: "bb" },
timestamp: { N: "2" },
},
},
},
],
},
};
const delete_batch = async () => {
const ddbClient = new DynamoDBClient({ region: "us-east-2" });
try {
const data = await ddbClient.send(new BatchWriteItemCommand(params_delete));
console.log("Success, item deleted");
return data;
} catch (err) {
console.log("Error", err);
}
};
delete_batch();

UpdateExpression: Add other attribute's value to list

Given the following DynamoDB document:
{
"myobject" : {"foo" : "bar"},
"mylist" : [{"some" : "stuff}]
}
My goal is to update this document to get the following result:
{
"myobject" : {"foo" : "bar"},
"mylist" : [{"some" : "stuff}, {"foo" : "bar"}]
}
My request's params look like this:
let params = {
TableName: doctorSlotsTable,
Key: {
hashKey: hash,
rangeKey: range
},
UpdateExpression: 'SET mylist = list_append(if_not_exists(mylist, :empty_list), [myobject])',
ExpressionAttributeValues : {
':empty_list' : []
},
ReturnValues : "UPDATED_NEW"
};
This obviously does not work because the [ in the list_append triggers a syntax error.
Is there any solution to achieve that without having to get the data in a previous request and add it manually to the list ?
Unfortunately you cannot use an attribute name as an operand to list_append(...) unless that attribute is itself a list. The best you can do I believe would be to store myobject in the proper type up front, and then update it as expected.
Since storage is cheap & network/compute are expensive here, you could even duplicate the data to have one of them in the right form.
Here's a full example, where createTable() and deleteTable() do exactly what you think:
const PK = 'the item';
async function createObjAndList() {
const docClient = new DocumentClient();
const myObject = { foo: "bar" };
const theItem = {
PK,
myObject,
myObjectAsList: [ myObject ],
myList: [ { some : "stuff" } ],
};
const putParams = {
TableName,
Item: theItem
}
await docClient.put(putParams).promise();
console.log(`Put item ${util.inspect(theItem)}`);
}
async function updateListWithObject() {
const docClient = new DocumentClient();
const updateParams = {
TableName,
Key: { PK },
UpdateExpression: `SET #myList = list_append(if_not_exists(#myList, :emptyList), #myObjectAsList)`,
ExpressionAttributeNames: {
'#myList': 'myList',
'#myObjectAsList': 'myObjectAsList',
},
ExpressionAttributeValues: {
':emptyList': [],
}
}
await docClient.update(updateParams).promise();
console.log(`Updated list to include object`);
}
async function getObjAndList() {
const docClient = new DocumentClient();
const results = await docClient.get({ TableName, Key: { PK }}).promise();
console.log(`Item is now: ${util.inspect(results.Item)}`);
}
if (module === require.main) {
(async () => {
try {
await createTable();
await createObjAndList()
await updateListWithObject();
await getObjAndList();
} catch (err) {
console.log(`Error: ${err.message}`);
} finally {
await deleteTable();
}
})();
}
The output from this is:
Put item {
PK: 'the item',
myObject: { foo: 'bar' },
myObjectAsList: [ { foo: 'bar' } ],
myList: [ { some: 'stuff' } ]
}
Updated list to include object
Item is now: {
myList: [ { some: 'stuff' }, { foo: 'bar' } ],
myObject: { foo: 'bar' },
PK: 'the item',
myObjectAsList: [ { foo: 'bar' } ]
}

Apollo Server - how to create and add object in resolver when type isn't available in namespace

In the following example, I am attempting to create a post and add it to the Dictionary 'post'. How is the Mutation expected to create, add to the hash, and return the type of the item created when the item type isn't available to the namespace of the resolver?
mutation createPost {
createPost(input: {name: "Post Name"}){
name
}
}
index.js:
const { ApolloServer, gql } = require('apollo-server');
const dictionary = {};
const typeDefs = gql`
input PostSpecInput {
name: String
}
type PostSpec {
id: ID!
name: String
}
type Mutation {
createPost(input: PostSpecInput): PostSpec
}
type Query {
post_specs: [PostSpec]
}
`;
const resolvers = {
Query: {
post_specs: () => Object.keys(dictionary).map(function(key){
return dictionary[key];
})
},
Mutation: {
createPost(parent, args, context, info) {
var id = require('crypto').randomBytes(10).toString('hex');
const postSpec = new PostSpec(id, args.input);
posts_mock_database[id] = args.input;
return postSpec;
}
}
}
const server = new ApolloServer({typeDefs, resolvers})
server.listen().then(({url}) => {
console.log(`Server Ready at ${url}`);
})
Error:
{
"errors": [
{
"message": "PostSpec is not defined",
"locations": [
{
"line": 2,
"column": 3
}
],
"path": [
"createPost"
],
"extensions": {
"code": "INTERNAL_SERVER_ERROR",
"exception": {
"stacktrace": [
"ReferenceError: PostSpec is not defined",
" at createPost (index.js:38:34)",
Type definitions are not classes nor object instances, they are just for enforcing type. Even if you were in the namespace, calling 'new' would not work. Here is the solution for your mock database:
Mutation: {
createPost(parent, args, context, info) {
var id = require('crypto').randomBytes(10).toString('hex');
const newPostSpec = { id: id, name: args.input.name }
posts_mock_database[id] = newPostSpec;
return newPostSpec;
}
}

How can i parse express query param to mongodb regex query?

I try to build a test backend with express.
And got a problem that i cannot seem to overcome.
Trying to give the query object $regex param a value from the req.query.name. But it only returns an empty array. When i console.log(laptopName) it outputs the right value that is being given as a query param.
If i just replace $regex: laptopName with a concrete value $regex: 'vivobook' i do get results.
With $regex: laptopName console.log looks like this { name: { '$regex': '"vivobook"', '$options': 'i' } }
With $regex: 'vivobook' console.log looks like this: { name: { '$regex': 'vivobook', '$options': 'i' } }
app.get("/api/v1/laptops", (req, res) => {
MongoClient.connect(mongo_url, { useNewUrlParser: true }, (err, client) => {
const db = client.db("laptop_backend");
const collection = db.collection("laptops");
let laptopName = req.query.name;
let query = {
name: {
$regex: laptopName,
$options: 'i'
}
};
collection
.find(query)
.toArray()
.then(response => res.status(200).json(response))
.catch(err => console.error(err));
});
});
Try getting laptop name directly from params such as
let { name } = req.query;
let query = {
name: {
$regex: name,
$options: 'i',
}
};
if you are to modify completely i suggest changing code without promise.
app.get("/api/v1/laptops", async (req, res) => {
try {
const client = await MongoClient.connect(mongo_url, { useNewUrlParser: true });
const db = client.db('laptop_backend');
const collection = db.collection('laptops');
const { name } = req.query;
const query = {
name: {
$regex: name,
$options: 'i',
},
};
const result = await collection.find(query);
res.status(200).json({ result });
} catch (e) {
console.log(e);
res.status(400).json({ error: true });
}
});