Условно поместить Item в DynamoDB

В моей лямбда-функции я хочу условно помещать элементы в мою DynamoDB, только если значение НЕ СУЩЕСТВУЕТ. Я видел несколько разных источников, где они используют это ConditionExpression, и я не могу понять, что с этим не так.

body = await dynamo.put({
                    TableName: 'polit-stream',
                    Item: {
                        urlPath: data.urlPath,
                    },
                    ConditionExpression: "attribute_not_exists(urlPath)"
                }).promise();

Размещение всегда будет успешным, даже если значение моего вторичного индекса (urlPath) уже существует.

Полный код:

const AWS = require('aws-sdk');
const crypto = require("crypto");
const dynamo = new AWS.DynamoDB.DocumentClient();

/**
 * Demonstrates a simple HTTP endpoint using API Gateway. You have full
 * access to the request and response payload, including headers and
 * status code.
 *
 * To scan a DynamoDB table, make a GET request with the TableName as a
 * query string parameter. To put, update, or delete an item, make a POST,
 * PUT, or DELETE request respectively, passing in the payload to the
 * DynamoDB API as a JSON body.
 */
exports.handler = async(event, context) => {

    let body;
    let statusCode = '200';
    const headers = {
        'Content-Type': 'application/json',
        'Access-Control-Allow-Origin': '* '
    }

    const data = JSON.parse(event.body);
    const generateUUID = () => crypto.randomBytes(16).toString("hex");

    try {
        switch (event.httpMethod) {
            case 'DELETE':
                body = await dynamo.delete(JSON.parse(event.body)).promise();
                break;
            case 'GET':
                body = await dynamo.scan({
                        TableName: 'db',
                        IndexName: 'urlPath',
                        FilterExpression: "urlPath = :urlPath",
                        ExpressionAttributeValues: {
                            ":urlPath": event.queryStringParameters.urlPath
                        }
                    },
                    function(data) {

                    }).promise();
                break;
            case 'POST':
                body = await dynamo.put({
                    TableName: 'db',
                    Item: {
                        id: generateUUID(),
                        name: data.name,
                        date: data.date,
                        place: data.place,
                        goals: data.goals,
                        type: data.type,
                        org: data.org,
                        email: data.email,
                        urlPath: data.urlPath,
                        createdAt: new Date().toISOString()
                    },
                    ConditionExpression: "attribute_not_exists(urlPath)"
                }).promise();
                break;
            case 'PUT':
                body = await dynamo.update(JSON.parse(event.body)).promise();
                break;
            default:
                throw new Error(`Unsupported method "${event.httpMethod}"`);
        }
    }
    catch (err) {
        statusCode = '400';
        body = err.message;
    }
    finally {
        body = JSON.stringify(body);
    }

    return {
        statusCode,
        body,
        headers,
    };
};



Ответы (2)


Ваша ошибка в том, что вы неправильно поняли, что ConditionExpression может сделать. Ваш полный PutItem код:

               body = await dynamo.put({
                    TableName: 'db',
                    Item: {
                        id: generateUUID(),
                        name: data.name,
                        date: data.date,
                        place: data.place,
                        goals: data.goals,
                        type: data.type,
                        org: data.org,
                        email: data.email,
                        urlPath: data.urlPath,
                        createdAt: new Date().toISOString()
                    },
                    ConditionExpression: "attribute_not_exists(urlPath)"
                }

Что вы ожидали от ConditionExpression: "attribute_not_exists(urlPath)"?

По-видимому, вы думали, что он проверит, существует ли какой-либо элемент с этим значением urlPath. Но, к сожалению, это выражение не так. Он просматривает один конкретный элемент - элемент с тем же ключом (я не знаю, какой у вас ключ, id?) И проверяет, имеет ли этот конкретный элемент атрибут urlPath (с любым значением).

Если бы urlPath был ключом элемента, это сработало бы так, как вы надеялись. Если urlPath уникален (что кажется таковым, в соответствии с тем, что вы хотели сделать), то он действительно может служить ключом элемента.

person Nadav Har'El    schedule 01.09.2020
comment
Добавляя к ответу @nadav, операции put-item работают только с основным ключом раздела. Глядя на ваши фрагменты кода, похоже, что url - это ключ раздела в индексе. Индексы являются копиями только для чтения и, следовательно, не могут использоваться для обновления. - person sandboxbohemian; 01.09.2020

Чтобы использовать ConditionExpression, вам необходимо указать имя и значение для атрибутов. Попробуй это:

await dynamo.put({
  TableName: 'db',
  Item: {
    id: generateUUID(),
    name: data.name,
    date: data.date,
    place: data.place,
    goals: data.goals,
    type: data.type,
    org: data.org,
    email: data.email,
    urlPath: data.urlPath,
    createdAt: new Date().toISOString(),
  },
  ConditionExpression: "attribute_not_exists(#u) or (#u=:urlPath)",
  ExpressionAttributeNames: { "#u": "urlPath" },
  ExpressionAttributeValues: { ":urlPath": data.urlPath },
});
person atomNULL    schedule 01.09.2020
comment
Я пробовал это, но, к сожалению, получаю тот же результат (элемент вставляется, даже если условие совпадает). может мне нужно использовать putItem? - person King Bufo; 01.09.2020
comment
Операция putItem вставит элемент, если он не существует, и полностью заменит элемент, если он уже существует. Если указанное выше условие истинно, будет создан новый элемент. Если элемент уже существует, заменять его не следует. Другой подход - сначала выполнить операцию GetItem, и если элемент с этим первичным ключом существует, ничего не делать. Если нет, создайте его. - person atomNULL; 01.09.2020