marko kraljevic
marko kraljevic

Reputation: 353

Retweet schema in MongoDB

What is the best way to model retweet schema in MongoDB? It is important that I have createdAt times of both original message and the time when retweet occurred because of pagination, I use createdAt as cursor for GraphQL query.

I also need a flag weather the message itself is retweet or original, and id references to original message and original user and reposter user.

I came up with 2 solutions, first one is that I keep ids of reposters and createdAt in array in Message model. The downside is that I have to generate timeline every time and for subscription its not clear what message to push to client.

The second is that I treat retweet as message on its own, I have createdAt and reposterId in place but I have a lot of replication, if I were to add like to message i have to push in array of every single retweet.

I could use help with this what is the most efficient way to do it in MongoDB?

First way:


import mongoose from 'mongoose';

const messageSchema = new mongoose.Schema(
  {
    text: {
      type: mongoose.Schema.Types.String,
      required: true,
    },
    userId: {
      type: mongoose.Schema.Types.ObjectId,
      ref: 'User',
      required: true,
    },
    likesIds: [{ type: mongoose.Schema.Types.ObjectId, ref: 'User' }],
    reposts: [
      {
        reposterId: {
          type: mongoose.Schema.Types.ObjectId,
          ref: 'User',
        },
        createdAt: { type: Date, default: Date.now },
      },
    ],
  },
  {
    timestamps: true,
  },
);

const Message = mongoose.model('Message', messageSchema);

Second way:


import mongoose from 'mongoose';

const messageSchema = new mongoose.Schema(
  {
    text: {
      type: mongoose.Schema.Types.String,
      required: true,
    },
    userId: {
      type: mongoose.Schema.Types.ObjectId,
      ref: 'User',
      required: true,
    },
    likesIds: [{ type: mongoose.Schema.Types.ObjectId, ref: 'User' }],
    isReposted: {
      type: mongoose.Schema.Types.Boolean,
      default: false,
    },
    repost: {
      reposterId: {
        type: mongoose.Schema.Types.ObjectId,
        ref: 'User',
      },
      originalMessageId: {
        type: mongoose.Schema.Types.ObjectId,
        ref: 'Message',
      },
    },
  },
  {
    timestamps: true,
  },
);

const Message = mongoose.model('Message', messageSchema);

export default Message;

Upvotes: 1

Views: 278

Answers (1)

MahnQL
MahnQL

Reputation: 81

Option 2 is the better choice here. I'm operating with the assumption that this is a Twitter re-tweet or Facebook share like functionality. You refer to this functionality as both retweet and repost so I'll stick to "repost" here.

Option 1 creates an efficiency problem where, to find reposts for a user, the db needs to iterate over all of the repost arrays of all the messageSchema collections to ensure it found all of the reposterIds. Storing ids in mongo arrays in collection X referencing collection Y is great if you want to traverse from X to Y. It's not as nice if you want to traverse from Y to X.

With option 2, you can specify a more classic one-to-many relationship between messages and reposts that will be simpler and more efficient to query. Reposts and non-repost messages alike will ultimately be placed into messageSchema in the order the user made them, making organization easier. Option 2 also makes it easy to allow reposting users to add text of their own to the repost, where it can be displayed alongside the repost in the view this feeds into. This is popular on facebook where people add context to the things they share.

My one question is, why are three fields being used to track reposts in Option 2?

isReposted, repost.reposterId and repost.originalMessageId provide redundant data. All that you should need is an originalMessageId field that, if not null, contains a messageSchema key and, if null, signifies that the message is not itself a repost. If you really need it, the userId of the original message's creator can be found in that message when you query for it.

Hope this helps!

Upvotes: 2

Related Questions