调用查询操作时发生dynamo错误(ValidationException):KeyConditionExpression无效:

2024-04-20 14:58:12 发布

您现在位置:Python中文网/ 问答频道 /正文

我对dynamodb有点陌生

请参阅下面链接中StackOverflow帖子中的说明,尝试在python lambda函数中获取dynamodb表的最大id时出现的错误 Dynamodb max value

 An error occurred (ValidationException) when calling the Query operation: Invalid KeyConditionExpression: The expression can not be empty;\"}"

请参阅下面我的lambda函数代码

import json
import boto3

TABLE_NAME = 'user-profiles'

dynamo_DB = boto3.resource('dynamodb')    

def lambda_handler(event, context):
    user_id = event['user_id']
    email = event['email']
    bvn = event['bvn']
    password = event['password']
    phone = event['phone']
    gender = event['gender']

    output = ''


    if len(user_id) > 1 and len(password) > 5:
        try:


            table = dynamo_DB.Table(TABLE_NAME)

            values = list(table.query(
                KeyConditionExpression='',
                ScanIndexForward=False,
                Limit=1
                )
            )
            max_id = values[0]['id']
            new_id = max_id + 1

            Item = {
                'id': str(new_id),
                'profile-id': str(new_id),
                'user_id': user_id,
                'email': email,
                'bvn': bvn,
                'password': password,
                'phone': phone,
                'gender': gender
            }
            table.put_item(Item=Item)

            output += 'Data Inserted To Dynamodb Successfully'
        except Exception as e:
            output += 'error with dynamo registration ' + str(e)
            # print(output)

    else:
        output += 'invalid user or password entered, this is ' \
                  'what i received:\nusername: ' \
                  + str(user_id) + '\npassword: ' + str(password)

    return {
        "statusCode": 200,
        "body": json.dumps({
            "message": output,
        }),
    }
    # print(output)

Tags: lambdaeventidoutputemailtablephonepassword
1条回答
网友
1楼 · 发布于 2024-04-20 14:58:12

如果需要从需要使用^{}的表中读取所有记录,则不能使用空的query。但是您不能使用ScanIndexForward在那里对记录进行排序

看起来您正在尝试实现主键递增。我想警告你,你的解决方案并不是非常棒,因为你很容易达到比赛条件

我的建议是: 我猜您正在使用id作为主键(也称为分区键)。没关系。我要做的是在表中插入一条额外的记录,比如说increment值:

increment = table.update_item(
    Key={'id': 'increment'},
    UpdateExpression='ADD #increment :increment',
    ExpressionAttributeNames={'#increment': 'increment'},
    ExpressionAttributeValues={':increment': 1},
    ReturnValues='UPDATED_NEW',
)

new_id = increment['Attributes']['increment']

此查询将使用id: 'increment'更新现有记录,并在记录中存储一个新的递增数字,如果这是第一个查询,则将使用increment: 1创建记录,随后的调用将递增该记录。ReturnValues表示查询将在更新后返回结果,您将获得一个新id

将代码放在适当的位置,而不是最后一条记录所在的位置

因此,您的代码如下所示:

import json
import boto3

TABLE_NAME = 'user-profiles'

dynamo_DB = boto3.resource('dynamodb')    

def lambda_handler(event, context):
    user_id = event['user_id']
    email = event['email']
    bvn = event['bvn']
    password = event['password']
    phone = event['phone']
    gender = event['gender']

    output = ''


    if len(user_id) > 1 and len(password) > 5:
        try:


            table = dynamo_DB.Table(TABLE_NAME)

            increment = table.update_item(
                Key={'id': 'increment'},
                UpdateExpression='ADD #increment :increment',
                ExpressionAttributeNames={'#increment': 'increment'},
                ExpressionAttributeValues={':increment': 1},
                ReturnValues='UPDATED_NEW',
            )

            new_id = increment['Attributes']['increment']

            Item = {
                'id': str(new_id),
                'profile-id': str(new_id),
                'user_id': user_id,
                'email': email,
                'bvn': bvn,
                'password': password,
                'phone': phone,
                'gender': gender
            }
            table.put_item(Item=Item)

            output += 'Data Inserted To Dynamodb Successfully'
        except Exception as e:
            output += 'error with dynamo registration ' + str(e)
            # print(output)

    else:
        output += 'invalid user or password entered, this is ' \
                  'what i received:\nusername: ' \
                  + str(user_id) + '\npassword: ' + str(password)

    return {
        "statusCode": 200,
        "body": json.dumps({
            "message": output,
        }),
    }
    # print(output)

你很好

额外想法:

为了100%确保增量上没有竞争条件,您可以通过以下方式实现锁定机制:在增量之前,使用idvaluelocklock属性和任何值放置一个额外的记录,并使用ConditionExpression='attribute_not_exists(lock)'。然后进行增量,然后通过删除记录lock来释放锁。因此,当记录存在时,第二次尝试“锁定”会因属性lock存在的条件而中断,并抛出错误ConditionalCheckFailedException(您可以捕获错误并向用户显示记录已锁定或其他情况)

下面是JavaScript中的一个示例:

module.exports.DynamoDbClient = class DynamoDbClient {
  constructor(tableName) {
    this.dynamoDb = new DynamoDB.DocumentClient();
    this.tableName = tableName;
  }

  async increment() {
    await this.lock();

    const {Attributes: {increment}} = await this.dynamoDb.update({
      TableName: this.tableName,
      Key: {id: 'increment'},
      UpdateExpression: 'ADD #increment :increment',
      ExpressionAttributeNames: {'#increment': 'increment'},
      ExpressionAttributeValues: {':increment': 1},
      ReturnValues: 'UPDATED_NEW',
    }).promise();

    await this.unlock();

    return increment;
  }

  async lock(key) {
    try {
      await this.dynamoDb.put({
        TableName: this.tableName,
        Item: {id: 'lock', _lock: true},
        ConditionExpression: 'attribute_not_exists(#lock)',
        ExpressionAttributeNames: {'#lock': '_lock'},
      }).promise();
    } catch (error) {
      if (error.code === 'ConditionalCheckFailedException') {
        throw new LockError(`Key is locked.`);
      }

      throw error;
    }
  }

  unlock() {
    return this.delete({id: 'lock'});
  }

  async delete(key) {
    await this.dynamoDb.delete({
      TableName: this.tableName,
      Key: key,
    }).promise();
  }
}

// usage

const client = new DynamoDbClient('table');
const newId = await client.increment();

...

相关问题 更多 >