public void testQueryWithGlobalSecondaryIndex()

in services/dynamodb/src/it/java/software/amazon/awssdk/services/dynamodb/SecondaryIndexesIntegrationTest.java [143:339]


    public void testQueryWithGlobalSecondaryIndex() throws InterruptedException {
        // GSI attributes don't have to be unique
        // so items with the same GSI keys but different primary keys
        // could co-exist in the table.
        int totalDuplicateGSIKeys = 10;
        Random random = new Random();
        String duplicateGSIHashValue = UUID.randomUUID().toString();
        int duplicateGSIRangeValue = random.nextInt();
        for (int i = 0; i < totalDuplicateGSIKeys; i++) {
            Map<String, AttributeValue> item = new HashMap<String, AttributeValue>();
            item.put(HASH_KEY_NAME, AttributeValue.builder().s(UUID.randomUUID().toString()).build());
            item.put(RANGE_KEY_NAME, AttributeValue.builder().n(Integer.toString(i)).build());
            item.put(GSI_HASH_KEY_NAME, AttributeValue.builder().s(duplicateGSIHashValue).build());
            item.put(GSI_RANGE_KEY_NAME, AttributeValue.builder().n(Integer.toString(duplicateGSIRangeValue)).build());
            dynamo.putItem(PutItemRequest.builder().tableName(tableName).item(item).build());
        }

        // Query the duplicate GSI key values should return all the items
        Map<String, Condition> keyConditions = new HashMap<String, Condition>();
        keyConditions.put(
                GSI_HASH_KEY_NAME,
                Condition.builder().attributeValueList(
                        AttributeValue.builder().s((duplicateGSIHashValue)).build())
                               .comparisonOperator(ComparisonOperator.EQ).build());
        keyConditions.put(
                GSI_RANGE_KEY_NAME,
                Condition.builder().attributeValueList(
                        AttributeValue.builder().n(Integer
                                                           .toString(duplicateGSIRangeValue)).build())
                               .comparisonOperator(ComparisonOperator.EQ).build());

        // All the items with the GSI keys should be returned
        assertQueryResponseCount(totalDuplicateGSIKeys, QueryRequest.builder()
                .tableName(tableName)
                .indexName(GSI_NAME)
                .keyConditions(keyConditions).build());

        // Other than this, the behavior of GSI query should be the similar
        // as LSI query. So following code is similar to that used for
        // LSI query test.

        String randomPrimaryHashKeyValue = UUID.randomUUID().toString();
        String randomGSIHashKeyValue = UUID.randomUUID().toString();
        int totalItemsPerHash = 10;
        int totalIndexedItemsPerHash = 5;
        Map<String, AttributeValue> item = new HashMap<String, AttributeValue>();

        item.put(HASH_KEY_NAME, AttributeValue.builder().s(randomPrimaryHashKeyValue).build());
        item.put(GSI_HASH_KEY_NAME, AttributeValue.builder().s(randomGSIHashKeyValue).build());
        // Items with GSI keys
        for (int i = 0; i < totalIndexedItemsPerHash; i++) {
            item.put(RANGE_KEY_NAME, AttributeValue.builder().n(Integer.toString(i)).build());
            item.put(GSI_RANGE_KEY_NAME, AttributeValue.builder().n(Integer.toString(i)).build());
            item.put("attribute_" + i, AttributeValue.builder().s(UUID.randomUUID().toString()).build());
            dynamo.putItem(PutItemRequest.builder().tableName(tableName).item(item).build());
            item.remove("attribute_" + i);
        }
        item.remove(GSI_RANGE_KEY_NAME);
        // Items with incomplete GSI keys (no GSI range key)
        for (int i = totalIndexedItemsPerHash; i < totalItemsPerHash; i++) {
            item.put(RANGE_KEY_NAME, AttributeValue.builder().n(Integer.toString(i)).build());
            item.put("attribute_" + i, AttributeValue.builder().s(UUID.randomUUID().toString()).build());
            dynamo.putItem(PutItemRequest.builder().tableName(tableName).item(item).build());
            item.remove("attribute_" + i);
        }

        /**
         *  1) Query-with-GSI (only by GSI hash key)
         */
        QueryResponse result = dynamo.query(QueryRequest.builder()
                                                  .tableName(tableName)
                                                  .indexName(GSI_NAME)
                                                  .keyConditions(
                                                          Collections.singletonMap(
                                                                  GSI_HASH_KEY_NAME,
                                                                  Condition.builder()
                                                                          .attributeValueList(AttributeValue.builder()
                                                                                  .s(randomGSIHashKeyValue).build())
                                                                          .comparisonOperator(
                                                                                         ComparisonOperator.EQ).build())).build());
        // Only the indexed items should be returned
        assertEquals((Object) totalIndexedItemsPerHash, (Object) result.count());
        // By default, the result includes all the key attributes (2 primary + 2 GSI).
        assertEquals(4, result.items().get(0).size());

        /**
         * 2) Query-with-GSI (by GSI hash + range)
         */
        int rangeKeyConditionRange = 2;
        keyConditions = new HashMap<String, Condition>();
        keyConditions.put(
                GSI_HASH_KEY_NAME,
                Condition.builder()
                        .attributeValueList(AttributeValue.builder()
                                .s(randomGSIHashKeyValue)
                                .build())
                        .comparisonOperator(ComparisonOperator.EQ)
                        .build());
        keyConditions.put(
                GSI_RANGE_KEY_NAME,
                Condition.builder()
                        .attributeValueList(AttributeValue.builder()
                                .n(Integer.toString(rangeKeyConditionRange))
                                .build())
                        .comparisonOperator(ComparisonOperator.LT)
                        .build());
        result = dynamo.query(QueryRequest.builder()
                                      .tableName(tableName)
                                      .indexName(GSI_NAME)
                                      .keyConditions(keyConditions)
                                      .build());
        assertEquals((Object) rangeKeyConditionRange, (Object) result.count());

        /**
         * 3) Query-with-GSI does not support Select.ALL_ATTRIBUTES if the index
         * was not created with this projection type.
         */
        try {
            result = dynamo.query(QueryRequest.builder()
                                          .tableName(tableName)
                                          .indexName(GSI_NAME)
                                          .keyConditions(
                                                  Collections.singletonMap(
                                                          GSI_HASH_KEY_NAME,
                                                          Condition.builder()
                                                                  .attributeValueList(AttributeValue.builder()
                                                                          .s(randomGSIHashKeyValue)
                                                                          .build())
                                                                         .comparisonOperator(ComparisonOperator.EQ)
                                                                  .build()))
                                          .select(Select.ALL_ATTRIBUTES).build());
            fail("SdkServiceException is expected");
        } catch (SdkServiceException exception) {
            assertTrue(exception.getMessage().contains("Select type ALL_ATTRIBUTES is not supported for global secondary"));
        }

        /**
         * 4) Query-with-GSI on selected attributes (by AttributesToGet)
         */
        result = dynamo.query(QueryRequest.builder()
                                      .tableName(tableName)
                                      .indexName(GSI_NAME)
                                      .keyConditions(
                                              Collections.singletonMap(
                                                      GSI_HASH_KEY_NAME,
                                                      Condition.builder()
                                                              .attributeValueList(AttributeValue.builder()
                                                                      .s(randomGSIHashKeyValue).build())
                                                                     .comparisonOperator(ComparisonOperator.EQ).build()))
                                      .attributesToGet(HASH_KEY_NAME, RANGE_KEY_NAME).build());
        // Only the indexed items should be returned
        assertEquals((Object) totalIndexedItemsPerHash, (Object) result.count());
        // Two attributes as specified in AttributesToGet
        assertEquals(2, result.items().get(0).size());

        /**
         * 5) Exception when using both Selection and AttributeToGet
         */
        try {
            result = dynamo.query(QueryRequest.builder()
                                          .tableName(tableName)
                                          .indexName(GSI_NAME)
                                          .keyConditions(
                                                  Collections.singletonMap(
                                                          GSI_HASH_KEY_NAME,
                                                          Condition.builder().attributeValueList(
                                                                  AttributeValue.builder()
                                                                          .s(randomGSIHashKeyValue).build())
                                                                         .comparisonOperator(ComparisonOperator.EQ).build()))
                                          .attributesToGet(HASH_KEY_NAME, RANGE_KEY_NAME, LSI_RANGE_KEY_NAME)
                                          .select(Select.ALL_PROJECTED_ATTRIBUTES).build());
            fail("Should trigger exception when using both Select and AttributeToGet.");
        } catch (SdkServiceException exception) {
            // Ignored or expected.
        }

        /**
         * 6) Query-with-GSI on selected attributes (by Select.SPECIFIC_ATTRIBUTES)
         */
        result = dynamo.query(QueryRequest.builder()
                                      .tableName(tableName)
                                      .indexName(GSI_NAME)
                                      .keyConditions(
                                              Collections.singletonMap(
                                                      GSI_HASH_KEY_NAME,
                                                      Condition.builder().attributeValueList(
                                                              AttributeValue.builder()
                                                                      .s(randomGSIHashKeyValue).build())
                                                                     .comparisonOperator(
                                                                             ComparisonOperator.EQ).build()))
                                      .attributesToGet(HASH_KEY_NAME)
                                      .select(Select.SPECIFIC_ATTRIBUTES).build());
        // Only the indexed items should be returned
        assertEquals((Object) totalIndexedItemsPerHash, (Object) result.count());
        // Only one attribute as specified in AttributesToGet
        assertEquals(1, result.items().get(0).size());
    }