DynamoDB DocumentClient GET all items that match a selection of values

854 Views Asked by At

I am playing around with AWS CDK, and so far have implemented API Gateway, with Lambda functions pointing to a DynamoDB. So far, my POST/GET basic lambda functions do as they should, as it is fairly simple. However, I am now stuck on a problem as I need a more specific GET method.

For context, lets say I have a product DB. Each record has the following attributes:

{
  "id": "0981a086-7c78-4ce1-a1e7-3af641233a46",
  "name": "test product",
  "price": 1.99,
  "tags": ["tag1", "tag2", "tag3"...]
}

I will have an endpoint that will pass in tags, and I need to get all products from the table that contain the tags in the list passed in. Hopefully that makes sense. I believe the problem lies in my incorrect usage of FilterExpression, which currently returns Missing required key 'RequestItems' in params. My research shows I could use query instead but I dont think thats the best way, and it gives me some other error anyway. So before I go down the rabbit hole, I thought I'd ask for help.

Here is my Lambda that is currently failing:

const db = new AWS.DynamoDB.DocumentClient();
const TABLE_NAME = process.env.TABLE_NAME || "";
const PRIMARY_KEY = process.env.PRIMARY_KEY || "";

async function getProductByTags(event) {
  const { tags } = event; // ["tag2", "tag3"]

  const params = {
    FilterExpression: `contains (tags, ${tags})`,
    TableName: TABLE_NAME
  };
  try {
    const response = await db 
    .batchGet(params)
    .promise();

    return {
      statusCode: 200,
      body: response
    }
  } catch (err) {
    return {
      statusCode: 500,
      body: `Error getting product. ${err.message}`
    }
  }
}

Changing batchGet to query gives me this error: Either the KeyConditions or KeyConditionExpression parameter must be specified in the request

1

There are 1 best solutions below

1
On

With batchGet, you have to identify requested items by primary key.

Let's try using .scan instead of .batchGet.

Update:

You need to use logical OR conditions for each value.

CONTAINS is supported for lists: When evaluating "a CONTAINS b", "a" can be a list; however, "b" cannot be a set, a map, or a list.

  const FilterExpresstion = tags.map((tag) => `contains(tags, :v_${tag})`).join(' or ');
  const ExpressionAttributeValues = tags.reduce((obj, tag) => {
     obj[`:v_${tag}`] = tag;
     return obj;
  }, {});
  const params = {
    FilterExpression,
    ExpressionAttributeValues,
    TableName: TABLE_NAME
  };

// {
//   TableName: 'your_table_name',
//   FilterExpression: 'contains(tags, :v_tag1) or contains(tags, :v_tag2)',
//   ExpressionAttributeValues: {
//     ':v_tag1': "tag1",
//     ':v_tag2': "tag2",
//   }
// }