I'm trying to model a "simple" table of 3 columns:
userId | topicId | views
The table should be able to:
have repeating userId
have repeating topicId
enforce unique combination of userId and topicId
In other words, there are many topicId's associated with a userId, and views will be a number that is incremented.
How would I create my indexes if I need to do the following queries:
get a list of records for provided userId
get and update one unique record for provided userId and topicId
Since you have more topicIds than userIds you should create your table with:
hash key - topicId
range key - userId
Add a global secondary index with:
hash key - userId
range key - topicId
You can then insert and update records using the combo topicId + userId and query for lists of topics for a specific user id.
Related
Let's say, I have Users writing reviews of Products.
User and Product are separate entities with their own ids.
Review is an entity with a composite id composed of userId and productId.
I have created a table review in DynamoDB with both userId and productId as HASH keys.
aws dynamodb create-table --table-name review \
--attribute-definitions \
AttributeName=user_id,AttributeType=S \
AttributeName=product_id,AttributeType=S \
--key-schema \
AttributeName=user_id,KeyType=HASH \
AttributeName=product_id,KeyType=RANGE \
--provisioned-throughput ReadCapacityUnits=10,WriteCapacityUnits=5
Thus making userId+productId the composite key.
The review data object is held against that key.
Querying for a review by user and product is fine.
But how do I query for all reviews by a user or all reviews for a product?
With a single parameter, e.g. if I do a query by single key conditional expression with just "#user_id = :userId" or just "#product_id = :productId"
I get an error of the form
Query condition missed key schema element: user_id
or
Query condition missed key schema element: product_id
I have created a table review in DynamoDB with both userId and productId as HASH keys.
You've created a composite primary key for your review table, which consists of a Partition Key of userId and a and a Sort Key of 'productId' . You did not create two HASH keys.
Logically, your review table will look something like this (I've made up some data for illustration purposes):
This table structure makes it easy to fetch reviews by user. Here's an example of a query for all reviews of USER#ABC
ddbClient.query(
"TableName": "<YOUR TABLE NAME>",
"KeyConditionExpression": "#userId = :userId",
"ExpressionAttributeValues": {
":userId": {
"S": "USER#ABC"
}
},
"ExpressionAttributeNames": {
"#userId": "userId"
}
)
This will return a collection of items reviewed by USER#ABC.
DynamoDB will not allow you to fetch items by only specifying the Sort Key (e.g. productId). You always need to provide the Partition Key. So how do you get a list of Users who have reviewed a given product?
If you want to search for all Users that have reviewed a single Product, you could introduce a global secondary index that swaps the Partition Key and Sort Key of your table. This pattern is known as an inverted index. Using my example from above, an inverted index would look like this:
This would allow you to fetch users by productId:
ddbclient.query(
{
"TableName": "<YOUR TABLE NAME>",
"IndexName": "reviews_by_product_index",
"KeyConditionExpression": "#productId = :productId",
"ExpressionAttributeValues": {
":productId": {
"S": "PRODUCT#456"
}
},
"ExpressionAttributeNames": {
"#productId": "productId"
}
}
)
This query would return a collection of two items representing reviews for PRODUCT#456.
When working with a composite primary key, you can search based on conditions of the sort key as long as you also specify the partition key. That's a mouthful, but it allows you to perform queries like (in pseudocode)
query where partition key = "USER#ABC" and sort key begins_with "PRODUCT"
Having the following table definition in DynamoDB
pk: userId (string)
sk: carId (string)
index: carModelIndex (string)
If I would like to query all the cars rented by a given user for a given model. How I would go about that?.
Please note I am using the javascript sdk for dynamodb.
I have three Tables:
Teachers
PK: A_pk
Students
PK: B_pk
Post
PK: C_pk
FK: A_pk
FK: B_pk
I have a Website page where users write different posts.
When teachers post in that group, I will insert data into the Post table like this:
A_pk = teacherName
C_pk = Post_text
When students post in that group, I will insert data into the Post table like this:
B_pk = studentName
C_pk = Post_text.
Reason is that I want to keep record which user posted data in my group.
Now the Question is how to insert record in Post table?
You cannot have one field in your table be a foreign key to two different tables. What you need to do is change your data structure to either hold your students and teachers in the same table and use that primary key in your Post table, or to have two columns in your Post table - one for the Teacher primary key and one for the Student primary key and populate the appropriate one based on who made the post.
There is absolutely no need nor reason to have the ID of two different tables within the same field. Doing so would be bad design.
Finally--- I found the Solution.
Actually its very simple... Like you can have nullable FK.so when you insert for student then A_PK can be null and vice versa. –
If teacher posted data in group than
if(A_pk != null) { Insert A_pk and Insert C_pk }
If student posted data in group than
else if(B_pk != null) { Insert B_pk and Insert C_pk }
Thanks alot Sir
KumarHarsh 1 hour ago
I have a bidding website, each user can bid his own price for a product.
The schema:
productId: S // the product, probably the Hash key
day: S // for example: "2016-08-05"
userId: S // the user id that offer his bid for this day
bidPrice: N
I don't know how to design the table(s), i need to be able to a query like this:
SELECT userId, bidPrice FROM product-history WHERE productId = "sony-tv" AND day between "2016-01-05" AND "2016-05-05"
However, the day field cannot be a range key because i can't store multiple rows per productId+day
I also need to be able to update the bidding price for a user... something like:
UPDATE product-history SET price = 12 WHERE productId = "sony-tv" and day="2016-01-05" and userId = "steve"
What the best way to approach this?
Here is the design for the above use case.
Hash Key : User Id
Range Key : Product Id
Each user can bid on a product id.
This design allows an user to bid on multiple product ids as well.
Create global secondary index (GSI) on product id and date:-
You can use the GSI to query the database using product id and date. While creating the GSI, there is an option to include only few attributes or all the attributes from main table.
GSI Link
Limitation:-
The user bidding history of a product id would not be available. For example, if an user is changing the bidding on a product id multiple times, it would not be available on this data model.
i have 100 records in collection,
collection name:'users'
{
"name":'senthilkumar',
"email":'senthily88#gmail.com', //HashKey
"age":21,
"created":1465733486137, //RangeKey-timestamp
}
i need to fetch records the following sql query wise
select * from users order by created desc limit 10
How i can get above query format records from DynamoDB
Dynamodb sorts the results by the range key attribute. You can set the ScanIndexForward boolean parameter to true for ascending or false for descending.
resource: http://docs.aws.amazon.com/amazondynamodb/latest/APIReference/API_Query.html
Use the KeyConditionExpression parameter to provide a specific value
for the partition key. The Query operation will return all of the
items from the table or index with that partition key value. You can
optionally narrow the scope of the Query operation by specifying a
sort key value and a comparison operator in KeyConditionExpression.
You can use the ScanIndexForward parameter to get results in forward
or reverse order, by sort key.
To Save Json Data to DynamoDB us put()
var Newparams = {
TableName: this.SuffleTableName,
Item: {
"userId": /* YOUR PRIMARY KEY */,
"addedAt": /* YOUR SORT KEY */,
"status": /* Additional Datas */,
}
}
Fetch Data From DynamoDB using Query()
QueryParam = {
TableName: 'YOUR TABLE NAME HERE',
IndexName: 'YOUR INDEX NAME HERE', //IF YOUR CREATED NEW INDEX
KeyConditionExpression: "UserId = :UserId ", //YOUR PRIMARY KEY
ExpressionAttributeValues: {
":UserId": UserId,
},
ScanIndexForward: false, //DESC ORDER, Set 'true' if u want asc order
ExclusiveStartKey: LastEvalVal, //Pagination - LastEvaluatedKeyPair
Limit: 10 //DataPerReq
}
If you want to return all rows in your table, you cannot use the query API, because that API requires you to provide a partition key value to filter your results by (i.e. assuming that your partition key is name you would only be able to use the query API to bring back the subset of results that have name = a given value, i.e. name= senthilkumar
If you want to return all rows in your table, you must use the Scan API: https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/SQLtoNoSQL.ReadData.Scan.html
Note that all results will be provided in ascending order by the value of the Range Key. You cannot reverse sort the contents with the Scan API. You would need to reverse your resultset in the application tier using whatever language you're writing your code in to turn the results upside down.
Scan does not scale well and it is not possible to use Scan to create a paginated, reverse sorted solution if your table contains items with unique partition keys.
If this is your situation, and if you want to return paginated + reverse sorted sets back from DynamoDB, you will need to re-consider the design of your table and which columns are the partition key/range key/index so that you can use the Query API.