Skip to content
Snippets Groups Projects
Commit 5c01033b authored by Sebastien DUMETZ's avatar Sebastien DUMETZ
Browse files

better search that matches multiple words and can match usernames

parent a23db103
No related branches found
No related tags found
No related merge requests found
import path from "path";
import express, { Router} from "express";
const router = Router();
const libsDir = path.join(process.cwd(), "node_modules");
router.use("/three", express.static(path.join(libsDir, "three/build")));
router.use("/quill", express.static(path.join(libsDir, "quill/dist")));
export default router;
......@@ -181,10 +181,6 @@ export default async function createServer(config = defaultConfig) :Promise<expr
}
app.use("/libs", (await import("./routes/libs/index.js")).default);
//Privilege-protected routes
app.use("/scenes", (await import("./routes/scenes/index.js")).default);
......
......@@ -8,7 +8,11 @@ import { DocProps } from "./types.js";
export default abstract class DocsVfs extends BaseVfs{
/**
*
* Write a document for a scene
* Unlike regular files, documents are stored as a string in the database
* @param $data the document data
* @param scene the scene id or name
* @param author the user submitting this document's version
* @returns the created document id
*/
async writeDoc($data :string, scene :number|string, author :null|number = 0) :Promise<number>{
......
......@@ -95,14 +95,51 @@ export default abstract class ScenesVfs extends BaseVfs{
if(typeof offset != "number" || Number.isNaN(offset) || offset < 0) throw new BadRequestError(`When provided, offset must be a number`);
let with_filter = typeof user_id === "number" || match;
if(match){
if(match.startsWith("^")) match = match.slice(1);
else if(!match.startsWith("%")) match = "%"+ match;
let likeness = "";
let mParams :Record<string, string> = {};
function addMatch(ms :string, words :number){
let name = `$match${words}`;
let fname = `$fmatch${words}`; //fuzzy
let fm = ms;
if(ms.startsWith("^")) fm = fm.slice(1);
else if(!ms.startsWith("%")) fm = "%"+ fm;
if(ms.endsWith("$")) fm = fm.slice(0, -1);
else if(!ms.endsWith("%")) fm = fm + "%";
mParams[fname] = fm;
mParams[name] = ms;
if(match.endsWith("$")) match = match.slice(0, -1);
else if(!match.endsWith("%")) match = match + "%";
likeness += `${words ==0 ? " ": " AND "}(
name LIKE ${fname}
OR document.metas LIKE ${fname}
OR author = ${name}
OR json_extract(access, '$.' || ${name}) IN (${AccessTypes.slice(2).map(a=>`'${a}'`).join(", ")})
OR access LIKE '%"' || ${name} || '":%'
)`;
}
if(match){
let words = 0;
likeness = `AND (`;
let quoted = false;
let ms = "";
[...match].forEach(c=>{
if(c == '"') quoted = !quoted;
else if(c != " " || quoted) ms += c;
else{
addMatch(ms, words++);
ms = "";
}
});
if(ms.length) addMatch(ms, words++);
likeness += `)`;
}
return (await this.db.all(`
WITH last_docs AS (
SELECT
......@@ -147,16 +184,13 @@ export default abstract class ScenesVfs extends BaseVfs{
) IN (${ AccessTypes.slice(2).map(s=>`'${s}'`).join(", ") })
`:""}
${(access?.length)? `AND json_extract(scenes.access, '$.' || $user_id) IN (${ access.map(s=>`'${s}'`).join(", ") })`:""}
${match? `AND
name LIKE $match
OR document.metas LIKE $match
`: ""}
${likeness}
GROUP BY scene_id
ORDER BY LOWER(scene_name) ASC
LIMIT $offset, $limit
`, {
...mParams,
$user_id: user_id?.toString(10),
$match: match,
$limit: Math.min(limit, 100),
$offset: offset,
})).map(({ctime, mtime, id, access, ...m})=>({
......
......@@ -271,6 +271,52 @@ describe("Vfs", function(){
let s = await vfs.getScenes(user.uid, {match: "hello"})
expect(s, `[${s.map(s=>s.name).join(", ")}]`).to.have.property("length", 1);
});
it("can match the author's name", async function(){
let scene_id = await vfs.createScene("foo", user.uid);
await vfs.writeDoc(JSON.stringify({}), scene_id, user.uid);
let s = await vfs.getScenes(user.uid, {match: user.username});
expect(s, `[${s.map(s=>s.name).join(", ")}]`).to.have.property("length", 1);
});
it("can match an editor's name", async function(){
let scene_id = await vfs.createScene("foo", admin.uid);
await vfs.writeDoc(JSON.stringify({}), scene_id, admin.uid);
let s = await vfs.getScenes(user.uid, {match: user.username});
expect(s, `[${s.map(s=>s.name).join(", ")}]`).to.have.property("length", 0);
await vfs.writeDoc(JSON.stringify({scene: 0}), scene_id, user.uid);
s = await vfs.getScenes(user.uid, {match: user.username});
expect(s, `[${s.map(s=>s.name).join(", ")}]`).to.have.property("length", 1);
});
it("can search against multiple search terms", async function(){
let scene_id = await vfs.createScene("bar", user.uid);
await vfs.writeDoc(JSON.stringify({
metas: [{
articles:[
{leads:{EN: "Hello World, this is User"}}
]
}]
}), scene_id, user.uid);
scene_id = await vfs.createScene("foo1", admin.uid);
await vfs.writeDoc(JSON.stringify({}), scene_id, admin.uid);
scene_id = await vfs.createScene("foo2", user.uid);
await vfs.writeDoc(JSON.stringify({}), scene_id, user.uid);
let s = await vfs.getScenes(user.uid, {match: `foo ${user.username}`});
expect(s, `[${s.map(s=>s.name).join(", ")}]`).to.have.property("length", 1);
expect(s[0]).to.have.property("name", "foo2");
s = await vfs.getScenes(user.uid, {match: `Hello User`});
expect(s, `[${s.map(s=>s.name).join(", ")}]`).to.have.property("length", 1);
expect(s[0]).to.have.property("name", "bar");
});
});
describe("pagination", function(){
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment