Files
Trilium/src/services/search/expressions/note_flat_text.js

168 lines
5.4 KiB
JavaScript
Raw Normal View History

2020-05-17 09:48:24 +02:00
"use strict";
2020-05-22 09:38:30 +02:00
const Expression = require('./expression');
2020-05-17 10:11:19 +02:00
const NoteSet = require('../note_set');
2021-06-29 22:15:57 +02:00
const becca = require('../../../becca/becca');
const utils = require("../../utils");
2020-05-17 10:11:19 +02:00
class NoteFlatTextExp extends Expression {
2020-05-16 23:12:29 +02:00
constructor(tokens) {
2020-05-22 09:38:30 +02:00
super();
2020-05-16 23:12:29 +02:00
this.tokens = tokens;
}
execute(inputNoteSet, executionContext, searchContext) {
2020-05-20 00:03:33 +02:00
// has deps on SQL which breaks unit test so needs to be dynamically required
2021-06-29 22:15:57 +02:00
const beccaService = require('../../../becca/becca_service');
2020-05-16 23:12:29 +02:00
const resultNoteSet = new NoteSet();
/**
* @param {BNote} note
* @param {string[]} tokens
* @param {string[]} path
*/
2023-04-16 09:26:52 +02:00
const searchDownThePath = (note, tokens, path) => {
if (tokens.length === 0) {
2023-04-16 09:22:24 +02:00
const retPath = this.getNotePath(note, path);
if (retPath) {
const noteId = retPath[retPath.length - 1];
if (!resultNoteSet.hasNoteId(noteId)) {
// we could get here from multiple paths, the first one wins because the paths
// are sorted by importance
executionContext.noteIdToNotePath[noteId] = retPath;
resultNoteSet.add(becca.notes[noteId]);
}
}
return;
}
if (note.parents.length === 0 || note.noteId === 'root') {
return;
}
const foundAttrTokens = [];
for (const token of tokens) {
if (note.type.includes(token) || note.mime.includes(token)) {
foundAttrTokens.push(token);
}
}
for (const attribute of note.ownedAttributes) {
const normalizedName = utils.normalize(attribute.name);
const normalizedValue = utils.normalize(attribute.value);
for (const token of tokens) {
if (normalizedName.includes(token) || normalizedValue.includes(token)) {
foundAttrTokens.push(token);
}
}
}
for (const parentNote of note.parents) {
const title = utils.normalize(beccaService.getNoteTitle(note.noteId, parentNote.noteId));
const foundTokens = foundAttrTokens.slice();
for (const token of tokens) {
if (title.includes(token)) {
foundTokens.push(token);
}
}
if (foundTokens.length > 0) {
const remainingTokens = tokens.filter(token => !foundTokens.includes(token));
searchDownThePath(parentNote, remainingTokens, [...path, note.noteId]);
}
else {
searchDownThePath(parentNote, tokens, [...path, note.noteId]);
}
}
}
const candidateNotes = this.getCandidateNotes(inputNoteSet);
2020-05-16 23:12:29 +02:00
for (const note of candidateNotes) {
// autocomplete should be able to find notes by their noteIds as well (only leafs)
2020-08-06 23:55:17 +02:00
if (this.tokens.length === 1 && note.noteId.toLowerCase() === this.tokens[0]) {
searchDownThePath(note, [], []);
2020-05-16 23:12:29 +02:00
continue;
}
const foundAttrTokens = [];
for (const token of this.tokens) {
if (note.type.includes(token) || note.mime.includes(token)) {
foundAttrTokens.push(token);
}
2020-08-30 23:19:55 +02:00
for (const attribute of note.ownedAttributes) {
if (utils.normalize(attribute.name).includes(token)
|| utils.normalize(attribute.value).includes(token)) {
2020-12-15 15:09:00 +01:00
2020-05-16 23:12:29 +02:00
foundAttrTokens.push(token);
}
}
}
for (const parentNote of note.parents) {
const title = utils.normalize(beccaService.getNoteTitle(note.noteId, parentNote.noteId));
2020-05-16 23:12:29 +02:00
const foundTokens = foundAttrTokens.slice();
for (const token of this.tokens) {
if (title.includes(token)) {
foundTokens.push(token);
}
}
if (foundTokens.length > 0) {
const remainingTokens = this.tokens.filter(token => !foundTokens.includes(token));
searchDownThePath(parentNote, remainingTokens, [note.noteId]);
2020-05-16 23:12:29 +02:00
}
}
}
return resultNoteSet;
}
2023-04-16 09:22:24 +02:00
getNotePath(note, path) {
if (path.length === 0) {
return note.getBestNotePath();
} else {
const closestNoteId = path[0];
2023-04-16 09:26:52 +02:00
const closestNoteBestNotePath = becca.getNote(closestNoteId).getBestNotePath();
2023-04-16 09:22:24 +02:00
return [...closestNoteBestNotePath, ...path.slice(1)];
}
}
2020-05-16 23:12:29 +02:00
/**
* Returns noteIds which have at least one matching tokens
*
* @param {NoteSet} noteSet
2023-01-05 23:38:41 +01:00
* @returns {BNote[]}
2020-05-16 23:12:29 +02:00
*/
getCandidateNotes(noteSet) {
const candidateNotes = [];
for (const note of noteSet.notes) {
for (const token of this.tokens) {
2021-05-17 22:35:36 +02:00
if (note.getFlatText().includes(token)) {
2020-05-16 23:12:29 +02:00
candidateNotes.push(note);
break;
}
}
}
return candidateNotes;
}
}
2020-05-17 09:48:24 +02:00
module.exports = NoteFlatTextExp;