简体   繁体   中英

DynamoDB: Query to find an item in an array of strings

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.

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 MM 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.

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);
});

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM