awspilot / dynamodb-oop

Speak fluent DynamoDB, write code with fashion, I Promise() 😃
https://awspilot.dev
MIT License
119 stars 28 forks source link

Expression error when using a having('key').between(1, 10) #29

Closed luizstacio closed 7 years ago

luizstacio commented 7 years ago

I using the library and working good. In this point I found this error. I resolved this error using the native aws-sdk and works well.

Error

{
  "message": "Value provided in ExpressionAttributeValues unused in expressions: keys: {:create_at_2_v1, :create_at_1_v1}",
  "code": "ValidationException",
  "time": "2016-11-23T18:06:22.315Z",
  "requestId": "CVAO4IP0SKPNVCULVHTTS7DC8RVV4KQNSO5AEMVJF66Q9ASUAAJG",
  "statusCode": 400,
  "retryable": false,
  "retryDelay": 0
}

Example

//...
    function (init, end) {
        const def = new Defer();
        const initDate = new Date(init);
        const endDate = new Date(end);

        console.log(+initDate, +endDate); // log for test

        dynamodb
          .table('table')
          .having('key1').null()
          .having('create_at').between(+initDate, +endDate)
          .scan(function(err, data) {
              if (err) return def.reject(err);
              def.resolve(data);
          });

        return def.promise;
    }
//...
adrianpraja commented 7 years ago

Hi Luiz,

Looks like a bug!

Can you add the following line and paste the output of it dynamodb.on('beforeRequest', console.log )

Thanks

luizstacio commented 7 years ago

I will make this and past the log here

adrianpraja commented 7 years ago

Allright, i'll check it and get back to you

luizstacio commented 7 years ago
scan { TableName: 'table',
  Select: undefined,
  ProjectionExpression: undefined,
  ExpressionAttributeNames: { '#domain': 'domain', '#create_at': 'create_at' },
  FilterExpression: '( attribute_not_exists(#domain) ) AND \n( #create_at BETWEEN :create_at_1_v1 AND :create_at_2_v1 )',
  ExpressionAttributeValues: 
   { ':create_at_1_v1': { N: '1479772800000' },
     ':create_at_2_v1': { N: '1479945540000' } },
  ReturnConsumedCapacity: 'TOTAL',
  ExclusiveStartKey: undefined }

scan { TableName: 'table',
  Select: undefined,
  ProjectionExpression: undefined,
  ExpressionAttributeNames: { '#domain': 'domain', '#create_at': 'create_at' },
  FilterExpression: '( attribute_not_exists(#domain) ) AND \n( #create_at BETWEEN :create_at_1_v2 AND :create_at_2_v2 )',
  ExpressionAttributeValues: 
   { ':create_at_1_v1': { N: '1479772800000' },
     ':create_at_2_v1': { N: '1479945540000' },
     ':create_at_1_v2': { N: '1479772800000' },
     ':create_at_2_v2': { N: '1479945540000' } },
  ReturnConsumedCapacity: 'TOTAL',
  ExclusiveStartKey: { id: { S: '2cb44130-99e5-11a6-21b5-0203581001e5' } } }
adrianpraja commented 7 years ago

I see the problem, I'll try to reproduce

adrianpraja commented 7 years ago

Seems to me that the error is produced by the nested call

I was not able to reproduce the same

I'm using:

    DynamoDB.on('beforeRequest', console.log )
    DynamoDB
        .table($tableName)
        .having('domain').null()
        .having('create_at').between(1,3)
        .scan( function(err, data) {

            DynamoDB
                .table($tableName)
                .having('domain').null()
                .having('create_at').between(1,3)
                .resume({id: {'S': "test"}})
                .scan( function(err, data) {

                })
        })

and my local output is

scan { TableName: 'test_hash_range',
  Select: undefined,
  ProjectionExpression: undefined,
  ExpressionAttributeNames: { '#domain': 'domain', '#create_at': 'create_at' },
  FilterExpression: '( attribute_not_exists(#domain) ) AND \n( #create_at BETWEEN :create_at_1_v1 AND :create_at_2_v1 )',
  ExpressionAttributeValues: { ':create_at_1_v1': { N: '1' }, ':create_at_2_v1': { N: '3' } },
  ReturnConsumedCapacity: 'TOTAL' }
scan { TableName: 'test_hash_range',
  Select: undefined,
  ProjectionExpression: undefined,
  ExpressionAttributeNames: { '#domain': 'domain', '#create_at': 'create_at' },
  FilterExpression: '( attribute_not_exists(#domain) ) AND \n( #create_at BETWEEN :create_at_1_v1 AND :create_at_2_v1 )',
  ExpressionAttributeValues: { ':create_at_1_v1': { N: '1' }, ':create_at_2_v1': { N: '3' } },
  ReturnConsumedCapacity: 'TOTAL',
  ExclusiveStartKey: { id: { S: 'test' } } }

ExpressionAttributeValues seems fine.

are you using this.scan() for the second call ?

luizstacio commented 7 years ago

No completly! I create a recursive function and only call again passing LastEvaluatedKey.

Clinic.findAllClinicsWithoutSitesBetween = function (init, end) {
  const initDate = new Date(init);
  const endDate = new Date(end);

  const preparatedQuery = repository
                            .getTableConnection()
                            .having('domain').null()
                            .having('create_at').between(+initDate, +endDate);

  return Clinic._findAllClinicsWithoutSites(preparatedQuery);
}

Clinic._findAllClinicsWithoutSites = function (preparatedQuery, $lastKey, finalData = []) {
  let def = new Defer();

  preparatedQuery
      .resume($lastKey)
      .scan(function(err, data) {
          if (err) return def.reject(err);

          finalData.push(...data);

          if (this.LastEvaluatedKey === null) {
            return def.resolve(finalData);
          }

          setTimeout(() => {
              Clinic
                ._findAllClinicsWithoutSites(preparatedQuery, this.LastEvaluatedKey, finalData)
                .then(def.resolve)
                .catch(def.reject);
          }, 500);
      });

    return def.promise;
}
adrianpraja commented 7 years ago

Oh, I see

I finally was able to get something similar with:

var dynamodb = DynamoDB.table($tableName)
dynamodb
            .having('domain').null()
            .having('create_at').between(1,3)
            .scan( function(err, data) {
                dynamodb
                    .having('domain').null()
                    .having('create_at').between(1,3)
                    .resume({id: {'S': "test"}})
                    .scan( function(err, data) {

                    })
            })

Unfortunately you can not re-use the query with the current version of this library.

You will have to do the DynamoDB.table() every time

I'm adding a self note to reset the internal vars after each query and probably the next version will work the way you;re trying to use it.

luizstacio commented 7 years ago

😱 hehe no problem I will wait for this update. In the moment I will continue using the native sdk for this specific part.

Thanks man!

adrianpraja commented 7 years ago

released version 0.1.60,

with minimum changes this fixed my local replication of your described issue.

I still can not recommend using

    var $users = DynamoDB.table('users')
    $users.insert(...)
    $users.update(...)
    ...

there are no tests to confirm that is ready for this change.

I'm closing the issue for now, please re-open it if you still see the issue.

Thanks </adrian>