I am new to DynamoDB, i have a table: post_reaction (it stores user's reaction on a post)
PK: postId
SK: reactionType#timestamp#userId
userId: userId
PK | SK | userId |
---|---|---|
123 | 1#1676444573#324 | 324 |
Since my SK is non-deterministic (because of timestamp), is there any way to avoid duplicates? Same user cannot give same reactionType on the same Post
I have tried using ConditionExpression attribute_not_exists, but not working
The conditions for conditional item changes are only enforced on the item being changed and not across other items as that wouldn't scale well. An exception are transactions, which are a different topic.
I assume you have the timestamp for sorting purposes or making the record unique.
What you want to achieve can be done by using a global secondary index, I'm going to call it GSI1 with GSI1PK as its partition and GSI1SK as its sort key:
PK | SK | GSI1PK | GSI1SK | DATA |
---|---|---|---|---|
POST#123 | USER#123#REACTION#1 | POST#123 | REACTION#1#TS#20230215 | reactionType: 1 |
POST#123 | USER#123#REACTION#2 | POST#123 | REACTION#2#TS#20230213 | reactionType: 2 |
POST#123 | USER#456#REACTION#2 | POST#123 | REACTION#2#TS#20230214 | reactionType: 2 |
Your business rule that you need to enforce appears to be:
A user can only react to a post with a given reaction type exactly once.
Given this table structure you try to put the item into the base table if it doesn't exist. This will enforce your constraint as the combination of partition and sort key needs to be unique in the base table.
You can use the global secondary index to get all reactions for a post. Partition and Sort Key combinations in GSIs don't need to be unique, so you can query GSI1 with the Post Id and get all the reactions. You could still append a timestamp to GSI1Sk if you need sorting.
The drawback is higher costs because of the GSI.
And alternative (simpler) setup looks like this and omits the GSI at the cost of not being able to sort reactions by timestamps:
PK | SK | DATA |
---|---|---|
POST#123 | REACTION#1#USER#123 | reactionType: 1 |
POST#123 | REACTION#1#USER#456 | reactionType: 1 |
POST#123 | REACTION#2#USER#123 | reactionType: 2 |
You can again do your conditional PutItem requests as above to enforce the constraint.
To get all reactions for a post you do: Query: PK = Post#<id> & SK starts_with(REACTION#)
To get all reactions of type x for a post you do: Query: PK = Post#<id> & SK starts_with(REACTION#<x>#)