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

feat: Improve Engagement Dashboard's "Channels" tab performance #32493

Merged
merged 33 commits into from
Jul 19, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
d6bf987
feat: Improve engagement-dashboard/channels/list endpoint performance
matheusbsilva137 May 24, 2024
af24241
Create changeset
matheusbsilva137 May 24, 2024
7c2fc0b
Apply suggestions from code review
matheusbsilva137 May 24, 2024
a50744a
Apply suggestions from code review
KevLehman May 24, 2024
6a633fb
test: Add end-to-end tests
matheusbsilva137 May 27, 2024
0d05e78
fix typecheck
matheusbsilva137 May 27, 2024
01a5144
test: fix default voip direction
matheusbsilva137 May 27, 2024
309ac03
improve: Add room type check to aggregation pipeline
matheusbsilva137 May 29, 2024
96effb6
Merge branch 'develop' into feat/improve-engagement-dashboard-chanels
debdutdeb Jun 5, 2024
015775c
improve: Added hideRoomsWithNoActivity param
matheusbsilva137 Jun 6, 2024
2786a7f
Merge branch 'feat/improve-engagement-dashboard-chanels' of https://g…
matheusbsilva137 Jun 6, 2024
45653ba
Update changeset
matheusbsilva137 Jun 6, 2024
1142b9d
Create changeset
matheusbsilva137 Jun 6, 2024
3e13306
fix end-to-end test
matheusbsilva137 Jun 6, 2024
e1fbce2
Merge branch 'feat/improve-engagement-dashboard-chanels' of https://g…
matheusbsilva137 Jun 6, 2024
c74186a
Merge branch 'develop' of https://github.com/RocketChat/Rocket.Chat i…
matheusbsilva137 Jun 6, 2024
3ba5355
deprecate new hideRoomsWithNoActivity param
matheusbsilva137 Jun 10, 2024
450ec1a
Update apps/meteor/tests/end-to-end/api/34-engagement-dashboard.ts
matheusbsilva137 Jun 11, 2024
787d0ff
Improve indexes
matheusbsilva137 Jun 11, 2024
d832107
Remove index fix (to be added in another PR)
matheusbsilva137 Jun 12, 2024
68d6774
improve deprecation message
matheusbsilva137 Jun 17, 2024
60b8c4e
Merge branch 'develop' of https://github.com/RocketChat/Rocket.Chat i…
matheusbsilva137 Jun 17, 2024
dfd1c57
Merge branch 'develop' of https://github.com/RocketChat/Rocket.Chat i…
matheusbsilva137 Jun 18, 2024
28ff4d9
only display the deprecation message when param is sent
matheusbsilva137 Jun 21, 2024
c405e02
Always display deprecation message
matheusbsilva137 Jul 16, 2024
752265a
Merge branch 'develop' of https://github.com/RocketChat/Rocket.Chat i…
matheusbsilva137 Jul 16, 2024
29f8118
update imports (converted to TS)
matheusbsilva137 Jul 16, 2024
5b5e9dd
fix lint
matheusbsilva137 Jul 16, 2024
7aea864
fix: handle case for when there are no messages analytics in the prov…
matheusbsilva137 Jul 18, 2024
ea33446
replace count by findOne
matheusbsilva137 Jul 18, 2024
5cebc20
fix: do not extract data if aggregation returns an empty result
matheusbsilva137 Jul 18, 2024
fc7fbfb
Merge remote-tracking branch 'origin/develop' into feat/improve-engag…
sampaiodiego Jul 19, 2024
f3ca6f0
chore: show deprecation only without param
sampaiodiego Jul 19, 2024
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
6 changes: 6 additions & 0 deletions .changeset/many-tables-love.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
KevLehman marked this conversation as resolved.
Show resolved Hide resolved
"@rocket.chat/meteor": minor
"@rocket.chat/model-typings": minor
---

