Home > Enterprise >  dynamodb table update item not detecting attribute
dynamodb table update item not detecting attribute

Time:01-01

when updating dynamodb table i get this error:

ERROR:root:------- here: An error occurred (ValidationException) when calling the 
 UpdateItem operation: The provided expression refers to an attribute that does not exist 
 in the item

I think its the expression in update_vote function that causes trouble

#!/usr/bin/env python3

import boto3
import json
import logging
import sys

logging.basicConfig(stream=sys.stdout, level=logging.INFO)

queue = boto3.resource('sqs', region_name='us-east-1').get_queue_by_name(QueueName="erjan")
table = boto3.resource('dynamodb', region_name='us-east-1').Table('Votes')

def process_message(message):
    try:
        
        payload = message.message_attributes
        voter = payload['voter']['StringValue']
        vote  = payload['vote']['StringValue']

        logging.info("Voter: %s, Vote: %s", voter, vote)
        store_vote(voter, vote)
        update_count(vote)
        message.delete()
    except Exception as e:
        print('-------')
        print('message.attr')
        print(message.message_attributes)
        try:
            vote = payload['vote']['StringValue']
            logging.error("Failed to process message")
            logging.error('vote %d' % vote)
        except TypeError:           
            logging.error("yes error catched")

def store_vote(voter, vote):
    try:
        logging.info('table put item.......')
        print('table put item......')
        response = table.put_item(
           Item={'voter': voter, 'vote': vote}
        )
    except:
        logging.error(" Failed to store message")
        raise


#this function causes error i think

def update_count(vote):
    logging.info('update count....')
    print('update count....')
    table.update_item(
        Key={'voter': 'default_voter'},
        UpdateExpression="set #vote = #vote   :incr", #this update expression
            ExpressionAttributeNames={'#vote': vote},
            ExpressionAttributeValues={':incr': 1}
    )

if __name__ == "__main__":
    while True:
        try:
            messages = queue.receive_messages(MessageAttributeNames=['vote','voter'])
        except KeyboardInterrupt:
           logging.info("Stopping...")
           break
        except:
            logging.error(sys.exc_info()[0])
            continue
        for message in messages:
            process_message(message)

dynamodb table has 1 partition key - 'voter'. it should store counts for how many clicks "a" or "b" received.

CodePudding user response:

You may want to set a default value if currently there is no value stored to be incremented:

def update_count(vote):
    logging.info('update count....')
    print('update count....')
    table.update_item(
        Key={'voter': 'default_voter'},
        UpdateExpression="set #vote = if_not_exists(#vote, :start)   :incr",  
        ExpressionAttributeNames={'#vote': vote},
        ExpressionAttributeValues={':incr': 1, ':start': 0}
    )

Notice the if_not_exists which will set the initial value for the votes to 0.

CodePudding user response:

The reason you are seeing this exception is due to the fact no vote attribute exists in your item currently, causing the exception:

The provided expression refers to an attribute that does not exist in the item


1. Use ADD instead of SET

This is the recommended approach. It automatically sets the value to 0 should it not exist, then adds your provided value. The only time you would use option #2 is when the attribute is stored within a nested value, for which ADD will not work.

Use the ADD action in an update expression to add a new attribute and its values to an item. DOCS

  • ADD path value
table.update_item(
    Key={'voter': 'default_voter'},
    UpdateExpression="ADD #vote :incr",
    ExpressionAttributeNames={'#vote': vote},
    ExpressionAttributeValues={':incr': 1}
)

2. Use if_not_exists Function

If the item does not contain an attribute at the specified path, if_not_exists evaluates to value; otherwise, it evaluates to path.

This approach requires you to set a value which is initially set if the attribute should not exist: :empty=0. This is more of a workaround than how you are recommended to add values in DynamoDB.

  • if_not_exists (path, value)
table.update_item(
    Key={'voter': 'default_voter'},
    UpdateExpression="set #vote = if_not_exists(#vote, :empty)   :incr",
    ExpressionAttributeNames={'#vote': vote},
    ExpressionAttributeValues={':incr': 1, ':empty': 0}
)
  • Related