Open
Description
Hi,
I've run into a problem with PynamoDB: I wanted to list all objects of a given type, but avoid using scan. I can't use an index, in which the discriminator is the primary key.
Example output (notice the first line).
BUG:pynamodb.connection.base:Calling Query with arguments {'TableName': 'test-table-1234', 'KeyConditionExpression': '#0 = :0', 'FilterExpression': '#0 IN (:1, :2)', 'IndexName': 'type_index', 'ExpressionAttributeNames': {'#0': 'type'}, 'ExpressionAttributeValues': {':0': {'S': 'user'}, ':1': {'S': 'user'}, ':2': {'S': 'group'}}, 'ReturnConsumedCapacity': 'TOTAL'}
DEBUG:botocore.hooks:Event before-parameter-build.dynamodb.Query: calling handler <function generate_idempotent_uuid at 0x103777a60>
DEBUG:botocore.hooks:Event before-parameter-build.dynamodb.Query: calling handler <function block_endpoint_discovery_required_operations at 0x1036c4d30>
DEBUG:botocore.auth:Calculating signature using v4 auth.
DEBUG:botocore.auth:CanonicalRequest:
POST
/
content-type:application/x-amz-json-1.0
host:dynamodb.eu-west-1.amazonaws.com
x-amz-date:20211129T163717Z
x-amz-security-token: ---
content-type;host;x-amz-date;x-amz-security-token;x-amz-target
a59ab9b0c3b122603f12e5cfe46020899ce8049b56f790b4f57624d06dbd4874
DEBUG:botocore.auth:StringToSign:
AWS4-HMAC-SHA256
20211129T163717Z
20211129/eu-west-1/dynamodb/aws4_request
b0edb2380f43edb04697e68de7150e6743491d52b379e6df5512eee3ab1f704b
DEBUG:botocore.auth:Signature:
df460887d908a2db232bcb69042424b37f6b227eb53a77dad760069a711c9be2
DEBUG:botocore.httpsession:Certificate path: /Users/f0ff/Code/tameshi/framework/venv/lib/python3.8/site-packages/botocore/cacert.pem
DEBUG:urllib3.connectionpool:https://dynamodb.eu-west-1.amazonaws.com:443 "POST / HTTP/1.1" 400 161
DEBUG:pynamodb.connection.base:Calling DeleteTable with arguments {'TableName': 'test-table-1234'}
DEBUG:botocore.hooks:Event before-parameter-build.dynamodb.DeleteTable: calling handler <function generate_idempotent_uuid at 0x103777a60>
DEBUG:botocore.hooks:Event before-parameter-build.dynamodb.DeleteTable: calling handler <function block_endpoint_discovery_required_operations at 0x1036c4d30>
DEBUG:botocore.auth:Calculating signature using v4 auth.
DEBUG:botocore.auth:CanonicalRequest:
POST
/
content-type:application/x-amz-json-1.0
host:dynamodb.eu-west-1.amazonaws.com
x-amz-date:20211129T163717Z
x-amz-security-token: ---
x-amz-target:DynamoDB_20120810.DeleteTable
content-type;host;x-amz-date;x-amz-security-token;x-amz-target
06d198ac62ba8fda04ad1ea0035c6f0d13afa201e775f979a968d05da3334522
DEBUG:botocore.auth:StringToSign:
AWS4-HMAC-SHA256
20211129T163717Z
20211129/eu-west-1/dynamodb/aws4_request
364ed68b24bcc49e0afbc665c3a01e40374cd0691b5ecd7b2cb7722a44dcd778
DEBUG:botocore.auth:Signature:
94262ec8d585ab0cd4bfad5d9e5dcc3c429af853f0d74f566253d835d026d133
DEBUG:botocore.httpsession:Certificate path: /Users/f0ff/Code/tameshi/framework/venv/lib/python3.8/site-packages/botocore/cacert.pem
DEBUG:urllib3.connectionpool:https://dynamodb.eu-west-1.amazonaws.com:443 "POST / HTTP/1.1" 200 332
Traceback (most recent call last):
File "/Users/f0ff/Code/tameshi/framework/venv/lib/python3.8/site-packages/pynamodb/connection/base.py", line 1333, in query
return self.dispatch(QUERY, operation_kwargs, settings)
File "/Users/f0ff/Code/tameshi/framework/venv/lib/python3.8/site-packages/pynamodb/connection/base.py", line 329, in dispatch
data = self._make_api_call(operation_name, operation_kwargs, settings)
File "/Users/f0ff/Code/tameshi/framework/venv/lib/python3.8/site-packages/pynamodb/connection/base.py", line 440, in _make_api_call
raise VerboseClientError(botocore_expected_format, operation_name, verbose_properties)
pynamodb.exceptions.VerboseClientError: An error occurred (ValidationException) on request (V50EJSD64U1GCHQ6B499PCNVEFVV4KQNSO5AEMVJF66Q9ASUAAJG) on table (test-table-1234) when calling the Query operation: Filter Expression can only contain non-primary key attributes: Primary key attribute: type
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "bug.py", line 49, in <module>
print([user for user in User.type_index.query("user")])
File "bug.py", line 49, in <listcomp>
print([user for user in User.type_index.query("user")])
File "/Users/f0ff/Code/tameshi/framework/venv/lib/python3.8/site-packages/pynamodb/pagination.py", line 194, in __next__
self._get_next_page()
File "/Users/f0ff/Code/tameshi/framework/venv/lib/python3.8/site-packages/pynamodb/pagination.py", line 179, in _get_next_page
page = next(self.page_iter)
File "/Users/f0ff/Code/tameshi/framework/venv/lib/python3.8/site-packages/pynamodb/pagination.py", line 115, in __next__
page = self._operation(*self._args, settings=self._settings, **self._kwargs)
File "/Users/f0ff/Code/tameshi/framework/venv/lib/python3.8/site-packages/pynamodb/connection/table.py", line 270, in query
return self.connection.query(
File "/Users/f0ff/Code/tameshi/framework/venv/lib/python3.8/site-packages/pynamodb/connection/base.py", line 1335, in query
raise QueryError("Failed to query items: {}".format(e), e)
pynamodb.exceptions.QueryError: Failed to query items: An error occurred (ValidationException) on request (V50EJSD64U1GCHQ6B499PCNVEFVV4KQNSO5AEMVJF66Q9ASUAAJG) on table (test-table-1234) when calling the Query operation: Filter Expression can only contain non-primary key attributes: Primary key attribute: type
Code to recreate:
import logging
from pynamodb.models import Model
from pynamodb.attributes import UnicodeAttribute, DiscriminatorAttribute
from pynamodb.indexes import GlobalSecondaryIndex, IncludeProjection
class TypeIndex(GlobalSecondaryIndex):
class Meta:
index_name = "type_index"
read_capacity_units = 1
write_capacity_units = 1
region = "eu-west-1"
projection = IncludeProjection(["type", "path"])
type = UnicodeAttribute(hash_key=True)
class CoreModel(Model):
class Meta:
table_name = "test-table-1234"
read_capacity_units = 2
write_capacity_units = 1
region = "eu-west-1"
path = UnicodeAttribute(hash_key=True)
type = DiscriminatorAttribute()
type_index = TypeIndex()
class User(CoreModel, discriminator="user"):
pass
class Group(CoreModel, discriminator="group"):
pass
if __name__ == "__main__":
CoreModel.create_table(wait=True)
# Load some test data
user1 = User("user.user1")
user1.save()
logging.basicConfig(level=logging.DEBUG)
try:
print([user for user in User.type_index.query("user")])
finally:
CoreModel.delete_table()