From e55a61045d6d4d2791687f3deb952b53e375af0d Mon Sep 17 00:00:00 2001 From: Kevin Aleman Date: Fri, 18 Oct 2024 15:16:00 -0600 Subject: [PATCH] Replace some easy cursor.count() calls with countDocuments or estimatedDocumentCount --- .../app/statistics/server/lib/statistics.ts | 16 +++++++------- apps/meteor/package.json | 2 +- apps/meteor/server/models/raw/BaseRaw.ts | 6 +++++- apps/meteor/server/models/raw/NpsVote.ts | 13 ++++++++++++ apps/meteor/server/models/raw/Rooms.ts | 19 +++++++++++++++++ apps/meteor/server/models/raw/ServerEvents.ts | 16 +++++++------- apps/meteor/server/models/raw/TeamMember.ts | 8 +++++++ .../server/models/raw/VideoConference.ts | 17 +++++++++++---- apps/meteor/server/services/nps/service.ts | 6 +++--- apps/meteor/server/services/team/service.ts | 21 +++++++------------ .../model-typings/src/models/INpsVoteModel.ts | 2 ++ .../model-typings/src/models/IRoomsModel.ts | 4 ++++ .../src/models/ITeamMemberModel.ts | 3 +++ 13 files changed, 95 insertions(+), 38 deletions(-) diff --git a/apps/meteor/app/statistics/server/lib/statistics.ts b/apps/meteor/app/statistics/server/lib/statistics.ts index 84b8f23f7790..9defbaa42bef 100644 --- a/apps/meteor/app/statistics/server/lib/statistics.ts +++ b/apps/meteor/app/statistics/server/lib/statistics.ts @@ -126,10 +126,10 @@ export const statistics = { // Room statistics statistics.totalRooms = await Rooms.col.countDocuments({}); - statistics.totalChannels = await Rooms.findByType('c').count(); - statistics.totalPrivateGroups = await Rooms.findByType('p').count(); - statistics.totalDirect = await Rooms.findByType('d').count(); - statistics.totalLivechat = await Rooms.findByType('l').count(); + statistics.totalChannels = await Rooms.countByType('c'); + statistics.totalPrivateGroups = await Rooms.countByType('p'); + statistics.totalDirect = await Rooms.countByType('d'); + statistics.totalLivechat = await Rooms.countByType('l'); statistics.totalDiscussions = await Rooms.countDiscussions(); statistics.totalThreads = await Messages.countThreads(); @@ -183,7 +183,7 @@ export const statistics = { // Number of triggers statsPms.push( - LivechatTrigger.col.count().then((count) => { + LivechatTrigger.estimatedDocumentCount().then((count) => { statistics.totalTriggers = count; }), ); @@ -205,13 +205,13 @@ export const statistics = { // Number of Email Inboxes statsPms.push( - EmailInbox.col.count().then((count) => { + EmailInbox.estimatedDocumentCount().then((count) => { statistics.emailInboxes = count; }), ); statsPms.push( - LivechatBusinessHours.col.count().then((count) => { + LivechatBusinessHours.estimatedDocumentCount().then((count) => { statistics.BusinessHours = { // Number of Business Hours total: count, @@ -556,7 +556,7 @@ export const statistics = { statistics.totalUserEmail2fa = await Users.countActiveUsersEmail2faEnable({ readPreference }); statistics.totalPinned = await Messages.findPinned({ readPreference }).count(); statistics.totalStarred = await Messages.findStarred({ readPreference }).count(); - statistics.totalLinkInvitation = await Invites.find().count(); + statistics.totalLinkInvitation = await Invites.estimatedDocumentCount(); statistics.totalLinkInvitationUses = await Invites.countUses(); statistics.totalEmailInvitation = settings.get('Invitation_Email_Count'); statistics.totalE2ERooms = await Rooms.findByE2E({ readPreference }).count(); diff --git a/apps/meteor/package.json b/apps/meteor/package.json index fafecd90266b..d63575dd02f3 100644 --- a/apps/meteor/package.json +++ b/apps/meteor/package.json @@ -15,7 +15,7 @@ "scripts": { "start": "meteor", "build:ci": "METEOR_DISABLE_OPTIMISTIC_CACHING=1 meteor build --server-only --directory /tmp/dist", - "dev": "meteor --exclude-archs \"web.browser.legacy, web.cordova\"", + "dev": "NODE_OPTIONS=\"--trace-warnings\" meteor --exclude-archs \"web.browser.legacy, web.cordova\"", "dsv": "meteor npm run dev", "ha": "meteor npm run ha:start", "ms": "TRANSPORTER=${TRANSPORTER:-TCP} meteor npm run dev", diff --git a/apps/meteor/server/models/raw/BaseRaw.ts b/apps/meteor/server/models/raw/BaseRaw.ts index 8e1bd302b50d..cc599ab394b3 100644 --- a/apps/meteor/server/models/raw/BaseRaw.ts +++ b/apps/meteor/server/models/raw/BaseRaw.ts @@ -27,6 +27,7 @@ import type { DeleteResult, DeleteOptions, FindOneAndDeleteOptions, + CountDocumentsOptions, } from 'mongodb'; import { setUpdatedAt } from './setUpdatedAt'; @@ -494,7 +495,10 @@ export abstract class BaseRaw< return this.col.watch(pipeline); } - countDocuments(query: Filter): Promise { + countDocuments(query: Filter, options?: CountDocumentsOptions): Promise { + if (options) { + return this.col.countDocuments(query, options); + } return this.col.countDocuments(query); } diff --git a/apps/meteor/server/models/raw/NpsVote.ts b/apps/meteor/server/models/raw/NpsVote.ts index 508c6302c596..1559444fe631 100644 --- a/apps/meteor/server/models/raw/NpsVote.ts +++ b/apps/meteor/server/models/raw/NpsVote.ts @@ -97,4 +97,17 @@ export class NpsVoteRaw extends BaseRaw implements INpsVoteModel { }; return this.updateMany(query, update); } + + countByNpsId(npsId: string): Promise { + return this.countDocuments({ npsId }); + } + + countByNpsIdAndStatus(npsId: string, status: INpsVoteStatus): Promise { + const query = { + npsId, + status, + }; + + return this.countDocuments(query); + } } diff --git a/apps/meteor/server/models/raw/Rooms.ts b/apps/meteor/server/models/raw/Rooms.ts index 05afc102d164..7fc3be7479ed 100644 --- a/apps/meteor/server/models/raw/Rooms.ts +++ b/apps/meteor/server/models/raw/Rooms.ts @@ -224,6 +224,17 @@ export class RoomsRaw extends BaseRaw implements IRoomsModel { return this.find(query, options); } + countByTeamId(teamId: ITeam['_id']): Promise { + const query: Filter = { + teamId, + teamMain: { + $exists: false, + }, + }; + + return this.countDocuments(query); + } + findPaginatedByTeamIdContainingNameAndDefault( teamId: ITeam['_id'], name: IRoom['name'], @@ -702,6 +713,14 @@ export class RoomsRaw extends BaseRaw implements IRoomsModel { }); } + countRoomsInsideTeams(autoJoin = false): Promise { + return this.countDocuments({ + teamId: { $exists: true }, + teamMain: { $exists: false }, + ...(autoJoin && { teamDefault: true }), + }); + } + countByType(t: IRoom['t']): Promise { return this.col.countDocuments({ t }); } diff --git a/apps/meteor/server/models/raw/ServerEvents.ts b/apps/meteor/server/models/raw/ServerEvents.ts index ef6365c9609b..a36288e7604d 100644 --- a/apps/meteor/server/models/raw/ServerEvents.ts +++ b/apps/meteor/server/models/raw/ServerEvents.ts @@ -55,36 +55,36 @@ export class ServerEventsRaw extends BaseRaw implements IServerEve } async countFailedAttemptsByUsernameSince(username: string, since: Date): Promise { - return this.find({ + return this.countDocuments({ 'u.username': username, 't': ServerEventType.FAILED_LOGIN_ATTEMPT, 'ts': { $gte: since, }, - }).count(); + }); } countFailedAttemptsByIpSince(ip: string, since: Date): Promise { - return this.find({ + return this.countDocuments({ ip, t: ServerEventType.FAILED_LOGIN_ATTEMPT, ts: { $gte: since, }, - }).count(); + }); } countFailedAttemptsByIp(ip: string): Promise { - return this.find({ + return this.countDocuments({ ip, t: ServerEventType.FAILED_LOGIN_ATTEMPT, - }).count(); + }); } countFailedAttemptsByUsername(username: string): Promise { - return this.find({ + return this.countDocuments({ 'u.username': username, 't': ServerEventType.FAILED_LOGIN_ATTEMPT, - }).count(); + }); } } diff --git a/apps/meteor/server/models/raw/TeamMember.ts b/apps/meteor/server/models/raw/TeamMember.ts index 53c19b0c2fef..0a5b2a2755f4 100644 --- a/apps/meteor/server/models/raw/TeamMember.ts +++ b/apps/meteor/server/models/raw/TeamMember.ts @@ -72,6 +72,10 @@ export class TeamMemberRaw extends BaseRaw implements ITeamMemberMo return options ? this.col.find({ teamId }, options) : this.col.find({ teamId }, options); } + countByTeamId(teamId: string): Promise { + return this.countDocuments({ teamId }); + } + findByTeamIds(teamIds: Array): FindCursor; findByTeamIds(teamIds: Array, options: FindOptions): FindCursor; @@ -99,6 +103,10 @@ export class TeamMemberRaw extends BaseRaw implements ITeamMemberMo return options ? this.col.find({ teamId, roles: role }, options) : this.col.find({ teamId, roles: role }); } + countByTeamIdAndRole(teamId: string, role: IRole['_id']): Promise { + return this.countDocuments({ teamId, roles: role }); + } + findByUserIdAndTeamIds(userId: string, teamIds: Array, options: FindOptions = {}): FindCursor { const query = { userId, diff --git a/apps/meteor/server/models/raw/VideoConference.ts b/apps/meteor/server/models/raw/VideoConference.ts index 73080ac7b41e..4c324d938e2a 100644 --- a/apps/meteor/server/models/raw/VideoConference.ts +++ b/apps/meteor/server/models/raw/VideoConference.ts @@ -8,7 +8,16 @@ import type { } from '@rocket.chat/core-typings'; import { VideoConferenceStatus } from '@rocket.chat/core-typings'; import type { FindPaginated, InsertionModel, IVideoConferenceModel } from '@rocket.chat/model-typings'; -import type { FindCursor, UpdateOptions, UpdateFilter, UpdateResult, IndexDescription, Collection, Db, FindOptions } from 'mongodb'; +import type { + FindCursor, + UpdateOptions, + UpdateFilter, + UpdateResult, + IndexDescription, + Collection, + Db, + CountDocumentsOptions, +} from 'mongodb'; import { BaseRaw } from './BaseRaw'; @@ -63,15 +72,15 @@ export class VideoConferenceRaw extends BaseRaw implements IVid public async countByTypeAndStatus( type: VideoConference['type'], status: VideoConferenceStatus, - options: FindOptions, + options: CountDocumentsOptions, ): Promise { - return this.find( + return this.countDocuments( { type, status, }, options, - ).count(); + ); } public async createDirect({ diff --git a/apps/meteor/server/services/nps/service.ts b/apps/meteor/server/services/nps/service.ts index be5b44582a5a..d1c5023fca89 100644 --- a/apps/meteor/server/services/nps/service.ts +++ b/apps/meteor/server/services/nps/service.ts @@ -60,14 +60,14 @@ export class NPSService extends ServiceClassInternal implements INPSService { return; } - const total = await NpsVote.findByNpsId(nps._id).count(); + const total = await NpsVote.countByNpsId(nps._id); const votesToSend = await NpsVote.findNotSentByNpsId(nps._id).toArray(); // if there is nothing to sent, check if something gone wrong if (votesToSend.length === 0) { // check if still has votes left to send - const totalSent = await NpsVote.findByNpsIdAndStatus(nps._id, INpsVoteStatus.SENT).count(); + const totalSent = await NpsVote.countByNpsIdAndStatus(nps._id, INpsVoteStatus.SENT); if (totalSent === total) { await Nps.updateStatusById(nps._id, NPSStatus.SENT); return; @@ -130,7 +130,7 @@ export class NPSService extends ServiceClassInternal implements INPSService { await NpsVote.updateVotesToSent(voteIds); } - const totalSent = await NpsVote.findByNpsIdAndStatus(nps._id, INpsVoteStatus.SENT).count(); + const totalSent = await NpsVote.countByNpsIdAndStatus(nps._id, INpsVoteStatus.SENT); if (totalSent < total) { // send more in five minutes setTimeout(() => NPS.sendResults(), 5 * 60 * 1000); diff --git a/apps/meteor/server/services/team/service.ts b/apps/meteor/server/services/team/service.ts index bdca222b246c..85da5cb68a17 100644 --- a/apps/meteor/server/services/team/service.ts +++ b/apps/meteor/server/services/team/service.ts @@ -251,12 +251,10 @@ export class TeamService extends ServiceClassInternal implements ITeamService { const results: ITeamInfo[] = []; for await (const record of records) { - const rooms = Rooms.findByTeamId(record._id); - const users = TeamMember.findByTeamId(record._id); results.push({ ...record, - rooms: await rooms.count(), - numberOfUsers: await users.count(), + rooms: await Rooms.countByTeamId(record._id), + numberOfUsers: await TeamMember.countByTeamId(record._id), }); } @@ -279,12 +277,10 @@ export class TeamService extends ServiceClassInternal implements ITeamService { const results: ITeamInfo[] = []; for await (const record of records) { - const rooms = Rooms.findByTeamId(record._id); - const users = TeamMember.findByTeamId(record._id); results.push({ ...record, - rooms: await rooms.count(), - numberOfUsers: await users.count(), + rooms: await Rooms.countByTeamId(record._id), + numberOfUsers: await TeamMember.countByTeamId(record._id), }); } @@ -797,8 +793,7 @@ export class TeamService extends ServiceClassInternal implements ITeamService { if (existingMember) { if (existingMember.roles?.includes('owner')) { - const owners = TeamMember.findByTeamIdAndRole(team._id, 'owner'); - const totalOwners = await owners.count(); + const totalOwners = await TeamMember.countByTeamIdAndRole(team._id, 'owner'); if (totalOwners === 1) { throw new Error('last-owner-can-not-be-removed'); } @@ -1002,9 +997,9 @@ export class TeamService extends ServiceClassInternal implements ITeamService { async getStatistics(): Promise { return { - totalTeams: await Team.find({}).count(), - totalRoomsInsideTeams: await Rooms.findRoomsInsideTeams().count(), - totalDefaultRoomsInsideTeams: await Rooms.findRoomsInsideTeams(true).count(), + totalTeams: await Team.estimatedDocumentCount(), + totalRoomsInsideTeams: await Rooms.countRoomsInsideTeams(), + totalDefaultRoomsInsideTeams: await Rooms.countRoomsInsideTeams(true), }; } diff --git a/packages/model-typings/src/models/INpsVoteModel.ts b/packages/model-typings/src/models/INpsVoteModel.ts index a40bbef1b786..0b764299fb76 100644 --- a/packages/model-typings/src/models/INpsVoteModel.ts +++ b/packages/model-typings/src/models/INpsVoteModel.ts @@ -10,4 +10,6 @@ export interface INpsVoteModel extends IBaseModel { save(vote: Omit): Promise; updateVotesToSent(voteIds: string[]): Promise; updateOldSendingToNewByNpsId(npsId: string): Promise; + countByNpsId(npsId: string): Promise; + countByNpsIdAndStatus(npsId: string, status: INpsVoteStatus): Promise; } diff --git a/packages/model-typings/src/models/IRoomsModel.ts b/packages/model-typings/src/models/IRoomsModel.ts index 74d2fbe301e9..6419d021887e 100644 --- a/packages/model-typings/src/models/IRoomsModel.ts +++ b/packages/model-typings/src/models/IRoomsModel.ts @@ -49,6 +49,8 @@ export interface IRoomsModel extends IBaseModel { findByTeamId(teamId: ITeam['_id'], options?: FindOptions): FindCursor; + countByTeamId(teamId: ITeam['_id']): Promise; + findPaginatedByTeamIdContainingNameAndDefault( teamId: ITeam['_id'], name: IRoom['name'], @@ -139,6 +141,8 @@ export interface IRoomsModel extends IBaseModel { findRoomsInsideTeams(autoJoin?: boolean): FindCursor; + countRoomsInsideTeams(autoJoin?: boolean): Promise; + findOneDirectRoomContainingAllUserIDs(uid: IDirectMessageRoom['uids'], options?: FindOptions): Promise; countByType(t: IRoom['t']): Promise; diff --git a/packages/model-typings/src/models/ITeamMemberModel.ts b/packages/model-typings/src/models/ITeamMemberModel.ts index 46093c05458f..0a4264c9579a 100644 --- a/packages/model-typings/src/models/ITeamMemberModel.ts +++ b/packages/model-typings/src/models/ITeamMemberModel.ts @@ -38,6 +38,7 @@ export interface ITeamMemberModel extends IBaseModel { options?: undefined | FindOptions | FindOptions

, ): FindCursor

| FindCursor; + countByTeamId(teamId: string): Promise; findByTeamIds(teamIds: Array): FindCursor; findByTeamIds(teamIds: Array, options: FindOptions): FindCursor; @@ -61,6 +62,8 @@ export interface ITeamMemberModel extends IBaseModel { options?: undefined | FindOptions | FindOptions

, ): FindCursor

| FindCursor; + countByTeamIdAndRole(teamId: string, role: IRole['_id']): Promise; + findByUserIdAndTeamIds(userId: string, teamIds: Array, options?: FindOptions): FindCursor; findPaginatedMembersInfoByTeamId(