简体   繁体   中英

DynamoDB: How can I create a table with nested JSON structure?

I want to create a table in dynamoDB with below structure.

{
  "CartId": 123,
  "UserId": 356,
  "CartItems": [
    {
      "ProductId": 100,
      "Quantity": 50
    },
    {
      "ProductId": 121,
      "Quantity": 51
    }
  ]
}

Everywhere in tutorials and documents it says that we can only have below type of attributes in the table:

  1. Set of Strings

  2. Set of Numbers

  3. Set of Binary

I can't think of a way to store above structure in DynamoDB. Could you please help out?

I am using object mapper Api of java. It would be great if you can also tell me how can I create a class which can be mapped to this particular table structure.

Simplest way is use @DynamoDBDocument

  1. Add Maven dependency
<dependency>
        <groupId>com.amazonaws</groupId>
        <artifactId>aws-java-sdk-dynamodb</artifactId>
        <version>1.11.186</version>
</dependency>
  1. Create POJO
@DynamoDBTable(tableName = "Customer")
public class Customer
{ 
    @DynamoDBHashKey
    @DynamoDBAutoGeneratedKey
    private String id;

    private String firstName;

    private List<Foo> fooList;
}

@DynamoDBDocument
public static class Foo {
    private String name;
}
  1. Create a repository
@EnableScan
public interface CustomerRepository extends CrudRepository<Customer,String>

Then call customerRepository.save(customer) . The result will be like this:

{
  "firstName": "Test",
  "fooList": [
    {
      "name": "foo"
    },
    {
      "name": "foo2"
    }
  ],
  "id": "e57dd681-8608-4712-a39a-f3e0f31a5e27"
}

Posting to an old question because I did not think the solution was easy to find. Hope this helps someone.

Step 1: Create a complex Java object that mirrors the structure desired.

List<HashMap<String, Integer>> cartItems = new ArrayList<HashMap<String, Integer>>();
HashMap<String, Integer> item1 = new HashMap<String, Integer>();
item1.put("ProductId", 100);
item1.put("Quantity", 50);
cartItems.add(item1);
HashMap<String, Integer> item2 = new HashMap<String, Integer>();
item2.put("ProductId", 121);
item2.put("Quantity", 51);
cartItems.add(item2);

Step 2: Update DynamoDB item with the complex object.

I use a helper method:

private void updateAttribute(int id, String newAttribute, Object newValue){
    Map<String, Object> newValues = new HashMap<String, Object>();
    newValues.put(":value", newValue);

    UpdateItemSpec updateItemSpec = new UpdateItemSpec()
            .withPrimaryKey("id", id)
            .withUpdateExpression("set " + newAttribute + " = :value")
            .withValueMap(newValues);

    siteTable.updateItem(updateItemSpec);
}

and then call thus:

updateAttribute(123, "CartItems", cartItems);

The newly added cart items attribute displays in DynamoDB like:

{
 "CartItems": [
   {
     "ProductId": 100,
     "Quantity": 50
   },
   {
     "ProductId": 121,
     "Quantity": 51
   }
 ]
}

I have not tested an upsert scenario. In the past, upsert functionality did not seem to be present: https://forums.aws.amazon.com/thread.jspa?threadID=162907

Regarding reads of deeply nested items: http://docs.aws.amazon.com/amazondynamodb/latest/developerguide/Expressions.AccessingItemAttributes.html#DocumentPaths

You can store JSONs in Dynamodb as Strings. It all depends on what you want to do with the data and how to retrieve it.

The DynamoDB Java API for instance, introduces Marshaller objects that transforms any Java object into a String so you can store it and fetch it automagically from a DynamoDB attribute.

If you have the JSON in a string, you can simply call

table.putItem(Item.fromJSON(jsonString));

The JSON needs to contain keys/values for the partition and sort key. For a conditional put, use

table.putItem(Item.fromJSON(jsonString), conditionString, nameMap, valueMap)

Not sure if these data types were available when this question was asked (there is a good chance that they were not) but these days you'd use the List datatype for the CartItems and each cart item would be of Map datatype.

Reference: DynamoDB Data Types

Bit Late to the party but would like to share this. I was able to store a deep nested Json Doc by defining the column attribute as below in my Java Entity.

@DynamoDBAttribute
@DynamoDBTypeConvertedJson
private List<Map<String,Object>> pageData;

Input Json:

 {
    "userId": 359628,
    "platform": "learn-web",
    "inactive_duration": 0,
    "total_time": 15,
    "device_type": "web",
    "page_data": [{
        "page_details": {
            "page_type": "segmentView",
            "time": 15,
            "segment_id": 65590,
            "session_id": 13140,
            "module_id": 4363
        },
        "items": [{
            "type": "component",
            "id": 267307,
            "sub_type": "video",
            "time": 10,
            "metadata": {
                "lastPlaybackRate": 1,
                "currentTime": 0,
                "isLocked": false,
                "video": {
                    "videoType": "BRIGHTCOVE",
                    "viewingTime": 156,
                    "videoSize": 7120441,
                    "url": "5378655747001",
                    "URL": "5378655747001"
                }
            }
        }]
    }]
}

As stored in Dynamo DB-Snap

From the JSON structure you gave, I think you are trying to make DynamoDB like a relational database, when really you should be thinking of DynamoDB as a simple flat key-value store. So instead of a table structure like you suggested, I would instead flatten it, and have many rows for each item the customer has in their cart.

For example

Primary hashkey (UserId) | Hash range key (CartId) | ProductId | Qty
356                        123                       100         50
356                        123                       121         51

In this way, you can get all items in a cart by providing a primary key of 356, with range key 123. This is the DynamoDB way of storing indexable and queryable data. This does mean you are storing two separate rows, however.

A format like this has many advantages, in particular it enables you to create Local Secondary indices on the ProductId to find out how many customers put a particular product into their cart. It should also make it easy to see how the object mapper will work with this table structure.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM