I'm just starting out on an application and I've heard some good things about MongoDB so I thought I'd give it a crack in this new project to see how well it works.
Please bear in mind that prior to this I've only ever really used RDBMSs like MySQL, Oracle, etc. so it's been quite an adjustment to think of this in a more document-oriented fashion which is why I figured I should ask for help now before I become hopelessly lost.
The basic context behind this application is that it's a "Guild" system. A basic guild consists of
- A leader
- Basic metadata (name, description, tagline...etc.)
- Ranking hierarchy (master, officer, fresher), these are unique to each guild so that the master can create their hierarchy as they like, each rank has certain 'permissions' associated with it.
- Members (sub-users of the application, explained later). Each sub-user can only belong to one guild.
At the moment I've created a collection called users
, which contains all the User
documents, I figured that it'd be better to have these separate from the eventual Guild
documents rather than embedded as I'll probably need to use them in other areas of the application. The basic User
document will look something like:
{
_id: ObjectId,
email: String,
password: String,
displayName: String,
dateJoined: Date,
characters: Array({ _id: ObjectId, uniqName: String, ... })
...
}
Basically each registered User
gets to add multiple characters
to their account, these characters
are the ones that actually get the Guild
affiliation rather than the overarching User
, this is what's making it quite tricky to structure.
My basic idea for the Guild
document was something like the following:
{
_id: ObjectId,
leader_id: ObjectId(references a `character` in a `User` document),
name: String,
tagline: String,
description: String,
rankings: Array({
_id: ObjectId,
title: String,
weight: Integer, // for ordering
canAddMembers: Boolean,
canDelMembers: Boolean,
// ...additional permissions and stuff per-ranking here
}),
members: Array({
character_id: ObjectId(references a `character` in a `User` document),
ranking_id: ObjectId(references a `ranking` in the `rankings` array),
dateJoined: Date
})
}
Which seemed okay-ish to me. The main problem I'm forseeing is that there's no straightforward way to find all Guild
s that a User
's characters are a part of.
An example use case: The User
's profile, it should show all of their characters
and their Guild
affiliations, won't this be a rather slow operation as it'll need to search every single Guild
's members
array for the character_id
? This seems incredibly expensive, especially if there are a lot of guilds with large memberships.
My next though was to simply add a guild_id
field the character
sub-document, instead of storing the members
in the Guild
document. This obviously leads to another problem: Viewing all of the Guild's members, you'd then need to search every User
's characters
array for the guild_id
field. Seems about as expensive as the first solution to me (if not more so as there's likely to be a lot more User
documents than Guild
documents).
The only solution I could see that works for both use cases is to simply do both: Have a members
array in the Guild
document, and maintain a guild_id
field in the characters
sub-document. However this really goes against the grain; now I need to maintain two fields for any membership changes rather than just one which seems like it'd breed database inconsistencies (it relies extensively on good application-level code).