This commit is contained in:
Bert Blommers 2019-11-03 07:33:27 -08:00
parent 5876dc0186
commit eaa23800bd
3 changed files with 437 additions and 195 deletions

View File

@ -108,14 +108,23 @@ class DynamoType(object):
self.value.pop(key) self.value.pop(key)
def filter(self, projection_expressions): def filter(self, projection_expressions):
nested_projections = [expr[0:expr.index('.')] for expr in projection_expressions if '.' in expr] nested_projections = [
expr[0 : expr.index(".")] for expr in projection_expressions if "." in expr
]
if self.is_map(): if self.is_map():
expressions_to_delete = [] expressions_to_delete = []
for attr in self.value: for attr in self.value:
if attr not in projection_expressions and attr not in nested_projections: if (
attr not in projection_expressions
and attr not in nested_projections
):
expressions_to_delete.append(attr) expressions_to_delete.append(attr)
elif attr in nested_projections: elif attr in nested_projections:
relevant_expressions = [expr[len(attr + '.'):] for expr in projection_expressions if expr.startswith(attr + '.')] relevant_expressions = [
expr[len(attr + ".") :]
for expr in projection_expressions
if expr.startswith(attr + ".")
]
self.value[attr].filter(relevant_expressions) self.value[attr].filter(relevant_expressions)
for expr in expressions_to_delete: for expr in expressions_to_delete:
self.value.pop(expr) self.value.pop(expr)
@ -493,13 +502,19 @@ class Item(BaseModel):
# Filter using projection_expression # Filter using projection_expression
# Ensure a deep copy is used to filter, otherwise actual data will be removed # Ensure a deep copy is used to filter, otherwise actual data will be removed
def filter(self, projection_expression): def filter(self, projection_expression):
expressions = [x.strip() for x in projection_expression.split(',')] expressions = [x.strip() for x in projection_expression.split(",")]
top_level_expressions = [expr[0:expr.index('.')] for expr in expressions if '.' in expr] top_level_expressions = [
expr[0 : expr.index(".")] for expr in expressions if "." in expr
]
for attr in list(self.attrs): for attr in list(self.attrs):
if attr not in expressions and attr not in top_level_expressions: if attr not in expressions and attr not in top_level_expressions:
self.attrs.pop(attr) self.attrs.pop(attr)
if attr in top_level_expressions: if attr in top_level_expressions:
relevant_expressions = [expr[len(attr + '.'):] for expr in expressions if expr.startswith(attr + '.')] relevant_expressions = [
expr[len(attr + ".") :]
for expr in expressions
if expr.startswith(attr + ".")
]
self.attrs[attr].filter(relevant_expressions) self.attrs[attr].filter(relevant_expressions)

View File

@ -346,7 +346,9 @@ class DynamoHandler(BaseResponse):
projection_expression = self.body.get("ProjectionExpression") projection_expression = self.body.get("ProjectionExpression")
expression_attribute_names = self.body.get("ExpressionAttributeNames", {}) expression_attribute_names = self.body.get("ExpressionAttributeNames", {})
projection_expression = self._adjust_projection_expression(projection_expression, expression_attribute_names) projection_expression = self._adjust_projection_expression(
projection_expression, expression_attribute_names
)
try: try:
item = self.dynamodb_backend.get_item(name, key, projection_expression) item = self.dynamodb_backend.get_item(name, key, projection_expression)
@ -413,7 +415,9 @@ class DynamoHandler(BaseResponse):
filter_expression = self.body.get("FilterExpression") filter_expression = self.body.get("FilterExpression")
expression_attribute_values = self.body.get("ExpressionAttributeValues", {}) expression_attribute_values = self.body.get("ExpressionAttributeValues", {})
projection_expression = self._adjust_projection_expression(projection_expression, expression_attribute_names) projection_expression = self._adjust_projection_expression(
projection_expression, expression_attribute_names
)
filter_kwargs = {} filter_kwargs = {}
@ -569,10 +573,20 @@ class DynamoHandler(BaseResponse):
def _adjust_projection_expression(self, projection_expression, expr_attr_names): def _adjust_projection_expression(self, projection_expression, expr_attr_names):
def _adjust(expression): def _adjust(expression):
return expr_attr_names[expression] if expression in expr_attr_names else expression return (
expr_attr_names[expression]
if expression in expr_attr_names
else expression
)
if projection_expression and expr_attr_names: if projection_expression and expr_attr_names:
expressions = [x.strip() for x in projection_expression.split(',')] expressions = [x.strip() for x in projection_expression.split(",")]
return ','.join(['.'.join([_adjust(expr) for expr in nested_expr.split('.')]) for nested_expr in expressions]) return ",".join(
[
".".join([_adjust(expr) for expr in nested_expr.split(".")])
for nested_expr in expressions
]
)
return projection_expression return projection_expression

