aws-sdk-js icon indicating copy to clipboard operation
aws-sdk-js copied to clipboard

Dyanmodb.update to delete entry from StringSet does not work

Open haandol opened this issue 5 years ago • 16 comments

Confirm by changing [ ] to [x] below to ensure that it's a bug:

Describe the bug Can not delete a entry from string set using DELETE operation.

Is the issue in the browser/Node.js? Node.js

If on Node.js, are you running this on AWS Lambda?

Details of the browser/Node.js version 10.17.0

SDK version number 2.580.0

To Reproduce (observed behavior) Steps to reproduce the behavior (please share code or minimal repo)

  1. Create StringSet and put some data in it.
  2. Delete entry from Set (error) Invalid UpdateExpression: Incorrect operand type for operator or function; operator: DELETE, operand type: MAP"

problem code.

const dynamoDb = new AWS.DynamoDB.DocumentClient();
  const resp = await dynamoDb.scan({
    TableName: TABLE_NAME,
    FilterExpression: 'attribute_exists(Cids)',
  }).promise();
  console.info(`Update ${resp.Count} users`);

  for (const item of resp.Items || []) {
    console.info(item.Uid);
    await dynamoDb.update({
      TableName: TABLE_NAME,
      Key: {
        Uid: { S: item.Uid },
      },
      UpdateExpression: 'DELETE Cids :cameraId',
      ReturnValues: 'ALL_NEW',
      ExpressionAttributeValues: {
        ':cameraId': { SS: [id] },
      }
    }).promise();
  }

but same operation in python and CLI works.

Expected behavior Delete the entry from string set.

haandol avatar Dec 24 '19 10:12 haandol

Hey @haandol, thank-you for reach out to us with your issue.

I believe you are not using the correct syntax in your code:

For example:

 ExpressionAttributeValues: {
        ":cameraId": { "SS": [id] },
      }

//similar for the ```key``` as well.

Please reach out if that doesn't solve the problem.

ajredniwja avatar Dec 27 '19 01:12 ajredniwja

This error has nothing to do with syntax error (and I tried your suggestion too, just in case) because the exact same request payload works in Python

client.update_item(
        TableName=SESSION_TABLE_NAME,
        Key={
          'Uid': { 'S': user_id },
        },
        UpdateExpression='DELETE Cids :cameraId',
        ReturnValues='ALL_NEW',
        ExpressionAttributeValues={
          ':cameraId': { 'SS': [rig_id] },
        }
      )

haandol avatar Dec 27 '19 08:12 haandol

I have seen similar errors coming because of incorrect syntax.

Trying to reproduce this.

ajredniwja avatar Dec 27 '19 20:12 ajredniwja

const dynamoDb = new AWS.DynamoDB.DocumentClient({region: 'ap-northeast-2'});
  const resp = await dynamoDb.scan({
    TableName: TABLE_NAME,
    FilterExpression: 'attribute_exists(Cids)',
  }).promise();
  console.info(`Update ${resp.Count} users`);

  for (const item of resp.Items || []) {
    console.info(item.Uid);
    await dynamoDb.update({
      TableName: TABLE_NAME,
      Key: {
        Uid: item.Uid,
      },
      UpdateExpression: 'DELETE Cids :cameraId',
      ReturnValues: 'ALL_NEW',
      ExpressionAttributeValues: {
        ':cameraId': dynamodb.createSet([id]),
      }
    }).promise();
  }

DocumentClient.createSet() works.

or

const AWS = require('aws-sdk');                                                    
const dynamoDb = new AWS.DynamoDB({region: 'ap-northeast-2'});                     
dynamoDb.updateItem({                                                              
  TableName: 'test',                                                               
  Key: {                                                                           
    Uid: { S: 'id1'},                                                          
  },                                                                               
  UpdateExpression: 'ADD Cids :cameraId',                                          
  ExpressionAttributeValues: {                                                     
    ':cameraId': { SS: ['cid3'] },                                               
  }                                                                                
}, (err, data) => {                                                                
  if (err) console.log(err);                                                       
  else console.log(data);                                                          
})

no DocumentClient version works too.

haandol avatar Jan 04 '20 13:01 haandol

DocumentClient generated the body goes like this.

{"TableName":"test","Key":{"Uid":{"M":{"S":{"S":"id1"}}}},"UpdateExpression":"DELETE Cids :cameraId","ExpressionAttributeValues":{":cameraId":{"M":{"SS":{"L":[{"S":"cid1"}]}}}}}

DocumentClient wraps 'SS' with 'M' and it occurs the error, operator: DELETE, operand type: MAP

haandol avatar Jan 04 '20 13:01 haandol

Hey @haandol, I was able to reproduce this.

Will reach out to the service team for them to fix it.

ajredniwja avatar Jan 08 '20 02:01 ajredniwja

any updates on this, @ajredniwja ? experiencing the same issue.


npm: 
+ [email protected]

 sls --version
Framework Core: 1.74.1 (standalone)
Plugin: 3.6.14
SDK: 2.3.1
Components: 2.31.6
Fri Jul 24 - 00:00:47

