5

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?

2
  • What is the code do you use? Commented Oct 10, 2017 at 14:33
  • @Kannaiyan I have edited the question. Commented Oct 11, 2017 at 5:25

2 Answers 2

5

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.

Sign up to request clarification or add additional context in comments.

7 Comments

the expect block will still give error "The provided key element does not match the schema", because imagePath is the dynamic field which I am trying to add (imagePath is not in the table schema). What is the solution for this? @notionquest
Dynamodb accepts dynamic fields. You dont need to define all the fields in schema. In the update item api, you need to use the key attributes correctly. What is your partition key and sort key?
Your partition key attribute name is RequestId or Id?
My update function - table.update_item(Key = {'RequestId' : id, 'Source' : 'Ironwood' }, UpdateExpression = 'SET ImagePath = :value1', ExpressionAttributeValues = {':value1': imagePath}). RequestId is the partition key and Source is the sort key.
RequestId is the partition key is and Source is the sort key. I have put id in the question is for understanding.
|
1

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

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.