Skip to content

Commit

Permalink
chore: Replace cursor.count with countDocuments or `estimatedDocu…
Browse files Browse the repository at this point in the history
…mentCount` (#33693)
  • Loading branch information
KevLehman authored Oct 22, 2024
1 parent dc457ac commit d69be40
Show file tree
Hide file tree
Showing 24 changed files with 111 additions and 52 deletions.
6 changes: 2 additions & 4 deletions apps/meteor/app/api/server/v1/roles.ts
Original file line number Diff line number Diff line change
Expand Up @@ -165,9 +165,7 @@ API.v1.addRoute(
throw new Meteor.Error('error-role-protected', 'Cannot delete a protected role');
}

const existingUsers = await Roles.findUsersInRole(role._id);

if (existingUsers && (await existingUsers.count()) > 0) {
if ((await Roles.countUsersInRole(role._id)) > 0) {
throw new Meteor.Error('error-role-in-use', "Cannot delete role because it's in use");
}

Expand Down Expand Up @@ -217,7 +215,7 @@ API.v1.addRoute(
}

if (role._id === 'admin') {
const adminCount = await (await Roles.findUsersInRole('admin')).count();
const adminCount = await Roles.countUsersInRole('admin');
if (adminCount === 1) {
throw new Meteor.Error('error-admin-required', 'You need to have at least one admin');
}
Expand Down
2 changes: 1 addition & 1 deletion apps/meteor/app/authorization/server/methods/deleteRole.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ Meteor.methods<ServerMethods>({
});
}

const users = await (await Roles.findUsersInRole(role._id)).count();
const users = await Roles.countUsersInRole(role._id);

if (users > 0) {
throw new Meteor.Error('error-role-in-use', "Cannot delete role because it's in use", {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,7 @@ export class PendingAvatarImporter extends Importer {
this.logger.debug('start preparing import operation');
await super.updateProgress(ProgressStep.PREPARING_STARTED);

const users = Users.findAllUsersWithPendingAvatar();
const fileCount = await users.count();
const fileCount = await Users.countAllUsersWithPendingAvatar();

if (fileCount === 0) {
await super.updateProgress(ProgressStep.DONE);
Expand Down
4 changes: 2 additions & 2 deletions apps/meteor/app/lib/server/functions/updateGroupDMsName.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,14 @@ const getName = (members: IUser[]): string => members.map(({ username }) => user

async function getUsersWhoAreInTheSameGroupDMsAs(user: IUser) {
// add all users to single array so we can fetch details from them all at once
const rooms = Rooms.findGroupDMsByUids([user._id], { projection: { uids: 1 } });
if ((await rooms.count()) === 0) {
if ((await Rooms.countGroupDMsByUids([user._id])) === 0) {
return;
}

const userIds = new Set();
const users = new Map();

const rooms = Rooms.findGroupDMsByUids([user._id], { projection: { uids: 1 } });
await rooms.forEach((room) => {
if (!room.uids) {
return;
Expand Down
3 changes: 1 addition & 2 deletions apps/meteor/app/lib/server/methods/leaveRoom.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,8 +45,7 @@ export const leaveRoomMethod = async (user: IUser, rid: string): Promise<void> =

// If user is room owner, check if there are other owners. If there isn't anyone else, warn user to set a new owner.
if (await hasRoleAsync(user._id, 'owner', room._id)) {
const cursor = await Roles.findUsersInRole('owner', room._id);
const numOwners = await cursor.count();
const numOwners = await Roles.countUsersInRole('owner', room._id);
if (numOwners === 1) {
throw new Meteor.Error('error-you-are-last-owner', 'You are the last owner. Please set new owner before leaving the room.', {
method: 'leaveRoom',
Expand Down
10 changes: 5 additions & 5 deletions apps/meteor/app/slashcommands-inviteall/server/server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -64,20 +64,20 @@ function inviteAll<T extends string>(type: T): SlashCommand<T>['callback'] {
return;
}

const cursor = Subscriptions.findByRoomIdWhenUsernameExists(baseChannel._id, {
projection: { 'u.username': 1 },
});

try {
const APIsettings = settings.get<number>('API_User_Limit');
if (!APIsettings) {
return;
}
if ((await cursor.count()) > APIsettings) {
if ((await Subscriptions.countByRoomIdWhenUsernameExists(baseChannel._id)) > APIsettings) {
throw new Meteor.Error('error-user-limit-exceeded', 'User Limit Exceeded', {
method: 'addAllToRoom',
});
}

const cursor = Subscriptions.findByRoomIdWhenUsernameExists(baseChannel._id, {
projection: { 'u.username': 1 },
});
const users = (await cursor.toArray()).map((s: ISubscription) => s.u.username).filter(isTruthy);

if (!targetChannel && ['c', 'p'].indexOf(baseChannel.t) > -1) {
Expand Down
2 changes: 1 addition & 1 deletion apps/meteor/ee/server/configuration/videoConference.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ Meteor.startup(async () => {
}
}

if ((await Subscriptions.findByRoomId(_id).count()) > 10) {
if ((await Subscriptions.countByRoomId(_id)) > 10) {
return false;
}

Expand Down
2 changes: 1 addition & 1 deletion apps/meteor/server/methods/browseChannels.ts
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,7 @@ const getChannelsAndGroups = async (
};
};

const getChannelsCountForTeam = mem((teamId) => Rooms.findByTeamId(teamId, { projection: { _id: 1 } }).count(), {
const getChannelsCountForTeam = mem((teamId) => Rooms.countByTeamId(teamId), {
maxAge: 2000,
});

Expand Down
5 changes: 2 additions & 3 deletions apps/meteor/server/methods/removeRoomOwner.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
import { api, Message, Team } from '@rocket.chat/core-services';
import { isRoomFederated } from '@rocket.chat/core-typings';
import type { ServerMethods } from '@rocket.chat/ddp-client';
import { Subscriptions, Rooms, Users } from '@rocket.chat/models';
import { Subscriptions, Rooms, Users, Roles } from '@rocket.chat/models';
import { check } from 'meteor/check';
import { Meteor } from 'meteor/meteor';

import { getUsersInRole } from '../../app/authorization/server';
import { hasPermissionAsync } from '../../app/authorization/server/functions/hasPermission';
import { notifyOnSubscriptionChangedById } from '../../app/lib/server/lib/notifyListener';
import { settings } from '../../app/settings/server';
Expand Down Expand Up @@ -64,7 +63,7 @@ Meteor.methods<ServerMethods>({
});
}

const numOwners = await (await getUsersInRole('owner', rid)).count();
const numOwners = await Roles.countUsersInRole('owner', rid);

if (numOwners === 1) {
throw new Meteor.Error('error-remove-last-owner', 'This is the last owner. Please set a new owner before removing this one.', {
Expand Down
6 changes: 3 additions & 3 deletions apps/meteor/server/methods/removeUserFromRoom.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,11 @@ import { Apps, AppEvents } from '@rocket.chat/apps';
import { AppsEngineException } from '@rocket.chat/apps-engine/definition/exceptions';
import { Message, Team, Room } from '@rocket.chat/core-services';
import type { ServerMethods } from '@rocket.chat/ddp-client';
import { Subscriptions, Rooms, Users } from '@rocket.chat/models';
import { Subscriptions, Rooms, Users, Roles } from '@rocket.chat/models';
import { Match, check } from 'meteor/check';
import { Meteor } from 'meteor/meteor';

import { canAccessRoomAsync, getUsersInRole } from '../../app/authorization/server';
import { canAccessRoomAsync } from '../../app/authorization/server';
import { hasPermissionAsync } from '../../app/authorization/server/functions/hasPermission';
import { hasRoleAsync } from '../../app/authorization/server/functions/hasRole';
import { notifyOnRoomChanged, notifyOnSubscriptionChanged } from '../../app/lib/server/lib/notifyListener';
Expand Down Expand Up @@ -70,7 +70,7 @@ export const removeUserFromRoomMethod = async (fromId: string, data: { rid: stri
}

if (await hasRoleAsync(removedUser._id, 'owner', room._id)) {
const numOwners = await (await getUsersInRole('owner', room._id)).count();
const numOwners = await Roles.countUsersInRole('owner', room._id);

if (numOwners === 1) {
throw new Meteor.Error('error-you-are-last-owner', 'You are the last owner. Please set new owner before leaving the room.', {
Expand Down
2 changes: 1 addition & 1 deletion apps/meteor/server/methods/requestDataDownload.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ Meteor.methods<ServerMethods>({

const lastOperation = await ExportOperations.findLastOperationByUser(userId, fullExport);
const requestDay = lastOperation ? lastOperation.createdAt : new Date();
const pendingOperationsBeforeMyRequestCount = await ExportOperations.findAllPendingBeforeMyRequest(requestDay).count();
const pendingOperationsBeforeMyRequestCount = await ExportOperations.countAllPendingBeforeMyRequest(requestDay);

if (lastOperation) {
const yesterday = new Date();
Expand Down
9 changes: 9 additions & 0 deletions apps/meteor/server/models/raw/ExportOperations.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,15 @@ export class ExportOperationsRaw extends BaseRaw<IExportOperation> implements IE
return this.find(query);
}

countAllPendingBeforeMyRequest(requestDay: Date): Promise<number> {
const query = {
status: { $nin: ['completed', 'skipped'] },
createdAt: { $lt: requestDay },
};

return this.countDocuments(query);
}

updateOperation(data: IExportOperation): Promise<UpdateResult> {
const update = {
$set: {
Expand Down
16 changes: 16 additions & 0 deletions apps/meteor/server/models/raw/Roles.ts
Original file line number Diff line number Diff line change
Expand Up @@ -254,6 +254,22 @@ export class RolesRaw extends BaseRaw<IRole> implements IRolesModel {
}
}

async countUsersInRole(roleId: IRole['_id'], scope?: IRoom['_id']): Promise<number> {
const role = await this.findOneById<Pick<IRole, '_id' | 'scope'>>(roleId, { projection: { scope: 1 } });

if (!role) {
throw new Error('RolesRaw.countUsersInRole: role not found');
}

switch (role.scope) {
case 'Subscriptions':
return Subscriptions.countUsersInRoles([role._id], scope);
case 'Users':
default:
return Users.countUsersInRoles([role._id]);
}
}

async createWithRandomId(
name: IRole['name'],
scope: IRole['scope'] = 'Users',
Expand Down
7 changes: 7 additions & 0 deletions apps/meteor/server/models/raw/Rooms.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1464,6 +1464,13 @@ export class RoomsRaw extends BaseRaw<IRoom> implements IRoomsModel {
);
}

countGroupDMsByUids(uids: NonNullable<IRoom['uids']>): Promise<number> {
return this.countDocuments({
usersCount: { $gt: 2 },
uids: { $in: uids },
});
}

find1On1ByUserId(userId: IRoom['_id'], options: FindOptions<IRoom> = {}): FindCursor<IRoom> {
return this.find(
{
Expand Down
11 changes: 11 additions & 0 deletions apps/meteor/server/models/raw/Subscriptions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -264,6 +264,17 @@ export class SubscriptionsRaw extends BaseRaw<ISubscription> implements ISubscri
return Users.find<P>({ _id: { $in: users } }, options || {});
}

async countUsersInRoles(roles: IRole['_id'][], rid: IRoom['_id'] | undefined): Promise<number> {
const query = {
roles: { $in: roles },
...(rid && { rid }),
};

// Ideally, the count of subscriptions would be the same (or really similar) to the count in users
// As sub/user/room is a 1:1 relation.
return this.countDocuments(query);
}

addRolesByUserId(uid: IUser['_id'], roles: IRole['_id'][], rid?: IRoom['_id']): Promise<UpdateResult> {
if (!Array.isArray(roles)) {
roles = [roles];
Expand Down
20 changes: 20 additions & 0 deletions apps/meteor/server/models/raw/Users.js
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,16 @@ export class UsersRaw extends BaseRaw {
return this.find(query, options);
}

countUsersInRoles(roles) {
roles = [].concat(roles);

const query = {
roles: { $in: roles },
};

return this.countDocuments(query);
}

findPaginatedUsersInRoles(roles, options) {
roles = [].concat(roles);

Expand Down Expand Up @@ -3074,6 +3084,16 @@ export class UsersRaw extends BaseRaw {
return this.find(query, options);
}

countAllUsersWithPendingAvatar() {
const query = {
_pendingAvatarUrl: {
$exists: true,
},
};

return this.countDocuments(query);
}

updateCustomFieldsById(userId, customFields) {
return this.updateOne(
{ _id: userId },
Expand Down
9 changes: 4 additions & 5 deletions apps/meteor/server/startup/initialData.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
import { Settings, Rooms, Users } from '@rocket.chat/models';
import { Settings, Rooms, Users, Roles } from '@rocket.chat/models';
import colors from 'colors/safe';
import { Accounts } from 'meteor/accounts-base';
import { Meteor } from 'meteor/meteor';

import { getUsersInRole } from '../../app/authorization/server';
import { FileUpload } from '../../app/file-upload/server';
import { RocketChatFile } from '../../app/file/server';
import { addUserToDefaultChannels } from '../../app/lib/server/functions/addUserToDefaultChannels';
Expand All @@ -15,7 +14,7 @@ import { addUserRolesAsync } from '../lib/roles/addUserRoles';

export async function insertAdminUserFromEnv() {
if (process.env.ADMIN_PASS) {
if ((await (await getUsersInRole('admin')).count()) === 0) {
if ((await Roles.countUsersInRole('admin')) === 0) {
const adminUser = {
name: 'Administrator',
username: 'admin',
Expand Down Expand Up @@ -188,7 +187,7 @@ Meteor.startup(async () => {
}
}

if ((await (await getUsersInRole('admin')).count()) === 0) {
if ((await Roles.countUsersInRole('admin')) === 0) {
const oldestUser = await Users.getOldest({ projection: { _id: 1, username: 1, name: 1 } });

if (oldestUser) {
Expand All @@ -197,7 +196,7 @@ Meteor.startup(async () => {
}
}

if ((await (await getUsersInRole('admin')).count()) !== 0) {
if ((await Roles.countUsersInRole('admin')) !== 0) {
if (settings.get('Show_Setup_Wizard') === 'pending') {
console.log('Setting Setup Wizard to "in_progress" because, at least, one admin was found');

Expand Down
Loading

0 comments on commit d69be40

Please sign in to comment.