const DBName = "EMI_SOCIAL"; let userProfileCache = {}; userDB = (DB) => { DB.profileCols = DB.db.db(DBName).collection("profiles"); DB.newProfile = (profileObj) => { return DB.profileCols.insertOne(profileObj.toObj()).catch((err) => { console.log(err); return false; }); } DB.removeProfile = (profileid) => { const _id = DB.ObjectID(profileid); if (userProfileCache[profileid]) delete userProfileCache[profileid]; return DB.profileCols.deleteOne({ _id }).catch((err) => { console.log(err); return false; }); } DB.updateProfile = async (profileid, profileObj) => { let tempProfile = profileObj.toObj(); if (!DB.ObjectID.isValid(profileid)) return false; const _id = DB.ObjectID(profileid); const query = { _id }; const update = { $set: { profile: tempProfile.profile, data: tempProfile.data } }; let r = await DB.profileCols.updateOne(query, update).catch((err) => { console.log(err); return false; }); if (userProfileCache[profileid]) delete userProfileCache[profileid]; return r; } DB.getProfile = async (profileId) => { //if (userProfileCache[profileId] && !userProfileCache[profileId].isGroup) return userProfileCache[profileId]; if (!profileId) return false; try { const _id = DB.ObjectID(profileId); let r = await DB.profileCols.findOne({ _id }).catch((err) => { console.log(err); return false; }); if (r) userProfileCache[profileId] = r; return r; } catch (_) { return {}; } } DB.getPopularProfiles = async (limit = 10) => { return DB.profileCols.aggregate([ { $match: { isGroup: { $ne: true } } }, { $addFields: { subscribed_count: { $size: { "$ifNull": ["$following", []] } } } }, { $sort: { "subscribed_count": -1 } }, { $project: { _id: 1, "subscribed_count": 1 } } ]).limit(limit).toArray().catch((err) => { console.log(err); return []; }); } DB.getPopularGroups = async (limit = 10) => { return DB.profileCols.aggregate([ { $match: { isGroup: true, isPrivate: { $ne: true }, isCourse: { $ne: true } } }, { $addFields: { subscribed_count: { $size: { "$ifNull": [{ "$objectToArray": "$subscribed" }, []] } } } }, { $sort: { "subscribed_count": -1 } }, { $project: { _id: 1, "subscribed_count": 1 } } ]).limit(limit).toArray().catch((err) => { console.log(err); return []; }); } DB.getFriendsFriends = async (profileId, limit = 10) => { const profile = await DB.getProfile(profileId); if (!profile) return []; const following = Array.isArray(profile.following) ? profile.following : []; let ids = following.filter((id) => DB.ObjectID.isValid(id)).map((id) => DB.ObjectID(id)); let alreadyFollowingMap = {}; alreadyFollowingMap[profileId] = 1; //skip that profile following.forEach(id => { if (!alreadyFollowingMap[id]) alreadyFollowingMap[id] = 1; }) return DB.profileCols.find({ _id: { $in: ids } }).project({ following: 1 }).limit(limit).toArray().then(profiles => { let friendsOfFriendsMap = {}; profiles.forEach(p => { const related = Array.isArray(p.following) ? p.following : []; related.forEach(followingId => { if (alreadyFollowingMap[followingId]) return 0; if (!friendsOfFriendsMap[followingId]) friendsOfFriendsMap[followingId] = 0; friendsOfFriendsMap[followingId] = friendsOfFriendsMap[followingId] + 1; }); }); // sort by most related? return Object.keys(friendsOfFriendsMap); }).catch((err) => { console.log(err); return []; }); } DB.getProfileCache = async (profileId) => { if (userProfileCache[profileId] && !userProfileCache[profileId].isGroup) return userProfileCache[profileId]; return DB.getProfile(profileId); } DB.getProfileCache = async (profileId) => { const cachedProfile = userProfileCache[profileId]; if (cachedProfile?.isGroup === false) { return cachedProfile; } return await DB.getProfile(profileId); }; DB.searchProfile = async (queryStr) => { let regEx = new RegExp(queryStr, 'i'); let query = { isGroup: false, isChat: { $ne: true }, $or: [ { "profile.firstName": { $regex: regEx } }, { "profile.lastName": { $regex: regEx } }, { "profile.description": { $regex: regEx } }, ] }; let r = await DB.profileCols.find(queryStr ? query : { isGroup: false, isChat: { $ne: true } }) .sort({ lastUpdate: -1 }).limit(20) .toArray().catch((err) => { console.log(err); return false; }); return r; } DB.getUserProfiles = async (userId) => { const userid = DB.ObjectID(userId); return await DB.profileCols.find({ userid }).toArray().catch((err) => { console.log(err); return []; }); } DB.latestProfile = async (userId) => { const userid = DB.ObjectID(userId); let r = await DB.profileCols.find({ userid }) .sort({ lastUpdate: -1 }) .toArray().catch((err) => { console.log(err); return false; }); let index = 0; while (r[index].isGroup || r[index].isChat) index += 1; if (r[index]) userProfileCache[r[index]._id] = r[index]; return r[index]; } DB.followProfile = async (profileId, followProfileId) => { const _id = DB.ObjectID(profileId); let update = { $addToSet: { following: followProfileId + '' //converts to str } } return DB.profileCols.updateOne({ _id }, update).catch((err) => { console.log(err); return false; }); } DB.unfollowProfile = async (profileId, followProfileId) => { const _id = DB.ObjectID(profileId); let update = { $pull: { following: followProfileId + '' //converts to str } } return DB.profileCols.updateOne({ _id }, update).catch((err) => { console.log(err); return false; }); } DB.getFollowingTheProfile = async (profileId) => { //const profile_id = DB.ObjectID(profileId); let r = await DB.profileCols.find({ following: (profileId + '') }) .toArray().catch((err) => { console.log(err); return []; }); return r; } DB.getData = async (profileid, key) => { let profile = await DB.getProfile(profileid); return profile.data[key]; } DB.setData = async (profileid, key, value) => { const _id = DB.ObjectID(profileid); let update = { $set: { ["data." + key]: value } } return DB.profileCols.updateOne({ _id }, update).catch((err) => { console.log(err); return false; }); } DB.setProfileToken = (profileid, token) => { if (!token) return false; const _id = DB.ObjectID(profileid); let update = { $addToSet: { token } } if (userProfileCache[profileid]) delete userProfileCache[profileid]; return DB.profileCols.updateOne({ _id }, update).catch((err) => { console.log(err); return false; }); } DB.setWebSubscription = (profileid, webSubscription) => { const _id = DB.ObjectID(profileid); let update = { $set: { webSubscription } } if (userProfileCache[profileid]) delete userProfileCache[profileid]; return DB.profileCols.updateOne({ _id }, update).catch((err) => { console.log(err); return false; }); } DB.addNotification = async (profileid, message, postid, commentIndx, actorid) => { const _id = DB.ObjectID(profileid); let update = { $push: { notifications: { ts: new Date(), body: message, postid, commentIndx, actorid, } } } return DB.profileCols.updateOne({ _id }, update).catch((err) => { console.log(err); return false; }); } DB.isSubscriptor = async (profileid) => { const profile = DB.getProfileCache(profileid); return profile && profile.subscription && profile.subscription > (new Date() - 0); } //Groups DB.getGroups = async (excludePrivate = false) => { let query = { isGroup: true, isCourse: { $ne: true }, isChat: { $ne: true }, }; if (excludePrivate) query.isPrivate = false; let r = await DB.profileCols.find(query).sort({ lastUpdate: -1 }).limit(10) .toArray().catch((err) => { console.log(err); return false; }); return r; } DB.getFollowingGroups = async (profileid) => { const profile = await DB.getProfile(profileid); let ids = []; const following = Array.isArray(profile?.following) ? profile.following : []; for (id in following) { try { let oId = DB.ObjectID(following[id]); let checkProfile = await DB.getProfileCache(oId) if (checkProfile && checkProfile.isGroup && !checkProfile.isChat) { ids.push(oId) } } catch { } } let query = { isGroup: true, isCourse: { $ne: true }, isChat: { $ne: true }, _id: { $in: ids } }; let r = await DB.profileCols.find(query).sort({ lastUpdate: -1 }) .toArray().catch((err) => { console.log(err); return false; }); return r; } DB.searchGroups = async (queryStr, coursesB = false) => { let regEx = new RegExp(queryStr, 'i'); let query = queryStr ? { isGroup: true, isChat: { $ne: true }, isCourse: coursesB, $or: [ { "profile.firstName": { $regex: regEx } }, { "profile.lastName": { $regex: regEx } }, { "profile.description": { $regex: regEx } }, { "data.author": { $regex: regEx } } ] } : { isGroup: true, isChat: { $ne: true }, isCourse: coursesB }; let r = await DB.profileCols.find(query) .sort({ lastUpdate: -1 }).limit(20) .toArray().catch((err) => { console.log(err); return false; }); return r; } let privateGroupsCache = {}; DB.isGroupPrivate = async (groupid) => { if (userProfileCache[groupid]) return userProfileCache[groupid].isPrivate; let g = await DB.getGroup(groupid); return g ? g.isPrivate : false; } DB.isGroupNewsOnly = async (groupid) => { if (userProfileCache[groupid]) return userProfileCache[groupid].newsOnly; let g = await DB.getGroup(groupid); return g ? g.newsOnly : false; } DB.isOwnerOfGroup = async (profileid, groupid) => { let profile = await DB.getProfileCache(profileid); let group = userProfileCache[groupid] ? userProfileCache[groupid] : await DB.getGroup(groupid); return profile.userid == group.userid; } DB.getGroup = async (groupid) => { const _id = DB.ObjectID(groupid); //if(userProfileCache[groupid]) return userProfileCache[groupid]; let r = await DB.profileCols.findOne({ _id, isGroup: true, isChat: { $ne: true }, }).catch((err) => { console.log(err); return false; }); if (r) userProfileCache[r.id] = r; return r; } DB.subscribeToGroup = async (profileid, groupid, reqSubscription = false) => { const _id = DB.ObjectID(groupid); const subOrRequest = reqSubscription ? "pending." : "subscribed."; let update = { $set: { [subOrRequest + profileid]: new Date() } } if (!reqSubscription) DB.followProfile(profileid, groupid); delete userProfileCache[groupid]; return DB.profileCols.updateOne({ _id }, update).catch((err) => { console.log(err); return false; }); } DB.acceptGroupJoinReq = async (profileid, groupid) => { const _id = DB.ObjectID(groupid); let update = { $set: { ["subscribed." + profileid]: new Date() }, $unset: { ["pending." + profileid]: "" } } DB.followProfile(profileid, groupid); delete userProfileCache[groupid]; return DB.profileCols.updateOne({ _id }, update).catch((err) => { console.log(err); return false; }); } DB.rejectGroupJoinReq = async (profileid, groupid) => { const _id = DB.ObjectID(groupid); let update = { $unset: { ["pending." + profileid]: "" } } delete userProfileCache[groupid]; return DB.profileCols.updateOne({ _id }, update).catch((err) => { console.log(err); return false; }); } DB.unsubscribeToGroup = async (profileid, groupid) => { const _id = DB.ObjectID(groupid); let update = { $unset: { ["subscribed." + profileid]: "", } } DB.unfollowProfile(profileid, groupid) return DB.profileCols.updateOne({ _id }, update).catch((err) => { console.log(err); return false; }); } //Courses DB.getCourses = async () => { let r = await DB.profileCols.find({ isGroup: true, isCourse: true, isChat: { $ne: true } }) .sort({ lastUpdate: -1 }).limit(20) .toArray().catch((err) => { console.log(err); return false; }); return r; } } module.exports = userDB;