From 9919e7359a3dbe3c605c072c9d61b81c99138031 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Quentin=20Guid=C3=A9e?= Date: Thu, 29 Feb 2024 07:53:51 -0500 Subject: [PATCH 01/11] performances: Accelerate time listened MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Quentin Guidée --- apps/server/src/database/queries/stats.ts | 17 ++++++----------- 1 file changed, 6 insertions(+), 11 deletions(-) diff --git a/apps/server/src/database/queries/stats.ts b/apps/server/src/database/queries/stats.ts index c43dfd07..ad13eda4 100644 --- a/apps/server/src/database/queries/stats.ts +++ b/apps/server/src/database/queries/stats.ts @@ -152,7 +152,10 @@ export const getSongsPer = async ( const res = await InfosModel.aggregate([ { $match: basicMatch(user._id, start, end) }, { - $project: { ...getGroupByDateProjection(user.settings.timezone), id: 1 }, + $project: { + ...getGroupByDateProjection(user.settings.timezone), + id: 1, + }, }, { $group: { @@ -185,22 +188,14 @@ export const getTimePer = async ( { $project: { ...getGroupByDateProjection(user.settings.timezone), + durationMs: 1, id: 1, }, }, - { - $lookup: { - from: "tracks", - localField: "id", - foreignField: "id", - as: "track", - }, - }, - { $unwind: "$track" }, { $group: { _id: getGroupingByTimeSplit(timeSplit), - count: { $sum: "$track.duration_ms" }, + count: { $sum: "$durationMs" }, }, }, ...sortByTimeSplit(timeSplit, "_id"), From 755cff89aa6cd310555630dd1a0cbd6a38dd36fc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Quentin=20Guid=C3=A9e?= Date: Thu, 29 Feb 2024 08:01:55 -0500 Subject: [PATCH 02/11] performances: Accelerate artists listened MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Quentin Guidée --- apps/server/src/database/queries/stats.ts | 17 +++++------------ 1 file changed, 5 insertions(+), 12 deletions(-) diff --git a/apps/server/src/database/queries/stats.ts b/apps/server/src/database/queries/stats.ts index ad13eda4..4a1e381f 100644 --- a/apps/server/src/database/queries/stats.ts +++ b/apps/server/src/database/queries/stats.ts @@ -407,32 +407,25 @@ export const differentArtistsPer = async ( { $project: { ...getGroupByDateProjection(user.settings.timezone), + primaryArtistId: 1, + durationMs: 1, id: 1, }, }, - { - $lookup: { - from: "tracks", - localField: "id", - foreignField: "id", - as: "track", - }, - }, - { $unwind: "$track" }, { $group: { _id: { ...getGroupingByTimeSplit(timeSplit), - artId: { $arrayElemAt: ["$track.artists", 0] }, + artistId: `$primaryArtistId`, }, count: { $sum: 1 }, }, }, - { $sort: { count: -1, "_id.artId": 1 } }, + { $sort: { count: -1, "_id.artistId": 1 } }, { $lookup: { from: "artists", - localField: "_id.artId", + localField: "_id.artistId", foreignField: "id", as: "artist", }, From af93f143c30a33e8f2c561cf6cd4328debff0e1b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Quentin=20Guid=C3=A9e?= Date: Thu, 29 Feb 2024 08:05:37 -0500 Subject: [PATCH 03/11] performances: Accelerate day repartition MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Quentin Guidée --- apps/server/src/database/queries/stats.ts | 12 ++---------- apps/server/src/database/queries/statsTools.ts | 4 ++-- 2 files changed, 4 insertions(+), 12 deletions(-) diff --git a/apps/server/src/database/queries/stats.ts b/apps/server/src/database/queries/stats.ts index 4a1e381f..995fa6a7 100644 --- a/apps/server/src/database/queries/stats.ts +++ b/apps/server/src/database/queries/stats.ts @@ -451,22 +451,14 @@ export const getDayRepartition = async (user: User, start: Date, end: Date) => { { $project: { ...getGroupByDateProjection(user.settings.timezone), + durationMs: 1, id: 1, }, }, - { - $lookup: { - from: "tracks", - localField: "id", - foreignField: "id", - as: "track", - }, - }, - { $unwind: "$track" }, { $group: { _id: "$hour", - count: { $sum: getTrackSumType(user) }, + count: { $sum: getTrackSumType(user, "$durationMs") }, }, }, { $sort: { _id: 1 } }, diff --git a/apps/server/src/database/queries/statsTools.ts b/apps/server/src/database/queries/statsTools.ts index 94a11648..2f857d2c 100644 --- a/apps/server/src/database/queries/statsTools.ts +++ b/apps/server/src/database/queries/statsTools.ts @@ -131,12 +131,12 @@ export const getGroupByDateProjection = (userTimezone: string | undefined) => ({ }, }); -export const getTrackSumType = (user: User) => { +export const getTrackSumType = (user: User, idField = "$track.duration_ms") => { if (user.settings.metricUsed === "number") { return 1; } if (user.settings.metricUsed === "duration") { - return "$track.duration_ms"; + return idField; } return 1; }; From 7ae6dbbe3ba483bca8fb854357b26b86e53197b4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Quentin=20Guid=C3=A9e?= Date: Thu, 29 Feb 2024 15:47:05 -0500 Subject: [PATCH 04/11] performances: Accelerate artist listening distribution MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Quentin Guidée --- apps/server/src/database/queries/stats.ts | 21 ++++++++------------- 1 file changed, 8 insertions(+), 13 deletions(-) diff --git a/apps/server/src/database/queries/stats.ts b/apps/server/src/database/queries/stats.ts index 995fa6a7..049f39d2 100644 --- a/apps/server/src/database/queries/stats.ts +++ b/apps/server/src/database/queries/stats.ts @@ -86,31 +86,26 @@ export const getMostListenedArtist = async ( const res = await InfosModel.aggregate([ { $match: basicMatch(user._id, start, end) }, { - $project: { ...getGroupByDateProjection(user.settings.timezone), id: 1 }, - }, - { - $lookup: { - from: "tracks", - localField: "id", - foreignField: "id", - as: "track", + $project: { + ...getGroupByDateProjection(user.settings.timezone), + primaryArtistId: 1, + id: 1, }, }, - { $unwind: "$track" }, { $group: { _id: { ...getGroupingByTimeSplit(timeSplit), - art: { $arrayElemAt: ["$track.artists", 0] }, + artistId: "$primaryArtistId", }, - count: { $sum: getTrackSumType(user) }, + count: { $sum: getTrackSumType(user, "$durationMs") }, }, }, - { $sort: { count: -1, "_id.art": 1 } }, + { $sort: { count: -1, "_id.artistId": 1 } }, { $group: { _id: getGroupingByTimeSplit(timeSplit, "_id"), - artists: { $push: "$_id.art" }, + artists: { $push: "$_id.artistId" }, counts: { $push: "$count" }, }, }, From 1464ffe8d650abfc1141ab80aeeae70f4bd1433d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Quentin=20Guid=C3=A9e?= Date: Thu, 29 Feb 2024 23:47:37 -0500 Subject: [PATCH 05/11] refactor: Simplify info basicMatch call MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Quentin Guidée --- apps/server/src/database/queries/stats.ts | 28 +++++++++---------- .../server/src/database/queries/statsTools.ts | 17 +++++++---- 2 files changed, 25 insertions(+), 20 deletions(-) diff --git a/apps/server/src/database/queries/stats.ts b/apps/server/src/database/queries/stats.ts index 049f39d2..0cceee64 100644 --- a/apps/server/src/database/queries/stats.ts +++ b/apps/server/src/database/queries/stats.ts @@ -20,7 +20,7 @@ export const getMostListenedSongs = async ( timeSplit: Timesplit = Timesplit.hour, ) => { const res = await InfosModel.aggregate([ - { $match: basicMatch(user._id, start, end) }, + ...basicMatch(user._id, start, end), { $project: { ...getGroupByDateProjection(user.settings.timezone), id: 1 }, }, @@ -84,7 +84,7 @@ export const getMostListenedArtist = async ( timeSplit = Timesplit.hour, ) => { const res = await InfosModel.aggregate([ - { $match: basicMatch(user._id, start, end) }, + ...basicMatch(user._id, start, end), { $project: { ...getGroupByDateProjection(user.settings.timezone), @@ -145,7 +145,7 @@ export const getSongsPer = async ( timeSplit = Timesplit.day, ) => { const res = await InfosModel.aggregate([ - { $match: basicMatch(user._id, start, end) }, + ...basicMatch(user._id, start, end), { $project: { ...getGroupByDateProjection(user.settings.timezone), @@ -179,7 +179,7 @@ export const getTimePer = async ( timeSplit = Timesplit.day, ) => { const res = await InfosModel.aggregate([ - { $match: basicMatch(user._id, start, end) }, + ...basicMatch(user._id, start, end), { $project: { ...getGroupByDateProjection(user.settings.timezone), @@ -205,7 +205,7 @@ export const albumDateRatio = async ( timeSplit = Timesplit.day, ) => { const res = await InfosModel.aggregate([ - { $match: basicMatch(user._id, start, end) }, + ...basicMatch(user._id, start, end), { $project: { ...getGroupByDateProjection(user.settings.timezone), @@ -263,7 +263,7 @@ export const featRatio = async ( timeSplit: Timesplit, ) => { const res = await InfosModel.aggregate([ - { $match: basicMatch(user._id, start, end) }, + ...basicMatch(user._id, start, end), { $project: { ...getGroupByDateProjection(user.settings.timezone), @@ -356,7 +356,7 @@ export const popularityPer = async ( timeSplit = Timesplit.day, ) => { const res = await InfosModel.aggregate([ - { $match: basicMatch(user._id, start, end) }, + ...basicMatch(user._id, start, end), { $project: { ...getGroupByDateProjection(user.settings.timezone), @@ -398,7 +398,7 @@ export const differentArtistsPer = async ( timeSplit = Timesplit.day, ) => { const res = await InfosModel.aggregate([ - { $match: basicMatch(user._id, start, end) }, + ...basicMatch(user._id, start, end), { $project: { ...getGroupByDateProjection(user.settings.timezone), @@ -442,7 +442,7 @@ export const differentArtistsPer = async ( export const getDayRepartition = async (user: User, start: Date, end: Date) => { const res = await InfosModel.aggregate([ - { $match: basicMatch(user._id, start, end) }, + ...basicMatch(user._id, start, end), { $project: { ...getGroupByDateProjection(user.settings.timezone), @@ -468,7 +468,7 @@ export const getBestArtistsPer = async ( timeSplit = Timesplit.day, ) => { const res = await InfosModel.aggregate([ - { $match: basicMatch(user._id, start, end) }, + ...basicMatch(user._id, start, end), { $project: { ...getGroupByDateProjection(user.settings.timezone), id: 1 }, }, @@ -553,7 +553,7 @@ export const getBestAlbumsNbOffseted = ( export const getBestSongsOfHour = (user: User, start: Date, end: Date) => { return InfosModel.aggregate([ - { $match: basicMatch(user._id, start, end) }, + ...basicMatch(user._id, start, end), { $addFields: { hour: getGroupByDateProjection(user.settings.timezone).hour, @@ -597,7 +597,7 @@ export const getBestSongsOfHour = (user: User, start: Date, end: Date) => { export const getBestAlbumsOfHour = (user: User, start: Date, end: Date) => { return InfosModel.aggregate([ - { $match: basicMatch(user._id, start, end) }, + ...basicMatch(user._id, start, end), { $addFields: { hour: getGroupByDateProjection(user.settings.timezone).hour, @@ -645,7 +645,7 @@ export const getBestAlbumsOfHour = (user: User, start: Date, end: Date) => { export const getBestArtistsOfHour = (user: User, start: Date, end: Date) => { return InfosModel.aggregate([ - { $match: basicMatch(user._id, start, end) }, + ...basicMatch(user._id, start, end), { $addFields: { hour: getGroupByDateProjection(user.settings.timezone).hour, @@ -710,7 +710,7 @@ export const getLongestListeningSession = ( const item = { subtract, info: "$$this" }; return InfosModel.aggregate([ - { $match: basicMatch(userId, start, end) }, + ...basicMatch(userId, start, end), { $sort: { played_at: 1 } }, { $lookup: lightTrackLookupPipeline() }, { $unwind: "$track" }, diff --git a/apps/server/src/database/queries/statsTools.ts b/apps/server/src/database/queries/statsTools.ts index 2f857d2c..03732e2e 100644 --- a/apps/server/src/database/queries/statsTools.ts +++ b/apps/server/src/database/queries/statsTools.ts @@ -8,11 +8,16 @@ export const basicMatch = ( userId: string | Types.ObjectId, start: Date, end: Date, -) => ({ - owner: userId instanceof Types.ObjectId ? userId : new Types.ObjectId(userId), - blacklistedBy: { $exists: 0 }, - played_at: { $gt: start, $lt: end }, -}); +) => [ + { + $match: { + owner: + userId instanceof Types.ObjectId ? userId : new Types.ObjectId(userId), + blacklistedBy: { $exists: 0 }, + played_at: { $gt: start, $lt: end }, + }, + }, +]; export const basicMatchUsers = ( userIds: string[] | Types.ObjectId[], @@ -193,7 +198,7 @@ export const getBestInfos = ( offset: number, ) => InfosModel.aggregate([ - { $match: basicMatch(user._id, start, end) }, + ...basicMatch(user._id, start, end), { $group: { _id: `$${idField}`, From 33c3cfca9a69a066dc6caa269d33316e351d9eea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Quentin=20Guid=C3=A9e?= Date: Fri, 1 Mar 2024 10:20:47 -0500 Subject: [PATCH 06/11] performances: Accelerate and refactor getRankOf MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Quentin Guidée --- apps/server/src/database/queries/album.ts | 105 ++++++--------------- apps/server/src/database/queries/artist.ts | 67 +++---------- apps/server/src/database/queries/stats.ts | 56 +++++++++++ apps/server/src/database/queries/track.ts | 43 --------- apps/server/src/routes/album.ts | 40 ++++---- apps/server/src/routes/artist.ts | 23 +++-- apps/server/src/routes/track.ts | 5 +- 7 files changed, 138 insertions(+), 201 deletions(-) diff --git a/apps/server/src/database/queries/album.ts b/apps/server/src/database/queries/album.ts index 0133a61d..ab2f8045 100644 --- a/apps/server/src/database/queries/album.ts +++ b/apps/server/src/database/queries/album.ts @@ -1,38 +1,41 @@ -import { AlbumModel, InfosModel } from '../Models'; -import { User } from '../schemas/user'; +import { AlbumModel, InfosModel } from "../Models"; +import { User } from "../schemas/user"; export const getAlbums = (albumsId: string[]) => AlbumModel.find({ id: { $in: albumsId } }); export const searchAlbum = (str: string) => - AlbumModel.find({ name: { $regex: new RegExp(str, 'i') } }); + AlbumModel.find({ name: { $regex: new RegExp(str, "i") } }); export const getAlbumInfos = (albumId: string) => [ { $lookup: { - from: 'tracks', - let: { targetId: '$id' }, + from: "tracks", + let: { targetId: "$id" }, pipeline: [ { $match: { $expr: { $and: [ - { $eq: ['$album', albumId] }, - { $eq: ['$id', '$$targetId'] }, + { $eq: ["$album", albumId] }, + { $eq: ["$id", "$$targetId"] }, ], }, }, }, - { $project: { trackId: '$id', albumId: '$album' } }, + { $project: { trackId: "$id", albumId: "$album" } }, ], - as: 'albumInfos', - } + as: "albumInfos", + }, }, - { $match: { 'albumInfos.albumId': { $exists: true } } }, - { $unwind: '$albumInfos' } -] + { $match: { "albumInfos.albumId": { $exists: true } } }, + { $unwind: "$albumInfos" }, +]; -export const getFirstAndLastListenedAlbum = async (user: User, albumId: string) => { +export const getFirstAndLastListenedAlbum = async ( + user: User, + albumId: string, +) => { const res = await InfosModel.aggregate([ { $match: { owner: user._id } }, ...getAlbumInfos(albumId), @@ -40,88 +43,42 @@ export const getFirstAndLastListenedAlbum = async (user: User, albumId: string) { $group: { _id: null, - first: { $first: '$$ROOT' }, - last: { $last: '$$ROOT' }, + first: { $first: "$$ROOT" }, + last: { $last: "$$ROOT" }, }, }, - ...['first', 'last'] + ...["first", "last"] .map(e => [ { $lookup: { - from: 'tracks', + from: "tracks", localField: `${e}.albumInfos.trackId`, - foreignField: 'id', + foreignField: "id", as: `${e}.track`, }, }, - { $unwind: `$${e}.track` } + { $unwind: `$${e}.track` }, ]) .flat(1), ]); return res[0]; -} +}; export const getAlbumSongs = async (user: User, albumId: string) => { const res = await InfosModel.aggregate([ { $match: { owner: user._id } }, ...getAlbumInfos(albumId), - { $group: { _id: '$id', count: { $sum: 1 } } }, + { $group: { _id: "$id", count: { $sum: 1 } } }, { $sort: { count: -1 } }, { $lookup: { - from: 'tracks', - localField: '_id', - foreignField: 'id', - as: 'track', + from: "tracks", + localField: "_id", + foreignField: "id", + as: "track", }, }, - { $unwind: '$track' }, + { $unwind: "$track" }, ]); return res; -} - -export const getRankOfAlbum = async (user: User, albumId: string) => { - const res = await InfosModel.aggregate([ - { $match: { owner: user._id } }, - { $lookup: { - from: 'tracks', - localField: 'id', - foreignField: 'id', - as: 'track', - }}, - { $unwind: '$track' }, - { $group: { - _id: '$track.album', - count: { $sum: 1 } - }}, - { $sort: { count: -1, _id: 1 } }, - { $group: { _id: 1, array: { $push: { id: '$_id', count: '$count' } } } }, - { - $project: { - index: { - $indexOfArray: ['$array.id', albumId], - }, - array: 1, - }, - }, - { - $project: { - index: 1, - isMax: { - $cond: { if: { $eq: ['$index', 0] }, then: true, else: false }, - }, - isMin: { - $cond: { - if: { $eq: ['$index', { $subtract: [{ $size: '$array' }, 1] }] }, - then: true, - else: false, - }, - }, - results: { - $slice: ['$array', { $max: [{ $subtract: ['$index', 1] }, 0] }, 3], - }, - }, - } - ]); - return res[0]; -} \ No newline at end of file +}; diff --git a/apps/server/src/database/queries/artist.ts b/apps/server/src/database/queries/artist.ts index cc387e77..f9b2619d 100644 --- a/apps/server/src/database/queries/artist.ts +++ b/apps/server/src/database/queries/artist.ts @@ -166,36 +166,6 @@ export const getMostListenedAlbumOfArtist = async ( user: User, artistId: string, ) => { - const res = await InfosModel.aggregate([ - { $match: { owner: user._id } }, - { $lookup: { - from: 'tracks', - localField: 'id', - foreignField: 'id', - as: 'track' - }}, - { $match: { 'track.artists.0': artistId } }, - { $group: { - _id: '$track.album', - count: { $sum: 1 } - }}, - { $sort: { count: -1 } }, - { - $lookup: { - from: 'albums', - localField: '_id', - foreignField: 'id', - as: 'album', - }, - }, - { $unwind: '$album' }, - { $limit: 10} - ]); - return res; -}; - -export const getRankOfArtist = async (user: User, artistId: string) => { - // Non sense to compute blacklist here const res = await InfosModel.aggregate([ { $match: { owner: user._id } }, { @@ -206,41 +176,26 @@ export const getRankOfArtist = async (user: User, artistId: string) => { as: "track", }, }, - { $unwind: "$track" }, + { $match: { "track.artists.0": artistId } }, { $group: { - _id: { $arrayElemAt: ["$track.artists", 0] }, + _id: "$track.album", count: { $sum: 1 }, }, }, - { $sort: { count: -1, _id: 1 } }, - { $group: { _id: 1, array: { $push: { id: "$_id", count: "$count" } } } }, - { - $project: { - index: { $indexOfArray: ["$array.id", artistId] }, - array: 1, - }, - }, + { $sort: { count: -1 } }, { - $project: { - index: 1, - isMax: { - $cond: { if: { $eq: ["$index", 0] }, then: true, else: false }, - }, - isMin: { - $cond: { - if: { $eq: ["$index", { $subtract: [{ $size: "$array" }, 1] }] }, - then: true, - else: false, - }, - }, - results: { - $slice: ["$array", { $max: [{ $subtract: ["$index", 1] }, 0] }, 3], - }, + $lookup: { + from: "albums", + localField: "_id", + foreignField: "id", + as: "album", }, }, + { $unwind: "$album" }, + { $limit: 10 }, ]); - return res[0]; + return res; }; export const getDayRepartitionOfArtist = (user: User, artistId: string) => diff --git a/apps/server/src/database/queries/stats.ts b/apps/server/src/database/queries/stats.ts index 0cceee64..b48aa797 100644 --- a/apps/server/src/database/queries/stats.ts +++ b/apps/server/src/database/queries/stats.ts @@ -789,3 +789,59 @@ export const getLongestListeningSession = ( { $limit: 5 }, ]); }; + +export type ItemType = { + field: string; +}; + +export const itemTypes: { + [key in "track" | "album" | "artist"]: ItemType; +} = { + track: { + field: "$id", + }, + album: { + field: "$albumId", + }, + artist: { + field: "$primaryArtistId", + }, +}; + +export const getRankOf = async ( + itemType: ItemType, + user: User, + itemId: string, +) => { + const res = await InfosModel.aggregate([ + { $match: { owner: user._id } }, + { $group: { _id: itemType.field, count: { $sum: 1 } } }, + { $sort: { count: -1, _id: 1 } }, + { $group: { _id: 1, array: { $push: { id: "$_id", count: "$count" } } } }, + { + $project: { + index: { $indexOfArray: ["$array.id", itemId] }, + array: 1, + }, + }, + { + $project: { + index: 1, + isMax: { + $cond: { if: { $eq: ["$index", 0] }, then: true, else: false }, + }, + isMin: { + $cond: { + if: { $eq: ["$index", { $subtract: [{ $size: "$array" }, 1] }] }, + then: true, + else: false, + }, + }, + results: { + $slice: ["$array", { $max: [{ $subtract: ["$index", 1] }, 0] }, 3], + }, + }, + }, + ]); + return res[0]; +}; diff --git a/apps/server/src/database/queries/track.ts b/apps/server/src/database/queries/track.ts index 26b036f4..fb671d4a 100644 --- a/apps/server/src/database/queries/track.ts +++ b/apps/server/src/database/queries/track.ts @@ -11,49 +11,6 @@ export const searchTrack = (str: string) => "full_album", ); -export const getRankOfTrack = async (user: User, trackId: string) => { - const res = await InfosModel.aggregate([ - { $match: { owner: user._id } }, - { - $lookup: { - from: "tracks", - localField: "id", - foreignField: "id", - as: "track", - }, - }, - { $unwind: "$track" }, - { $group: { _id: "$id", count: { $sum: 1 } } }, - { $sort: { count: -1, _id: 1 } }, - { $group: { _id: 1, array: { $push: { id: "$_id", count: "$count" } } } }, - { - $project: { - index: { $indexOfArray: ["$array.id", trackId] }, - array: 1, - }, - }, - { - $project: { - index: 1, - isMax: { - $cond: { if: { $eq: ["$index", 0] }, then: true, else: false }, - }, - isMin: { - $cond: { - if: { $eq: ["$index", { $subtract: [{ $size: "$array" }, 1] }] }, - then: true, - else: false, - }, - }, - results: { - $slice: ["$array", { $max: [{ $subtract: ["$index", 1] }, 0] }, 3], - }, - }, - }, - ]); - return res[0]; -}; - export const getTrackListenedCount = (user: User, trackId: string) => InfosModel.where({ owner: user._id, id: trackId }).countDocuments(); diff --git a/apps/server/src/routes/album.ts b/apps/server/src/routes/album.ts index 4f48d057..6fe21c37 100644 --- a/apps/server/src/routes/album.ts +++ b/apps/server/src/routes/album.ts @@ -1,10 +1,14 @@ -import { Router } from 'express'; -import { z } from 'zod'; -import { getAlbumSongs, getAlbums, getFirstAndLastListenedAlbum, getRankOfAlbum } from '../database/queries/album'; -import { logger } from '../tools/logger'; -import { isLoggedOrGuest, validating } from '../tools/middleware'; -import { LoggedRequest, TypedPayload } from '../tools/types'; -import { getArtists } from '../database'; +import { Router } from "express"; +import { z } from "zod"; +import { + getAlbumSongs, + getAlbums, + getFirstAndLastListenedAlbum, +} from "../database/queries/album"; +import { logger } from "../tools/logger"; +import { isLoggedOrGuest, validating } from "../tools/middleware"; +import { LoggedRequest, TypedPayload } from "../tools/types"; +import { getArtists, getRankOf, itemTypes } from "../database"; export const router = Router(); @@ -19,7 +23,7 @@ router.get( async (req, res) => { try { const { ids } = req.params as TypedPayload; - const albums = await getAlbums(ids.split(',')); + const albums = await getAlbums(ids.split(",")); if (!albums || albums.length === 0) { return res.status(404).end(); } @@ -36,8 +40,8 @@ const getAlbumStats = z.object({ }); router.get( - '/:id/stats', - validating(getAlbumStats, 'params'), + "/:id/stats", + validating(getAlbumStats, "params"), isLoggedOrGuest, async (req, res) => { try { @@ -58,18 +62,18 @@ router.get( album, artists, firstLast, - tracks + tracks, }); } catch (e) { logger.error(e); return res.status(500).end(); } - } -) + }, +); router.get( - '/:id/rank', - validating(getAlbumStats, 'params'), + "/:id/rank", + validating(getAlbumStats, "params"), isLoggedOrGuest, async (req, res) => { try { @@ -79,11 +83,11 @@ router.get( if (!album) { return res.status(404).end(); } - const rank = await getRankOfAlbum(user, id); + const rank = await getRankOf(itemTypes.album, user, id); return res.status(200).send(rank); } catch (e) { logger.error(e); return res.status(500).end(); } - } -) \ No newline at end of file + }, +); diff --git a/apps/server/src/routes/artist.ts b/apps/server/src/routes/artist.ts index 8a74bc05..d46f4edd 100644 --- a/apps/server/src/routes/artist.ts +++ b/apps/server/src/routes/artist.ts @@ -6,7 +6,6 @@ import { getMostListenedSongOfArtist, bestPeriodOfArtist, getTotalListeningOfArtist, - getRankOfArtist, searchArtist, getDayRepartitionOfArtist, blacklistArtist, @@ -14,10 +13,12 @@ import { blacklistByArtist, unblacklistByArtist, getMostListenedAlbumOfArtist, -} from '../database'; -import { logger } from '../tools/logger'; -import { isLoggedOrGuest, logged, validating } from '../tools/middleware'; -import { LoggedRequest, TypedPayload } from '../tools/types'; + getRankOf, + itemTypes, +} from "../database"; +import { logger } from "../tools/logger"; +import { isLoggedOrGuest, logged, validating } from "../tools/middleware"; +import { LoggedRequest, TypedPayload } from "../tools/types"; export const router = Router(); @@ -69,8 +70,14 @@ router.get( getTotalListeningOfArtist(user, id), getDayRepartitionOfArtist(user, id), ]; - const [firstLast, mostListened, albumMostListened, bestPeriod, total, dayRepartition] = - await Promise.all(promises); + const [ + firstLast, + mostListened, + albumMostListened, + bestPeriod, + total, + dayRepartition, + ] = await Promise.all(promises); if (!total) { return res.status(200).send({ code: "NEVER_LISTENED", @@ -105,7 +112,7 @@ router.get( if (!artist) { return res.status(404).end(); } - const rank = await getRankOfArtist(user, id); + const rank = await getRankOf(itemTypes.artist, user, id); return res.status(200).send(rank); } catch (e) { logger.error(e); diff --git a/apps/server/src/routes/track.ts b/apps/server/src/routes/track.ts index 2da015ea..7b73af17 100644 --- a/apps/server/src/routes/track.ts +++ b/apps/server/src/routes/track.ts @@ -2,12 +2,13 @@ import { Router } from "express"; import { z } from "zod"; import { getArtists, - getRankOfTrack, getTracks, getTrackListenedCount, getTrackFirstAndLastListened, bestPeriodOfTrack, getTrackRecentHistory, + getRankOf, + itemTypes, } from "../database"; import { getAlbums } from "../database/queries/album"; import { logger } from "../tools/logger"; @@ -100,7 +101,7 @@ router.get( if (!track) { return res.status(404).end(); } - const rank = await getRankOfTrack(user, id); + const rank = await getRankOf(itemTypes.track, user, id); return res.status(200).send(rank); } catch (e) { logger.error(e); From c0b2a7fd0bcf05d5070577194a1c52555fc3b145 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Quentin=20Guid=C3=A9e?= Date: Fri, 1 Mar 2024 10:24:07 -0500 Subject: [PATCH 07/11] refactor: move getBest to stats.ts MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Quentin Guidée --- apps/server/src/database/queries/stats.ts | 108 ++++++++++++++---- .../server/src/database/queries/statsTools.ts | 66 ----------- 2 files changed, 86 insertions(+), 88 deletions(-) diff --git a/apps/server/src/database/queries/stats.ts b/apps/server/src/database/queries/stats.ts index b48aa797..dcd147f3 100644 --- a/apps/server/src/database/queries/stats.ts +++ b/apps/server/src/database/queries/stats.ts @@ -3,7 +3,6 @@ import { InfosModel } from "../Models"; import { User } from "../schemas/user"; import { basicMatch, - getBestInfos, getGroupByDateProjection, getGroupingByTimeSplit, getTrackSumType, @@ -13,6 +12,24 @@ import { sortByTimeSplit, } from "./statsTools"; +export type ItemType = { + field: string; +}; + +export const itemTypes: { + [key in "track" | "album" | "artist"]: ItemType; +} = { + track: { + field: "$id", + }, + album: { + field: "$albumId", + }, + artist: { + field: "$primaryArtistId", + }, +}; + export const getMostListenedSongs = async ( user: User, start: Date, @@ -527,13 +544,78 @@ export const getBestArtistsPer = async ( return res; }; +export const getBest = ( + itemType: ItemType, + user: User, + start: Date, + end: Date, + nb: number, + offset: number, +) => + InfosModel.aggregate([ + ...basicMatch(user._id, start, end), + { + $group: { + _id: itemType.field, + duration_ms: { $sum: "$durationMs" }, + count: { $sum: 1 }, + trackId: { $first: "$id" }, + albumId: { $first: "$albumId" }, + primaryArtistId: { $first: "$primaryArtistId" }, + trackIds: { $addToSet: "$id" }, + }, + }, + { $addFields: { differents: { $size: "$trackIds" } } }, + { + $facet: { + infos: [ + { $sort: { count: -1, _id: 1 } }, + { $skip: offset }, + { $limit: nb }, + ], + computations: [ + { + $group: { + _id: null, + total_duration_ms: { $sum: "$duration_ms" }, + total_count: { $sum: "$count" }, + }, + }, + ], + }, + }, + { $unwind: "$infos" }, + { $unwind: "$computations" }, + { + $project: { + _id: "$infos._id", + result: { + $mergeObjects: ["$infos", "$computations"], + }, + }, + }, + { + $replaceRoot: { + newRoot: { + $mergeObjects: ["$result", { _id: "$_id" }], + }, + }, + }, + { $lookup: lightTrackLookupPipeline("trackId") }, + { $unwind: "$track" }, + { $lookup: lightAlbumLookupPipeline("albumId") }, + { $unwind: "$album" }, + { $lookup: lightArtistLookupPipeline("primaryArtistId", false) }, + { $unwind: "$artist" }, + ]); + export const getBestSongsNbOffseted = ( user: User, start: Date, end: Date, nb: number, offset: number, -) => getBestInfos("id", user, start, end, nb, offset); +) => getBest(itemTypes.track, user, start, end, nb, offset); export const getBestArtistsNbOffseted = ( user: User, @@ -541,7 +623,7 @@ export const getBestArtistsNbOffseted = ( end: Date, nb: number, offset: number, -) => getBestInfos("primaryArtistId", user, start, end, nb, offset); +) => getBest(itemTypes.artist, user, start, end, nb, offset); export const getBestAlbumsNbOffseted = ( user: User, @@ -549,7 +631,7 @@ export const getBestAlbumsNbOffseted = ( end: Date, nb: number, offset: number, -) => getBestInfos("albumId", user, start, end, nb, offset); +) => getBest(itemTypes.album, user, start, end, nb, offset); export const getBestSongsOfHour = (user: User, start: Date, end: Date) => { return InfosModel.aggregate([ @@ -790,24 +872,6 @@ export const getLongestListeningSession = ( ]); }; -export type ItemType = { - field: string; -}; - -export const itemTypes: { - [key in "track" | "album" | "artist"]: ItemType; -} = { - track: { - field: "$id", - }, - album: { - field: "$albumId", - }, - artist: { - field: "$primaryArtistId", - }, -}; - export const getRankOf = async ( itemType: ItemType, user: User, diff --git a/apps/server/src/database/queries/statsTools.ts b/apps/server/src/database/queries/statsTools.ts index 03732e2e..df320985 100644 --- a/apps/server/src/database/queries/statsTools.ts +++ b/apps/server/src/database/queries/statsTools.ts @@ -2,7 +2,6 @@ import { PipelineStage, Types } from "mongoose"; import { getWithDefault } from "../../tools/env"; import { Timesplit } from "../../tools/types"; import { User } from "../schemas/user"; -import { InfosModel } from "../Models"; export const basicMatch = ( userId: string | Types.ObjectId, @@ -188,68 +187,3 @@ export const lightArtistLookupPipeline = ( from: "artists", as: "artist", }); - -export const getBestInfos = ( - idField: string, - user: User, - start: Date, - end: Date, - nb: number, - offset: number, -) => - InfosModel.aggregate([ - ...basicMatch(user._id, start, end), - { - $group: { - _id: `$${idField}`, - duration_ms: { $sum: "$durationMs" }, - count: { $sum: 1 }, - trackId: { $first: "$id" }, - albumId: { $first: "$albumId" }, - primaryArtistId: { $first: "$primaryArtistId" }, - trackIds: { $addToSet: "$id" }, - }, - }, - { $addFields: { differents: { $size: "$trackIds" } } }, - { - $facet: { - infos: [ - { $sort: { count: -1, _id: 1 } }, - { $skip: offset }, - { $limit: nb }, - ], - computations: [ - { - $group: { - _id: null, - total_duration_ms: { $sum: "$duration_ms" }, - total_count: { $sum: "$count" }, - }, - }, - ], - }, - }, - { $unwind: "$infos" }, - { $unwind: "$computations" }, - { - $project: { - _id: "$infos._id", - result: { - $mergeObjects: ["$infos", "$computations"], - }, - }, - }, - { - $replaceRoot: { - newRoot: { - $mergeObjects: ["$result", { _id: "$_id" }], - }, - }, - }, - { $lookup: lightTrackLookupPipeline("trackId") }, - { $unwind: "$track" }, - { $lookup: lightAlbumLookupPipeline("albumId") }, - { $unwind: "$album" }, - { $lookup: lightArtistLookupPipeline("primaryArtistId", false) }, - { $unwind: "$artist" }, - ]); From 5c0fc0c313bb8c42f235aba1ad13d369fe69b3be Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Quentin=20Guid=C3=A9e?= Date: Fri, 1 Mar 2024 10:27:49 -0500 Subject: [PATCH 08/11] refactor: simplify getBest calls MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Quentin Guidée --- apps/server/src/database/queries/stats.ts | 24 ----------------------- apps/server/src/routes/spotify.ts | 23 +++++++++++++++------- 2 files changed, 16 insertions(+), 31 deletions(-) diff --git a/apps/server/src/database/queries/stats.ts b/apps/server/src/database/queries/stats.ts index dcd147f3..e27a64da 100644 --- a/apps/server/src/database/queries/stats.ts +++ b/apps/server/src/database/queries/stats.ts @@ -609,30 +609,6 @@ export const getBest = ( { $unwind: "$artist" }, ]); -export const getBestSongsNbOffseted = ( - user: User, - start: Date, - end: Date, - nb: number, - offset: number, -) => getBest(itemTypes.track, user, start, end, nb, offset); - -export const getBestArtistsNbOffseted = ( - user: User, - start: Date, - end: Date, - nb: number, - offset: number, -) => getBest(itemTypes.artist, user, start, end, nb, offset); - -export const getBestAlbumsNbOffseted = ( - user: User, - start: Date, - end: Date, - nb: number, - offset: number, -) => getBest(itemTypes.album, user, start, end, nb, offset); - export const getBestSongsOfHour = (user: User, start: Date, end: Date) => { return InfosModel.aggregate([ ...basicMatch(user._id, start, end), diff --git a/apps/server/src/routes/spotify.ts b/apps/server/src/routes/spotify.ts index 96ce8cd7..5c62f0f1 100644 --- a/apps/server/src/routes/spotify.ts +++ b/apps/server/src/routes/spotify.ts @@ -13,13 +13,12 @@ import { differentArtistsPer, getDayRepartition, getBestArtistsPer, - getBestSongsNbOffseted, - getBestArtistsNbOffseted, - getBestAlbumsNbOffseted, getBestSongsOfHour, getBestAlbumsOfHour, getBestArtistsOfHour, getLongestListeningSession, + getBest, + itemTypes, } from "../database"; import { CollaborativeMode, @@ -359,7 +358,14 @@ router.get( >; try { - const result = await getBestSongsNbOffseted(user, start, end, nb, offset); + const result = await getBest( + itemTypes.track, + user, + start, + end, + nb, + offset, + ); return res.status(200).send(result); } catch (e) { logger.error(e); @@ -379,7 +385,8 @@ router.get( >; try { - const result = await getBestArtistsNbOffseted( + const result = await getBest( + itemTypes.artist, user, start, end, @@ -405,7 +412,8 @@ router.get( >; try { - const result = await getBestAlbumsNbOffseted( + const result = await getBest( + itemTypes.album, user, start, end, @@ -653,7 +661,8 @@ router.post( let spotifyIds: string[]; if (body.type === "top") { const { interval: intervalData, nb } = body; - const items = await getBestSongsNbOffseted( + const items = await getBest( + itemTypes.track, user, intervalData.start, intervalData.end, From e29dab8d651329182144f40da7d7f2b0c6e4c211 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Quentin=20Guid=C3=A9e?= Date: Fri, 1 Mar 2024 10:54:51 -0500 Subject: [PATCH 09/11] performances: Accelerate artists/albums/tracks pages MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Quentin Guidée --- apps/server/src/database/queries/album.ts | 4 ++-- apps/server/src/database/queries/artist.ts | 13 ++++++------- 2 files changed, 8 insertions(+), 9 deletions(-) diff --git a/apps/server/src/database/queries/album.ts b/apps/server/src/database/queries/album.ts index ab2f8045..2b6debf3 100644 --- a/apps/server/src/database/queries/album.ts +++ b/apps/server/src/database/queries/album.ts @@ -37,7 +37,7 @@ export const getFirstAndLastListenedAlbum = async ( albumId: string, ) => { const res = await InfosModel.aggregate([ - { $match: { owner: user._id } }, + { $match: { owner: user._id, albumId: albumId } }, ...getAlbumInfos(albumId), { $sort: { played_at: 1 } }, { @@ -66,7 +66,7 @@ export const getFirstAndLastListenedAlbum = async ( export const getAlbumSongs = async (user: User, albumId: string) => { const res = await InfosModel.aggregate([ - { $match: { owner: user._id } }, + { $match: { owner: user._id, albumId: albumId } }, ...getAlbumInfos(albumId), { $group: { _id: "$id", count: { $sum: 1 } } }, { $sort: { count: -1 } }, diff --git a/apps/server/src/database/queries/artist.ts b/apps/server/src/database/queries/artist.ts index f9b2619d..43a9baa0 100644 --- a/apps/server/src/database/queries/artist.ts +++ b/apps/server/src/database/queries/artist.ts @@ -37,7 +37,7 @@ export const getArtistInfos = (artistId: string) => [ export const getFirstAndLastListened = async (user: User, artistId: string) => { // Non sense to compute blacklist here const res = await InfosModel.aggregate([ - { $match: { owner: user._id } }, + { $match: { owner: user._id, primaryArtistId: artistId } }, ...getArtistInfos(artistId), { $sort: { played_at: 1 } }, { @@ -79,7 +79,7 @@ export const getMostListenedSongOfArtist = async ( ) => { const res = await InfosModel.aggregate([ // Non sense to compute blacklist here - { $match: { owner: user._id } }, + { $match: { owner: user._id, primaryArtistId: artistId } }, ...getArtistInfos(artistId), { $group: { _id: "$id", count: { $sum: 1 } } }, { $sort: { count: -1 } }, @@ -109,7 +109,7 @@ export const getMostListenedSongOfArtist = async ( export const bestPeriodOfArtist = async (user: User, artistId: string) => { // Non sense to compute blacklist here const res = await InfosModel.aggregate([ - { $match: { owner: user._id } }, + { $match: { owner: user._id, primaryArtistId: artistId } }, ...getArtistInfos(artistId), { $project: { @@ -141,7 +141,7 @@ export const getTotalListeningOfArtist = async ( ) => { // Non sense to compute blacklist here const res = await InfosModel.aggregate([ - { $match: { owner: user._id } }, + { $match: { owner: user._id, primaryArtistId: artistId } }, ...getArtistInfos(artistId), { $group: { @@ -167,7 +167,7 @@ export const getMostListenedAlbumOfArtist = async ( artistId: string, ) => { const res = await InfosModel.aggregate([ - { $match: { owner: user._id } }, + { $match: { owner: user._id, primaryArtistId: artistId } }, { $lookup: { from: "tracks", @@ -176,7 +176,6 @@ export const getMostListenedAlbumOfArtist = async ( as: "track", }, }, - { $match: { "track.artists.0": artistId } }, { $group: { _id: "$track.album", @@ -201,7 +200,7 @@ export const getMostListenedAlbumOfArtist = async ( export const getDayRepartitionOfArtist = (user: User, artistId: string) => // Non sense to compute blacklist here InfosModel.aggregate([ - { $match: { owner: user._id } }, + { $match: { owner: user._id, primaryArtistId: artistId } }, { $addFields: getGroupByDateProjection(user.settings.timezone) }, ...getArtistInfos(artistId), { From f70c0a96bc37a941c3cc2e26d24c2643288deedd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Quentin=20Guid=C3=A9e?= Date: Fri, 1 Mar 2024 12:05:02 -0500 Subject: [PATCH 10/11] feat: benchmarks hidden page MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Quentin Guidée --- apps/client/src/App.tsx | 9 + .../src/scenes/Benchmarks/Benchmarks.tsx | 195 ++++++++++++++++++ apps/client/src/scenes/Benchmarks/index.ts | 1 + 3 files changed, 205 insertions(+) create mode 100644 apps/client/src/scenes/Benchmarks/Benchmarks.tsx create mode 100644 apps/client/src/scenes/Benchmarks/index.ts diff --git a/apps/client/src/App.tsx b/apps/client/src/App.tsx index b4016020..9a0cc345 100644 --- a/apps/client/src/App.tsx +++ b/apps/client/src/App.tsx @@ -28,6 +28,7 @@ import PlaylistDialog from "./components/PlaylistDialog"; import TrackStats from "./scenes/TrackStats"; import LongestSessions from "./scenes/LongestSessions"; import AlbumStats from "./scenes/AlbumStats"; +import Benchmarks from "./scenes/Benchmarks"; function App() { const dark = useSelector(selectDarkMode); @@ -169,6 +170,14 @@ function App() { } /> + + + + } + /> diff --git a/apps/client/src/scenes/Benchmarks/Benchmarks.tsx b/apps/client/src/scenes/Benchmarks/Benchmarks.tsx new file mode 100644 index 00000000..94ed75bd --- /dev/null +++ b/apps/client/src/scenes/Benchmarks/Benchmarks.tsx @@ -0,0 +1,195 @@ +import { useSelector } from "react-redux"; +import { + Button, + CircularProgress, + IconButton, + LinearProgress, + Table, + TableCell, + TableHead, + TableRow, +} from "@mui/material"; +import { HourglassTop, PlayArrow } from "@mui/icons-material"; +import { useState } from "react"; +import Header from "../../components/Header"; +import { + selectRawIntervalDetail, + selectUser, +} from "../../services/redux/modules/user/selector"; +import { api } from "../../services/apis/api"; +import { Timesplit } from "../../services/types"; +import TitleCard from "../../components/TitleCard"; + +type Request = { + id: string; + title: string; + request: () => Promise; +}; + +export default function Benchmarks() { + const { interval } = useSelector(selectRawIntervalDetail); + + const [elapsedTime, setElapsedTime] = useState>({}); + + const user = useSelector(selectUser); + + if (!user) { + return null; + } + + const requests: Request[] = [ + { + id: "getTracks", + title: "Get tracks", + request: () => api.getTracks(interval.start, interval.end, 20, 1), + }, + { + id: "getMostListened", + title: "Get most listened", + request: () => + api.mostListened(interval.start, interval.end, Timesplit.all), + }, + { + id: "getMostListenedArtists", + title: "Get most listened artists", + request: () => + api.mostListenedArtist(interval.start, interval.end, Timesplit.all), + }, + { + id: "getSongsPer", + title: "Get songs per", + request: () => api.songsPer(interval.start, interval.end, Timesplit.all), + }, + { + id: "getTimePer", + title: "Get time per", + request: () => api.timePer(interval.start, interval.end, Timesplit.all), + }, + { + id: "getFeatRatio", + title: "Get feat ratio", + request: () => api.featRatio(interval.start, interval.end, Timesplit.all), + }, + { + id: "getAlbumDateRatio", + title: "Get album date ratio", + request: () => + api.albumDateRatio(interval.start, interval.end, Timesplit.all), + }, + { + id: "getPopularityPer", + title: "Get popularity per", + request: () => + api.popularityPer(interval.start, interval.end, Timesplit.all), + }, + { + id: "getDifferentArtistsPer", + title: "Get different artists per", + request: () => + api.differentArtistsPer(interval.start, interval.end, Timesplit.all), + }, + { + id: "getTimePerHourOfDay", + title: "Get time per hour of day", + request: () => api.timePerHourOfDay(interval.start, interval.end), + }, + { + id: "getBestSongs", + title: "Get best songs", + request: () => api.getBestSongs(interval.start, interval.end, 30, 1), + }, + { + id: "getBestArtists", + title: "Get best artists", + request: () => api.getBestArtists(interval.start, interval.end, 30, 1), + }, + { + id: "getBestAlbums", + title: "Get best albums", + request: () => api.getBestAlbums(interval.start, interval.end, 30, 1), + }, + { + id: "getBestSongsOfHour", + title: "Get best songs of hour", + request: () => api.getBestSongsOfHour(interval.start, interval.end), + }, + { + id: "getBestAlbumsOfHour", + title: "Get best albums of hour", + request: () => api.getBestAlbumsOfHour(interval.start, interval.end), + }, + { + id: "getBestArtistsOfHour", + title: "Get best artists of hour", + request: () => api.getBestArtistsOfHour(interval.start, interval.end), + }, + { + id: "getLongestSessions", + title: "Get longest sessions", + request: () => api.getLongestSessions(interval.start, interval.end), + }, + ]; + + const run = async (req: Request) => { + setElapsedTime(prev => ({ ...prev, [req.id]: -1 })); + const start = Date.now(); + await req.request(); + const end = Date.now(); + setElapsedTime(prev => ({ ...prev, [req.id]: end - start })); + }; + + const runAll = async () => { + // We need to run the requests in sequence to get a more + // accurate measure of the time it takes to run each request separately. + for (const req of requests) { + await run(req); + } + }; + + return ( +
+
+ Run All}> + + + Request + Elapsed time + + Actions + + + {requests.map(req => { + let elapsed; + if (elapsedTime[req.id] === -1) { + elapsed = Loading...; + } else if (elapsedTime[req.id]) { + elapsed = `${elapsedTime[req.id]}ms`; + } + + return ( + + + {req.title} + + {elapsed} + + run(req)}> + + + + + ); + })} +
+
+
+ ); +} diff --git a/apps/client/src/scenes/Benchmarks/index.ts b/apps/client/src/scenes/Benchmarks/index.ts new file mode 100644 index 00000000..d2de13c9 --- /dev/null +++ b/apps/client/src/scenes/Benchmarks/index.ts @@ -0,0 +1 @@ +export { default } from "./Benchmarks"; From 661fa677c124971463bc81747a9cc8ec9d511e41 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Quentin=20Guid=C3=A9e?= Date: Fri, 1 Mar 2024 12:16:57 -0500 Subject: [PATCH 11/11] feat: improve ItemType MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Quentin Guidée --- apps/server/src/database/queries/stats.ts | 6 ++---- apps/server/src/routes/album.ts | 4 ++-- apps/server/src/routes/artist.ts | 4 ++-- apps/server/src/routes/spotify.ts | 10 +++++----- apps/server/src/routes/track.ts | 4 ++-- 5 files changed, 13 insertions(+), 15 deletions(-) diff --git a/apps/server/src/database/queries/stats.ts b/apps/server/src/database/queries/stats.ts index e27a64da..c50ef4a0 100644 --- a/apps/server/src/database/queries/stats.ts +++ b/apps/server/src/database/queries/stats.ts @@ -16,9 +16,7 @@ export type ItemType = { field: string; }; -export const itemTypes: { - [key in "track" | "album" | "artist"]: ItemType; -} = { +export const ItemType = { track: { field: "$id", }, @@ -28,7 +26,7 @@ export const itemTypes: { artist: { field: "$primaryArtistId", }, -}; +} as const satisfies Record; export const getMostListenedSongs = async ( user: User, diff --git a/apps/server/src/routes/album.ts b/apps/server/src/routes/album.ts index 6fe21c37..1a5e2ded 100644 --- a/apps/server/src/routes/album.ts +++ b/apps/server/src/routes/album.ts @@ -8,7 +8,7 @@ import { import { logger } from "../tools/logger"; import { isLoggedOrGuest, validating } from "../tools/middleware"; import { LoggedRequest, TypedPayload } from "../tools/types"; -import { getArtists, getRankOf, itemTypes } from "../database"; +import { getArtists, getRankOf, ItemType } from "../database"; export const router = Router(); @@ -83,7 +83,7 @@ router.get( if (!album) { return res.status(404).end(); } - const rank = await getRankOf(itemTypes.album, user, id); + const rank = await getRankOf(ItemType.album, user, id); return res.status(200).send(rank); } catch (e) { logger.error(e); diff --git a/apps/server/src/routes/artist.ts b/apps/server/src/routes/artist.ts index d46f4edd..287e9d89 100644 --- a/apps/server/src/routes/artist.ts +++ b/apps/server/src/routes/artist.ts @@ -14,7 +14,7 @@ import { unblacklistByArtist, getMostListenedAlbumOfArtist, getRankOf, - itemTypes, + ItemType, } from "../database"; import { logger } from "../tools/logger"; import { isLoggedOrGuest, logged, validating } from "../tools/middleware"; @@ -112,7 +112,7 @@ router.get( if (!artist) { return res.status(404).end(); } - const rank = await getRankOf(itemTypes.artist, user, id); + const rank = await getRankOf(ItemType.artist, user, id); return res.status(200).send(rank); } catch (e) { logger.error(e); diff --git a/apps/server/src/routes/spotify.ts b/apps/server/src/routes/spotify.ts index 5c62f0f1..2a8b03a5 100644 --- a/apps/server/src/routes/spotify.ts +++ b/apps/server/src/routes/spotify.ts @@ -18,7 +18,7 @@ import { getBestArtistsOfHour, getLongestListeningSession, getBest, - itemTypes, + ItemType, } from "../database"; import { CollaborativeMode, @@ -359,7 +359,7 @@ router.get( try { const result = await getBest( - itemTypes.track, + ItemType.track, user, start, end, @@ -386,7 +386,7 @@ router.get( try { const result = await getBest( - itemTypes.artist, + ItemType.artist, user, start, end, @@ -413,7 +413,7 @@ router.get( try { const result = await getBest( - itemTypes.album, + ItemType.album, user, start, end, @@ -662,7 +662,7 @@ router.post( if (body.type === "top") { const { interval: intervalData, nb } = body; const items = await getBest( - itemTypes.track, + ItemType.track, user, intervalData.start, intervalData.end, diff --git a/apps/server/src/routes/track.ts b/apps/server/src/routes/track.ts index 7b73af17..54c75ba7 100644 --- a/apps/server/src/routes/track.ts +++ b/apps/server/src/routes/track.ts @@ -8,7 +8,7 @@ import { bestPeriodOfTrack, getTrackRecentHistory, getRankOf, - itemTypes, + ItemType, } from "../database"; import { getAlbums } from "../database/queries/album"; import { logger } from "../tools/logger"; @@ -101,7 +101,7 @@ router.get( if (!track) { return res.status(404).end(); } - const rank = await getRankOf(itemTypes.track, user, id); + const rank = await getRankOf(ItemType.track, user, id); return res.status(200).send(rank); } catch (e) { logger.error(e);