celerno avatar Jul 24 '20 07:07 celerno

const dynamoDb = new AWS.DynamoDB.DocumentClient({region: 'ap-northeast-2'});
  const resp = await dynamoDb.scan({
    TableName: TABLE_NAME,
    FilterExpression: 'attribute_exists(Cids)',
  }).promise();
  console.info(`Update ${resp.Count} users`);

  for (const item of resp.Items || []) {
    console.info(item.Uid);
    await dynamoDb.update({
      TableName: TABLE_NAME,
      Key: {
        Uid: item.Uid,
      },
      UpdateExpression: 'DELETE Cids :cameraId',
      ReturnValues: 'ALL_NEW',
      ExpressionAttributeValues: {
        ':cameraId': dynamodb.createSet([id]),
      }
    }).promise();
  }

DocumentClient.createSet() works.

or

const AWS = require('aws-sdk');                                                    
const dynamoDb = new AWS.DynamoDB({region: 'ap-northeast-2'});                     
dynamoDb.updateItem({                                                              
  TableName: 'test',                                                               
  Key: {                                                                           
    Uid: { S: 'id1'},                                                          
  },                                                                               
  UpdateExpression: 'ADD Cids :cameraId',                                          
  ExpressionAttributeValues: {                                                     
    ':cameraId': { SS: ['cid3'] },                                               
  }                                                                                
}, (err, data) => {                                                                
  if (err) console.log(err);                                                       
  else console.log(data);                                                          
})

no DocumentClient version works too.

I found the same issue and the first suggestion on the above worked for me (using the dynamodb.createSet function in the ExpressionAttributes).

markussen145 avatar Oct 04 '20 10:10 markussen145

This does not work for me too.

    params = {
        TableName : accountDataTableName,
        Key: { id: '0' },
        UpdateExpression: "DELETE evidenceBacklog :evidenceID",
        ExpressionAttributeValues: {
            ":evidenceID": ID,
        }
    };

    result = await docClient.update(params).promise();

Data: image

throws:

