Search code examples
amazon-dynamodbaws-amplifydynamodb-queries

Query data from Dynamo DB using Global secondary index


I am setting a serverless application using AWS Amplify

My frontend app has the following code

import React, { Component } from 'react';
import './App.css';
import Layout from './Containers/Layout';
import { Amplify, API } from 'aws-amplify';
import aws_exports from './aws-exports';

Amplify.configure(aws_exports);
const apiName = 'top3DynamoDBAPI';
let path = '/listings/';
let partitionKey = 'Restaurant';

class App extends Component {
  
  componentDidMount() {
    API.get(apiName, path + partitionKey).then(response => {
      console.log(response)
    });
  }

  state = {
    listings: {
    }
  }
  render() {
    return (
    <div className="App">
      <Layout />
    </div>
    );
  }
}

export default App;

in my backend API the get method to retrieve items from the table is as follows

/********************************
 * HTTP Get method for list objects *
 ********************************/

app.get(path + hashKeyPath, function(req, res) {
  var condition = {}
  condition[partitionKeyName] = {
    ComparisonOperator: 'EQ'
  }

  if (userIdPresent && req.apiGateway) {
    condition[partitionKeyName]['AttributeValueList'] = [req.apiGateway.event.requestContext.identity.cognitoIdentityId || UNAUTH ];
  } else {
    try {
      condition[partitionKeyName]['AttributeValueList'] = [ convertUrlType(req.params[partitionKeyName], partitionKeyType) ];
    } catch(err) {
      res.statusCode = 500;
      res.json({error: 'Wrong column type ' + err});
    }
  }

  let queryParams = {
    TableName: tableName,
    KeyConditions: condition
  }

  dynamodb.query(queryParams, (err, data) => {
    if (err) {
      res.statusCode = 500;
      res.json({error: 'Could not load items: ' + err});
    } else {
      res.json(data.Items);
    }
  });
});

In my Dynamo DB table, I have a primary partition which has categories and one of them is called 'Restaurant' . So in my App.js I set some variables and call the API to get the items in ComponentDidMount

const apiName = 'top3DynamoDBAPI';
let path = '/listings/';
let partitionKey = 'Restaurant';

  componentDidMount() {
    API.get(apiName, path + partitionKey).then(response => {
      console.log(response)
    });

this returns all the items from the table where the primary partition matches a value called 'Restaurant'

Now I have global Secondary Partition called 'Listing_Location' which currently has two values -- Sydney and Brisbane.

The backend API uses DynamoDB's Document Client and has the following variable initialised

const userIdPresent = false; // TODO: update in case is required to use that definition
const partitionKeyName = "Listing_Category";
const partitionKeyType = "S";
const sortKeyName = "Listing_Id";
const sortKeyType = "S";
const hasSortKey = sortKeyName !== "";
const path = "/listings";
const UNAUTH = 'UNAUTH';
const hashKeyPath = '/:' + partitionKeyName;
const sortKeyPath = hasSortKey ? '/:' + sortKeyName : '';

I am stuck at trying to figure out how to pass the secondary partition to my backend so I can lookup items based on location. Please can you help with this.


Solution

  • I was able to solve it with a combination of info from DynamoDb how to query a Global Secondary Index? and https://medium.com/@ole.ersoy/sending-an-email-parameter-with-amplify-api-get-request-4c1c8dc0c952

    Now, my App.js looks like

      componentDidMount() {
        let params = {
          'queryStringParameters': {
            location: 'Brisbane'
          }
        }
    
        API.get(apiName, path, params).then(response => {
          this.setState({
            listings: response
          })
          console.log(response)
        });
      }
    

    New get function is

    /* NEW GET ATTEMPT*/
    app.get(path, function (req, res) {
    
      if (userIdPresent) {
        req.body['userId'] = req.apiGateway.event.requestContext.identity.cognitoIdentityId || UNAUTH;
      }
    
      const location = req.query.location;
    
      var queryItemParams = {
        TableName: tableName,
        IndexName: "ListingGSI",
        KeyConditionExpression: "#location = :v_location",
        ExpressionAttributeNames: {
          "#location": "Listing_Location"
        },
        ExpressionAttributeValues: {
          ":v_location": location
        }
      };
    
      dynamodb.query(queryItemParams, (err, data) => {
        if (err) {
          res.statusCode = 500;
          res.json({ error: 'Could not load items: ' + err });
        } else {
          res.json(data.Items);
        }
      });
    });