The DynamoDB.DocumentClient
automatically marshals & unmarshals values between JavaScript types DynamoDB's more descriptive AttributeMap
type. However, when working with an Item that has a StringSet
attribute, it does not seem to do the conversion automatically.
When adding a StringSet
attribute to the table using DocumentClient
, I use the createSet(...)
method to convert the array to a Set. When retrieving the value back, what is the inverse of createSet(...)
? Is the best practice to just access the Set's .values
directly? And if so, is that documented somewhere?
Here's sample code adding an Item with a StringSet
attribute, then retrieving that item:
const docClient = new DocumentClient();
const TableName = "StringSets-Example";
const PK = "Names";
const Values = ["Peter", "Paul", "Mary"];
const putParams = {
TableName,
Item: {
PK,
names: docClient.createSet(Values)
}
}
await docClient.put(putParams).promise();
// ... some time later, I can retrieve the value with ...
const getParams = {
TableName,
Key: { PK }
}
const result = await docClient.get(getParams).promise();
The result.Item
there is a Set
object, whereas I would expect it to be the same array I passed into createSet(...)
.
If interested in seeing this live, this repo has a fully-functioning example. Clone it, npm install
, and run index.js
and you'll see something like:
$ ./index.js
Running On: darwin 19.6.0
Node version: v12.20.0
AWS SDK version: 2.799.0
-------------------------
Creating table "StringSets-Example"
Waiting for "StringSets-Example" status to be "ACTIVE"
Table status is: CREATING
Table status is: ACTIVE
Put String Set "["Peter, "Paul, "Mary"]" into "StringSets-Example" with key "Names" and attribute "names"
Retrieved Item with key "Names" from "StringSets-Example"
The raw Item: {
PK: 'Names',
names: Set {
wrapperName: 'Set',
values: [ 'Mary', 'Paul', 'Peter' ],
type: 'String'
}
}
The raw Item.names.values: [ 'Mary', 'Paul', 'Peter' ]
-------------------------
Done. To clean up, run:
./src/deleteTable.js
The best solution I have here is to avoid the DocumentClient
and the createSet(...)
method. Here's a sample using AWS SDK V3:
const key = { PK: `SampleNames`, SK: `SampleNames` };
const names = new Set([`Peter`, `Paul`, `Mary`]);
const item = { ...key, names };
const marshalledItem = marshall(item);
console.log(`Raw item: ${inspect(item)}`)
console.log(`Marshalled item to PUT: ${inspect(marshalledItem, { depth: 4 })}`)
const client = new DynamoDBClient({});
await client.send(new PutItemCommand({
TableName: tableName,
Item: marshalledItem,
}));
const { Item } = await client.send(new GetItemCommand({
TableName: tableName,
Key: marshall(key),
}));
console.log(`Returned item: ${inspect(Item, { depth: 4 })}`);
console.log(`Unmarshalled returned item: ${inspect(unmarshall(Item))}`);
The console output from there is:
Raw item: {
PK: 'SampleNames',
SK: 'SampleNames',
names: Set { 'Peter', 'Paul', 'Mary' }
}
Marshalled item to PUT: {
PK: { S: 'SampleNames' },
SK: { S: 'SampleNames' },
names: { SS: [ 'Peter', 'Paul', 'Mary' ] }
}
Returned item: {
PK: { S: 'SampleNames' },
SK: { S: 'SampleNames' },
names: { SS: [ 'Mary', 'Paul', 'Peter' ] }
}
Unmarshalled returned item: {
PK: 'SampleNames',
SK: 'SampleNames',
names: Set { 'Mary', 'Paul', 'Peter' }
}
... which makes a lot more sense to me. I expect using the marshall
/unmarshall
methods from AWS SDK V2's Converter module would work similarly.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With