使用Datetime过滤表达式查询DynamoDB报操作数类型错误
我尝试查询DynamoDB中早于5分钟的条目,数据存储格式如下:
{ "main_event_code": { "S": "1160" }, "event_code": { "S": "9999" }, "last_upload": { "S": "2024-05-10T04:09:29.614535Z" } }
我的Python脚本代码如下:
current_utc_time = datetime.now(timezone.utc) past_time = current_utc_time - timedelta(minutes=5) # Format both times as ISO 8601 strings current_utc_time_iso = current_utc_time.strftime('%Y-%m-%dT%H:%M:%SZ') past_time_iso = past_time.strftime('%Y-%m-%dT%H:%M:%SZ') # Print the current UTC time and the time minus 5 minutes print(f"Current UTC Time: {current_utc_time_iso}") print(f"Time 5 Minutes Ago: {past_time_iso}") try: # Scan the table with a filter for items older than 'past_time' response = table.scan( FilterExpression="last_upload < :past_time", ExpressionAttributeValues={":past_time": {"S": past_time_iso}} )
运行脚本后出现以下错误:
Error querying DynamoDB table: An error occurred (ValidationException) when calling the Scan operation: Invalid FilterExpression: Incorrect operand type for operator or function; operator or function: <, operand type: M
但我用AWS CLI执行相同逻辑的命令可以正常返回结果:
aws dynamodb scan --table-name ingestion_queue --filter-expression "last_upload < :val" --expression-attribute-values '{":val":{"S":"2024-05-10T04:23:08Z"}}'
报错原因
错误信息里的operand type: M表示过滤时last_upload被识别为Map类型(M),但实际数据中它是字符串类型(S)。这是因为你使用的boto3资源客户端(boto3.resource('dynamodb'))会自动将DynamoDB原生格式(如{"S": "..."})转换为Python原生类型,但你在ExpressionAttributeValues里又手动指定了DynamoDB原生格式,导致类型不匹配。
AWS CLI直接使用原生DynamoDB格式,所以不会出现这个问题。
解决方法
方法一:使用Python原生类型(推荐)
如果table是通过boto3资源创建的,直接传入Python字符串即可,SDK会自动处理类型转换:
response = table.scan( FilterExpression="last_upload < :past_time", ExpressionAttributeValues={":past_time": past_time_iso} )
方法二:使用条件表达式构建器
通过boto3.dynamodb.conditions.Attr构建过滤表达式,避免类型歧义:
from boto3.dynamodb.conditions import Attr response = table.scan( FilterExpression=Attr('last_upload').lt(past_time_iso) )
方法三:切换到boto3低级客户端
如果坚持使用DynamoDB原生格式,可改用boto3.client('dynamodb'),此时所有参数都需要用原生格式:
import boto3 client = boto3.client('dynamodb') response = client.scan( TableName='ingestion_queue', FilterExpression="last_upload < :past_time", ExpressionAttributeValues={":past_time": {"S": past_time_iso}} )
内容的提问来源于stack exchange,提问作者Matt Winer

