trilium/src/services/search/expressions/note_content_unprotected_fulltext.js

76 lines
2.5 KiB
JavaScript

"use strict";
const Expression = require('./expression');
const NoteSet = require('../note_set');
const becca = require('../../../becca/becca');
const striptags = require('striptags');
const utils = require("../../utils");
// FIXME: create common subclass with NoteContentProtectedFulltextExp to avoid duplication
class NoteContentUnprotectedFulltextExp extends Expression {
constructor(operator, {tokens, raw, flatText}) {
super();
if (operator !== '*=*') {
throw new Error(`Note content can be searched only with *=* operator`);
}
this.tokens = tokens;
this.raw = !!raw;
this.flatText = !!flatText;
}
execute(inputNoteSet) {
const resultNoteSet = new NoteSet();
const sql = require('../../sql');
for (let {noteId, type, mime, content} of sql.iterateRows(`
SELECT noteId, type, mime, content
FROM notes JOIN note_contents USING (noteId)
WHERE type IN ('text', 'code', 'mermaid') AND isDeleted = 0 AND isProtected = 0`)) {
if (!inputNoteSet.hasNoteId(noteId) || !(noteId in becca.notes)) {
continue;
}
content = this.preprocessContent(content, type, mime);
const nonMatchingToken = this.tokens.find(token =>
!content.includes(token) &&
(
// in case of default fulltext search we should consider both title, attrs and content
// so e.g. "hello world" should match when "hello" is in title and "world" in content
!this.flatText
|| !becca.notes[noteId].getFlatText().includes(token)
)
);
if (!nonMatchingToken) {
resultNoteSet.add(becca.notes[noteId]);
}
}
return resultNoteSet;
}
preprocessContent(content, type, mime) {
content = utils.normalize(content.toString());
if (type === 'text' && mime === 'text/html') {
if (!this.raw && content.length < 20000) { // striptags is slow for very large notes
// allow link to preserve URLs: https://github.com/zadam/trilium/issues/2412
content = striptags(content, ['a']);
// at least the closing tag can be easily stripped
content = content.replace(/<\/a>/ig, "");
}
content = content.replace(/&nbsp;/g, ' ');
}
return content;
}
}
module.exports = NoteContentUnprotectedFulltextExp;