Reputation: 69
(First of, explaining complex things in English is not really a strength of mine. I have tried to be as elaborate as possible so you might understand my problem).
I recently started digging into Mongoose for a node.js-based web app I'm working on. The usual, including an user system where each user is able to follow others. If userA follows userB, it adds a new document to the Connections collection, containing userA's "_id" as "idA" and userB's "_id" as "idB".
Users collection
{
"_id" : ObjectId("HarrysID"),
"name" : "Harry"
"password": "..."
}
{
"_id" : ObjectId("TomsID"),
"name" : "Tom"
"password": "..."
}
Connections collection
{
"_id" : ObjectId("5ac130e9d6239151a86035c7"),
"idA" : ObjectId("TomsID"),
"idB" : ObjectId("HarrysID"),
}
{
"_id" : ObjectId("5ac13102d6239151a86035c8"),
"idA" : ObjectId("HarrysID"),
"idB" : ObjectId("TomsID"),
}
What I try to achieve
Without getting into much detail, you might know about Twitter's system, where you can only write a message to an user if this user followed you back.
This obviously can easily done with two requests:
Does Harry follow Tom? If true, does user Tom follow Harry? => Harry can contact Tom!.
Now I want to do this on a bigger scale. I need a function which returns a list of users someone can contact.
I have already done it using a recursive function, which first finds every document in Connections where "idA" is the current user. Then, it fetches through the returned list of connections and checks whether a document exists where "idB" follows "idA" – if not, it removes the document from the final resulting array. I would not consider myself being a database pro, but this system seems really inefficient in multiple ways and is pretty much a magnet for all kinds of errors.
Is there a better way for doing this? I am sure that aggregate(), project() and / or group() will be helpful, but how?
What should it somewhat look like in the end?
Since Harry follows Tom, and Tom follows Harry, Harry's contacts should contain Tom('s following).
listContacts(ObjectId("HarrysID"), function(err, result) {
console.log("Harry can message these users = ", result);
/* Harry can message these users =
[{
"_id" : ObjectId("5ac13102d6239151a86035c8"),
"idA" : ObjectId("HarrysID"),
"idB" : ObjectId("TomsID"),
}]
*/
});
Thank you for taking your time!
Upvotes: 1
Views: 60
Reputation: 5638
One way to approach this would be to create a new collection that holds onto the information you require to answer that question, can User B contact User B? I don't know of a good collection name but one possibility is contactable
.
This can help you answer the question with only 1 request if each document inside represents one user, and the documents contain a list of other user IDs belonging to those who can be contacted, like this:
Contactable collection
{
"_id" : ObjectId("HarrysID"),
"can_contact" : [ObjectId("TomsID"), ObjectId("AlicesID"), ...]
}
{
"_id" : ObjectId("TomsID"),
"can_contact": [ObjectID("HarrysID")]
}
The challenging part using this approach is keeping this collection up to date, which means anytime a user follows another user, there's a possibility you would need to modify two documents (if that following action creates a reciprocal connection where both users follow each other). And similarly, the unfollow action, for example where user B unfollows user A, would require you to remove user A's ID from the list inside user B's can_contact
list, and vice versa for user A's list.
Now I want to do this on a bigger scale. I need a function which returns a list of users someone can contact.
In order to return a full list of users, instead of just user IDs, this can be extended to maintaining a list of objects representing the users. However that denormalization would pose a similar maintainability challenge, because anytime a user updates any of his/her profile information which you want to keep in this list, you would have to run a query to determine which documents in the collection need to be updated. Actually, to make the querying easier, it might be better to keep that original list of user IDs as string values as well, because that way your query can be simple like in this example.
In a JavaScript environment with mongoose you can have a function that takes the userId and user object as arguments, and does the required updating like so:
contactableSchema = {
id : ObjectId,
can_contact : [Object],
can_contact_user_ids : [String]
}
var Contactable = mongoose.model('Contactable', contactableSchema);
function updateContactableCollection(userId, updatedUser) {
Contactable.find({can_contact_user_ids: userId}, (err, contactables) => {
if (contactables) {
contactables.forEach(contactable => {
// now must update the right element within this contactable's "can_contact" list
})
}
})
}
Upvotes: 1
Reputation: 167
Another way 1 add new field friend into user schema and type array .if user a add friend b and b allow this request system push a and b into their friend fields.user a can send message b and b too. Another way 2 create schema friends.if user a add friend b and b allow this request system push a and b into friends.user a can send message b and b too.
Upvotes: 0