(actually never mind, just don’t use the pagination interface with dynamodb it makes everything harder and inscrutable)
Invalid type for parameter FilterExpression, value:
<boto3.dynamodb.conditions.AttributeExists object at 0x10729fb10>,
type: <class 'boto3.dynamodb.conditions.AttributeExists'>,
valid types: <type 'basestring'>
Well that’s annoying.
But that isn’t where our story starts.
Using boto3 to query DynamoDb to find, for example, all the records that have a latitude field you might issue a query like this
resp = table.scan(
FilterExpression=Attr('lat').exists())
Except DynamoDb is capped at only scanning 1Mb of results per call.
With I ussue the above scan against my db this returns the first 2735 records (out of ~35,000).
If DyDB believes there are more rows matching your scan it will inject LastEvaluatedKey into the results, which you then pass as ExclusiveStartKey to your next scan call. With me?
Except boto3 has pagination built in to handle the bookkeeping, sweet!
Instead of
dynamodb = boto3.resource(...)
table = dynamodb.Table(table_name)
table.scan(FilterExpression=Attr('lat').exists())
we now need
dynamodb = boto3.client(...)
paginator = dynamodb.get_paginator('scan')
iter = paginator.page(
TableName=table_name
FilterExpression=Attr('lat').exists()
)
Which brings us back to where we started this story
Invalid type for parameter FilterExpression, value:
<boto3.dynamodb.conditions.AttributeExists object at 0x10729fb10>,
type: <class 'boto3.dynamodb.conditions.AttributeExists'>,
valid types: <type 'basestring'>
After some digging (yay endless twisty maze of metaprogramming!) and staring in disbelief at the documentation it became clear that while
table.scan(FilterExpression= takes an attribute builder object DynamoDB.Paginator.Scan takes a raw DynamoDb “Conditional Expression” string
So you actually need
resp = paginator.paginate(
TableName=config.dynamo_table,
FilterExpression='attribute_exists(lat)'
)
Why the baffling inconsistency? No idea. But boy was it frustrating, and surprising from a library which seems as mature as boto. If was doing this for anything other than my own gratification I would have bailed out at least an hour earlier and just rolled my own pagination.
Addendum. I tried for a while to see if I could get Attr(...) to just return a string representation of itself without success 😦
Addendum 2. Keep running into fscking-weirdisms with the pagination interface, I’m declaring it DOA, rolling your own isn’t that hard.