DynamoDB Query: ScanIndexForward not working - amazon-dynamodb

Quick one I hope. I have a GSI on a table with a pk (composite string CAMP#g56gd-6…) and a sk (timestamp 166637384). When I apply ScanIndexForward to the query as true, it always returns the results in descending order.
Also a side issue is it doesn't seem to set the applied Limit to the results. Always returns the full list of matched items. Any ideas?
const campaignIdParams: DynamoDB.DocumentClient.QueryInput = {
TableName: Table.CAMPAIGNS,
IndexName: TableIndexes.CAMPAIGNS.skCreatedAt,
KeyConditionExpression: 'sk = :sk',
ExpressionAttributeValues: {
':sk': parsedSK,
},
ProjectionExpression: 'pk',
ScanIndexForward: sortDir.localeCompare('<') === 0 ? false : true,
ReturnConsumedCapacity: 'TOTAL',
Limit: +limit
}

Related

DynamoDB update map value only if versions match

I have the following schema:
{
balance: {
customData: string,
version: number
}
}
I need to update balance.customData only if version == 2. Once update operation succeeds, I need to increment version by 1.
I tried the following update transaction
Update: {
Key: {
'pk': pk,
'sk': sk
},
TableName: this.config.getTable(),
UpdateExpression: 'SET #balance.#customData = :customData, #balance.#version = #balance.#version + :incr',
ConditionExpression: '#balance.#customData.#version = :version',
ExpressionAttributeNames: {
'#customData': 'customData',
'#balance': 'balance',
'#version': 'version'
},
ExpressionAttributeValues: {
':customData': balance.customData,
':version': balance.version,
':incr': {'N': 1},
}
}
But when I run this, I get ValidationException: Invalid UpdateExpression: Incorrect operand type for operator or function; operator or function: +, operand type: M
I assume I'm not providing correct syntax to increment map value.
Even if I remove increment part
Update: {
Key: {
'pk': pk,
'sk': sk
},
TableName: this.config.getTable(),
UpdateExpression: 'SET #balance.#customData = :customData,
ConditionExpression: '#balance.#customData.#version = :version',
ExpressionAttributeNames: {
'#customData': 'customData',
'#balance': 'balance',
'#version': 'version'
},
ExpressionAttributeValues: {
':customData': balance.customData,
':version': balance.version
}
}
I still get ValidationError with reason Other, which is not very helpful. Can you please help me understand what I'm doing wrong? Thanks

Why do I get empty list when doing filter?

I have a table 'test-table':
id (string) - primaryKey
type (string)
I have items like this in that table, for example:
34 AWC
56 BDE
I want to do scan table and filter by type:
I use:
async getItems(typeInput) {
const params: ScanCommandInput = {
TableName: "test-table",
FilterExpression: "type in (:type)", // also tried with type = :type
ExpressionAttributeValues: { ":type": { "S": typeInput } },
};
return await dynamodbdocumentclient.send(new ScanCommand(params));
}
I get as a result empty Items. Why ?
You appear to be using the DocumentClient, which automatically marshalls attribute values from their native JavaScript type. You do not need to wrap all values in {'S': 'xxx'}, {'N': '999'}, etc. Use ExpressionAttributeValues: { ":type": typeInput }.

Can't query on DynamoDB records where attribute is null

Can't get query on DynamoDB null attributes.
I have below query, I am getting NameError: name 'true' is not defined
const filter = {
FilterExpression: 'id = :null',
ExpressionAttributeValues: {
':null': true,
},
};
You need to specify the type of null as well.
Try this :
const filter = {
FilterExpression: 'id = :null',
ExpressionAttributeValues: {
':null': {BOOL: true}
}
};

Dynamoose - How to Query Two GSI on the same Key?

I have a key in dynamo that has two Global Secondary Indexes with different range keys. Like this:
const productSchema = new Schema(
{
productCategory: {
type: String,
index: [{
global: true,
rangeKey: 'serialNumberEnd',
name: 'productCategory',
throughput: { read: 1, write: 1 },
project: ['quantity'],
},
{
global: true,
rangeKey: 'orderType',
name: 'openOrders',
throughput: { read: 1, write: 1 },
project: true,
}],
},
{
throughput: { read: 1, write: 1 },
useNativeBooleans: true,
saveUnknown: true,
},
);`
Trying to use the 'name' does not seem to be the answer.
Resource.query('openOrder').eq(id)
How am I supposed to distinguish between the two GSI's on the same Key in a resource when constructing a query?
EDIT - Added additional context to the schema, moved answer to the answer section
In this case you don't want to be using the name property of the index. You want to be using the actual property to do this.
For example:
Resource.query('openOrder').eq(id)
.where('serialNumberEnd').lt(5)
I don't know your entire schema so it's not an exact match of what you want to do. But something like that should work.
You can also look at this test in the source code for an example of using multiple indexes on one property and querying.
const result = await Product.query(
{
hash: { productCategory: { eq: 'tags' } },
range: { orderType: { eq: 'sale' } },
},
{ indexName: 'openOrders' },).exec();
I was a bit slow at getting to this syntax. Hope this helps someone else.
EDIT: A little cleaner syntax/more context to the schema
const result = await Product.query('productCategory', { indexName: 'openOrders' }).eq('tags')
.where('orderType').eq('purchase')
.exec();
I had no luck getting Dynamoose to recognize the correct index based on the Range found in a 'where' statement. The { indexName: 'value' } is needed.

Querying DynamoDB without Primary Key

I need to query a DynamoDB table by a key different than its Primary Key. I tried to create a Global Secondary Index for it. However I get this error: "query key condition not supported dynamodb". By seeing some examples, it looks like I can't query by a secondary index unless I also include the primary index/key, is this correct? Let's say I need to query by all employees that work in a certain city, can I do that without the employeeID?
Updated info
Maybe my index is not created as it should?
Table info:
id-->Primary partition key
primary sort key-->name
GSI:
Partition Key/primary key--> city
Projected-->All
When I query from node I sent as a parameter the city, and the index name:
const filter = { city: city};
return this.getRecordsFromDb(filter, { IndexName: "myIndexName" })
.then(records => __.head(records));
Note:- As you have not provided the full code, it is difficult to simulate and identify the issue. However, I have created the similar tables and indexes. It works fine for me. You can refer the below code for more details.
Here is the table create script and query the index.
You can change the table name and index name if required. I have followed the same key attributes structure that you have mentioned on post.
This has been tested and working fine.
1) Create table 'city' with index 'city_index':-
var params = {
TableName: 'city',
KeySchema: [ // The type of of schema. Must start with a HASH type, with an optional second RANGE.
{ // Required HASH type attribute
AttributeName: 'id',
KeyType: 'HASH',
},
{ // Required HASH type attribute
AttributeName: 'name',
KeyType: 'RANGE',
}
],
AttributeDefinitions: [ // The names and types of all primary and index key attributes only
{
AttributeName: 'id',
AttributeType: 'S', // (S | N | B) for string, number, binary
},
{
AttributeName: 'name',
AttributeType: 'S', // (S | N | B) for string, number, binary
},
{
AttributeName: 'city',
AttributeType: 'S', // (S | N | B) for string, number, binary
},
],
ProvisionedThroughput: { // required provisioned throughput for the table
ReadCapacityUnits: 400,
WriteCapacityUnits: 400,
},
GlobalSecondaryIndexes: [ // optional (list of GlobalSecondaryIndex)
{
IndexName: 'city_index',
KeySchema: [
{ // Required HASH type attribute
AttributeName: 'city',
KeyType: 'HASH',
}
],
Projection: { // attributes to project into the index
ProjectionType: 'ALL' // (ALL | KEYS_ONLY | INCLUDE)
},
ProvisionedThroughput: { // throughput to provision to the index
ReadCapacityUnits: 400,
WriteCapacityUnits: 400,
},
},
// ... more global secondary indexes ...
],
};
dynamodb.createTable(params, function(err, data) {
if (err){ console.log("error :" +JSON.stringify(err));} // an error occurred
else console.log("success :" +JSON.stringify(data)); // successful response
});
2) Insert some data to city table
3) Query using index:-
var docClient = new AWS.DynamoDB.DocumentClient();
var table = "city";
var params = {
TableName : table,
IndexName : 'city_index',
KeyConditionExpression : 'city = :cityVal',
ExpressionAttributeValues : {
':cityVal' : 'london'
}
};
docClient.query(params, function(err, data) {
if (err) {
console.error("Unable to read item. Error JSON:", JSON.stringify(err,
null, 2));
} else {
console.log("GetItem succeeded:", JSON.stringify(data, null, 2));
}
});
This is my implementation with Node.js (of querying by another field) with scan:
var params = {
TableName: 'TableName',
FilterExpression: 'AnotherFieldName = :email',
ExpressionAttributeValues: {
":email": { S: emailISearchFor }
}
};
ddb.scan(params, function(err, data){
if(err){
...
} else {
if(data.Items.length > 0){ // here is the info
valueIWant = data.Items[0].PrimaryKeyName.S;
}
}
});

Resources