From 4dbb662269a0038da5264cd3c1776e503892bb6c Mon Sep 17 00:00:00 2001 From: Kevin Aleman Date: Thu, 24 Oct 2024 14:59:15 -0600 Subject: [PATCH] chore: Replace `cursor.count` with calls to `countDocuments` or `estimatedDocumentCount` (#33725) --- apps/meteor/app/livechat/server/lib/Helper.ts | 1 - .../app/livechat/server/lib/LivechatTyped.ts | 10 ++++++- .../app/livechat/server/lib/QueueManager.ts | 1 - .../server/lib/analytics/dashboards.ts | 4 +-- .../app/livechat/server/lib/departmentsLib.ts | 4 +-- .../app/statistics/server/lib/statistics.ts | 22 +++++++-------- .../models/raw/LivechatDepartmentAgents.ts | 23 ++++++++++++++++ .../server/models/raw/LivechatVisitors.ts | 4 +-- apps/meteor/server/models/raw/Messages.ts | 19 +++++++++++++ apps/meteor/server/models/raw/Roles.ts | 18 ++++++++++++- apps/meteor/server/models/raw/Rooms.ts | 23 ++++++++++++++++ apps/meteor/server/models/raw/Users.js | 27 +++++++++++++++++++ .../models/ILivechatDepartmentAgentsModel.ts | 2 ++ .../src/models/ILivechatVisitorsModel.ts | 2 +- .../src/models/IMessagesModel.ts | 2 ++ .../model-typings/src/models/IRolesModel.ts | 4 ++- .../model-typings/src/models/IRoomsModel.ts | 4 +++ .../model-typings/src/models/IUsersModel.ts | 2 ++ 18 files changed, 149 insertions(+), 23 deletions(-) diff --git a/apps/meteor/app/livechat/server/lib/Helper.ts b/apps/meteor/app/livechat/server/lib/Helper.ts index a6af9cee2f241..cf11c8d12b2a8 100644 --- a/apps/meteor/app/livechat/server/lib/Helper.ts +++ b/apps/meteor/app/livechat/server/lib/Helper.ts @@ -407,7 +407,6 @@ export const dispatchInquiryQueued = async (inquiry: ILivechatInquiryRecord, age return; } - logger.debug(`Notifying ${await onlineAgents.count()} agents of new inquiry`); const notificationUserName = v && (v.name || v.username); for await (const agent of onlineAgents) { diff --git a/apps/meteor/app/livechat/server/lib/LivechatTyped.ts b/apps/meteor/app/livechat/server/lib/LivechatTyped.ts index 9e6636d46e72a..c522218f283e8 100644 --- a/apps/meteor/app/livechat/server/lib/LivechatTyped.ts +++ b/apps/meteor/app/livechat/server/lib/LivechatTyped.ts @@ -132,7 +132,7 @@ class LivechatClass { Livechat.logger.debug(`Fetching online bot agents for department ${department}`); const botAgents = await Livechat.getBotAgents(department); if (botAgents) { - const onlineBots = await botAgents.count(); + const onlineBots = await Livechat.countBotAgents(department); this.logger.debug(`Found ${onlineBots} online`); if (onlineBots > 0) { return true; @@ -632,6 +632,14 @@ class LivechatClass { return Users.findBotAgents(); } + private async countBotAgents(department?: string) { + if (department) { + return LivechatDepartmentAgents.countBotsForDepartment(department); + } + + return Users.countBotAgents(); + } + private async resolveChatTags( room: IOmnichannelRoom, options: CloseRoomParams['options'] = {}, diff --git a/apps/meteor/app/livechat/server/lib/QueueManager.ts b/apps/meteor/app/livechat/server/lib/QueueManager.ts index 4506e38b99d49..24be8d42b7a49 100644 --- a/apps/meteor/app/livechat/server/lib/QueueManager.ts +++ b/apps/meteor/app/livechat/server/lib/QueueManager.ts @@ -331,7 +331,6 @@ export class QueueManager { return; } - logger.debug(`Notifying ${await onlineAgents.count()} agents of new inquiry`); const notificationUserName = v && (v.name || v.username); for await (const agent of onlineAgents) { diff --git a/apps/meteor/app/livechat/server/lib/analytics/dashboards.ts b/apps/meteor/app/livechat/server/lib/analytics/dashboards.ts index dd0d54970065b..cf5a0abd5d54f 100644 --- a/apps/meteor/app/livechat/server/lib/analytics/dashboards.ts +++ b/apps/meteor/app/livechat/server/lib/analytics/dashboards.ts @@ -247,11 +247,11 @@ const getConversationsMetricsAsync = async ({ language: user.language || settings.get('Language') || 'en', })) || []; const metrics = ['Total_conversations', 'Open_conversations', 'On_Hold_conversations', 'Total_messages']; - const visitorsCount = await LivechatVisitors.getVisitorsBetweenDate({ + const visitorsCount = await LivechatVisitors.countVisitorsBetweenDate({ start: new Date(start), end: new Date(end), department: departmentId, - }).count(); + }); return { totalizers: [ ...totalizers.filter((metric: { title: string }) => metrics.includes(metric.title)), diff --git a/apps/meteor/app/livechat/server/lib/departmentsLib.ts b/apps/meteor/app/livechat/server/lib/departmentsLib.ts index f149078788e30..fa66fdbfe5735 100644 --- a/apps/meteor/app/livechat/server/lib/departmentsLib.ts +++ b/apps/meteor/app/livechat/server/lib/departmentsLib.ts @@ -296,8 +296,8 @@ export async function getRequiredDepartment(onlineRequired = true) { return dept; } - const onlineAgents = await LivechatDepartmentAgents.getOnlineForDepartment(dept._id); - if (onlineAgents && (await onlineAgents.count())) { + const onlineAgents = await LivechatDepartmentAgents.countOnlineForDepartment(dept._id); + if (onlineAgents) { return dept; } } diff --git a/apps/meteor/app/statistics/server/lib/statistics.ts b/apps/meteor/app/statistics/server/lib/statistics.ts index 9defbaa42befe..9ca39d2e9d5fa 100644 --- a/apps/meteor/app/statistics/server/lib/statistics.ts +++ b/apps/meteor/app/statistics/server/lib/statistics.ts @@ -2,7 +2,7 @@ import { log } from 'console'; import os from 'os'; import { Analytics, Team, VideoConf, Presence } from '@rocket.chat/core-services'; -import type { IRoom, IStats } from '@rocket.chat/core-typings'; +import type { IRoom, IStats, ISetting } from '@rocket.chat/core-typings'; import { UserStatus } from '@rocket.chat/core-typings'; import { NotificationQueue, @@ -92,7 +92,7 @@ export const statistics = { }; // Version - const uniqueID = await Settings.findOne('uniqueID'); + const uniqueID = await Settings.findOne>('uniqueID', { projection: { createdAt: 1 } }); statistics.uniqueId = settings.get('uniqueID'); if (uniqueID) { statistics.installedAt = uniqueID.createdAt.toISOString(); @@ -520,7 +520,7 @@ export const statistics = { ); statsPms.push( - NotificationQueue.col.estimatedDocumentCount().then((count) => { + NotificationQueue.estimatedDocumentCount().then((count) => { statistics.pushQueue = count; }), ); @@ -546,27 +546,27 @@ export const statistics = { statistics.messageAuditLoad = settings.get('Message_Auditing_Panel_Load_Count'); statistics.joinJitsiButton = settings.get('Jitsi_Click_To_Join_Count'); statistics.slashCommandsJitsi = settings.get('Jitsi_Start_SlashCommands_Count'); - statistics.totalOTRRooms = await Rooms.findByCreatedOTR().count(); + statistics.totalOTRRooms = await Rooms.countByCreatedOTR({ readPreference }); statistics.totalOTR = settings.get('OTR_Count'); - statistics.totalBroadcastRooms = await Rooms.findByBroadcast().count(); + statistics.totalBroadcastRooms = await Rooms.countByBroadcast({ readPreference }); statistics.totalTriggeredEmails = settings.get('Triggered_Emails_Count'); statistics.totalRoomsWithStarred = await Messages.countRoomsWithStarredMessages({ readPreference }); statistics.totalRoomsWithPinned = await Messages.countRoomsWithPinnedMessages({ readPreference }); statistics.totalUserTOTP = await Users.countActiveUsersTOTPEnable({ readPreference }); statistics.totalUserEmail2fa = await Users.countActiveUsersEmail2faEnable({ readPreference }); - statistics.totalPinned = await Messages.findPinned({ readPreference }).count(); - statistics.totalStarred = await Messages.findStarred({ readPreference }).count(); + statistics.totalPinned = await Messages.countPinned({ readPreference }); + statistics.totalStarred = await Messages.countStarred({ readPreference }); statistics.totalLinkInvitation = await Invites.estimatedDocumentCount(); statistics.totalLinkInvitationUses = await Invites.countUses(); statistics.totalEmailInvitation = settings.get('Invitation_Email_Count'); - statistics.totalE2ERooms = await Rooms.findByE2E({ readPreference }).count(); + statistics.totalE2ERooms = await Rooms.countByE2E({ readPreference }); statistics.logoChange = Object.keys(settings.get('Assets_logo') || {}).includes('url'); statistics.showHomeButton = settings.get('Layout_Show_Home_Button'); statistics.totalEncryptedMessages = await Messages.countByType('e2e', { readPreference }); statistics.totalManuallyAddedUsers = settings.get('Manual_Entry_User_Count'); - statistics.totalSubscriptionRoles = await RolesRaw.findByScope('Subscriptions').count(); - statistics.totalUserRoles = await RolesRaw.findByScope('Users').count(); - statistics.totalCustomRoles = await RolesRaw.findCustomRoles({ readPreference }).count(); + statistics.totalSubscriptionRoles = await RolesRaw.countByScope('Subscriptions', { readPreference }); + statistics.totalUserRoles = await RolesRaw.countByScope('Users', { readPreference }); + statistics.totalCustomRoles = await RolesRaw.countCustomRoles({ readPreference }); statistics.totalWebRTCCalls = settings.get('WebRTC_Calls_Count'); statistics.uncaughtExceptionsCount = settings.get('Uncaught_Exceptions_Count'); diff --git a/apps/meteor/server/models/raw/LivechatDepartmentAgents.ts b/apps/meteor/server/models/raw/LivechatDepartmentAgents.ts index 891542f03e7a9..6d7ec76222db3 100644 --- a/apps/meteor/server/models/raw/LivechatDepartmentAgents.ts +++ b/apps/meteor/server/models/raw/LivechatDepartmentAgents.ts @@ -267,6 +267,19 @@ export class LivechatDepartmentAgentsRaw extends BaseRaw { + const agents = await this.findByDepartmentId(departmentId, { projection: { username: 1 } }).toArray(); + + if (agents.length === 0) { + return 0; + } + + return Users.countOnlineUserFromList( + agents.map((a) => a.username), + isLivechatEnabledWhenAgentIdle, + ); + } + async getBotsForDepartment(departmentId: string): Promise> { const agents = await this.findByDepartmentId(departmentId).toArray(); @@ -287,6 +300,16 @@ export class LivechatDepartmentAgentsRaw extends BaseRaw { + const agents = await this.findByDepartmentId(departmentId, { projection: { username: 1 } }).toArray(); + + if (agents.length === 0) { + return 0; + } + + return Users.countBotAgents(agents.map((a) => a.username)); + } + async getNextBotForDepartment( departmentId: ILivechatDepartmentAgents['departmentId'], ignoreAgentId?: ILivechatDepartmentAgents['agentId'], diff --git a/apps/meteor/server/models/raw/LivechatVisitors.ts b/apps/meteor/server/models/raw/LivechatVisitors.ts index 396b728159ff6..58a56909cb06c 100644 --- a/apps/meteor/server/models/raw/LivechatVisitors.ts +++ b/apps/meteor/server/models/raw/LivechatVisitors.ts @@ -105,7 +105,7 @@ export class LivechatVisitorsRaw extends BaseRaw implements IL return this.findOne(query, options); } - getVisitorsBetweenDate({ start, end, department }: { start: Date; end: Date; department?: string }): FindCursor { + countVisitorsBetweenDate({ start, end, department }: { start: Date; end: Date; department?: string }): Promise { const query = { disabled: { $ne: true }, _updatedAt: { @@ -115,7 +115,7 @@ export class LivechatVisitorsRaw extends BaseRaw implements IL ...(department && department !== 'undefined' && { department }), }; - return this.find(query, { projection: { _id: 1 } }); + return this.countDocuments(query); } async getNextVisitorUsername(): Promise { diff --git a/apps/meteor/server/models/raw/Messages.ts b/apps/meteor/server/models/raw/Messages.ts index 3f79e174fec03..84013af5b895b 100644 --- a/apps/meteor/server/models/raw/Messages.ts +++ b/apps/meteor/server/models/raw/Messages.ts @@ -539,6 +539,16 @@ export class MessagesRaw extends BaseRaw implements IMessagesModel { return this.find(query, options); } + countPinned(options?: CountDocumentsOptions): Promise { + const query: Filter = { + t: { $ne: 'rm' as MessageTypesValues }, + _hidden: { $ne: true }, + pinned: true, + }; + + return this.countDocuments(query, options); + } + findPaginatedPinnedByRoom(roomId: IMessage['rid'], options?: FindOptions): FindPaginated> { const query: Filter = { t: { $ne: 'rm' }, @@ -559,6 +569,15 @@ export class MessagesRaw extends BaseRaw implements IMessagesModel { return this.find(query, options); } + countStarred(options?: CountDocumentsOptions): Promise { + const query: Filter = { + '_hidden': { $ne: true }, + 'starred._id': { $exists: true }, + }; + + return this.countDocuments(query, options); + } + async setFederationReactionEventId(username: string, _id: string, reaction: string, federationEventId: string): Promise { await this.updateOne( { _id }, diff --git a/apps/meteor/server/models/raw/Roles.ts b/apps/meteor/server/models/raw/Roles.ts index c3dfab7a702a4..d223e8249cae8 100644 --- a/apps/meteor/server/models/raw/Roles.ts +++ b/apps/meteor/server/models/raw/Roles.ts @@ -1,7 +1,7 @@ import type { IRole, IRoom, IUser, RocketChatRecordDeleted } from '@rocket.chat/core-typings'; import type { IRolesModel } from '@rocket.chat/model-typings'; import { Subscriptions, Users } from '@rocket.chat/models'; -import type { Collection, FindCursor, Db, Filter, FindOptions, Document } from 'mongodb'; +import type { Collection, FindCursor, Db, Filter, FindOptions, Document, CountDocumentsOptions } from 'mongodb'; import { notifyOnSubscriptionChangedByRoomIdAndUserId } from '../../../app/lib/server/lib/notifyListener'; import { BaseRaw } from './BaseRaw'; @@ -184,6 +184,14 @@ export class RolesRaw extends BaseRaw implements IRolesModel { return this.find(query, options || {}); } + countByScope(scope: IRole['scope'], options?: CountDocumentsOptions): Promise { + const query = { + scope, + }; + + return this.countDocuments(query, options); + } + findCustomRoles(options?: FindOptions): FindCursor { const query: Filter = { protected: false, @@ -192,6 +200,14 @@ export class RolesRaw extends BaseRaw implements IRolesModel { return this.find(query, options || {}); } + countCustomRoles(options?: CountDocumentsOptions): Promise { + const query: Filter = { + protected: false, + }; + + return this.countDocuments(query, options || {}); + } + async updateById( _id: IRole['_id'], name: IRole['name'], diff --git a/apps/meteor/server/models/raw/Rooms.ts b/apps/meteor/server/models/raw/Rooms.ts index 0d0770994fbdc..a33e161e0ad52 100644 --- a/apps/meteor/server/models/raw/Rooms.ts +++ b/apps/meteor/server/models/raw/Rooms.ts @@ -26,6 +26,7 @@ import type { UpdateOptions, UpdateResult, ModifyResult, + CountDocumentsOptions, } from 'mongodb'; import { readSecondaryPreferred } from '../../database/readSecondaryPreferred'; @@ -654,6 +655,15 @@ export class RoomsRaw extends BaseRaw implements IRoomsModel { ); } + countByBroadcast(options?: CountDocumentsOptions): Promise { + return this.countDocuments( + { + broadcast: true, + }, + options, + ); + } + setAsFederated(roomId: IRoom['_id']): Promise { return this.updateOne({ _id: roomId }, { $set: { federated: true } }); } @@ -695,6 +705,15 @@ export class RoomsRaw extends BaseRaw implements IRoomsModel { ); } + countByE2E(options?: CountDocumentsOptions): Promise { + return this.countDocuments( + { + encrypted: true, + }, + options, + ); + } + findE2ERoomById(roomId: IRoom['_id'], options: FindOptions = {}): Promise { return this.findOne( { @@ -1485,6 +1504,10 @@ export class RoomsRaw extends BaseRaw implements IRoomsModel { return this.find({ createdOTR: true }); } + countByCreatedOTR(options?: CountDocumentsOptions): Promise { + return this.countDocuments({ createdOTR: true }, options); + } + findByUsernamesOrUids(uids: IRoom['u']['_id'][], usernames: IRoom['u']['username'][]): FindCursor { return this.find({ $or: [{ usernames: { $in: usernames } }, { uids: { $in: uids } }] }); } diff --git a/apps/meteor/server/models/raw/Users.js b/apps/meteor/server/models/raw/Users.js index 3c1badc55e254..1c0c2abecf64d 100644 --- a/apps/meteor/server/models/raw/Users.js +++ b/apps/meteor/server/models/raw/Users.js @@ -1449,6 +1449,17 @@ export class UsersRaw extends BaseRaw { return this.find(query); } + countOnlineUserFromList(userList, isLivechatEnabledWhenAgentIdle) { + // TODO: Create class Agent + const username = { + $in: [].concat(userList), + }; + + const query = queryStatusAgentOnline({ username }, isLivechatEnabledWhenAgentIdle); + + return this.countDocuments(query); + } + findOneOnlineAgentByUserList(userList, options, isLivechatEnabledWhenAgentIdle) { // TODO:: Create class Agent const username = { @@ -1480,6 +1491,22 @@ export class UsersRaw extends BaseRaw { return this.find(query); } + countBotAgents(usernameList) { + // TODO:: Create class Agent + const query = { + roles: { + $all: ['bot', 'livechat-agent'], + }, + ...(usernameList && { + username: { + $in: [].concat(usernameList), + }, + }), + }; + + return this.countDocuments(query); + } + removeAllRoomsByUserId(_id) { return this.updateOne( { diff --git a/packages/model-typings/src/models/ILivechatDepartmentAgentsModel.ts b/packages/model-typings/src/models/ILivechatDepartmentAgentsModel.ts index f0f151fa70d47..827b266421b32 100644 --- a/packages/model-typings/src/models/ILivechatDepartmentAgentsModel.ts +++ b/packages/model-typings/src/models/ILivechatDepartmentAgentsModel.ts @@ -64,7 +64,9 @@ export interface ILivechatDepartmentAgentsModel extends IBaseModel | undefined>; + countOnlineForDepartment(departmentId: string, isLivechatEnabledWhenAgentIdle?: boolean): Promise; getBotsForDepartment(departmentId: string): Promise>; + countBotsForDepartment(departmentId: string): Promise; getNextBotForDepartment( departmentId: ILivechatDepartmentAgents['departmentId'], ignoreAgentId?: ILivechatDepartmentAgents['agentId'], diff --git a/packages/model-typings/src/models/ILivechatVisitorsModel.ts b/packages/model-typings/src/models/ILivechatVisitorsModel.ts index 3e17fc2a59624..998d9106cdf7d 100644 --- a/packages/model-typings/src/models/ILivechatVisitorsModel.ts +++ b/packages/model-typings/src/models/ILivechatVisitorsModel.ts @@ -16,7 +16,6 @@ import type { FindPaginated, IBaseModel } from './IBaseModel'; export interface ILivechatVisitorsModel extends IBaseModel { findById(_id: string, options?: FindOptions): FindCursor; getVisitorByToken(token: string, options?: FindOptions): Promise; - getVisitorsBetweenDate({ start, end, department }: { start: Date; end: Date; department?: string }): FindCursor; findByNameRegexWithExceptionsAndConditions

( searchTerm: string, exceptions: string[], @@ -77,4 +76,5 @@ export interface ILivechatVisitorsModel extends IBaseModel { data: { name?: string; username?: string; email?: string; phone?: string; livechatData: { [k: string]: any } }, ): Promise; setLastChatById(_id: string, lastChat: Required): Promise; + countVisitorsBetweenDate({ start, end, department }: { start: Date; end: Date; department?: string }): Promise; } diff --git a/packages/model-typings/src/models/IMessagesModel.ts b/packages/model-typings/src/models/IMessagesModel.ts index 986a50c8cd299..116b00fc6fee3 100644 --- a/packages/model-typings/src/models/IMessagesModel.ts +++ b/packages/model-typings/src/models/IMessagesModel.ts @@ -291,4 +291,6 @@ export interface IMessagesModel extends IBaseModel { findThreadsByRoomId(rid: string, skip: number, limit: number): FindCursor; decreaseReplyCountById(_id: string, inc?: number): Promise; + countPinned(options?: CountDocumentsOptions): Promise; + countStarred(options?: CountDocumentsOptions): Promise; } diff --git a/packages/model-typings/src/models/IRolesModel.ts b/packages/model-typings/src/models/IRolesModel.ts index f039253f7bc37..84d49ef6baee3 100644 --- a/packages/model-typings/src/models/IRolesModel.ts +++ b/packages/model-typings/src/models/IRolesModel.ts @@ -1,5 +1,5 @@ import type { IRole, IUser, IRoom } from '@rocket.chat/core-typings'; -import type { FindCursor, FindOptions } from 'mongodb'; +import type { FindCursor, FindOptions, CountDocumentsOptions } from 'mongodb'; import type { IBaseModel } from './IBaseModel'; @@ -62,4 +62,6 @@ export interface IRolesModel extends IBaseModel { canAddUserToRole(uid: IUser['_id'], roleId: IRole['_id'], scope?: IRoom['_id']): Promise; countUsersInRole(roleId: IRole['_id'], scope?: IRoom['_id']): Promise; + countByScope(scope: IRole['scope'], options?: CountDocumentsOptions): Promise; + countCustomRoles(options?: CountDocumentsOptions): Promise; } diff --git a/packages/model-typings/src/models/IRoomsModel.ts b/packages/model-typings/src/models/IRoomsModel.ts index d1235fc589583..83932626ed6ee 100644 --- a/packages/model-typings/src/models/IRoomsModel.ts +++ b/packages/model-typings/src/models/IRoomsModel.ts @@ -8,6 +8,7 @@ import type { UpdateOptions, UpdateResult, ModifyResult, + CountDocumentsOptions, } from 'mongodb'; import type { Updater } from '../updater'; @@ -309,4 +310,7 @@ export interface IRoomsModel extends IBaseModel { e2eQueue?: IRoom['usersWaitingForE2EKeys'], ): Promise>; countGroupDMsByUids(uids: NonNullable): Promise; + countByCreatedOTR(options?: CountDocumentsOptions): Promise; + countByBroadcast(options?: CountDocumentsOptions): Promise; + countByE2E(options?: CountDocumentsOptions): Promise; } diff --git a/packages/model-typings/src/models/IUsersModel.ts b/packages/model-typings/src/models/IUsersModel.ts index 6029b4913f2d2..a2863aba8fe5b 100644 --- a/packages/model-typings/src/models/IUsersModel.ts +++ b/packages/model-typings/src/models/IUsersModel.ts @@ -224,6 +224,7 @@ export interface IUsersModel extends IBaseModel { countFederatedExternalUsers(): Promise; findOnlineUserFromList(userList: string[], isLivechatEnabledWhenAgentIdle?: boolean): FindCursor; + countOnlineUserFromList(userList: string[], isLivechatEnabledWhenAgentIdle?: boolean): Promise; getUnavailableAgents( departmentId?: string, extraQuery?: Document, @@ -244,6 +245,7 @@ export interface IUsersModel extends IBaseModel { ): Promise; findBotAgents(usernameList?: string[]): FindCursor; + countBotAgents(usernameList?: string[]): Promise; removeAllRoomsByUserId(userId: string): Promise; removeRoomByUserId(userId: string, rid: string): Promise; addRoomByUserId(userId: string, rid: string): Promise;