1

It's possible that I'm not quite understanding how hash/primary keys work in DynamoDB, but I'm trying to create a model (using Serverless + Dynogels/NodeJS) for a messaging service.

The model looks like this:

const ConversationORM = dynogels.define('Conversation', {

  hashKey: 'id',

  timestamps: true,
  tableName: config.CONVERSATION_TABLE,

  schema: {

    id: Joi.string(),
    users: Joi.array(), // e.g. ['foo', 'bar', 'moo']
    messages: Joi.array()

  }
})

As you can see, users is an array, which lists the userIds of the conversation's participants.

I need to create a service which finds all conversations that a user is participating in. In MongoDB (which I'm far more familiar with), I'd do something like:

Conversation.find({users: {"$in": ['foo']} }).then(.... 

Is there something equivalent I can do in DynamoDB? This is an API call that will happen quite often so I'm hoping to make it as efficient as possible.

JVG
  • 20,198
  • 47
  • 132
  • 210

2 Answers2

2

This answer takes into account a comment on Hunter Frazier's answer saying you don't want to use a Scan.

When using a Query you need specify a single partition key in the operation. In your schema this would mean partitioning on the userid attribute, which is a set. Partition keys in DynamoDB must be a top-level scalar attribute. As userid is not scalar (its a set), you cannot use this attribute as an index, and therefore you cannot do a Query for the conversations a user is part of.

If you need to do this Query, I would suggest revisiting your schema. Specifically I would suggest implementing the Adjacency list pattern which works well in databases containing many-to-many relationships.

You can see some additional notes on the article above I have written on this answer DynamoDB M-M Adjacency List Design Pattern

In your case you would have:

  • Primary Key: ConversationID
  • Sort Key: UserID
  • GSI Primary Key: UserID

You can then use the GSI Primary key in a Query to return all conversations the user is part of.

F_SO_K
  • 13,640
  • 5
  • 54
  • 83
1

I'm not familiar with Dynogels or Serverless but if it uses the regular API this might work:

var params = {
    ExpressionAttributeNames: {
        "U": "users"
    },
    ExpressionAttributeValues: {
        ":a": {
            S: "John Doe"
        }
    },
    FilterExpression: "Author = :a",
    ProjectionExpression: "#U",
    TableName: "Conversations"
};
dynamodb.scan(params, function (err, data) {
    if (err) console.log(err, err.stack);
    else console.log(data);
});
Hunter Frazier
  • 487
  • 5
  • 9
  • Is there a way to do this without a full scan though? From what I've read they're expensive operations, and this will be a very-regularly-called endpoint so a full scan isn't ideal. – JVG May 21 '18 at 04:55