3

I'm using Dynamoose to simplify my interactions with DynamoDB in a node.js application. I'm trying to write a query using Dynamoose's Model.query function that will search a table using an index, but it seems like Dynamoose is not including all of the info required to process the query and I'm not sure what I'm doing wrong.

Here's what the schema looks like:

const UserSchema = new dynamoose.Schema({
  "user_id": {
    "hashKey": true,
    "type": String
  },
  "email": {
    "type": String,
    "index": {
      "global": true,
      "name": "email-index"
    }
  },
  "first_name": {
    "type": String,
    "index": {
      "global": true,
      "name": "first_name-index"
    }
  },
  "last_name": {
    "type": String,
    "index": {
      "global": true,
      "name": "last_name-index"
    }
  }
)

module.exports = dynamoose.model(config.usersTable, UserSchema)

I'd like to be able to search for users by their email address, so I'm writing a query that looks like this:

Users.query("email").contains(query.email)
    .using("email-index")
    .all()
    .exec()
    .then( results => {
      res.status(200).json(results)
    }).catch( err => {
      res.status(500).send("Error searching for users: " + err)
    })

I have a global secondary index defined for the email field:  email-index Active GSI email(String) - ALL

When I try to execute this query, I'm getting the following error:

Error searching for users: ValidationException: Either the KeyConditions or KeyConditionExpression parameter must be specified in the request.

Using the Dynamoose debugging output, I can see that the query winds up looking like this:

aws:dynamodb:query:request - {
"FilterExpression": "contains (#a0, :v0)",
"ExpressionAttributeNames": {
    "#a0": "email"
},
"ExpressionAttributeValues": {
    ":v0": {
        "S": "mel"
    }
},
"TableName": "user_qa",
"IndexName": "email-index"
}

I note that the actual query sent to DynamoDB does not contain KeyConditions or KeyConditionExpression, as the error message indicates. What am I doing wrong that prevents this query from being written correctly such that it executes the query against the global secondary index I've added for this table?

Mel Stanley
  • 375
  • 2
  • 8

2 Answers2

5

As it turns out, calls like .contains(text) are used as filters, not query parameters. DynamoDB can't figure out if the text in the index contains the text I'm searching for without looking at every single record, which is a scan, not a query. So it doesn't make sense to try to use .contains(text) in this context, even though it's possible to call it in a chain like the one I constructed. What I ultimately needed to do to make this work is turn my call into a table scan with the .contains(text) filter:

Users.scan({ email: { contains: query.email }}).all().exec().then( ... )
Mel Stanley
  • 375
  • 2
  • 8
  • Exactly. Indexes are just sorted lists of records. Hence, the only way to use them efficiently is by using queries that deal with that sorting (=, <, startsWith, endsWith, etc.). When you search whether an attribute "contains" a string, the pre-sorting of the index is useless, so it would still need to scan the whole table to find its answer. – samlaf Aug 16 '21 at 18:09
-2

I am not familiar with Dynamoose too much but the following code below will do an update on a record using node.JS and DynamoDB. See the key parameter I have below; by the error message you got it seems you are missing this.

To my knowledge, you must specify a key for an UPDATE request. You can checks the AWS DynamoDB docs to confirm.

var params = {
    TableName: table,
    Key: {
        "id": customerID,
    },

    UpdateExpression: "set customer_name= :s, customer_address= :p, customer_phone= :u, end_date = :u",
    ExpressionAttributeValues: {
        ":s": customer_name,
        ":p": customer_address,
        ":u": customer_phone
    },
    ReturnValues: "UPDATED_NEW"
};
    await docClient.update(params).promise();
Aleksandar Zoric
  • 1,343
  • 3
  • 18
  • 45