I have the following case: A user can add contacts (who are other users) on the website. Optionally, a user may also organize his contacts in groups. A user can have many emails, addresses and phone numbers.
I thought of the following schema design (document store/mongodb). Are there any ways to improve this? My main concern is that the profile picture is embedded inside the document. I know that this is not a good practice, but for this particular purpose (assignment), I have to embed the picture (blob/gridfs) here as well. But I'm wondering how this schema can be improved.
For users, I think your current schema is fine. Keeping multiple addresses, phone numbers, and email addresses in arrays is good because there shouldn't be too many of them for a particular person and it's easy to query for "who has this email address" or "give me all the phone numbers of this person". You do seem to have a redundant e-mail
field though - is that a special email, like the address for the account, that's distinguished from a contact email? If so I'd give it a descriptive name for the sake of other maintainers, a name like account_email
. I wouldn't keep the photo as a blob, but you said that is some other requirement so I won't critique it.
I like the idea of doing contacts with groups using a separate contacts collection. I'd have a contacts
collection with each document representing one contact
{
"_id" : ObjectId("..."),
"owner_id" : ObjectId("..."), // reference to user document of contact owner
"contact_id" : ObjectId("..."), // reference to user document of contact
"group" : "Rivals" // group name
}
Index on { "owner_id" : 1, "contact_id" : 1 }
and maybe { "owner_id" : 1, "group" : 1 }
and then queries like the following will be quick:
// get all contacts for user x
db.contacts.find({ "owner_id" : x })
// is user y a contact of user x?
db.contacts.count({ "owner_id" : x, "contact_id" : y }) != 0
// get all contacts in group "family" for user x
db.contacts.find({ "owner_id" : x, "group" : "family" })
Once you retrieve contacts, in order to retrieve human-friendly display information you will need to do a second query (application-level join) to retrieve the actual user documents of the contacts. If you want, you can denormalize some of the contact info into the contact document
{
"_id" : ObjectId("..."),
"owner_id" : ObjectId("..."), // reference to user document of contact owner
"contact_id" : ObjectId("..."), // reference to user document of contact
"group" : "Rivals", // group name
"contact_name" : "Franke Frankers"
}
You won't need to do a second query if you include commonly needed info, but if the contact updates his/her information you may need to update every contact document referencing them.