From 647ca1409cb1f39eb6a21ea2aa201343aefcd9c2 Mon Sep 17 00:00:00 2001 From: zkldi <20380519+zkldi@users.noreply.github.com> Date: Sun, 5 Jun 2022 14:50:24 +0100 Subject: [PATCH] feat: significantly improve global search Fixes #713 --- server/src/lib/search/search.ts | 16 ++++++ .../src/server/router/api/v1/search/router.ts | 55 ++++++++++++++++++- server/src/utils/user.ts | 20 +++++++ 3 files changed, 88 insertions(+), 3 deletions(-) diff --git a/server/src/lib/search/search.ts b/server/src/lib/search/search.ts index 2bda2b78e..52a5d48b7 100644 --- a/server/src/lib/search/search.ts +++ b/server/src/lib/search/search.ts @@ -184,6 +184,22 @@ export async function SearchGamesSongs(search: string, games: Array) { return res.flat(1).sort((a, b) => b.__textScore - a.__textScore); } +export async function SearchGamesSongsCharts(search: string, games: Array) { + const promises = []; + + for (const game of games) { + promises.push( + SearchSpecificGameSongsAndCharts(game, search).then((e) => ({ + game, + songs: e.songs, + charts: e.charts, + })) + ); + } + + return Promise.all(promises); +} + export async function SearchForChartHash(search: string) { const results = (await Promise.all([ db.charts.bms.findOne({ $or: [{ "data.hashMD5": search }, { "data.hashSHA256": search }] }), diff --git a/server/src/server/router/api/v1/search/router.ts b/server/src/server/router/api/v1/search/router.ts index 11a40e5a2..e8bf87e43 100644 --- a/server/src/server/router/api/v1/search/router.ts +++ b/server/src/server/router/api/v1/search/router.ts @@ -1,7 +1,17 @@ import { Router } from "express"; -import { SearchAllGamesSongs, SearchForChartHash, SearchUsersRegExp } from "lib/search/search"; +import { SYMBOL_TACHI_API_AUTH } from "lib/constants/tachi"; +import { + SearchFolders, + SearchForChartHash, + SearchGamesSongsCharts, + SearchUsersRegExp, +} from "lib/search/search"; +import { TachiConfig } from "lib/setup/config"; import { RequireBokutachi } from "server/middleware/type-require"; import { IsString } from "utils/misc"; +import { GetAllUserRivals, GetUserPlayedGPTs } from "utils/user"; +import type { FilterQuery } from "mongodb"; +import type { FolderDocument, Game, PublicUserDocument, SongDocument } from "tachi-common"; const router: Router = Router({ mergeParams: true }); @@ -20,17 +30,56 @@ router.get("/", async (req, res) => { }); } - const [users, songs] = await Promise.all([ + const userID = req[SYMBOL_TACHI_API_AUTH].userID; + + let filter: FilterQuery = {}; + let relevantGames: Array = TachiConfig.GAMES; + + if (userID !== null) { + // if the requesting user exists, and they've set this param, + // only return info related to games they've played. + if (IsString(req.query.hasPlayedGame)) { + const gpts = await GetUserPlayedGPTs(userID); + + // @hack This is a bit lazy. We should really be filtering the user stuff + // on GPTs with an $or query. + filter = { + game: { $in: gpts.map((e) => e.game) }, + playtype: { $in: gpts.map((e) => e.playtype) }, + }; + + relevantGames = gpts.map((e) => e.game); + } + } + + const [users, songChartData, folders] = await Promise.all([ SearchUsersRegExp(req.query.search), - SearchAllGamesSongs(req.query.search), + SearchGamesSongsCharts(req.query.search, relevantGames), + SearchFolders(req.query.search, filter), ]); + const songs = songChartData.map((e) => e.songs.map((song) => ({ game: e.game, ...song }))); + const charts = songChartData.map((e) => e.charts.map((chart) => ({ game: e.game, ...chart }))); + + // @ts-expect-error Handled below -- the field is added by the below for loop. + const usersWithRivalTag: Array = users; + + if (userID !== null) { + const rivals = await GetAllUserRivals(userID); + + for (const user of usersWithRivalTag) { + user.__isRival = rivals.includes(user.id); + } + } + return res.status(200).json({ success: true, description: `Searched everything.`, body: { users, songs, + charts, + folders, }, }); }); diff --git a/server/src/utils/user.ts b/server/src/utils/user.ts index 0ca5d2f17..ff5ddf8a9 100644 --- a/server/src/utils/user.ts +++ b/server/src/utils/user.ts @@ -265,3 +265,23 @@ export async function IsRequesterAdmin(request: APITokenDocument) { return user.authLevel === UserAuthLevels.ADMIN; } + +/** + * Return all the GPTs this userID has played. + */ +export async function GetUserPlayedGPTs(userID: integer) { + const gpts = (await db["game-stats"].find( + { userID }, + { projection: { game: 1, playtype: 1 } } + )) as Array>; + + return gpts; +} + +export async function GetAllUserRivals(userID: integer) { + const rivals = (await db["game-settings"].find({ userID }, { projection: { rivals: 1 } })) + .map((e) => e.rivals) + .flat(); + + return rivals; +}