Repository: getmoto/moto

Issue:
dynamodb update item with nested item issue
Hi,

When I update_item with Update expression as 
SET #name1.#name2 = :value'
where name1 is a map

When I scan and read the item, I get the result as
'Name1.Name2':
the item is saved as #name1.#name2 instead of a map structure. Is it a known issue


Discussion:
Not a known issue as far as im aware, can you create a simple test case for this and i'll look into it
I have the same issue. It seems UpdateExpression doesn't work with Nested Map Attributes properly. I'm using mock_dynamodb2.

If I run something like this
```python
table.update_item(
            Key={
                'ID': id,
            },
            UpdateExpression='SET ResultMap.#final.#key = :status',
            ExpressionAttributeNames={
                '#final': 'Final',
                '#key': 'Key'
            },
            ExpressionAttributeValues={
                ':status': 'new status'
            }
        )
```
and then read the data with scan, a result item contains a new attribute with the key 'ResultMap.Final.Key' instead of updating a nested attribute. It works properly if I run my function in AWS.

moto version 1.1.24

Resolving patch:
diff --git a/moto/dynamodb2/models.py b/moto/dynamodb2/models.py
--- a/moto/dynamodb2/models.py
+++ b/moto/dynamodb2/models.py
@@ -149,9 +149,38 @@ def update(self, update_expression, expression_attribute_names, expression_attri
                     key = key.strip()
                     value = value.strip()
                     if value in expression_attribute_values:
-                        self.attrs[key] = DynamoType(expression_attribute_values[value])
+                        value = DynamoType(expression_attribute_values[value])
                     else:
-                        self.attrs[key] = DynamoType({"S": value})
+                        value = DynamoType({"S": value})
+
+                    if '.' not in key:
+                        self.attrs[key] = value
+                    else:
+                        # Handle nested dict updates
+                        key_parts = key.split('.')
+                        attr = key_parts.pop(0)
+                        if attr not in self.attrs:
+                            raise ValueError()
+
+                        last_val = self.attrs[attr].value
+                        for key_part in key_parts:
+                            # Hack but it'll do, traverses into a dict
+                            if list(last_val.keys())[0] == 'M':
+                                last_val = last_val['M']
+
+                            if key_part not in last_val:
+                                raise ValueError()
+
+                            last_val = last_val[key_part]
+
+                        # We have reference to a nested object but we cant just assign to it
+                        current_type = list(last_val.keys())[0]
+                        if current_type == value.type:
+                            last_val[current_type] = value.value
+                        else:
+                            last_val[value.type] = value.value
+                            del last_val[current_type]
+
                 elif action == 'ADD':
                     key, value = value.split(" ", 1)
                     key = key.strip()


Test patch:
diff --git a/tests/test_dynamodb2/test_dynamodb.py b/tests/test_dynamodb2/test_dynamodb.py
--- a/tests/test_dynamodb2/test_dynamodb.py
+++ b/tests/test_dynamodb2/test_dynamodb.py
@@ -1006,3 +1006,64 @@ def test_query_missing_expr_names():
 
     resp['Count'].should.equal(1)
     resp['Items'][0]['client']['S'].should.equal('test2')
+
+
+# https://github.com/spulec/moto/issues/1342
+@mock_dynamodb2
+def test_update_item_on_map():
+    dynamodb = boto3.resource('dynamodb', region_name='us-east-1')
+
+    # Create the DynamoDB table.
+    dynamodb.create_table(
+        TableName='users',
+        KeySchema=[
+            {
+                'AttributeName': 'forum_name',
+                'KeyType': 'HASH'
+            },
+            {
+                'AttributeName': 'subject',
+                'KeyType': 'RANGE'
+            },
+        ],
+        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': {'nested': {'data': 'test'}},
+    })
+
+    resp = table.scan()
+    resp['Items'][0]['body'].should.equal({'nested': {'data': 'test'}})
+
+    table.update_item(Key={
+        'forum_name': 'the-key',
+        'subject': '123'
+        },
+        UpdateExpression='SET body.#nested.#data = :tb',
+        ExpressionAttributeNames={
+            '#nested': 'nested',
+            '#data': 'data'
+        },
+        ExpressionAttributeValues={
+            ':tb': 'new_value'
+    })
+
+    resp = table.scan()
+    resp['Items'][0]['body'].should.equal({'nested': {'data': 'new_value'}})