Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Query Dynamo table with only the secondary global index

Im trying to query a Dynamodb table using a secondary global index and I'm getting java.lang.IllegalArgumentException: Illegal query expression: No hash key condition is found in the query. All I'm trying to do is to get all items that have a timestamp greater than a value without considering the key. The timestamp is not part of a key or range key, so i created a global index for it.

Does anyone have a clue what i might be missing?

Table Definition:

{
   AttributeDefinitions:[
      {
         AttributeName:timestamp,
         AttributeType:N
      },
      {
         AttributeName:url,
         AttributeType:S
      }
   ],
   TableName:SitePageIndexed,
   KeySchema:[
      {
         AttributeName:url,
         KeyType:HASH
      }
   ],
   TableStatus:ACTIVE,
   CreationDateTime:   Mon May 12 18:45:57   EDT 2014,
   ProvisionedThroughput:{
      NumberOfDecreasesToday:0,
      ReadCapacityUnits:8,
      WriteCapacityUnits:4
   },
   TableSizeBytes:0,
   ItemCount:0,
   GlobalSecondaryIndexes:[
      {
         IndexName:TimestampIndex,
         KeySchema:[
            {
               AttributeName:timestamp,
               KeyType:HASH
            }
         ],
         Projection:{
            ProjectionType:ALL,

         },
         IndexStatus:ACTIVE,
         ProvisionedThroughput:{
            NumberOfDecreasesToday:0,
            ReadCapacityUnits:8,
            WriteCapacityUnits:4
         },
         IndexSizeBytes:0,
         ItemCount:0
      }
   ]
}

Code

Condition condition1 = new Condition().withComparisonOperator(ComparisonOperator.GE).withAttributeValueList(new AttributeValue().withN(Long.toString(start)));      
DynamoDBQueryExpression<SitePageIndexed> exp = new DynamoDBQueryExpression<SitePageIndexed>().withRangeKeyCondition("timestamp", condition1);
exp.setScanIndexForward(true);
exp.setLimit(100);
exp.setIndexName("TimestampIndex");

PaginatedQueryList<SitePageIndexed> queryList = client.query(SitePageIndexed.class,exp);
like image 719
webber Avatar asked May 13 '14 00:05

webber


People also ask

Can you query on global secondary index?

A global secondary index lets you query over the entire table, across all partitions. A local secondary index lets you query over a single partition, as specified by the partition key value in the query. Queries on global secondary indexes support eventual consistency only.

What is the use of global secondary index in DynamoDB?

DynamoDB supports two types of secondary indexes: Global secondary index — An index with a partition key and a sort key that can be different from those on the base table. A global secondary index is considered "global" because queries on the index can span all of the data in the base table, across all partitions.

Can a global secondary index create at the same time as the table creation?

Global Secondary Indexes (GSI) enable you to perform more efficient queries. Now, you can add or delete GSIs from your table at any time, instead of just during table creation.

How many global secondary indexes are permitted per table in DynamoDB?

Each table in DynamoDB can have up to 20 global secondary indexes (default quota) and 5 local secondary indexes. For more information about the differences between global secondary indexes and local secondary indexes, see Improving data access with secondary indexes.


2 Answers

All I'm trying to do is to get all items that have a timestamp greater than a value without considering the key.

This is not how Global Secondary Indexes (GSI) on Amazon DynamoDB work. To query a GSI you must specify a value for its hash key and then you may filter/sort by the range key -- just like you'd do with the primary key. This is exactly what the exception is trying to tell you, and also what you will find on the documentation page for the Query API:

A Query operation directly accesses items from a table using the table primary key, or from an index using the index key. You must provide a specific hash key value.

Think of a GSI as just another key that behaves almost exactly like the primary key (the main differences being that it is updated asynchronously, and you can only perform eventually consistent reads on GSIs).

Please refer to the Amazon DynamoDB Global Secondary Index documentation page for guidelines and best practices when creating GSIs: http://docs.aws.amazon.com/amazondynamodb/latest/developerguide/GSI.html

One possible way to achieve what you want would be to have a dummy attribute constrained to a finite, small set of possible values, create a GSI with hash key on that dummy attribute and range key on your timestamp. When querying, you would need to issue one Query API call for each possible value on your dummy hash key attribute, and then consolidate the results on your application. By constraining the dummy attribute to a singleton (i.e., a Set with a single element, i.e., a constant value), you can send only one Query API call and you get your result dataset directly -- but keep in mind that this will cause you problems related to hot partitions and you might have performance issues! Again, refer to the document linked above to learn the best practices and some patterns.

like image 156
Bruno Reis Avatar answered Sep 28 '22 06:09

Bruno Reis


It is possible to query DynamoDb with only the GSI; could be confirmed by going to the web interaface Query/Index.

Programatically the way it is done is as following:

DynamoDB dynamoDB = new DynamoDB(new AmazonDynamoDBClient(
    new ProfileCredentialsProvider()));

Table table = dynamoDB.getTable("WeatherData");
Index index = table.getIndex("PrecipIndex");

QuerySpec spec = new QuerySpec()
    .withKeyConditionExpression("#d = :v_date and Precipitation = :v_precip")
    .withNameMap(new NameMap()
        .with("#d", "Date"))
    .withValueMap(new ValueMap()
        .withString(":v_date","2013-08-10")
        .withNumber(":v_precip",0));

ItemCollection<QueryOutcome> items = index.query(spec);
Iterator<Item> iter = items.iterator(); 
while (iter.hasNext()) {
    System.out.println(iter.next().toJSONPretty());
}

http://docs.aws.amazon.com/amazondynamodb/latest/developerguide/GSIJavaDocumentAPI.html#GSIJavaDocumentAPI.QueryAnIndex

For doing it with DynamoDBMapper see: How to query a Dynamo DB having a GSI with only hashKeys using DynamoDBMapper

like image 44
Neil Avatar answered Sep 28 '22 05:09

Neil