Value provided in ExpressionAttributeNames unused in expressions: keys: {#loan} - node.js

I am trying to query for an Asset item and filter it only if it is on loan. An asset is considered not on loan if the loan attribute does not exists or is null. Querying for the item via the CLI works as intended. The documentClient.query() however throws the error:
NodeJS:
documentClient.query({
TableName: "Asset",
ProjectionExpression: "id",
KeyConditionExpression: "#id = :id",
ExpressionAttributeNames: {
"#id": "id",
"#loan": "loan"
},
ExpressionAttributeValues: {
":id": "8b8ea8f5-196f-440c-aff3-87ab66eb9669",
":null": null
},
FilterExpresssion: "attribute_exists(#loan) and #loan <> :null"
}, (err, data) => {
if (err) {
console.error(err); // ---> error thrown
} else {
console.log(data);
}
});
ValidationException: Value provided in ExpressionAttributeNames unused in expressions: keys: {#loan}
CLI:
// values.json
{
":id":{"S":"8b8ea8f5-196f-440c-aff3-87ab66eb9669"},
":null":{"NULL":true}
}
aws dynamodb query \
--table-name Asset \
--projection-expression "id" \
--key-condition-expression "#id = :id" \
--expression-attribute-names '{"#id": "id", "#loan": "loan"}' \
--expression-attribute-values file://values.json \
--filter-expression "attribute_exists(#loan) and #loan <> :null"
{
"Items": [],
"Count": 0,
"ScannedCount": 1,
"ConsumedCapacity": null
}
The schema of the Asset table:
Type: AWS::DynamoDB::Table
TableName: Asset
Properties:
AttributeDefinitions:
-
AttributeName: "id"
AttributeType: "S"
KeySchema:
-
AttributeName: "id"
KeyType: "HASH"
BillingMode: PAY_PER_REQUEST
ProvisionedThroughput:
ReadCapacityUnits: 0
WriteCapacityUnits: 0

Have you tried this way?
ExpressionAttributeValues = { ":null", new AttributeValue { NULL = true } }
Cheers

Related

Lambda - Querying DynamoDB through Lambda

I have a table called Customers with attributes CustId(partition Key), Fname, Lname, Dob.
I created a secondary index called LastNameIndex on Lname with the following params:
{
TableName: 'Customers'
AttributeDefinitions: [
{
AttributeName: 'Lname',
AttributeType: 'S'
}
],
GlobalSecondaryIndexUpdates: [
{
Create: {
IndexName: "LastNameIndex",
KeySchema: [
{AttributeName: "Lname", KeyType: "HASH"}
],
Projection: {
"ProjectionType": "ALL"
},
ProvisionedThroughput: {
"ReadCapacityUnits": 1,"WriteCapacityUnits": 1
}
}
}
]
}
Lambda function (snippet) - I want to get all records with Lname=Connors
params = {
TableName: "Customers",
IndexName: "LastNameIndex",
ExpressionAttributeNames: {
"#FN": "Fname",
"#LN": "Lname",
"#DB": "Dob",
},
ExpressionAttributeValues: {
":a": {
S: "Connors"
}
},
KeyConditionExpression: "Lname = :a",
ProjectionExpression: "#FN, #LN, #DB"
};
Running the query
ddb.query(params).promise().then(
function(data) {
console.log("Customer:" + data.Item)
return data.Item;
},
function() {
console.log("No records found...")
}
);
I have a record with Lname = Connors.
But the query does not return me any records - any idea what's wrong with the params?
The query operation returns multiple items not a single item like getItem operation. so I think returning data.Items instead of data.Item should work just fine.
ddb.query(params).promise().then(
function(data) {
console.log("Customer:" + data.Items)
return data.Items;
},
function() {
console.log("No records found...")
}
);

DynamoDB - Delete item - The provided key element does not match the schema

I've gone through many answers on stack overflow for similar questions. But the problem is a bit different here. I know that I should be sending the key to delete the operation (pk or pk/sk). But in my case, I only have one key in schema (verified by describe-table)
{ .....
"TableName": "**********",
"KeySchema": [
{
"AttributeName": "user_id",
"KeyType": "HASH"
}
],
....}
The table has been create using following sam code
DynamoDbTable:
Type: AWS::DynamoDB::Table
Properties:
AttributeDefinitions:
- AttributeName: "user_id"
AttributeType: "S"
KeySchema:
- AttributeName: "user_id"
KeyType: "HASH"
ProvisionedThroughput:
ReadCapacityUnits: 5
WriteCapacityUnits: 5
SSESpecification:
SSEEnabled: True
TableName: !Ref TableName
Here are the params that I'm creating for the delete call
var docClient = new AWS.DynamoDB.DocumentClient({
apiVersion: '2012-08-10',
region: process.env.AWS_REGION
});
const deleteParams = {
TableName: process.env.TABLE_NAME,
Key: {
user_id: connectionData.Items[0].user_id
}
};
try {
let response = await docClient.delete(deleteParams).promise();
console.log('deleted');
} catch (err) {
return {
statusCode: 500,
body: 'Failed to disconnect: ' + JSON.stringify(err)
};
}
Any help will be much appreciated

How to insert to DynamoDb just if the key does not exist

I want to add id + some values to a DynamoDb just once. If the id exists already it should do nothing or update
I can go with
search
if not found > insert
if found > do nothing or update (for now do nothing is fine)
But hopfully there is a better way to do it. The id should be the key to check for.
That's the code in node:
const dynamodbParams = {
TableName: process.env.DYNAMODB_TABLE_BLICKANALYTICS,
Item: {
id: userId,
createdAt: timestamp
},
};
dynamoDb.put(dynamodbParams).promise()
.then(data => {
console.log('saved: ', dynamodbParams);
})
.catch(err => {
console.error(err);
});
I use this in yml. Don't know if there are options to set this up in yml
resources:
Resources:
DynamoDbTableExpenses:
Type: 'AWS::DynamoDB::Table'
DeletionPolicy: Retain
Properties:
AttributeDefinitions:
-
AttributeName: id
AttributeType: S
-
AttributeName: createdAt
AttributeType: N
KeySchema:
-
AttributeName: id
KeyType: HASH
-
AttributeName: createdAt
KeyType: RANGE
ProvisionedThroughput:
ReadCapacityUnits: 1
WriteCapacityUnits: 1
TableName: ${self:provider.environment.DYNAMODB_TABLE_BLICKANALYTICS}
You can do the whole thing with a single UpdateItem operation:
const dynamodbParams = {
TableName: process.env.DYNAMODB_TABLE_BLICKANALYTICS,
Key: {id: userId},
UpdateExpression: 'SET createdAt = if_not_exists(createdAt, :ca)',
ExpressionAttributeValues: {
':ca': {'S': timestamp}
}
};
dynamoDb.updateItem(params, function(err, data) {
if (err) {
console.log(err, err.stack);
} else {
console.log(data);
}
}
If you only want to do insert if not exists, you can easily do that with PutItem:
const dynamodbParams = {
TableName: process.env.DYNAMODB_TABLE_BLICKANALYTICS,
Item: {
id: userId,
createdAt: timestamp
},
ConditionExpression: 'attribute_not_exists(id)'
};
dynamodb.putItem(params, function(err, data) {
if (err) {
console.log(err, err.stack);
} else {
console.log(data);
}
}
You can come up with more complex ways how to set or update attributes in an item by combining the condition expressions and update expressions.
Note I have not fully tested the code, so please comment if there's any error, but it should work.

Date range query not works in dynamodb

I am new to aws dynamodb and hit the query as shown below
let params = {
TableName: tableName
};
if (from && to) {
params.FilterExpression = 'createdOn >= :from and createdOn <= :to';
params.ExpressionAttributeValues = { ":from": from, ":to": to }
}
return new Promise((resolve, reject) => {
docClient.scan(params, (err, data) => {
if (err) {
//do stuff
}else{//do stuff}
});
});
and here is my db schema
const ApplicationDataSchema = {
TableName: "data1", //config.get('aws_tables.APPLICATIONS_DATA'),
KeySchema: [
{ AttributeName: "id", KeyType: "HASH" },
{ AttributeName: 'createdOn', KeyType: 'RANGE' }
],
AttributeDefinitions: [
{ AttributeName: "id", AttributeType: "S" },
{ AttributeName: 'createdOn', AttributeType: 'N' }
],
ProvisionedThroughput: {
ReadCapacityUnits: 10,
WriteCapacityUnits: 10
}
}
is there any need to change in schema I am not getting the correct result.
createdOn field contains epoc time eg:"1548659664131"
If you want to use a filter expression with 2 dates, you're probably looking for the BETWEEN statement.
'#createdOn between :from and :to'
I suggest that you take a look at this guide on AWS for using dynamodb with javascript.

node.js: How to add non-key attribute in DynamoDB while table creation?

I am using dynamoDB local. I want to create a table with 6 attributes, only one of them is key. How do I do that? Specify the key attribute in keySchema and all attributes in AttributeDefinitions?
var params = {
TableName : "Movies",
KeySchema: [
{ AttributeName: "year", KeyType: "HASH"}, //Partition key
],
AttributeDefinitions: [
{ AttributeName: "year", AttributeType: "N" },
{ AttributeName: "title", AttributeType: "S" }
],
ProvisionedThroughput: {
ReadCapacityUnits: 10,
WriteCapacityUnits: 10
}
};
dynamodb.createTable(params, function(err, data) {
if (err) {
console.error("Unable to create table. Error JSON:", JSON.stringify(err, null, 2));
} else {
console.log("Created table. Table description JSON:", JSON.stringify(data, null, 2));
}
});
Are you receiving the following error?
One or more parameter values were invalid: Number of attributes in
KeySchema does not exactly match number of attributes defined in
AttributeDefinitions
This is because your AttributeDefinitions contains an attribute that is not defined in the KeySchema. If you're only going to use a HASH key, and not going to need a RANGE key, you can remove the title attribute from the AttributeDefinitions.
DynamoDB is schemaless so you don't need to include any non-key attribute definitions in AttributeDefinitions. You can add any additional attribute(s) when you put an item in your table (have to include Partition/Sort keys).
The following code will create a table with only a HASH (Partition) key:
var dynamodb = new AWS_SDK.DynamoDB();
var params = {
TableName : "MyNewTable",
KeySchema: [
{ AttributeName: "year", KeyType: "HASH"}, //Partition key
//{ AttributeName: "title", KeyType: "RANGE"}, //Sort key
],
AttributeDefinitions: [
{ AttributeName: "year", AttributeType: "N" },
// { AttributeName: "title", AttributeType: "S" }
],
ProvisionedThroughput: {
ReadCapacityUnits: 10,
WriteCapacityUnits: 10
}
};
dynamodb.createTable(params, function(err, data) {
if (err) {
console.error("Unable to create table. Error JSON:", JSON.stringify(err, null, 2));
} else {
console.log("Created table. Table description JSON:", JSON.stringify(data, null, 2));
}
For more info, you can refer to the AWS SDK documentation for the createTable function on the DynamoDB service.
Hope this helps!
Short answer: You can't.
You can't add non-key attributes while creating the table. You have to create the table first, then add non-key attributes using dynamodb.putItem() or dynamodb.batchWriteItem().
Example:
var params = {
TableName: 'Movies',
Item: {
'year': {N: '1994'},
'title': {S: 'Pulp Fiction'},
'description': {S: 'Two hitmen with a penchant for philosophical discussions.'},
}
}
dynamodb.putItem(params, function(err, data) {
if (err) {
console.log("Error", err);
} else {
console.log("Success", data);
}
});

Resources