diff --git a/backend/package-lock.json b/backend/package-lock.json index 28c465c..42feaf4 100644 --- a/backend/package-lock.json +++ b/backend/package-lock.json @@ -15,6 +15,7 @@ "connect-mongo": "^5.0.0", "cors": "^2.8.5", "express": "^4.18.2", + "fuse.js": "^7.3.0", "mongoose": "^7.4.1", "multer": "^2.0.2", "node-schedule": "^2.1.1", @@ -1950,6 +1951,19 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/fuse.js": { + "version": "7.3.0", + "resolved": "https://registry.npmjs.org/fuse.js/-/fuse.js-7.3.0.tgz", + "integrity": "sha512-plz8RVjfcDedTGfVngWH1jmJvBvAwi1v2jecfDerbEnMcmOYUEEwKFTHbNoCiYyzaK2Ws8lABkTCcRSqCY1q4w==", + "license": "Apache-2.0", + "engines": { + "node": ">=10" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/krisk" + } + }, "node_modules/get-intrinsic": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", diff --git a/backend/package.json b/backend/package.json index cc02306..46e80ed 100644 --- a/backend/package.json +++ b/backend/package.json @@ -17,6 +17,7 @@ "connect-mongo": "^5.0.0", "cors": "^2.8.5", "express": "^4.18.2", + "fuse.js": "^7.3.0", "mongoose": "^7.4.1", "multer": "^2.0.2", "node-schedule": "^2.1.1", diff --git a/backend/src/helpers/CachedIndex.ts b/backend/src/helpers/CachedIndex.ts new file mode 100644 index 0000000..3e52054 --- /dev/null +++ b/backend/src/helpers/CachedIndex.ts @@ -0,0 +1,35 @@ +import User, { IUser } from "@/schemas/User"; +import Fuse, { FuseIndex } from "fuse.js"; +import { Types } from "mongoose"; + +class CachedIndex { + private index: FuseIndex = null + private list: (IUser & { _id: Types.ObjectId; })[] = null + private fuse: Fuse = null + + refresh() { + this.index = null + this.list = null + this.fuse = null + } + + private async getIndex() { + return this.index || (this.index = Fuse.createIndex(['uname', 'fname', 'surname', 'room'], await this.getList())) + } + + private async getList() { + return this.list || (this.list = await User.find({}, { uname: 1, fname: 1, surname: 1, room: 1 })) + } + + private async getFuse(): Promise { + return this.fuse || (this.fuse = new Fuse(await this.getList(), { + keys: ['uname', 'fname', 'surname', 'room'] + }, await this.getIndex())) + } + + async search(q: string) { + return (await this.getFuse()).search(q, { limit: 5 }).map(r => r.item) + } +} + +export default new CachedIndex() \ No newline at end of file diff --git a/backend/src/routes/api/admin/index.ts b/backend/src/routes/api/admin/index.ts index 236f806..40603f8 100644 --- a/backend/src/routes/api/admin/index.ts +++ b/backend/src/routes/api/admin/index.ts @@ -8,9 +8,9 @@ import { notifRouter } from "./notif"; import { keysRouter } from "./keys"; import { cleanRouter } from "./clean"; import { settingsRouter } from "./settings"; -import User from "@/schemas/User"; import Group from "@/schemas/Group"; import usettings from "@/helpers/usettings"; +import CachedIndex from "@/helpers/CachedIndex"; export const adminRouter = Router() @@ -25,7 +25,7 @@ adminRouter.use('/clean', cleanRouter) adminRouter.use('/settings', settingsRouter) adminRouter.get('/usearch', async (req, res) => { - var results = await User.find({$text: {$search: req.query['q'].toString()}}, {uname: 1, surname: 1, fname: 1, room: 1}) + var results = await CachedIndex.search(req.query['q'].toString()) res.send(results) }) diff --git a/backend/src/schemas/User.ts b/backend/src/schemas/User.ts index 752100b..6bac7ba 100644 --- a/backend/src/schemas/User.ts +++ b/backend/src/schemas/User.ts @@ -1,3 +1,4 @@ +import CachedIndex from "@/helpers/CachedIndex"; import { Perms, project } from "@/utility"; import mongoose, { Types, Schema } from "mongoose" @@ -29,6 +30,8 @@ const userSchema = new Schema({ userSchema.index({uname: "text", room: "text", fname: "text", surname: "text"}, {weights: {fname: 3, surname: 4, room: 2, uname: 1}, default_language: "none"}) +userSchema.post("save", () => {CachedIndex.refresh()}) + export default mongoose.model("logins", userSchema) export function userVisibleFields(user: IUser & {_id: mongoose.Types.ObjectId}): Pick & {_id: mongoose.Types.ObjectId} {