3

I have a product table in DynamoDB which has some items. Now I need to add list of buyers to the product which can grow i.e. append to list. It works for if I have an empty list or a list with some items in the table item but for the first addition it throws an error. Is there any way to check if list exists then append else add a list. here is my code

let params = {
    TableName: "product",
    ExpressionAttributeNames: {
        "#Y": "buyer"
    },
    ExpressionAttributeValues: {
        ":y": ["PersonXYZ"]
    },
    Key: {
        id: 'Hy2H4Z-lf'
    },
    UpdateExpression: "SET #Y = list_append(#Y,:y)"
};
updateItemInDDB(params).then((data) => {
    res.status(200).send(data);
}, err => {
    console.log(err);
    res.sendStatus(500);
});

UpdateItemInDDB is just a function which takes a params and run dnamodb code on it. I am using javascript sdk for DynamoDB with Document Client.

Rishabh Jain
  • 526
  • 1
  • 10
  • 26
  • How many "buyers" are you expecting for your products? DynamoDB items have a maximum size. By adding buyers as a property of a product, you limit the number of buyers you can have. Architecturally, it may be better to have a separate table for buyers. – Matt Houser Nov 21 '17 at 14:36
  • @MattHouser It can be around 100 buyers at max – Rishabh Jain Nov 21 '17 at 15:44
  • @MattHouser Having another table for such a small list is something that I would like to avoid, I think in my case having just a list is sufficient and more efficient. – Rishabh Jain Nov 21 '17 at 16:25

2 Answers2

26
var params = {
            TableName: "user",
            Key: {
                "user_id": {
                    S: user_id
                }
            },
            UpdateExpression: "SET #ri = list_append(if_not_exists(#ri, :empty_list), :vals)",
            ExpressionAttributeNames: {
                "#ri": "images"
            },
            ExpressionAttributeValues: {
                ":vals": {"L": [{
                    "S": "dummy data"
                }]},
                ":empty_list": {"L": []}
            },
            ReturnValues: 'ALL_NEW'
        };

":empty_list": {"L": []} this is important if you want to set empty list, otherwise it will give below exception.

"errorMessage": "ExpressionAttributeValues contains invalid value: Supplied AttributeValue is empty, must contain exactly one of the supported datatypes for key :empty_list",
  "errorType": "ValidationException"
Nasreen Ustad
  • 1,564
  • 1
  • 19
  • 24
  • 2
    thank you, this should be marked as the correct answer as you don't have to go to dynamodb twice. – kewur Jul 10 '21 at 16:53
2

EDIT: Nest the conditional expressions

You could run SET append_list with a ConditionalExpression that the attribute does exist, then if that fails run SET with a ConditinalExpression that the attribute does not exist.

let params1 = {
    TableName: "product",
    ExpressionAttributeNames: {
        "#Y": "buyer"
    },
    ExpressionAttributeValues: {
        ":y": ["PersonXYZ"]
    },
    Key: {
        id: 'Hy2H4Z-lf'
    },
    ConditionExpression: "attribute_exists(buyer)",
    UpdateExpression: "SET #Y = list_append(#Y,:y)"
};
updateItemInDDB(params1).then((data) => {
    res.status(200).send(data);
}, err => {
    console.log(err);
    let params2 = {
    TableName: "product",
    ExpressionAttributeNames: {
        "#Y": "buyer"
    },
    ExpressionAttributeValues: {
        ":y": ["PersonXYZ"]
    },
    Key: {
        id: 'Hy2H4Z-lf'
    },
    ConditionExpression: "attribute_not_exists(buyer)",
    UpdateExpression: "SET #Y = (#Y,:y)"
    };
    updateItemInDDB(params2).then((data) => {
    res.status(200).send(data);
    }, err => {
        console.log(err);
        res.sendStatus(500);
    });
});
F_SO_K
  • 13,640
  • 5
  • 54
  • 83
  • This helped. Just a followup shouldn't we fire second request in the error section of the first request because if first was successful we don't need the second request. ? – Rishabh Jain Nov 23 '17 at 02:01
  • Yes, sorry I was going to mention that. That would be a better solution. I wasn't sure what the first one would return if the attribute exists, is it an error? – F_SO_K Nov 23 '17 at 09:09
  • Yes, res.send will be called 2 times in this case. which will throw an error. Please update your answer accordingly so that I can accept and close the question – Rishabh Jain Nov 23 '17 at 09:59
  • 1
    Thanks. Hows that? – F_SO_K Nov 23 '17 at 16:42
  • First dynnamo db update is async so it will fire req and the second will start running. Now if any one finishes it will call res.send. when the second one finishes it will again call res.send which will throw error "can't set headers after they are sent" – Rishabh Jain Nov 24 '17 at 01:52