mirror of
https://github.com/zadam/trilium.git
synced 2024-09-22 00:26:01 +08:00
backend autocomplete WIP
This commit is contained in:
parent
5af0ba1fcb
commit
7b9b4fbb0c
|
@ -1,7 +1,6 @@
|
||||||
import treeService from '../services/tree.js';
|
import treeService from '../services/tree.js';
|
||||||
import linkService from '../services/link.js';
|
import linkService from '../services/link.js';
|
||||||
import utils from '../services/utils.js';
|
import server from '../services/server.js';
|
||||||
import autocompleteService from '../services/autocomplete.js';
|
|
||||||
|
|
||||||
const $dialog = $("#jump-to-note-dialog");
|
const $dialog = $("#jump-to-note-dialog");
|
||||||
const $autoComplete = $("#jump-to-note-autocomplete");
|
const $autoComplete = $("#jump-to-note-autocomplete");
|
||||||
|
@ -18,8 +17,12 @@ async function showDialog() {
|
||||||
});
|
});
|
||||||
|
|
||||||
await $autoComplete.autocomplete({
|
await $autoComplete.autocomplete({
|
||||||
source: await utils.stopWatch("building autocomplete", autocompleteService.getAutocompleteItems),
|
source: async function(request, response) {
|
||||||
minLength: 1
|
const result = await server.get('autocomplete?query=' + encodeURIComponent(request.term));
|
||||||
|
|
||||||
|
response(result);
|
||||||
|
},
|
||||||
|
minLength: 2
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
20
src/routes/api/autocomplete.js
Normal file
20
src/routes/api/autocomplete.js
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
const autocompleteService = require('../../services/autocomplete');
|
||||||
|
|
||||||
|
async function getAutocomplete(req) {
|
||||||
|
const query = req.query.query;
|
||||||
|
|
||||||
|
const results = autocompleteService.getResults(query);
|
||||||
|
|
||||||
|
return results.map(res => {
|
||||||
|
return {
|
||||||
|
value: res.title + '(' + res.path + ')',
|
||||||
|
title: res.title
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
getAutocomplete
|
||||||
|
};
|
|
@ -8,6 +8,7 @@ const multer = require('multer')();
|
||||||
const treeApiRoute = require('./api/tree');
|
const treeApiRoute = require('./api/tree');
|
||||||
const notesApiRoute = require('./api/notes');
|
const notesApiRoute = require('./api/notes');
|
||||||
const branchesApiRoute = require('./api/branches');
|
const branchesApiRoute = require('./api/branches');
|
||||||
|
const autocompleteApiRoute = require('./api/autocomplete');
|
||||||
const cloningApiRoute = require('./api/cloning');
|
const cloningApiRoute = require('./api/cloning');
|
||||||
const noteRevisionsApiRoute = require('./api/note_revisions');
|
const noteRevisionsApiRoute = require('./api/note_revisions');
|
||||||
const recentChangesApiRoute = require('./api/recent_changes');
|
const recentChangesApiRoute = require('./api/recent_changes');
|
||||||
|
@ -108,6 +109,8 @@ function register(app) {
|
||||||
apiRoute(PUT, '/api/branches/:branchId/expanded/:expanded', branchesApiRoute.setExpanded);
|
apiRoute(PUT, '/api/branches/:branchId/expanded/:expanded', branchesApiRoute.setExpanded);
|
||||||
apiRoute(DELETE, '/api/branches/:branchId', branchesApiRoute.deleteBranch);
|
apiRoute(DELETE, '/api/branches/:branchId', branchesApiRoute.deleteBranch);
|
||||||
|
|
||||||
|
apiRoute(GET, '/api/autocomplete', autocompleteApiRoute.getAutocomplete);
|
||||||
|
|
||||||
apiRoute(GET, '/api/notes/:noteId', notesApiRoute.getNote);
|
apiRoute(GET, '/api/notes/:noteId', notesApiRoute.getNote);
|
||||||
apiRoute(PUT, '/api/notes/:noteId', notesApiRoute.updateNote);
|
apiRoute(PUT, '/api/notes/:noteId', notesApiRoute.updateNote);
|
||||||
apiRoute(POST, '/api/notes/:parentNoteId/children', notesApiRoute.createNote);
|
apiRoute(POST, '/api/notes/:parentNoteId/children', notesApiRoute.createNote);
|
||||||
|
|
102
src/services/autocomplete.js
Normal file
102
src/services/autocomplete.js
Normal file
|
@ -0,0 +1,102 @@
|
||||||
|
const sql = require('./sql');
|
||||||
|
const sqlInit = require('./sql_init');
|
||||||
|
|
||||||
|
let noteTitles;
|
||||||
|
let noteIds;
|
||||||
|
const childToParent = {};
|
||||||
|
|
||||||
|
async function load() {
|
||||||
|
noteTitles = await sql.getMap(`SELECT noteId, LOWER(title) FROM notes WHERE isDeleted = 0 AND isProtected = 0`);
|
||||||
|
noteIds = Object.keys(noteTitles);
|
||||||
|
|
||||||
|
const relations = await sql.getRows(`SELECT noteId, parentNoteId FROM branches WHERE isDeleted = 0`);
|
||||||
|
|
||||||
|
for (const rel of relations) {
|
||||||
|
childToParent[rel.noteId] = childToParent[rel.noteId] || [];
|
||||||
|
childToParent[rel.noteId].push(rel.parentNoteId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function getResults(query) {
|
||||||
|
if (!noteTitles || query.length <= 2) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
const tokens = query.toLowerCase().split(" ");
|
||||||
|
const results = [];
|
||||||
|
|
||||||
|
for (const noteId in noteTitles) {
|
||||||
|
const title = noteTitles[noteId];
|
||||||
|
const foundTokens = [];
|
||||||
|
|
||||||
|
for (const token of tokens) {
|
||||||
|
if (title.includes(token)) {
|
||||||
|
foundTokens.push(token);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (foundTokens.length > 0) {
|
||||||
|
const remainingTokens = tokens.filter(token => !foundTokens.includes(token));
|
||||||
|
|
||||||
|
search(childToParent[noteId], remainingTokens, [noteId], results);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return results;
|
||||||
|
}
|
||||||
|
|
||||||
|
function search(noteIds, tokens, path, results) {
|
||||||
|
if (!noteIds) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const noteId of noteIds) {
|
||||||
|
if (noteId === 'root') {
|
||||||
|
if (tokens.length === 0) {
|
||||||
|
const reversedPath = path.slice();
|
||||||
|
reversedPath.reverse();
|
||||||
|
|
||||||
|
const noteTitle = getNoteTitle(reversedPath);
|
||||||
|
|
||||||
|
console.log(noteTitle);
|
||||||
|
|
||||||
|
results.push({
|
||||||
|
title: noteTitle,
|
||||||
|
path: reversedPath.join('/')
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const title = noteTitles[noteId];
|
||||||
|
const foundTokens = [];
|
||||||
|
|
||||||
|
for (const token of tokens) {
|
||||||
|
if (title.includes(token)) {
|
||||||
|
foundTokens.push(token);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (foundTokens.length > 0) {
|
||||||
|
const remainingTokens = tokens.filter(token => !foundTokens.includes(token));
|
||||||
|
|
||||||
|
search(childToParent[noteId], remainingTokens, path.concat([noteId]), results);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
search(childToParent[noteId], tokens, path.concat([noteId]), results);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function getNoteTitle(path) {
|
||||||
|
const titles = path.map(noteId => noteTitles[noteId]);
|
||||||
|
|
||||||
|
return titles.join(' / ');
|
||||||
|
}
|
||||||
|
|
||||||
|
sqlInit.dbReady.then(load);
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
getResults
|
||||||
|
};
|
Loading…
Reference in a new issue