Search code examples
node.jsaws-sdk-jsdynamoose

Dynamoose .update not working like the AWS javascript SDK. Do I need to use the SDK to update items?


Here is the schema and model

const orderSchema = new Schema({
    orderId: {
        type: String,
        required: true,
        hashKey: true,
    },
    serialNumberEnd: {
        type: Number,
    },
    productName: {
        type: String,
        required: true,
        index: {
            global: true,
            rangeKey: 'serialNumberEnd',
            name: 'productNameIndex',
            project: false,
            throughput: 1,
        },
    },
}, {
    throughput: {
        read: 1,
        write: 1
    },
    timestamps: true,
    saveUnknown: true,
}, );

const Order = dynamoose.model('Order-inv-dev', orderSchema, {
    update: true
});

module.exports = Order;

Here is the AWS SDK version of an update

function updateItem() {
    var table = "Order-inv-dev";

    var params = {
        TableName: table,
        Key: {
            "orderId": "1161a35c-afd7-4523-91c0-9ee397d89058",
        },
        UpdateExpression: "set fulfilled = :fulfilled",
        ExpressionAttributeValues: {
            ":fulfilled": true,
        },
        ReturnValues: "UPDATED_NEW"
    };

    dynamodb.update(params, function(err, data) {
        if (err) {
            console.log(err);
        } else {
            console.log(data);
        }
    });
}

Here is what I thought would be the equivalent action in Dynamoose

Order.update({
    orderId: 'e5aa37de-a4a9-456e-bea7-1471f404a424'
}, {
    fulfilled: true
}, function(error, result) {
    if (error) {
        return console.log(error);
    }
    console.log(result);
});

The only way I have been able to make this happen with dynamoose is to re-create the entire entry and use newOrder(replacementObject).save(). Any ideas for what I may be missing? It feels silly to use the sleek dynamoose syntax to grab the orderId then throw that into AWS SDK syntax to add a boolean key value pair. Thank you for reading and any help.

EDIT: When I use the same syntax but instead try and update an existing string it works.

Order.update({
    orderId: 'e5aa37de-a4a9-456e-bea7-1471f404a424'
}, {
    productName: 'blue tags'
}, function(error, result) {
    if (error) {
        return console.log(error);
    }
    console.log(result);
});

But when I try to modify an existing boolean value it does not change from true to false

Order.update({
    orderId: 'e5aa37de-a4a9-456e-bea7-1471f404a424'
}, {
    tag: false
}, function(error, result) {
    if (error) {
        return console.log(error);
    }
    console.log(result);
});

It also does not add a new key with type string

Order.update({
    orderId: 'e5aa37de-a4a9-456e-bea7-1471f404a424'
}, {
    tag: false
}, function(error, result) {
    if (error) {
        return console.log(error);
    }
    console.log(result);
});

To recap with update through dynamoose syntax it only appears to be able to change existing strings stored in dynamo. It won't add a new key as string or boolean and it will not change an existing boolean. The update through AWS SDK takes the primary key and the new key value pair and adds to the entry in dynamo. Can this behavior be achieved with dynamoose syntax?

Edit 2 - Dynamoose will add a key value to existing entry in dynamo if and only if that key is defined in the schema that is passed to the model. Any key value pair that was saved to dynamo without being in the dynamoose schema passed to the model can not be modified by dynamoose using update regardless of the saveUnkown: true. Added: fulfilled: { type: Boolean }, to orederSchema and the following code added { fulfilled: true } to the dynamo entry.

Order.update({
    orderId: 'e5aa37de-a4a9-456e-bea7-1471f404a424'
}, {
    fulfilled: true
}, function(error, result) {
    if (error) {
        return console.log(error);
    }
    console.log(result);
});

Is this expected behavior? It seems odd to me that data not explicitly defined in the model would be unmodifiable with the update command especially since the SDK does not have this restriction.

Takeaway: dynamoose models must contain at a minimum the keys to be used as hash, range, global secondary index, range of global secondary index, and surprising to me any key that might need to change after initial entry.


Solution

  • Currently there are three work arounds for your issue.

    1. Add the property to your schema
    2. Get the item, update the properties, and use model.save() to save the item back to DynamoDB
    3. Use the AWS SDK directly for this

    The second option would have its drawbacks since it isn't actually using the update functionality of DynamoDB, but might be the cleanest method depending on your setup and what your goals are.

    The fourth option is what you are doing. And your code should work. Because you have the saveUnknown set to true I 100% expect your code to work. Although, I have just tested it and it does not work. Therefor it is a bug in Dynamoose.


    Due to the fact that it looks to be a bug in Dynamoose, I have submitted an issue and PR and will try to get around to fixing this as soon as possible. Feel free to follow that issue and PR for updates on this. I will also try to post an update here with any major updates to it. Also if you'd like to try to take a shot at fixing it, that'd be awesome too, just make a branch off of my PR branch, try to fix it and submit a new PR and mention the old issue and PR in your new PR!!

    Technically it's best practice to have everything in your schema and model. That is one of the major goals of Dynamoose, it's meant to be a modeling tool for DynamoDB. So having a more strict system of having to define the structure of your models is kinda the objective of Dynamoose. But due to the fact that you have saveUnknown set to true it should be saving those properties.


    Update this has been fixed in Dynamoose PR #431. At the time of writing this that PR has been merged into master and will be included in the next release of Dynamoose (v1.0.0).