moto
moto copied to clipboard
Dynamodb2 query should not support begins_with #x :y, only begins_with(#x, :y)
Moto's Dynamodb KeyConditionExpression's begins_with query is not aligned with DynamoDB's
Table
with moto.mock_dynamodb2():
db = boto3.client("dynamodb")
db.create_table(
TableName=table_name,
KeySchema=[
{"AttributeName": "accountId", "KeyType": "HASH"}, # partition key
{"AttributeName": "vId", "KeyType": "RANGE"}, # sort key
],
AttributeDefinitions=[
{"AttributeName": "accountId", "AttributeType": "S"},
{"AttributeName": "vId", "AttributeType": "S"},
],
)
When doing a query for an object in a Dynamodb
DB.query(
TableName=TABLE_NAME,
Select="SPECIFIC_ATTRIBUTES",
ProjectionExpression=",".join(
[
"#account_id",
"#v_id",
"#name",
]
),
KeyConditionExpression='#account_id=:account_id AND begins_with(#v_id,:v_id)',
ExpressionAttributeValues={
":v_id": db_translate_item(DbAttrs.V_ID, make_v_id(LATEST_VERSION)),
":account_id": db_translate_item(DbAttrs.ACCOUNT_ID, account_id)
},
ExpressionAttributeNames={
"#account_id": DbAttrs.ACCOUNT_ID,
"#v_id": DbAttrs.V_ID,
"#name": DbAttrs.NAME,
},
)
Example: DynamoDB is expecting
KeyConditionExpression='#account_id=:account_id AND begins_with(#v_id,:v_id)'
But moto is expecting
KeyConditionExpression='#account_id=:account_id AND begins_with :v_id'
Code in question: https://github.com/spulec/moto/blob/acfdfb7d7c08be05a02101ff10cf467015bb0158/moto/dynamodb2/responses.py#L492
Moto does support the function-notation begins_with(x,y)
, see this internal test:
https://github.com/spulec/moto/blob/master/tests/test_dynamodb2/test_dynamodb.py#L3683
You're right that the free text notation is also supported - we're too flexible in that regard. Marking it as a bug. Thanks for raising this @jkar32!
Thanks Bert!
I missed that test in my investigation.
A little more testing on my end and I can't get it to work as described in that test.
https://github.com/spulec/moto/blob/c80f63fc8259bcadc8a9b157c3fa500938b72062/tests/test_dynamodb2/test_dynamodb.py#L1497
The test is testing begins_with(Desc, :v0)
but when trying to replicate it with my code I get the error
# TODO implement more than one range expression and OR operators
range_key_expression = expressions[0].strip("()")
range_key_expression_components = range_key_expression.split()
> range_comparison = range_key_expression_components[1]
E IndexError: list index out of range
.tox/venv/lib/python3.7/site-packages/moto/dynamodb2/responses.py:477: IndexError
When I have the KeyConditionExpression the same as the described in the test
KeyConditionExpression='#account_id=:account_id AND begins_with(vId,:v_id)',
ExpressionAttributeValues={
":v_id": {"S": "v0"},
":account_id": {"S": "123"},
},
Just detailing it if/when someone picks this up.
If I get time ill do some more investigation.
Thanks
I have a similar test case that is not working. If I remove the begins_with
it works as expected
table.query(
KeyConditionExpression='partition_key = :pk and begins_with( my_range_key, :rk)',
FilterExpression=' contains( third_field, :fc) and fourth_field = :fv and fifth_field= :fv',
ExpressionAttributeValues={':pk': '121AAD', ':rk': 'aaa', ':fc': 'PREFIX:', ':fv': 'Status'})