View File

@ -561,174 +561,304 @@ def test_basic_projection_expressions_using_scan():
@mock_dynamodb2 @mock_dynamodb2
def test_nested_projection_expression_using_get_item(): def test_nested_projection_expression_using_get_item():
dynamodb = boto3.resource('dynamodb', region_name='us-east-1') dynamodb = boto3.resource("dynamodb", region_name="us-east-1")
# Create the DynamoDB table. # Create the DynamoDB table.
dynamodb.create_table(TableName='users', dynamodb.create_table(
KeySchema=[{'AttributeName': 'forum_name', 'KeyType': 'HASH'}], TableName="users",
AttributeDefinitions=[{'AttributeName': 'forum_name', 'AttributeType': 'S'}], KeySchema=[{"AttributeName": "forum_name", "KeyType": "HASH"}],
ProvisionedThroughput={'ReadCapacityUnits': 5, 'WriteCapacityUnits': 5}) AttributeDefinitions=[{"AttributeName": "forum_name", "AttributeType": "S"}],
table = dynamodb.Table('users') ProvisionedThroughput={"ReadCapacityUnits": 5, "WriteCapacityUnits": 5},
table.put_item(Item={'forum_name': 'key1', 'nested': {'level1': {'id': 'id1', 'att': 'irrelevant'}, )
'level2': {'id': 'id2', 'include': 'all'}, table = dynamodb.Table("users")
'level3': {'id': 'irrelevant'}}, table.put_item(
'foo': 'bar'}) Item={
table.put_item(Item={'forum_name': 'key2', 'nested': {'id': 'id2', 'incode': 'code2'}, 'foo': 'bar'}) "forum_name": "key1",
"nested": {
"level1": {"id": "id1", "att": "irrelevant"},
"level2": {"id": "id2", "include": "all"},
"level3": {"id": "irrelevant"},
},
"foo": "bar",
}
)
table.put_item(
Item={
"forum_name": "key2",
"nested": {"id": "id2", "incode": "code2"},
"foo": "bar",
}
)
# Test a get_item returning all items # Test a get_item returning all items
result = table.get_item(Key={'forum_name': 'key1'}, result = table.get_item(
ProjectionExpression='nested.level1.id, nested.level2')['Item'] Key={"forum_name": "key1"},
result.should.equal({'nested': {'level1': {'id': 'id1'}, ProjectionExpression="nested.level1.id, nested.level2",
'level2': {'id': 'id2', 'include': 'all'}}}) )["Item"]
result.should.equal(
{"nested": {"level1": {"id": "id1"}, "level2": {"id": "id2", "include": "all"}}}
)
# Assert actual data has not been deleted # Assert actual data has not been deleted
result = table.get_item(Key={'forum_name': 'key1'})['Item'] result = table.get_item(Key={"forum_name": "key1"})["Item"]
result.should.equal({u'foo': u'bar', result.should.equal(
u'forum_name': u'key1', {
u'nested': {u'level1': {u'id': u'id1', u'att': u'irrelevant'}, "foo": "bar",
u'level2': {u'id': u'id2', u'include': u'all'}, "forum_name": "key1",
u'level3': {u'id': u'irrelevant'}}}) "nested": {
"level1": {"id": "id1", "att": "irrelevant"},
"level2": {"id": "id2", "include": "all"},
"level3": {"id": "irrelevant"},
},
}
)
@mock_dynamodb2 @mock_dynamodb2
def test_basic_projection_expressions_using_query(): def test_basic_projection_expressions_using_query():
dynamodb = boto3.resource('dynamodb', region_name='us-east-1') dynamodb = boto3.resource("dynamodb", region_name="us-east-1")
# Create the DynamoDB table. # Create the DynamoDB table.
dynamodb.create_table(TableName='users', dynamodb.create_table(
KeySchema=[{'AttributeName': 'forum_name', 'KeyType': 'HASH'}, TableName="users",
{'AttributeName': 'subject', 'KeyType': 'RANGE'}], KeySchema=[
AttributeDefinitions=[{'AttributeName': 'forum_name', 'AttributeType': 'S'}, {"AttributeName": "forum_name", "KeyType": "HASH"},
{'AttributeName': 'subject', 'AttributeType': 'S'}], {"AttributeName": "subject", "KeyType": "RANGE"},
ProvisionedThroughput={'ReadCapacityUnits': 5, 'WriteCapacityUnits': 5}) ],
table = dynamodb.Table('users') AttributeDefinitions=[
table.put_item(Item={'forum_name': 'the-key', 'subject': '123', 'body': 'some test message'}) {"AttributeName": "forum_name", "AttributeType": "S"},
table.put_item(Item={'forum_name': 'not-the-key', 'subject': '123', 'body': 'some other test message'}) {"AttributeName": "subject", "AttributeType": "S"},
],
ProvisionedThroughput={"ReadCapacityUnits": 5, "WriteCapacityUnits": 5},
)
table = dynamodb.Table("users")
table.put_item(
Item={"forum_name": "the-key", "subject": "123", "body": "some test message"}
)
table.put_item(
Item={
"forum_name": "not-the-key",
"subject": "123",
"body": "some other test message",
}
)
# Test a query returning all items # Test a query returning all items
result = table.query(KeyConditionExpression=Key('forum_name').eq('the-key'), result = table.query(
ProjectionExpression='body, subject')['Items'][0] KeyConditionExpression=Key("forum_name").eq("the-key"),
ProjectionExpression="body, subject",
)["Items"][0]
assert 'body' in result assert "body" in result
assert result['body'] == 'some test message' assert result["body"] == "some test message"
assert 'subject' in result assert "subject" in result
assert 'forum_name' not in result assert "forum_name" not in result
table.put_item(Item={'forum_name': 'the-key', 'subject': '1234', 'body': 'yet another test message'}) table.put_item(
Item={
"forum_name": "the-key",
"subject": "1234",
"body": "yet another test message",
}
)
items = table.query(KeyConditionExpression=Key('forum_name').eq('the-key'), items = table.query(
ProjectionExpression='body')['Items'] KeyConditionExpression=Key("forum_name").eq("the-key"),
ProjectionExpression="body",
)["Items"]
assert 'body' in items[0] assert "body" in items[0]
assert 'subject' not in items[0] assert "subject" not in items[0]
assert items[0]['body'] == 'some test message' assert items[0]["body"] == "some test message"
assert 'body' in items[1] assert "body" in items[1]
assert 'subject' not in items[1] assert "subject" not in items[1]
assert items[1]['body'] == 'yet another test message' assert items[1]["body"] == "yet another test message"
# The projection expression should not remove data from storage # The projection expression should not remove data from storage
items = table.query(KeyConditionExpression=Key('forum_name').eq('the-key'))['Items'] items = table.query(KeyConditionExpression=Key("forum_name").eq("the-key"))["Items"]
assert 'subject' in items[0] assert "subject" in items[0]
assert 'body' in items[1] assert "body" in items[1]
assert 'forum_name' in items[1] assert "forum_name" in items[1]
@mock_dynamodb2 @mock_dynamodb2
def test_nested_projection_expression_using_query(): def test_nested_projection_expression_using_query():
dynamodb = boto3.resource('dynamodb', region_name='us-east-1') dynamodb = boto3.resource("dynamodb", region_name="us-east-1")
# Create the DynamoDB table. # Create the DynamoDB table.
dynamodb.create_table(TableName='users', dynamodb.create_table(
KeySchema=[{'AttributeName': 'forum_name', 'KeyType': 'HASH'}], TableName="users",
AttributeDefinitions=[{'AttributeName': 'forum_name', 'AttributeType': 'S'}], KeySchema=[{"AttributeName": "forum_name", "KeyType": "HASH"}],
ProvisionedThroughput={'ReadCapacityUnits': 5, 'WriteCapacityUnits': 5}) AttributeDefinitions=[{"AttributeName": "forum_name", "AttributeType": "S"}],
table = dynamodb.Table('users') ProvisionedThroughput={"ReadCapacityUnits": 5, "WriteCapacityUnits": 5},
table.put_item(Item={'forum_name': 'key1', 'nested': {'level1': {'id': 'id1', 'att': 'irrelevant'}, )
'level2': {'id': 'id2', 'include': 'all'}, table = dynamodb.Table("users")
'level3': {'id': 'irrelevant'}}, table.put_item(
'foo': 'bar'}) Item={
table.put_item(Item={'forum_name': 'key2', 'nested': {'id': 'id2', 'incode': 'code2'}, 'foo': 'bar'}) "forum_name": "key1",
"nested": {
"level1": {"id": "id1", "att": "irrelevant"},
"level2": {"id": "id2", "include": "all"},
"level3": {"id": "irrelevant"},
},
"foo": "bar",
}
)
table.put_item(
Item={
"forum_name": "key2",
"nested": {"id": "id2", "incode": "code2"},
"foo": "bar",
}
)
# Test a query returning all items # Test a query returning all items
result = table.query(KeyConditionExpression=Key('forum_name').eq('key1'), result = table.query(
ProjectionExpression="nested.level1.id, nested.level2")['Items'][0] KeyConditionExpression=Key("forum_name").eq("key1"),
ProjectionExpression="nested.level1.id, nested.level2",
)["Items"][0]
assert 'nested' in result assert "nested" in result
result['nested'].should.equal({'level1': {'id': 'id1'}, result["nested"].should.equal(
'level2': {'id': 'id2', 'include': 'all'}}) {"level1": {"id": "id1"}, "level2": {"id": "id2", "include": "all"}}
assert 'foo' not in result )
assert "foo" not in result
# Assert actual data has not been deleted # Assert actual data has not been deleted
result = table.query(KeyConditionExpression=Key('forum_name').eq('key1'))['Items'][0] result = table.query(KeyConditionExpression=Key("forum_name").eq("key1"))["Items"][
result.should.equal({u'foo': u'bar', 0
u'forum_name': u'key1', ]
u'nested': {u'level1': {u'id': u'id1', u'att': u'irrelevant'}, result.should.equal(
u'level2': {u'id': u'id2', u'include': u'all'}, {
u'level3': {u'id': u'irrelevant'}}}) "foo": "bar",
"forum_name": "key1",
"nested": {
"level1": {"id": "id1", "att": "irrelevant"},
"level2": {"id": "id2", "include": "all"},
"level3": {"id": "irrelevant"},
},
}
)
@mock_dynamodb2 @mock_dynamodb2
def test_basic_projection_expressions_using_scan(): def test_basic_projection_expressions_using_scan():
dynamodb = boto3.resource('dynamodb', region_name='us-east-1') dynamodb = boto3.resource("dynamodb", region_name="us-east-1")
# Create the DynamoDB table. # Create the DynamoDB table.
dynamodb.create_table(TableName='users', dynamodb.create_table(
KeySchema=[{'AttributeName': 'forum_name', 'KeyType': 'HASH'}, TableName="users",
{'AttributeName': 'subject', 'KeyType': 'RANGE'}], KeySchema=[
AttributeDefinitions=[{'AttributeName': 'forum_name', 'AttributeType': 'S'}, {"AttributeName": "forum_name", "KeyType": "HASH"},
{'AttributeName': 'subject', 'AttributeType': 'S'}], {"AttributeName": "subject", "KeyType": "RANGE"},
ProvisionedThroughput={'ReadCapacityUnits': 5, 'WriteCapacityUnits': 5}) ],
table = dynamodb.Table('users') AttributeDefinitions=[
{"AttributeName": "forum_name", "AttributeType": "S"},
{"AttributeName": "subject", "AttributeType": "S"},
],
ProvisionedThroughput={"ReadCapacityUnits": 5, "WriteCapacityUnits": 5},
)
table = dynamodb.Table("users")
table.put_item(Item={'forum_name': 'the-key', 'subject': '123', 'body': 'some test message'}) table.put_item(
table.put_item(Item={'forum_name': 'not-the-key', 'subject': '123', 'body': 'some other test message'}) Item={"forum_name": "the-key", "subject": "123", "body": "some test message"}
)
table.put_item(
Item={
"forum_name": "not-the-key",
"subject": "123",
"body": "some other test message",
}
)
# Test a scan returning all items # Test a scan returning all items
results = table.scan(FilterExpression=Key('forum_name').eq('the-key'), results = table.scan(
ProjectionExpression='body, subject')['Items'] FilterExpression=Key("forum_name").eq("the-key"),
ProjectionExpression="body, subject",
)["Items"]
results.should.equal([{u'body': u'some test message', u'subject': u'123'}]) results.should.equal([{"body": "some test message", "subject": "123"}])
table.put_item(Item={'forum_name': 'the-key', 'subject': '1234', 'body': 'yet another test message'}) table.put_item(
Item={
"forum_name": "the-key",
"subject": "1234",
"body": "yet another test message",
}
)
results = table.scan(FilterExpression=Key('forum_name').eq('the-key'), results = table.scan(
ProjectionExpression='body')['Items'] FilterExpression=Key("forum_name").eq("the-key"), ProjectionExpression="body"
)["Items"]
assert {u'body': u'some test message'} in results assert {"body": "some test message"} in results
assert {u'body': u'yet another test message'} in results assert {"body": "yet another test message"} in results
# The projection expression should not remove data from storage # The projection expression should not remove data from storage
results = table.query(KeyConditionExpression=Key('forum_name').eq('the-key')) results = table.query(KeyConditionExpression=Key("forum_name").eq("the-key"))
assert 'subject' in results['Items'][0] assert "subject" in results["Items"][0]
assert 'body' in results['Items'][1] assert "body" in results["Items"][1]
assert 'forum_name' in results['Items'][1] assert "forum_name" in results["Items"][1]
@mock_dynamodb2 @mock_dynamodb2
def test_nested_projection_expression_using_scan(): def test_nested_projection_expression_using_scan():
dynamodb = boto3.resource('dynamodb', region_name='us-east-1') dynamodb = boto3.resource("dynamodb", region_name="us-east-1")
# Create the DynamoDB table. # Create the DynamoDB table.
dynamodb.create_table(TableName='users', dynamodb.create_table(
KeySchema=[{'AttributeName': 'forum_name', 'KeyType': 'HASH'}], TableName="users",
AttributeDefinitions=[{'AttributeName': 'forum_name', 'AttributeType': 'S'}], KeySchema=[{"AttributeName": "forum_name", "KeyType": "HASH"}],
ProvisionedThroughput={'ReadCapacityUnits': 5, 'WriteCapacityUnits': 5}) AttributeDefinitions=[{"AttributeName": "forum_name", "AttributeType": "S"}],
table = dynamodb.Table('users') ProvisionedThroughput={"ReadCapacityUnits": 5, "WriteCapacityUnits": 5},
table.put_item(Item={'forum_name': 'key1', 'nested': {'level1': {'id': 'id1', 'att': 'irrelevant'}, )
'level2': {'id': 'id2', 'include': 'all'}, table = dynamodb.Table("users")
'level3': {'id': 'irrelevant'}}, table.put_item(
'foo': 'bar'}) Item={
table.put_item(Item={'forum_name': 'key2', 'nested': {'id': 'id2', 'incode': 'code2'}, 'foo': 'bar'}) "forum_name": "key1",
"nested": {
"level1": {"id": "id1", "att": "irrelevant"},
"level2": {"id": "id2", "include": "all"},
"level3": {"id": "irrelevant"},
},
"foo": "bar",
}
)
table.put_item(
Item={
"forum_name": "key2",
"nested": {"id": "id2", "incode": "code2"},
"foo": "bar",
}
)
# Test a scan # Test a scan
results = table.scan(FilterExpression=Key('forum_name').eq('key1'), results = table.scan(
ProjectionExpression='nested.level1.id, nested.level2')['Items'] FilterExpression=Key("forum_name").eq("key1"),
results.should.equal([{'nested': {'level1': {'id': 'id1'}, ProjectionExpression="nested.level1.id, nested.level2",
'level2': {'include': 'all', 'id': 'id2'}}}]) )["Items"]
results.should.equal(
[
{
"nested": {
"level1": {"id": "id1"},
"level2": {"include": "all", "id": "id2"},
}
}
]
)
# Assert original data is still there # Assert original data is still there
results = table.scan(FilterExpression=Key('forum_name').eq('key1'))['Items'] results = table.scan(FilterExpression=Key("forum_name").eq("key1"))["Items"]
results.should.equal([{'forum_name': 'key1', results.should.equal(
'foo': 'bar', [
'nested': {'level1': {'att': 'irrelevant', 'id': 'id1'}, {
'level2': {'include': 'all', 'id': 'id2'}, "forum_name": "key1",
'level3': {'id': 'irrelevant'}}}]) "foo": "bar",
"nested": {
"level1": {"att": "irrelevant", "id": "id1"},
"level2": {"include": "all", "id": "id2"},
"level3": {"id": "irrelevant"},
},
}
]
)
@mock_dynamodb2 @mock_dynamodb2
@ -832,67 +962,117 @@ def test_basic_projection_expressions_using_query_with_attr_expression_names():
@mock_dynamodb2 @mock_dynamodb2
def test_nested_projection_expression_using_get_item_with_attr_expression(): def test_nested_projection_expression_using_get_item_with_attr_expression():
dynamodb = boto3.resource('dynamodb', region_name='us-east-1') dynamodb = boto3.resource("dynamodb", region_name="us-east-1")
# Create the DynamoDB table. # Create the DynamoDB table.
dynamodb.create_table(TableName='users', dynamodb.create_table(
KeySchema=[{'AttributeName': 'forum_name', 'KeyType': 'HASH'}], TableName="users",
AttributeDefinitions=[{'AttributeName': 'forum_name', 'AttributeType': 'S'}], KeySchema=[{"AttributeName": "forum_name", "KeyType": "HASH"}],
ProvisionedThroughput={'ReadCapacityUnits': 5, 'WriteCapacityUnits': 5}) AttributeDefinitions=[{"AttributeName": "forum_name", "AttributeType": "S"}],
table = dynamodb.Table('users') ProvisionedThroughput={"ReadCapacityUnits": 5, "WriteCapacityUnits": 5},
table.put_item(Item={'forum_name': 'key1', 'nested': {'level1': {'id': 'id1', 'att': 'irrelevant'}, )
'level2': {'id': 'id2', 'include': 'all'}, table = dynamodb.Table("users")
'level3': {'id': 'irrelevant'}}, table.put_item(
'foo': 'bar'}) Item={
table.put_item(Item={'forum_name': 'key2', 'nested': {'id': 'id2', 'incode': 'code2'}, 'foo': 'bar'}) "forum_name": "key1",
"nested": {
"level1": {"id": "id1", "att": "irrelevant"},
"level2": {"id": "id2", "include": "all"},
"level3": {"id": "irrelevant"},
},
"foo": "bar",
}
)
table.put_item(
Item={
"forum_name": "key2",
"nested": {"id": "id2", "incode": "code2"},
"foo": "bar",
}
)
# Test a get_item returning all items # Test a get_item returning all items
result = table.get_item(Key={'forum_name': 'key1'}, result = table.get_item(
ProjectionExpression='#nst.level1.id, #nst.#lvl2', Key={"forum_name": "key1"},
ExpressionAttributeNames={'#nst': 'nested', '#lvl2': 'level2'})['Item'] ProjectionExpression="#nst.level1.id, #nst.#lvl2",
result.should.equal({'nested': {'level1': {'id': 'id1'}, ExpressionAttributeNames={"#nst": "nested", "#lvl2": "level2"},
'level2': {'id': 'id2', 'include': 'all'}}}) )["Item"]
result.should.equal(
{"nested": {"level1": {"id": "id1"}, "level2": {"id": "id2", "include": "all"}}}
)
# Assert actual data has not been deleted # Assert actual data has not been deleted
result = table.get_item(Key={'forum_name': 'key1'})['Item'] result = table.get_item(Key={"forum_name": "key1"})["Item"]
result.should.equal({u'foo': u'bar', result.should.equal(
u'forum_name': u'key1', {
u'nested': {u'level1': {u'id': u'id1', u'att': u'irrelevant'}, "foo": "bar",
u'level2': {u'id': u'id2', u'include': u'all'}, "forum_name": "key1",
u'level3': {u'id': u'irrelevant'}}}) "nested": {
"level1": {"id": "id1", "att": "irrelevant"},
"level2": {"id": "id2", "include": "all"},
"level3": {"id": "irrelevant"},
},
}
)
@mock_dynamodb2 @mock_dynamodb2
def test_nested_projection_expression_using_query_with_attr_expression_names(): def test_nested_projection_expression_using_query_with_attr_expression_names():
dynamodb = boto3.resource('dynamodb', region_name='us-east-1') dynamodb = boto3.resource("dynamodb", region_name="us-east-1")
# Create the DynamoDB table. # Create the DynamoDB table.
dynamodb.create_table(TableName='users', dynamodb.create_table(
KeySchema=[{'AttributeName': 'forum_name', 'KeyType': 'HASH'}], TableName="users",
AttributeDefinitions=[{'AttributeName': 'forum_name', 'AttributeType': 'S'}], KeySchema=[{"AttributeName": "forum_name", "KeyType": "HASH"}],
ProvisionedThroughput={'ReadCapacityUnits': 5, 'WriteCapacityUnits': 5}) AttributeDefinitions=[{"AttributeName": "forum_name", "AttributeType": "S"}],
table = dynamodb.Table('users') ProvisionedThroughput={"ReadCapacityUnits": 5, "WriteCapacityUnits": 5},
table.put_item(Item={'forum_name': 'key1', 'nested': {'level1': {'id': 'id1', 'att': 'irrelevant'}, )
'level2': {'id': 'id2', 'include': 'all'}, table = dynamodb.Table("users")
'level3': {'id': 'irrelevant'}}, table.put_item(
'foo': 'bar'}) Item={
table.put_item(Item={'forum_name': 'key2', 'nested': {'id': 'id2', 'incode': 'code2'}, 'foo': 'bar'}) "forum_name": "key1",
"nested": {
"level1": {"id": "id1", "att": "irrelevant"},
"level2": {"id": "id2", "include": "all"},
"level3": {"id": "irrelevant"},
},
"foo": "bar",
}
)
table.put_item(
Item={
"forum_name": "key2",
"nested": {"id": "id2", "incode": "code2"},
"foo": "bar",
}
)
# Test a query returning all items # Test a query returning all items
result = table.query(KeyConditionExpression=Key('forum_name').eq('key1'), result = table.query(
ProjectionExpression="#nst.level1.id, #nst.#lvl2", KeyConditionExpression=Key("forum_name").eq("key1"),
ExpressionAttributeNames={'#nst': 'nested', '#lvl2': 'level2'})['Items'][0] ProjectionExpression="#nst.level1.id, #nst.#lvl2",
ExpressionAttributeNames={"#nst": "nested", "#lvl2": "level2"},
)["Items"][0]
assert 'nested' in result assert "nested" in result
result['nested'].should.equal({'level1': {'id': 'id1'}, result["nested"].should.equal(
'level2': {'id': 'id2', 'include': 'all'}}) {"level1": {"id": "id1"}, "level2": {"id": "id2", "include": "all"}}
assert 'foo' not in result )
assert "foo" not in result
# Assert actual data has not been deleted # Assert actual data has not been deleted
result = table.query(KeyConditionExpression=Key('forum_name').eq('key1'))['Items'][0] result = table.query(KeyConditionExpression=Key("forum_name").eq("key1"))["Items"][
result.should.equal( {u'foo': u'bar', 0
u'forum_name': u'key1', ]
u'nested': {u'level1': {u'id': u'id1', u'att': u'irrelevant'}, result.should.equal(
u'level2': {u'id': u'id2', u'include': u'all'}, {
u'level3': {u'id': u'irrelevant'}}}) "foo": "bar",
"forum_name": "key1",
"nested": {
"level1": {"id": "id1", "att": "irrelevant"},
"level2": {"id": "id2", "include": "all"},
"level3": {"id": "irrelevant"},
},
}
)
@mock_dynamodb2 @mock_dynamodb2
@ -958,33 +1138,66 @@ def test_basic_projection_expressions_using_scan_with_attr_expression_names():
@mock_dynamodb2 @mock_dynamodb2
def test_nested_projection_expression_using_scan_with_attr_expression_names(): def test_nested_projection_expression_using_scan_with_attr_expression_names():
dynamodb = boto3.resource('dynamodb', region_name='us-east-1') dynamodb = boto3.resource("dynamodb", region_name="us-east-1")
# Create the DynamoDB table. # Create the DynamoDB table.
dynamodb.create_table(TableName='users', dynamodb.create_table(
KeySchema=[{'AttributeName': 'forum_name', 'KeyType': 'HASH'}], TableName="users",
AttributeDefinitions=[{'AttributeName': 'forum_name', 'AttributeType': 'S'}], KeySchema=[{"AttributeName": "forum_name", "KeyType": "HASH"}],
ProvisionedThroughput={'ReadCapacityUnits': 5, 'WriteCapacityUnits': 5}) AttributeDefinitions=[{"AttributeName": "forum_name", "AttributeType": "S"}],
table = dynamodb.Table('users') ProvisionedThroughput={"ReadCapacityUnits": 5, "WriteCapacityUnits": 5},
table.put_item(Item={'forum_name': 'key1', 'nested': {'level1': {'id': 'id1', 'att': 'irrelevant'}, )
'level2': {'id': 'id2', 'include': 'all'}, table = dynamodb.Table("users")
'level3': {'id': 'irrelevant'}}, table.put_item(
'foo': 'bar'}) Item={
table.put_item(Item={'forum_name': 'key2', 'nested': {'id': 'id2', 'incode': 'code2'}, 'foo': 'bar'}) "forum_name": "key1",
"nested": {
"level1": {"id": "id1", "att": "irrelevant"},
"level2": {"id": "id2", "include": "all"},
"level3": {"id": "irrelevant"},
},
"foo": "bar",
}
)
table.put_item(
Item={
"forum_name": "key2",
"nested": {"id": "id2", "incode": "code2"},
"foo": "bar",
}
)
# Test a scan # Test a scan
results = table.scan(FilterExpression=Key('forum_name').eq('key1'), results = table.scan(
ProjectionExpression='nested.level1.id, nested.level2', FilterExpression=Key("forum_name").eq("key1"),
ExpressionAttributeNames={'#nst': 'nested', '#lvl2': 'level2'})['Items'] ProjectionExpression="nested.level1.id, nested.level2",
results.should.equal([{'nested': {'level1': {'id': 'id1'}, ExpressionAttributeNames={"#nst": "nested", "#lvl2": "level2"},
'level2': {'include': 'all', 'id': 'id2'}}}]) )["Items"]
results.should.equal(
[
{
"nested": {
"level1": {"id": "id1"},
"level2": {"include": "all", "id": "id2"},
}
}
]
)
# Assert original data is still there # Assert original data is still there
results = table.scan(FilterExpression=Key('forum_name').eq('key1'))['Items'] results = table.scan(FilterExpression=Key("forum_name").eq("key1"))["Items"]
results.should.equal([{'forum_name': 'key1', results.should.equal(
'foo': 'bar', [
'nested': {'level1': {'att': 'irrelevant', 'id': 'id1'}, {
'level2': {'include': 'all', 'id': 'id2'}, "forum_name": "key1",
'level3': {'id': 'irrelevant'}}}]) "foo": "bar",
"nested": {
"level1": {"att": "irrelevant", "id": "id1"},
"level2": {"include": "all", "id": "id2"},
"level3": {"id": "irrelevant"},
},
}
]
)
@mock_dynamodb2 @mock_dynamodb2