"After all, the engineers only needed to refuse to fix anything, and modern industry would grind to a halt." -Michael Lewis

Enable Massive Growth

Working with Nested Attributes, DynamoDB, and the Java SDK 2.0

Nov 2020

Nested attributes in DynamoDB are a way to group data within an item together. The attributes are said to be nested if they are embedded within another attribute.

Building off a previous post where we set up an embedded DynamoDB instance in a java test suite, I'll provide here some examples for working with nested attributes.

The source code that follows can be seen on Github.

First let's create a table and insert some data:


    @Test
    public void nestedAttributes() throws Exception {
        String currentTableName = "NestedAttributesTest";
        createTableAndWaitForComplete(currentTableName);

        Map<String, AttributeValue> attributes = Map.of(
                COMPANY, AttributeValue.builder().s("Motorola").build(),
                MODEL, AttributeValue.builder().s("G1").build(),
                "MetadataList", AttributeValue.builder().l(
                        AttributeValue.builder().s("Super Cool").build(),
                        AttributeValue.builder().n("100").build()).build(),
                "MetadataStringSet", AttributeValue.builder().ss("one", "two", "three").build(),
                "MetadataNumberSet", AttributeValue.builder()
                        .bs(SdkBytes.fromByteArray(new byte[] {43, 123}), SdkBytes.fromByteArray(new byte[] {78, 100}))
                        .build()
            );

        PutItemRequest populateDataItemRequest = PutItemRequest.builder()
                .tableName(currentTableName)
                .item(attributes)
                .build();

        StepVerifier.create(Mono.fromFuture(dynamoDbAsyncClient.putItem(populateDataItemRequest)))
                .expectNextCount(1)
                .verifyComplete();

Given the primary key as a composite partition and sort key, where here it is "Motorola" as the partition and "G1" as the sort, there are three nested attribute types in play:

  • A list of attributes. Note that these could be further nested attributes if we want, in this case we just put two scalar attributes in [string and number]
  • String Set. This is exactly what it sounds like: a set of string values.
  • Binary Set. This is also what it sounds like: a set of binary values. Each binary value [as are all binary values] is just a bunch of bytes.

Note that we omitted one set type of the three that are available, which is the number set.

We can now get this item out of dynamo, and the access patterns should be familiar to us at this point:

        GetItemRequest getItemRequest = GetItemRequest.builder()
                .tableName(currentTableName)
                .key(getMapWith("Motorola", "G1"))
                .build();

        StepVerifier.create(Mono.fromFuture(dynamoDbAsyncClient.getItem(getItemRequest)))
                .expectNextMatches(getItemResponse -> {
                    List<AttributeValue> listOfMetadata = getItemResponse.item().get("MetadataList").l();
                    List<String> stringSetMetadata = getItemResponse.item().get("MetadataStringSet").ss();

                    return listOfMetadata.size() == 2
                            && listOfMetadata.stream().anyMatch(attributeValue -> "Super Cool".equals(attributeValue.s()))
                            && listOfMetadata.stream().anyMatch(attributeValue -> "100".equals(attributeValue.n()))
                            && stringSetMetadata.contains("one")
                            && stringSetMetadata.contains("two");
                }).verifyComplete();
    }

Here, we get both the list of attributes and the string set from our item, then making assertions that they all are there and correct.

Remember to check out the source code for this post on Github.

Nick Fisher is a software engineer in the Pacific Northwest. He focuses on building highly scalable and maintainable backend systems.