{"errorType":"ValidationException","errorMessage":"Invalid UpdateExpression: Incorrect operand type for operator or function; operator: DELETE, operand type: STRING, typeSet: ALLOWED_FOR_DELETE_OPERAND","trace":["ValidationException: Invalid UpdateExpression: Incorrect operand type for operator or function; operator: DELETE, operand type: STRING, typeSet: ALLOWED_FOR_DELETE_OPERAND"

peterkruty avatar Nov 20 '20 17:11 peterkruty

I tried DocumentClient.createSet() but received: ValidationException: An operand in the update expression has an incorrect data type

{"TableName":"test.table","Key":{"room_id":"123"},"UpdateExpression":"set #lmd = :lmd DELETE viewer_ids :vid","ExpressionAttributeNames":{"#lmd":"last_activity_time"},"ExpressionAttributeValues":{":lmd":"2021-04-27T18:10:32.849+08:00",":vid":["test_school_id_T_1"]},"ReturnValues":"ALL_NEW"}

anyone can help? thanks.

chak774 avatar Apr 27 '21 10:04 chak774

@haandol @ajredniwja I've been facing a similar issue and I've posted its details in this Stack Overflow.

It is odd because if i use ADD it works but not DELETE...

The error is:

ERROR Invoke Error 
{
  "errorType":"Error",
  "errorMessage":"ValidationException: Invalid UpdateExpression: Incorrect operand type for operator or function;
   operator: DELETE, operand type: NUMBER, typeSet: ALLOWED_FOR_DELETE_OPERAND",
  "stack":[...]
}

Would you suggest any workarounds while this gets fixed?

I really appreciate any insights towards this.

Keep Rocking!

fagiani avatar Apr 30 '21 20:04 fagiani

The only workaround possible for me here was not to use a DELETE operation, instead, you gotta query the item, find the index in the array you wish to delete, and remove it a REMOVE operation:

like in this case, arrayField contains an array of Users, and I want to delete by user's phoneNumber.

const dataStore = await dynamodb.get(queryParams).promise(); let i=0; //save the index for(i = 0; i < dataStore.Item.myTable.length; i++){ if(dataStore.Item.arrayField[i].phone === phoneNumber) { break; } }

//extracted from for loop for clarity

if(i < dataStore.Item.arrayField.length){
    const updateStoreParams = {
        TableName: tableName,
        Key: storeTableKey,
        UpdateExpression: `REMOVE arrayField[${i}]`,
    }
    await dynamodb.update(updateStoreParams).promise().catch((err) => {
        console.log(err);
        throw err;
        });
}

.net dev celerino herrera g https://chamizo.pro/contact

On Fri, Apr 30, 2021 at 1:06 PM Paulo Fagiani @.***> wrote:

@haandol https://github.com/haandol @ajredniwja https://github.com/ajredniwja I've been facing a similar issue and I've posted its details in this Stack Overflow https://stackoverflow.com/questions/67338331/updating-dynamodb-record-with-delete-set-on-updateexpression-fails-with-node-js .

It is odd because if i use ADD it works but not DELETE...

The error is:

ERROR Invoke Error { "errorType":"Error", "errorMessage":"ValidationException: Invalid UpdateExpression: Incorrect operand type for operator or function; operator: DELETE, operand type: NUMBER, typeSet: ALLOWED_FOR_DELETE_OPERAND", "stack":[...] }

Would you suggest any workarounds while this gets fixed?

I really appreciate any insights towards this.

Keep Rocking!

— You are receiving this because you commented. Reply to this email directly, view it on GitHub https://github.com/aws/aws-sdk-js/issues/3025#issuecomment-830351372, or unsubscribe https://github.com/notifications/unsubscribe-auth/AAWDXZBL4OCPKKKBYJ2NAW3TLMEUTANCNFSM4J65GDGA .

celerno avatar Apr 30 '21 20:04 celerno

@celerno thanks for taking the time to point that! I actually tried but it became a bit inefficient with big sets and that led me to implement it with another SDK to compare and I ended up with the exactly same error. That's how I notice my mistake.

I explain the details here

but this is the code that worked for me:

// Notice below that I inject ADD if operation is DELETE and a comma otherwise
DynamoDB.update({
      TableName,
      Key,
      UpdateExpression: `
        ${operation} socketIds :valuesToModify
        ${operation == 'DELETE' ? 'ADD' : ','} version :incrementVersionBy
      `,
      ConditionExpression: `version = :version`,
      ExpressionAttributeValues: {
        ':version': channelRecord.version,
        ':incrementVersionBy': 1,
        ':valuesToModify': DynamoDB.createSet([socketId])
     }
})

I hope that to become helpful to others in the future!

Thanks again!!

fagiani avatar May 01 '21 19:05 fagiani

Thanks for the tip! Very clever. Good luck!

El sáb., 1 de mayo de 2021 12:38 p. m., Paulo Fagiani < @.***> escribió:

@celerno https://github.com/celerno thanks for taking the time to point that! I actually tried but it became a bit inefficient with big sets and that led me to implement it with another SDK to compare and I ended up with the exactly same error. That's how I notice my mistake.

I explain the details here https://stackoverflow.com/a/67350174/1463744

but this is the code that worked for me:

// Notice below that I inject ADD if operation is DELETE and a comma otherwiseDynamoDB.update({ TableName, Key, UpdateExpression: ${operation} socketIds :valuesToModify ${operation == 'DELETE' ? 'ADD' : ','} version :incrementVersionBy , ConditionExpression: version = :version, ExpressionAttributeValues: { ':version': channelRecord.version, ':incrementVersionBy': 1, ':valuesToModify': DynamoDB.createSet([socketId]) }})

I hope that to become helpful to others in the future!

Thanks again!!

— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub https://github.com/aws/aws-sdk-js/issues/3025#issuecomment-830683614, or unsubscribe https://github.com/notifications/unsubscribe-auth/AAWDXZHTKLFJ2MQCPJ3YES3TLRKDDANCNFSM4J65GDGA .

celerno avatar May 02 '21 01:05 celerno

Any update on a fix for this?

paul-uz avatar Mar 08 '22 13:03 paul-uz

const dynamoDb = new AWS.DynamoDB.DocumentClient({region: 'ap-northeast-2'});
  const resp = await dynamoDb.scan({
    TableName: TABLE_NAME,
    FilterExpression: 'attribute_exists(Cids)',
  }).promise();
  console.info(`Update ${resp.Count} users`);

  for (const item of resp.Items || []) {
    console.info(item.Uid);
    await dynamoDb.update({
      TableName: TABLE_NAME,
      Key: {
        Uid: item.Uid,
      },
      UpdateExpression: 'DELETE Cids :cameraId',
      ReturnValues: 'ALL_NEW',
      ExpressionAttributeValues: {
        ':cameraId': dynamodb.createSet([id]),
      }
    }).promise();
  }

DocumentClient.createSet() works.

or

const AWS = require('aws-sdk');                                                    
const dynamoDb = new AWS.DynamoDB({region: 'ap-northeast-2'});                     
dynamoDb.updateItem({                                                              
  TableName: 'test',                                                               
  Key: {                                                                           
    Uid: { S: 'id1'},                                                          
  },                                                                               
  UpdateExpression: 'ADD Cids :cameraId',                                          
  ExpressionAttributeValues: {                                                     
    ':cameraId': { SS: ['cid3'] },                                               
  }                                                                                
}, (err, data) => {                                                                
  if (err) console.log(err);                                                       
  else console.log(data);                                                          
})

no DocumentClient version works too.

If the above solution didn't work for you then check the Item in your Table and make sure that the attribute you're trying to delete an element from is of the type StringSet("SS") and not List("L")! Take a look here if you want to learn more about Dynamodb data types.

SiyabongaMabundza avatar Apr 27 '22 13:04 SiyabongaMabundza

Greetings! We’re closing this issue because it has been open a long time and hasn’t been updated in a while and may not be getting the attention it deserves. We encourage you to check if this is still an issue in the latest release and if you find that this is still a problem, please feel free to comment or open a new issue.

github-actions[bot] avatar Apr 28 '23 00:04 github-actions[bot]