Query dynamoDB with datetime filter expression - Incorrect operand

2 min read 04-10-2024
Query dynamoDB with datetime filter expression - Incorrect operand


Querying DynamoDB with Datetime Filters: Avoiding the "Incorrect Operand" Pitfall

Problem: When querying DynamoDB with date and time filters, you might encounter the error "Incorrect operand." This error can be frustrating, especially when dealing with complex datetime comparisons.

Rephrased Problem: Imagine you're trying to find all items in a DynamoDB table that were created within the last 24 hours. You might think you can simply use a >= operator with the current time minus 24 hours. However, DynamoDB doesn't understand date and time values directly. It only works with strings, numbers, and binary data. This mismatch leads to the "Incorrect Operand" error.

Scenario: Let's say you have a DynamoDB table called "events" with an attribute "createdAt" storing a datetime string:

{
  "TableName": "events",
  "KeySchema": [
    {
      "AttributeName": "eventId",
      "KeyType": "HASH"
    }
  ],
  "AttributeDefinitions": [
    {
      "AttributeName": "eventId",
      "AttributeType": "S"
    },
    {
      "AttributeName": "createdAt",
      "AttributeType": "S"
    }
  ]
}

You want to query for events created within the last 24 hours:

import boto3
import datetime

dynamodb = boto3.client('dynamodb')

now = datetime.datetime.utcnow()
yesterday = now - datetime.timedelta(hours=24)

response = dynamodb.query(
  TableName='events',
  KeyConditionExpression='eventId = :eventId',
  FilterExpression='createdAt >= :yesterday',
  ExpressionAttributeValues={
    ':eventId': {'S': 'someEventId'},
    ':yesterday': {'S': yesterday.strftime('%Y-%m-%dT%H:%M:%SZ')}
  }
)

This code will throw the "Incorrect Operand" error because DynamoDB can't compare strings directly.

Analysis:

  • DynamoDB's Data Types: DynamoDB primarily works with primitive data types like strings, numbers, and binary data. It doesn't have a dedicated datetime type.
  • Comparison Operators: DynamoDB's comparison operators are meant for comparing numerical values and strings lexicographically.
  • Datetime Conversion: You need to convert datetime objects into a format that DynamoDB can understand.

Solution:

The solution involves converting the datetime objects into comparable values. Here are a few options:

  1. Store Datetime as Numbers: Store the datetime as a number representing the number of seconds or milliseconds since the Unix epoch. This allows you to use standard numerical comparison operators.

    # Store as seconds since epoch
    createdAt = int(datetime.datetime.now().timestamp())
    
    # Query using numerical comparison
    response = dynamodb.query(
      TableName='events',
      KeyConditionExpression='eventId = :eventId',
      FilterExpression='createdAt >= :yesterday',
      ExpressionAttributeValues={
        ':eventId': {'S': 'someEventId'},
        ':yesterday': {'N': str(int((now - datetime.timedelta(hours=24)).timestamp()))}
      }
    )
    
  2. Use ISO 8601 Format: Store datetimes in ISO 8601 format. This allows you to perform lexicographical comparisons within the DynamoDB query:

    # Store as ISO 8601 string
    createdAt = datetime.datetime.now().isoformat()
    
    # Query using lexicographical comparison
    response = dynamodb.query(
      TableName='events',
      KeyConditionExpression='eventId = :eventId',
      FilterExpression='createdAt >= :yesterday',
      ExpressionAttributeValues={
        ':eventId': {'S': 'someEventId'},
        ':yesterday': {'S': (now - datetime.timedelta(hours=24)).isoformat()}
      }
    )
    

Additional Value:

  • Precision: When storing timestamps as numbers, be mindful of precision. Choosing seconds or milliseconds depends on your use case.
  • Optimization: For queries on a single attribute, consider using a Global Secondary Index (GSI) with the createdAt attribute as the sort key. This allows for efficient range queries on timestamps.

Conclusion:

Handling date and time values in DynamoDB requires careful consideration of data types and comparison methods. By understanding the limitations and using appropriate conversion techniques, you can effectively query your data based on datetime filters, avoiding the "Incorrect Operand" error.

References: