moto icon indicating copy to clipboard operation
moto copied to clipboard

Dynamodb2 query should not support begins_with #x :y, only begins_with(#x, :y)

Open jkar32 opened this issue 4 years ago • 4 comments

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

jkar32 avatar Mar 31 '20 03:03 jkar32

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!

bblommers avatar Mar 31 '20 09:03 bblommers

Thanks Bert!

I missed that test in my investigation.

jkar32 avatar Mar 31 '20 09:03 jkar32

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

jkar32 avatar Mar 31 '20 22:03 jkar32

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'})

ygivenx avatar Apr 10 '20 00:04 ygivenx