Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor: Improve Omnichannel queries #29711

Merged
merged 6 commits into from
Aug 25, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion apps/meteor/app/apps/server/converters/rooms.js
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ export class AppRoomsConverter {

let departmentId;
if (room.department) {
const department = await LivechatDepartment.findOneById(room.department.id);
const department = await LivechatDepartment.findOneById(room.department.id, { projection: { _id: 1 } });
departmentId = department._id;
}

Expand Down
2 changes: 1 addition & 1 deletion apps/meteor/app/e2e/server/methods/setRoomKeyID.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ Meteor.methods<ServerMethods>({
throw new Meteor.Error('error-invalid-room', 'Invalid room', { method: 'e2e.setRoomKeyID' });
}

const room = await Rooms.findOneById(rid, { fields: { e2eKeyId: 1 } });
const room = await Rooms.findOneById<Pick<IRoom, '_id' | 'e2eKeyId'>>(rid, { projection: { e2eKeyId: 1 } });

if (!room) {
throw new Meteor.Error('error-invalid-room', 'Invalid room', { method: 'e2e.setRoomKeyID' });
Expand Down
4 changes: 2 additions & 2 deletions apps/meteor/app/livechat/server/api/lib/departments.ts
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,7 @@ export async function findDepartmentById({
department: await LivechatDepartment.findOne(query),
...(includeAgents &&
canViewLivechatDepartments && {
agents: await LivechatDepartmentAgents.find({ departmentId }).toArray(),
agents: await LivechatDepartmentAgents.findByDepartmentId(departmentId).toArray(),
}),
};

Expand Down Expand Up @@ -192,6 +192,6 @@ export async function findDepartmentsBetweenIds({
ids: string[];
fields: Record<string, unknown>;
}): Promise<{ departments: ILivechatDepartment[] }> {
const departments = await LivechatDepartment.findInIds(ids, fields).toArray();
const departments = await LivechatDepartment.findInIds(ids, { projection: fields }).toArray();
return { departments };
}
6 changes: 4 additions & 2 deletions apps/meteor/app/livechat/server/api/lib/inquiries.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,10 @@ import { getOmniChatSortQuery } from '../../../lib/inquiries';
import { getInquirySortMechanismSetting } from '../../lib/settings';

const agentDepartments = async (userId: IUser['_id']): Promise<string[]> => {
const agentDepartments = (await LivechatDepartmentAgents.findByAgentId(userId).toArray()).map(({ departmentId }) => departmentId);
return (await LivechatDepartment.find({ _id: { $in: agentDepartments }, enabled: true }).toArray()).map(({ _id }) => _id);
const agentDepartments = (await LivechatDepartmentAgents.findByAgentId(userId, { projection: { departmentId: 1 } }).toArray()).map(
({ departmentId }) => departmentId,
);
return (await LivechatDepartment.findEnabledInIds(agentDepartments, { projection: { _id: 1 } }).toArray()).map(({ _id }) => _id);
};

const applyDepartmentRestrictions = async (
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import type { IOmnichannelRoom, IMessage, IBusinessHourWorkHour } from '@rocket.chat/core-typings';
import type { IOmnichannelRoom, IMessage, IBusinessHourWorkHour, ILivechatDepartment } from '@rocket.chat/core-typings';
import { isOmnichannelRoom } from '@rocket.chat/core-typings';
import { LivechatBusinessHours, LivechatDepartment, Messages, LivechatRooms } from '@rocket.chat/models';
import moment from 'moment';
Expand Down Expand Up @@ -27,7 +27,11 @@ const getSecondsSinceLastAgentResponse = async (room: IOmnichannelRoom, agentLas
return getSecondsWhenOfficeHoursIsDisabled(room, agentLastMessage);
}
let officeDays;
const department = room.departmentId ? await LivechatDepartment.findOneById(room.departmentId) : null;
const department = room.departmentId
? await LivechatDepartment.findOneById<Pick<ILivechatDepartment, 'businessHourId'>>(room.departmentId, {
projection: { businessHourId: 1 },
})
: null;
if (department?.businessHourId) {
const businessHour = await LivechatBusinessHours.findOneById(department.businessHourId);
if (!businessHour) {
Expand Down
6 changes: 5 additions & 1 deletion apps/meteor/app/livechat/server/lib/Departments.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import type { ILivechatDepartmentAgents } from '@rocket.chat/core-typings';
import { Logger } from '@rocket.chat/logger';
import { LivechatDepartment, LivechatDepartmentAgents, LivechatRooms } from '@rocket.chat/models';

Expand All @@ -24,7 +25,10 @@ class DepartmentHelperClass {
}
this.logger.debug(`Department record removed: ${_id}`);

const agentsIds: string[] = await LivechatDepartmentAgents.findAgentsByDepartmentId(department._id)
const agentsIds: string[] = await LivechatDepartmentAgents.findAgentsByDepartmentId<Pick<ILivechatDepartmentAgents, 'agentId'>>(
department._id,
{ projection: { agentId: 1 } },
)
.cursor.map((agent) => agent.agentId)
.toArray();

Expand Down
11 changes: 9 additions & 2 deletions apps/meteor/app/livechat/server/lib/Helper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import type {
ILivechatDepartmentAgents,
TransferByData,
ILivechatAgent,
ILivechatDepartment,
} from '@rocket.chat/core-typings';
import { LivechatInquiryStatus, OmnichannelSourceType, DEFAULT_SLA_CONFIG, UserStatus } from '@rocket.chat/core-typings';
import { LivechatPriorityWeight } from '@rocket.chat/core-typings/src/ILivechatPriority';
Expand Down Expand Up @@ -519,7 +520,9 @@ export const forwardRoomToDepartment = async (room: IOmnichannelRoom, guest: ILi
if (!user) {
throw new Error('error-user-is-offline');
}
const isInDepartment = await LivechatDepartmentAgents.findOneByAgentIdAndDepartmentId(agentId, departmentId);
const isInDepartment = await LivechatDepartmentAgents.findOneByAgentIdAndDepartmentId(agentId, departmentId, {
projection: { _id: 1 },
});
if (!isInDepartment) {
throw new Error('error-user-not-belong-to-department');
}
Expand Down Expand Up @@ -549,7 +552,11 @@ export const forwardRoomToDepartment = async (room: IOmnichannelRoom, guest: ILi

const { servedBy, chatQueued } = roomTaken;
if (!chatQueued && oldServedBy && servedBy && oldServedBy._id === servedBy._id) {
const department = departmentId ? await LivechatDepartment.findOneById(departmentId) : null;
const department = departmentId
? await LivechatDepartment.findOneById<Pick<ILivechatDepartment, '_id' | 'fallbackForwardDepartment'>>(departmentId, {
projection: { fallbackForwardDepartment: 1 },
})
: null;
if (!department?.fallbackForwardDepartment?.length) {
logger.debug(`Cannot forward room ${room._id}. Chat assigned to agent ${servedBy._id} (Previous was ${oldServedBy._id})`);
throw new Error('error-no-agents-online-in-department');
Expand Down
4 changes: 3 additions & 1 deletion apps/meteor/app/livechat/server/lib/Livechat.js
Original file line number Diff line number Diff line change
Expand Up @@ -703,7 +703,9 @@ export const Livechat = {
});
}
const ret = (await LivechatDepartmentRaw.removeById(_id)).deletedCount;
const agentsIds = (await LivechatDepartmentAgents.findByDepartmentId(_id).toArray()).map((agent) => agent.agentId);
const agentsIds = (await LivechatDepartmentAgents.findByDepartmentId(_id, { projection: { agentId: 1 } }).toArray()).map(
(agent) => agent.agentId,
);
await LivechatDepartmentAgents.removeByDepartmentId(_id);
await LivechatDepartmentRaw.unsetFallbackDepartmentByDepartmentId(_id);
if (ret) {
Expand Down
19 changes: 15 additions & 4 deletions apps/meteor/app/livechat/server/lib/LivechatTyped.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import type {
SelectedAgent,
ILivechatAgent,
IMessage,
ILivechatDepartment,
} from '@rocket.chat/core-typings';
import { UserStatus, isOmnichannelRoom } from '@rocket.chat/core-typings';
import { Logger, type MainLogger } from '@rocket.chat/logger';
Expand Down Expand Up @@ -298,7 +299,10 @@ class LivechatClass {
room = null;
}

if (guest.department && !(await LivechatDepartment.findOneById(guest.department))) {
if (
guest.department &&
!(await LivechatDepartment.findOneById<Pick<ILivechatDepartment, '_id'>>(guest.department, { projection: { _id: 1 } }))
) {
await LivechatVisitors.removeDepartmentById(guest._id);
const tmpGuest = await LivechatVisitors.findOneById(guest._id);
if (tmpGuest) {
Expand Down Expand Up @@ -356,7 +360,9 @@ class LivechatClass {
return onlineForDep;
}

const dep = await LivechatDepartment.findOneById(department);
const dep = await LivechatDepartment.findOneById<Pick<ILivechatDepartment, '_id' | 'fallbackForwardDepartment'>>(department, {
projection: { fallbackForwardDepartment: 1 },
});
if (!dep?.fallbackForwardDepartment) {
return onlineForDep;
}
Expand Down Expand Up @@ -584,7 +590,7 @@ class LivechatClass {

if (department) {
Livechat.logger.debug(`Attempt to find a department with id/name ${department}`);
const dep = await LivechatDepartment.findOneByIdOrName(department);
const dep = await LivechatDepartment.findOneByIdOrName(department, { projection: { _id: 1 } });
if (!dep) {
Livechat.logger.debug('Invalid department provided');
throw new Meteor.Error('error-invalid-department', 'The provided department is invalid');
Expand Down Expand Up @@ -671,7 +677,12 @@ class LivechatClass {
};
}

const department = await LivechatDepartment.findOneById(departmentId);
const department = await LivechatDepartment.findOneById<Pick<ILivechatDepartment, 'requestTagBeforeClosingChat' | 'chatClosingTags'>>(
departmentId,
{
projection: { requestTagBeforeClosingChat: 1, chatClosingTags: 1 },
},
);
if (!department) {
return {
updatedOptions: {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import type { IUser, ILivechatDepartment, IOmnichannelRoom } from '@rocket.chat/core-typings';
import type { IUser, IOmnichannelRoom } from '@rocket.chat/core-typings';
import { LivechatDepartmentAgents, LivechatInquiry, LivechatRooms, LivechatDepartment } from '@rocket.chat/models';

import { hasPermissionAsync } from '../../authorization/server/functions/hasPermission';
Expand Down Expand Up @@ -47,10 +47,10 @@ export const validators: OmnichannelRoomAccessValidator[] = [

let departmentIds;
if (!(await hasRoleAsync(user._id, 'livechat-manager'))) {
const departmentAgents = (await LivechatDepartmentAgents.findByAgentId(user._id).toArray()).map((d) => d.departmentId);
departmentIds = (await LivechatDepartment.find({ _id: { $in: departmentAgents }, enabled: true }).toArray()).map(
(d: ILivechatDepartment) => d._id,
const departmentAgents = (await LivechatDepartmentAgents.findByAgentId(user._id, { projection: { departmentId: 1 } }).toArray()).map(
(d) => d.departmentId,
);
departmentIds = (await LivechatDepartment.findEnabledInIds(departmentAgents, { projection: { _id: 1 } }).toArray()).map((d) => d._id);
}

const filter = {
Expand All @@ -75,7 +75,9 @@ export const validators: OmnichannelRoomAccessValidator[] = [
if (!room.departmentId || room.open || !user?._id) {
return;
}
const agentOfDepartment = await LivechatDepartmentAgents.findOneByAgentIdAndDepartmentId(user._id, room.departmentId);
const agentOfDepartment = await LivechatDepartmentAgents.findOneByAgentIdAndDepartmentId(user._id, room.departmentId, {
projection: { _id: 1 },
});
if (!agentOfDepartment) {
return;
}
Expand Down
8 changes: 4 additions & 4 deletions apps/meteor/app/statistics/server/lib/statistics.ts
Original file line number Diff line number Diff line change
Expand Up @@ -122,11 +122,11 @@ export const statistics = {
statistics.totalThreads = await Messages.countThreads();

// livechat visitors
statistics.totalLivechatVisitors = await LivechatVisitors.col.estimatedDocumentCount();
statistics.totalLivechatVisitors = await LivechatVisitors.estimatedDocumentCount();

// livechat agents
statistics.totalLivechatAgents = await Users.countAgents();
statistics.totalLivechatManagers = await Users.col.countDocuments({ roles: 'livechat-manager' });
statistics.totalLivechatManagers = await Users.countDocuments({ roles: 'livechat-manager' });

// livechat enabled
statistics.livechatEnabled = settings.get('Livechat_enabled');
Expand All @@ -147,14 +147,14 @@ export const statistics = {

// Number of departments
statsPms.push(
LivechatDepartment.col.count().then((count) => {
LivechatDepartment.estimatedDocumentCount().then((count) => {
statistics.departments = count;
}),
);

// Number of archived departments
statsPms.push(
LivechatDepartment.col.countDocuments({ archived: true }).then((count) => {
LivechatDepartment.countArchived().then((count) => {
statistics.archivedDepartments = count;
}),
);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import type { IOmnichannelCannedResponse } from '@rocket.chat/core-typings';
import type { IOmnichannelCannedResponse, ILivechatDepartment } from '@rocket.chat/core-typings';
import { LivechatDepartment, CannedResponse, Users } from '@rocket.chat/models';
import type { ServerMethods } from '@rocket.chat/ui-contexts';
import { Match, check } from 'meteor/check';
Expand Down Expand Up @@ -76,7 +76,10 @@ Meteor.methods<ServerMethods>({
});
}

if (responseData.departmentId && !(await LivechatDepartment.findOneById(responseData.departmentId))) {
if (
responseData.departmentId &&
!(await LivechatDepartment.findOneById<Pick<ILivechatDepartment, '_id'>>(responseData.departmentId, { projection: { _id: 1 } }))
) {
throw new Meteor.Error('error-invalid-department', 'Invalid department', {
method: 'saveCannedResponse',
});
Expand Down
28 changes: 12 additions & 16 deletions apps/meteor/ee/app/license/server/getStatistics.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,37 +55,37 @@ async function getEEStatistics(): Promise<EEOnlyStats | undefined> {

// Number of livechat tags
statsPms.push(
LivechatTag.col.count().then((count) => {
LivechatTag.estimatedDocumentCount().then((count) => {
statistics.livechatTags = count;
return true;
}),
);

// Number of canned responses
statsPms.push(
CannedResponse.col.estimatedDocumentCount().then((count) => {
CannedResponse.estimatedDocumentCount().then((count) => {
statistics.cannedResponses = count;
return true;
}),
);

// Number of Service Level Agreements
statsPms.push(
OmnichannelServiceLevelAgreements.col.count().then((count) => {
OmnichannelServiceLevelAgreements.estimatedDocumentCount().then((count) => {
statistics.slas = count;
return true;
}),
);

statsPms.push(
LivechatRooms.col.countDocuments({ priorityId: { $exists: true } }).then((count) => {
LivechatRooms.countPrioritizedRooms().then((count) => {
statistics.omnichannelRoomsWithPriorities = count;
return true;
}),
);

statsPms.push(
LivechatRooms.col.countDocuments({ slaId: { $exists: true } }).then((count) => {
LivechatRooms.countRoomsWithSla().then((count) => {
statistics.omnichannelRoomsWithSlas = count;
return true;
}),
Expand All @@ -101,28 +101,24 @@ async function getEEStatistics(): Promise<EEOnlyStats | undefined> {

statsPms.push(
// Total livechat monitors
Users.col.countDocuments({ roles: 'livechat-monitor' }).then((count) => {
Users.countByRole('livechat-monitor').then((count) => {
statistics.livechatMonitors = count;
return true;
}),
);

// Number of PDF transcript requested
statsPms.push(
LivechatRooms.find({ pdfTranscriptRequested: { $exists: true } })
.count()
.then((count) => {
statistics.omnichannelPdfTranscriptRequested = count;
}),
LivechatRooms.countRoomsWithPdfTranscriptRequested().then((count) => {
statistics.omnichannelPdfTranscriptRequested = count;
}),
);

// Number of PDF transcript that succeeded
statsPms.push(
LivechatRooms.find({ pdfTranscriptFileId: { $exists: true } })
.count()
.then((count) => {
statistics.omnichannelPdfTranscriptSucceeded = count;
}),
LivechatRooms.countRoomsWithTranscriptSent().then((count) => {
statistics.omnichannelPdfTranscriptSucceeded = count;
}),
);

await Promise.all(statsPms).catch(log);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ export const getDepartmentsWhichUserCanAccess = async (userId: string, includeDi
};

export const hasAccessToDepartment = async (userId: string, departmentId: string): Promise<boolean> => {
const department = await LivechatDepartmentAgents.findOneByAgentIdAndDepartmentId(userId, departmentId);
const department = await LivechatDepartmentAgents.findOneByAgentIdAndDepartmentId(userId, departmentId, { projection: { _id: 1 } });
if (department) {
helperLogger.debug(`User ${userId} has access to department ${departmentId} because they are an agent`);
return true;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import type { ILivechatDepartment } from '@rocket.chat/core-typings';
import { LivechatDepartment } from '@rocket.chat/models';

import { settings } from '../../../../../app/settings/server';
Expand All @@ -8,7 +9,12 @@ callbacks.add(
'livechat.applySimultaneousChatRestrictions',
async (_: any, { departmentId }: { departmentId?: string } = {}) => {
if (departmentId) {
const departmentLimit = (await LivechatDepartment.findOneById(departmentId))?.maxNumberSimultaneousChat || 0;
const departmentLimit =
(
await LivechatDepartment.findOneById<Pick<ILivechatDepartment, 'maxNumberSimultaneousChat'>>(departmentId, {
projection: { maxNumberSimultaneousChats: 1 },
})
)?.maxNumberSimultaneousChat || 0;
if (departmentLimit > 0) {
cbLogger.debug(`Applying department filters. Max chats per department ${departmentLimit}`);
return { $match: { 'queueInfo.chats': { $gte: Number(departmentLimit) } } };
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import type { ILivechatDepartment } from '@rocket.chat/core-typings';
import { LivechatDepartment } from '@rocket.chat/models';
import { Meteor } from 'meteor/meteor';

Expand All @@ -18,7 +19,9 @@ callbacks.add(
return options;
}
const { department: departmentToTransfer } = transferData;
const currentDepartment = await LivechatDepartment.findOneById(departmentId);
const currentDepartment = await LivechatDepartment.findOneById<Pick<ILivechatDepartment, 'departmentsAllowedToForward'>>(departmentId, {
projection: { departmentsAllowedToForward: 1 },
});
if (!currentDepartment) {
cbLogger.debug('Skipping callback. Current department does not exists');
return options;
Expand Down
Loading
Loading