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?
- If I have the datastore assign an id for the Conversation entity, then I need to (1) put the Conversation entity into the datastore to get its id, (2) put the Message entity into the datastore to get its id, and (3) update the Conversation entity to add the new message id and put it again into the datastore. This requires 3 trips to the datastore, which looks inefficient.
- I could also create a unique key name for the Conversation entity (e.g., ‘;’.join(sorted(conv.users)). By doing this, I could avoid one of the three trips to the datastore outlined above.
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.
You’re inverting the conventional design pattern, which would be this:
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.