cv12
cv12

Reputation: 128

Model of simple messaging in App Engine

I'm working on a very simple email-like messaging functionality as part of an App Engine application. It is simpler than email in that there are no "subject lines". In other words, two users (or more, but not hundreds) can just exchange messages in a conversation thread that contains all of their back-and-forth messages (like IM, but with a thread that persists between sessions). So far, I'm thinking of modeling this with two types of entities: Message and Conversation

class Message(db.Model):
    sender = StringProperty(required=True)
    receiver = StringProperty(required=True)
    message = TextProperty(required=True)
    timestamp = DateTimeProperty(auto_now_add=True)

class Conversation(db.Model):
    messages = ListProperty(int) # List holding the datastore-assigned integer ids of the messages in this conversation
    users = StringListProperty()

Since creating a Message requires the creation or modification of a Conversation, I would like to store Message and Conversation in the same entity group so that those changes can occur in a transaction. I figured that I would have the Message entities as children of their associated Conversation entities.

The Message needs the key for the Conversation entity to specify it as a parent. The Conversation needs the id of the Message to store it in the messages property.

What is the most efficient way to do this?

Is there a way to do this with a single trip to the datastore? Am I worrying about a marginal amount of efficiency, which I should really not worry about? Is there a smarter design pattern for what I'm looking to do? If so, I would really appreciate a pointer.

Upvotes: 1

Views: 205

Answers (1)

Drew Sears
Drew Sears

Reputation: 12838

You're inverting the conventional design pattern, which would be this:

class Conversation(db.Model):
    users = StringListProperty()

class Message(db.Model):
    sender = StringProperty(required=True)
    receiver = StringProperty(required=True)
    message = TextProperty(required=True)
    timestamp = DateTimeProperty(auto_now_add=True)
    conversation = db.ReferenceProperty(Conversation)

In the model above, you don't need to modify the conversation entity when you store a new message, unless you're updating the user list. I would recommend using this unless there's a reason not to.

This should also be a bit more scalable. If your conversation reaches 1000 messages, you don't have to store 1000 message ID properties on a single conversation entity.

Let's assume, though, that you want to keep your original model. To store the conversation and message in a single trip, you'd need to eager-assign the message ID. My advice here would be to generate a UUID (e.g. with uuid.uuid4()) and assign this as the message key name (and append it to the messages list), rather than waiting for the datastore to assign an ID.

Upvotes: 4

Related Questions