const DBName = "EMI_SOCIAL"; const toProfileIds = (DB, profileIds = []) => { return profileIds .filter((profileId) => DB.ObjectID.isValid(profileId)) .map((profileId) => DB.ObjectID(profileId)); }; const publicProfile = (profile) => { if (!profile) return null; return { _id: profile._id, profile: profile.profile, username: profile.username, isGroup: profile.isGroup, isCourse: profile.isCourse, }; }; const bibleDB = (DB) => { DB.bibleVerseCols = DB.db.db(DBName).collection("bible_verses"); DB.bibleVerseCols.createIndex({ highlightedBy: 1 }).catch(console.error); DB.bibleVerseCols.createIndex({ notedBy: 1 }).catch(console.error); const getVerseDoc = async (verseKey) => { return DB.bibleVerseCols.findOne({ _id: verseKey }).catch((err) => { console.log(err); return false; }); }; DB.getBibleProfileData = async (profileid) => { if (!DB.ObjectID.isValid(profileid)) return false; const profile = await DB.profileCols.findOne( { _id: DB.ObjectID(profileid) }, { projection: { bibleHighlights: 1, bibleNotes: 1 } } ).catch((err) => { console.log(err); return false; }); return { highlights: profile?.bibleHighlights || [], notes: profile?.bibleNotes || [], }; }; DB.getBibleVerseCounters = async (verseKey) => { const verse = await getVerseDoc(verseKey); const highlightedBy = Array.isArray(verse?.highlightedBy) ? verse.highlightedBy : []; const notedBy = Array.isArray(verse?.notedBy) ? verse.notedBy : []; return { verseKey, highlightCount: highlightedBy.length, noteProfileCount: notedBy.length, notesCount: verse?.notesCount || 0, }; }; DB.getBibleVerseActivity = async (verseKey, limit = 10) => { const verse = await getVerseDoc(verseKey); const highlightedBy = Array.isArray(verse?.highlightedBy) ? verse.highlightedBy : []; const notedBy = Array.isArray(verse?.notedBy) ? verse.notedBy : []; const highlightProfiles = toProfileIds(DB, highlightedBy); const noteProfiles = toProfileIds(DB, notedBy); const [highlights, noteUsers, notes] = await Promise.all([ highlightProfiles.length ? DB.profileCols.find({ _id: { $in: highlightProfiles } }).project({ profile: 1, username: 1, isGroup: 1, isCourse: 1, }).toArray() : [], noteProfiles.length ? DB.profileCols.find({ _id: { $in: noteProfiles } }).project({ profile: 1, username: 1, isGroup: 1, isCourse: 1, }).toArray() : [], DB.profileCols.aggregate([ { $match: { "bibleNotes.verseKey": verseKey } }, { $unwind: "$bibleNotes" }, { $match: { "bibleNotes.verseKey": verseKey } }, { $sort: { "bibleNotes.updatedAt": -1, "bibleNotes.createdAt": -1 } }, { $limit: limit }, { $project: { _id: 0, note: "$bibleNotes", profile: { _id: "$_id", profile: "$profile", username: "$username", isGroup: "$isGroup", isCourse: "$isCourse", } } } ]).toArray(), ]); return { counters: await DB.getBibleVerseCounters(verseKey), highlightedBy: highlights.map(publicProfile), notedBy: noteUsers.map(publicProfile), notes, }; }; DB.addBibleHighlight = async (profileid, verseKey) => { if (!DB.ObjectID.isValid(profileid)) return false; const now = new Date(); const _id = DB.ObjectID(profileid); const profileUpdate = await DB.profileCols.updateOne( { _id, "bibleHighlights.verseKey": { $ne: verseKey } }, { $push: { bibleHighlights: { verseKey, createdAt: now, } }, $set: { lastUpdate: now } } ).catch((err) => { console.log(err); return false; }); await DB.bibleVerseCols.updateOne( { _id: verseKey }, { $setOnInsert: { createdAt: now }, $set: { updatedAt: now }, $addToSet: { highlightedBy: profileid + "" } }, { upsert: true } ).catch((err) => { console.log(err); return false; }); if (DB.clearProfileCache) DB.clearProfileCache(profileid); return profileUpdate; }; DB.removeBibleHighlight = async (profileid, verseKey) => { if (!DB.ObjectID.isValid(profileid)) return false; const now = new Date(); const _id = DB.ObjectID(profileid); const profileUpdate = await DB.profileCols.updateOne( { _id }, { $pull: { bibleHighlights: { verseKey } }, $set: { lastUpdate: now } } ).catch((err) => { console.log(err); return false; }); await DB.bibleVerseCols.updateOne( { _id: verseKey }, { $pull: { highlightedBy: profileid + "" }, $set: { updatedAt: now } } ).catch((err) => { console.log(err); return false; }); if (DB.clearProfileCache) DB.clearProfileCache(profileid); return profileUpdate; }; DB.addBibleNote = async (profileid, verseKey, text) => { if (!DB.ObjectID.isValid(profileid)) return false; const now = new Date(); const note = { _id: DB.ObjectID().toString(), verseKey, text, createdAt: now, updatedAt: now, }; const _id = DB.ObjectID(profileid); const profileUpdate = await DB.profileCols.updateOne( { _id }, { $push: { bibleNotes: note }, $set: { lastUpdate: now } } ).catch((err) => { console.log(err); return false; }); await DB.bibleVerseCols.updateOne( { _id: verseKey }, { $setOnInsert: { createdAt: now }, $set: { updatedAt: now }, $addToSet: { notedBy: profileid + "" }, $inc: { notesCount: 1 } }, { upsert: true } ).catch((err) => { console.log(err); return false; }); if (DB.clearProfileCache) DB.clearProfileCache(profileid); return profileUpdate ? note : false; }; DB.updateBibleNote = async (profileid, verseKey, noteId, text) => { if (!DB.ObjectID.isValid(profileid)) return false; const now = new Date(); const _id = DB.ObjectID(profileid); const result = await DB.profileCols.findOneAndUpdate( { _id, bibleNotes: { $elemMatch: { _id: noteId, verseKey } } }, { $set: { "bibleNotes.$.text": text, "bibleNotes.$.updatedAt": now, lastUpdate: now, } }, { returnOriginal: false, projection: { bibleNotes: 1 } } ).catch((err) => { console.log(err); return false; }); if (DB.clearProfileCache) DB.clearProfileCache(profileid); return result?.value?.bibleNotes?.find((note) => note._id === noteId) || null; }; DB.removeBibleNote = async (profileid, verseKey, noteId) => { if (!DB.ObjectID.isValid(profileid)) return false; const now = new Date(); const _id = DB.ObjectID(profileid); const result = await DB.profileCols.updateOne( { _id, bibleNotes: { $elemMatch: { _id: noteId, verseKey } } }, { $pull: { bibleNotes: { _id: noteId, verseKey } }, $set: { lastUpdate: now } } ).catch((err) => { console.log(err); return false; }); if (result?.modifiedCount) { const stillHasNotes = await DB.profileCols.findOne( { _id, "bibleNotes.verseKey": verseKey }, { projection: { _id: 1 } } ); const verseUpdate = { $inc: { notesCount: -1 }, $set: { updatedAt: now } }; if (!stillHasNotes) { verseUpdate.$pull = { notedBy: profileid + "" }; } await DB.bibleVerseCols.updateOne({ _id: verseKey }, verseUpdate).catch((err) => { console.log(err); return false; }); } if (DB.clearProfileCache) DB.clearProfileCache(profileid); return result; }; } module.exports = bibleDB;