Search code examples
javascriptnode.jsamazon-dynamodbdynamoose

How do I query an index properly with Dynamoose


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?


Solution

  • 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( ... )