Fixed Livechat rooms being displayed in the Engagement Dashboard's "Channels" tab
6 changes: 6 additions & 0 deletions .changeset/proud-waves-bathe.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
"@rocket.chat/meteor": minor
"@rocket.chat/model-typings": minor
---

Improved Engagement Dashboard's "Channels" tab performance by not returning rooms that had no activity in the analyzed period
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ export const useChannelsList = ({ period, offset, count }: UseChannelsListOption
end: end.toISOString(),
offset,
count,
hideRoomsWithNoActivity: true,
});

return response
Expand Down
22 changes: 18 additions & 4 deletions apps/meteor/ee/server/api/engagementDashboard/channels.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,15 @@ import { check, Match } from 'meteor/check';

import { API } from '../../../../app/api/server';
import { getPaginationItems } from '../../../../app/api/server/helpers/getPaginationItems';
import { findAllChannelsWithNumberOfMessages } from '../../lib/engagementDashboard/channels';
import { apiDeprecationLogger } from '../../../../app/lib/server/lib/deprecationWarningLogger';
import { findChannelsWithNumberOfMessages } from '../../lib/engagementDashboard/channels';
import { isDateISOString, mapDateForAPI } from '../../lib/engagementDashboard/date';

declare module '@rocket.chat/rest-typings' {
// eslint-disable-next-line @typescript-eslint/naming-convention
interface Endpoints {
'/v1/engagement-dashboard/channels/list': {
GET: (params: { start: string; end: string; offset?: number; count?: number }) => {
GET: (params: { start: string; end: string; offset?: number; count?: number; hideRoomsWithNoActivity?: boolean }) => {
channels: {
room: {
_id: IRoom['_id'];
Expand Down Expand Up @@ -45,17 +46,30 @@ API.v1.addRoute(
Match.ObjectIncluding({
start: Match.Where(isDateISOString),
end: Match.Where(isDateISOString),
hideRoomsWithNoActivity: Match.Maybe(String),
offset: Match.Maybe(String),
count: Match.Maybe(String),
}),
);

const { start, end } = this.queryParams;
const { start, end, hideRoomsWithNoActivity } = this.queryParams;
const { offset, count } = await getPaginationItems(this.queryParams);

const { channels, total } = await findAllChannelsWithNumberOfMessages({
if (hideRoomsWithNoActivity === undefined) {
apiDeprecationLogger.deprecatedParameterUsage(
this.request.route,
'hideRoomsWithNoActivity',
'7.0.0',
this.response,
({ parameter, endpoint, version }) =>
`Returning rooms that had no activity in ${endpoint} is deprecated and will be removed on version ${version} along with the \`${parameter}\` param. Set \`${parameter}\` as \`true\` to check how the endpoint will behave starting on ${version}`,
);
}

const { channels, total } = await findChannelsWithNumberOfMessages({
start: mapDateForAPI(start),
end: mapDateForAPI(end),
hideRoomsWithNoActivity: hideRoomsWithNoActivity === 'true',
options: { offset, count },
});

Expand Down
76 changes: 65 additions & 11 deletions apps/meteor/ee/server/lib/engagementDashboard/channels.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,69 @@
import type { IDirectMessageRoom, IRoom } from '@rocket.chat/core-typings';
import { Rooms } from '@rocket.chat/models';
import { Analytics, Rooms } from '@rocket.chat/models';
import moment from 'moment';

import { roomCoordinator } from '../../../../server/lib/rooms/roomCoordinator';
import { convertDateToInt, diffBetweenDaysInclusive } from './date';

export const findChannelsWithNumberOfMessages = async ({
start,
end,
hideRoomsWithNoActivity,
options = {},
}: {
start: Date;
end: Date;
hideRoomsWithNoActivity: boolean;
options: {
offset?: number;
count?: number;
};
}): Promise<{
channels: {
room: {
_id: IRoom['_id'];
name: IRoom['name'] | IRoom['fname'];
ts: IRoom['ts'];
t: IRoom['t'];
_updatedAt: IRoom['_updatedAt'];
usernames?: IDirectMessageRoom['usernames'];
};
messages: number;
lastWeekMessages: number;
diffFromLastWeek: number;
}[];
total: number;
}> => {
if (!hideRoomsWithNoActivity) {
return findAllChannelsWithNumberOfMessages({ start, end, options });
}

const daysBetweenDates = diffBetweenDaysInclusive(end, start);
const endOfLastWeek = moment(start).subtract(1, 'days').toDate();
const startOfLastWeek = moment(endOfLastWeek).subtract(daysBetweenDates, 'days').toDate();
const roomTypes = roomCoordinator.getTypesToShowOnDashboard() as Array<IRoom['t']>;

const aggregationResult = await Analytics.findRoomsByTypesWithNumberOfMessagesBetweenDate({
types: roomTypes,
start: convertDateToInt(start),
end: convertDateToInt(end),
startOfLastWeek: convertDateToInt(startOfLastWeek),
endOfLastWeek: convertDateToInt(endOfLastWeek),
options,
}).toArray();

// The aggregation result may be undefined if there are no matching analytics or corresponding rooms in the period
if (!aggregationResult.length) {
return { channels: [], total: 0 };
}

const [{ channels, total }] = aggregationResult;
return {
channels,
total,
};
};

export const findAllChannelsWithNumberOfMessages = async ({
start,
end,
Expand Down Expand Up @@ -34,24 +94,18 @@ export const findAllChannelsWithNumberOfMessages = async ({
const daysBetweenDates = diffBetweenDaysInclusive(end, start);
const endOfLastWeek = moment(start).subtract(1, 'days').toDate();
const startOfLastWeek = moment(endOfLastWeek).subtract(daysBetweenDates, 'days').toDate();
const roomTypes = roomCoordinator.getTypesToShowOnDashboard() as Array<IRoom['t']>;

const channels = await Rooms.findChannelsWithNumberOfMessagesBetweenDate({
const channels = await Rooms.findChannelsByTypesWithNumberOfMessagesBetweenDate({
types: roomTypes,
start: convertDateToInt(start),
end: convertDateToInt(end),
startOfLastWeek: convertDateToInt(startOfLastWeek),
endOfLastWeek: convertDateToInt(endOfLastWeek),
options,
}).toArray();

const total =
(
await Rooms.countChannelsWithNumberOfMessagesBetweenDate({
start: convertDateToInt(start),
end: convertDateToInt(end),
startOfLastWeek: convertDateToInt(startOfLastWeek),
endOfLastWeek: convertDateToInt(endOfLastWeek),
}).toArray()
)[0]?.total ?? 0;
const total = await Rooms.countDocuments({ t: { $in: roomTypes } });
MarcosSpessatto marked this conversation as resolved.
Show resolved Hide resolved

return {
channels,
Expand Down
123 changes: 120 additions & 3 deletions apps/meteor/server/models/raw/Analytics.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import type { IAnalytic, IRoom } from '@rocket.chat/core-typings';
import type { IAnalyticsModel } from '@rocket.chat/model-typings';
import type { IAnalyticsModel, IChannelsWithNumberOfMessagesBetweenDate } from '@rocket.chat/model-typings';
import { Random } from '@rocket.chat/random';
import type { AggregationCursor, FindCursor, Db, IndexDescription, FindOptions, UpdateResult, Document } from 'mongodb';
import type { AggregationCursor, FindCursor, Db, IndexDescription, FindOptions, UpdateResult, Document, Collection } from 'mongodb';

import { readSecondaryPreferred } from '../../database/readSecondaryPreferred';
import { BaseRaw } from './BaseRaw';
Expand All @@ -14,7 +14,11 @@ export class AnalyticsRaw extends BaseRaw<IAnalytic> implements IAnalyticsModel
}

protected modelIndexes(): IndexDescription[] {
return [{ key: { date: 1 } }, { key: { 'room._id': 1, 'date': 1 }, unique: true, partialFilterExpression: { type: 'rooms' } }];
return [
{ key: { date: 1 } },
{ key: { 'room._id': 1, 'date': 1 }, unique: true, partialFilterExpression: { type: 'rooms' } },
{ key: { 'room.t': 1, 'date': 1 }, partialFilterExpression: { type: 'messages' } },
];
}

saveMessageSent({ room, date }: { room: IRoom; date: IAnalytic['date'] }): Promise<Document | UpdateResult> {
Expand Down Expand Up @@ -211,4 +215,117 @@ export class AnalyticsRaw extends BaseRaw<IAnalytic> implements IAnalyticsModel
findByTypeBeforeDate({ type, date }: { type: IAnalytic['type']; date: IAnalytic['date'] }): FindCursor<IAnalytic> {
return this.find({ type, date: { $lte: date } });
}

getRoomsWithNumberOfMessagesBetweenDateQuery({
types,
start,
end,
startOfLastWeek,
endOfLastWeek,
options,
}: {
types: Array<IRoom['t']>;
start: number;
end: number;
startOfLastWeek: number;
endOfLastWeek: number;
options?: any;
}) {
const typeAndDateMatch = {
$match: {
'type': 'messages',
'room.t': { $in: types },
'date': { $gte: startOfLastWeek, $lte: end },
},
matheusbsilva137 marked this conversation as resolved.
Show resolved Hide resolved
};
const roomsGroup = {
$group: {
_id: '$room._id',
room: { $first: '$room' },
messages: { $sum: { $cond: [{ $gte: ['$date', start] }, '$messages', 0] } },
lastWeekMessages: { $sum: { $cond: [{ $lte: ['$date', endOfLastWeek] }, '$messages', 0] } },
},
};
const lookup = {
$lookup: {
from: 'rocketchat_room',
localField: '_id',
foreignField: '_id',
as: 'room',
},
};
const roomsUnwind = {
$unwind: {
path: '$room',
preserveNullAndEmptyArrays: false,
},
};
const project = {
$project: {
_id: 0,
room: {
_id: '$room._id',
name: { $ifNull: ['$room.name', '$room.fname'] },
ts: '$room.ts',
t: '$room.t',
_updatedAt: '$room._updatedAt',
usernames: '$room.usernames',
},
messages: '$messages',
lastWeekMessages: '$lastWeekMessages',
diffFromLastWeek: { $subtract: ['$messages', '$lastWeekMessages'] },
},
};

const sort = { $sort: options?.sort || { messages: -1 } };
const sortAndPaginationParams: Exclude<Parameters<Collection<IRoom>['aggregate']>[0], undefined> = [sort];
if (options?.offset) {
sortAndPaginationParams.push({ $skip: options.offset });
}

if (options?.count) {
sortAndPaginationParams.push({ $limit: options.count });
}
const facet = {
$facet: {
channels: [...sortAndPaginationParams],
total: [{ $count: 'total' }],
},
};
const totalUnwind = { $unwind: '$total' };
const totalProject = {
$project: {
channels: '$channels',
total: '$total.total',
},
};

const params: Exclude<Parameters<Collection<IRoom>['aggregate']>[0], undefined> = [
typeAndDateMatch,
roomsGroup,
lookup,
roomsUnwind,
project,
facet,
totalUnwind,
totalProject,
];

return params;
}

findRoomsByTypesWithNumberOfMessagesBetweenDate(params: {
types: Array<IRoom['t']>;
start: number;
end: number;
startOfLastWeek: number;
endOfLastWeek: number;
options?: any;
}): AggregationCursor<{ channels: IChannelsWithNumberOfMessagesBetweenDate[]; total: number }> {
const aggregationParams = this.getRoomsWithNumberOfMessagesBetweenDateQuery(params);
return this.col.aggregate<{ channels: IChannelsWithNumberOfMessagesBetweenDate[]; total: number }>(aggregationParams, {
allowDiskUse: true,
readPreference: readSecondaryPreferred(),
});
}
}
Loading
Loading