manojpt
manojpt

Reputation: 1269

update an item adding an attribute in dynamodb

Is it not possible to add an attribute in dynamodb dynamically?

I got this error when I tried-"The provided key element does not match the schema".

Scenario -

{ id : "123",
  imageName : "elephant.jpg"
}

I want to add an attribute - imagePath : "/path/to/image" to the above data. I used put_item, but it replaces the old item if exists.

I am searching for the solution for - If id = "123", then add imagePath attribute else add a new item to the table.

Adding an attribute can be achieved using put_item but it will replace the existing item. How can we dynamically add an attribute to the existing data using update_item ? (appending imagePath to the the given json)

Should I alter the schema of the table with imagePath and then use update_item function?

How can we achieve this using python?

Upvotes: 5

Views: 21164

Answers (2)

matt_linker
matt_linker

Reputation: 187

Lastest version of update_item will handle attribute creation if does not already exists

import boto3 
from boto3.dynamodb.conditions import Key

def query_status(asset_id, dynamodb, table):
    try:        
        response = table.query(
            ProjectionExpression="#asset_id, status_id" ,
            ExpressionAttributeNames={"#asset_id": "asset_id"},
            KeyConditionExpression=Key('asset_id').eq(asset_id)        
        )
        if response['Items']:
            return response['Items'][0]["status_id"]
    except:
        pass # if attribute does not exists, return None

    
 
def update_asset_status(asset_id, status_id, dynamodb, table):  
    response = table.update_item(
        Key={'asset_id': asset_id},
        UpdateExpression="set status_id=:r",
        ExpressionAttributeValues={':r': status_id},
        ReturnValues="UPDATED_NEW"
    )
    return response



dynamodb = boto3.resource('dynamodb')
table = dynamodb.Table('assets')

asset_id='1234'

print("Current Status: ", query_status(asset_id, dynamodb, table))

new_status_id='4'
update_asset_status(asset_id, new_status_id, dynamodb, table)

print("New Status: ", query_status(id, dynamodb, table))

Upvotes: 1

notionquest
notionquest

Reputation: 39226

Unfortunately, it can't be achieved in single step. However, it can be achieved in two step process:-

1) Try to insert the data conditionally i.e. if the key value is already present don't perform any operation (i.e. insert or update - nothing happens)

2) If there is a ConditionalCheckFailedException, then update the item

Sample code:-

In the below code, usertable is the table name. The key attributes of the table are userid and score. You need to change the below code accordingly for your table structure.

Also, I have assigned the key value (as "Mike"). You need to change it accordingly for your use case.

from __future__ import print_function # Python 2/3 compatibility
from boto.dynamodb2.exceptions import ConditionalCheckFailedException
from botocore.exceptions import ClientError
from boto3.dynamodb.conditions import Attr
import boto3
import json
import decimal

# Helper class to convert a DynamoDB item to JSON.
class DecimalEncoder(json.JSONEncoder):
    def default(self, o):
        if isinstance(o, decimal.Decimal):
            if o % 1 > 0:
                return float(o)
            else:
                return int(o)
        return super(DecimalEncoder, self).default(o)

dynamodb = boto3.resource('dynamodb', region_name='us-west-2', endpoint_url="http://localhost:8000")

table = dynamodb.Table('usertable')

userId = "Mike"

try :
    response = table.put_item(
    Item={
            'userid': userId,
            'score' : 100,
            'imagePath' : '/path/to/image'        
        },
        ConditionExpression=Attr('userid').ne(userId)        
    )

    print("Conditional PutItem succeeded:")
    print(json.dumps(response, indent=4, cls=DecimalEncoder))
except ClientError as ce :    
    print("Conditional check failed:", ce)
    if ce.response['Error']['Code'] == 'ConditionalCheckFailedException':
        print("Key already exists")
        response = table.update_item(
            Key={'userid': userId, 'score' : 100},
            UpdateExpression="set imagePath = :imagePathVal",
            ExpressionAttributeValues={":imagePathVal" : "/path/to/image" }
        )
        print("Update existing item succeeded:")
        print(json.dumps(response, indent=4, cls=DecimalEncoder))        
    else:
        print("Unexpected error: %s" % e

)

Update:-

The data type of variable id and key attribute RequestId should match.

Upvotes: 5

Related Questions