mirror of
https://github.com/zadam/trilium.git
synced 2025-03-01 14:22:32 +01:00
332 lines
12 KiB
JavaScript
332 lines
12 KiB
JavaScript
const searchService = require('../src/services/search/search');
|
|
const Note = require('../src/services/note_cache/entities/note');
|
|
const Branch = require('../src/services/note_cache/entities/branch');
|
|
const Attribute = require('../src/services/note_cache/entities/attribute');
|
|
const ParsingContext = require('../src/services/search/parsing_context');
|
|
const noteCache = require('../src/services/note_cache/note_cache');
|
|
const randtoken = require('rand-token').generator({source: 'crypto'});
|
|
|
|
describe("Search", () => {
|
|
let rootNote;
|
|
|
|
beforeEach(() => {
|
|
noteCache.reset();
|
|
|
|
rootNote = new NoteBuilder(new Note(noteCache, {noteId: 'root', title: 'root'}));
|
|
new Branch(noteCache, {branchId: 'root', noteId: 'root', parentNoteId: 'none'});
|
|
});
|
|
|
|
it("simple path match", async () => {
|
|
rootNote
|
|
.child(note("Europe")
|
|
.child(note("Austria"))
|
|
);
|
|
|
|
const parsingContext = new ParsingContext();
|
|
const searchResults = await searchService.findNotesWithQuery('europe austria', parsingContext);
|
|
|
|
expect(searchResults.length).toEqual(1);
|
|
expect(findNoteByTitle(searchResults, "Austria")).toBeTruthy();
|
|
});
|
|
|
|
it("only end leafs are results", async () => {
|
|
rootNote
|
|
.child(note("Europe")
|
|
.child(note("Austria"))
|
|
);
|
|
|
|
const parsingContext = new ParsingContext();
|
|
const searchResults = await searchService.findNotesWithQuery('europe', parsingContext);
|
|
|
|
expect(searchResults.length).toEqual(1);
|
|
expect(findNoteByTitle(searchResults, "Europe")).toBeTruthy();
|
|
});
|
|
|
|
it("only end leafs are results", async () => {
|
|
rootNote
|
|
.child(note("Europe")
|
|
.child(note("Austria")
|
|
.label('capital', 'Vienna'))
|
|
);
|
|
|
|
const parsingContext = new ParsingContext();
|
|
|
|
const searchResults = await searchService.findNotesWithQuery('Vienna', parsingContext);
|
|
expect(searchResults.length).toEqual(1);
|
|
expect(findNoteByTitle(searchResults, "Austria")).toBeTruthy();
|
|
});
|
|
|
|
it("label comparison with short syntax", async () => {
|
|
rootNote
|
|
.child(note("Europe")
|
|
.child(note("Austria")
|
|
.label('capital', 'Vienna'))
|
|
.child(note("Czech Republic")
|
|
.label('capital', 'Prague'))
|
|
);
|
|
|
|
const parsingContext = new ParsingContext();
|
|
|
|
let searchResults = await searchService.findNotesWithQuery('#capital=Vienna', parsingContext);
|
|
expect(searchResults.length).toEqual(1);
|
|
expect(findNoteByTitle(searchResults, "Austria")).toBeTruthy();
|
|
});
|
|
|
|
it("label comparison with full syntax", async () => {
|
|
rootNote
|
|
.child(note("Europe")
|
|
.child(note("Austria")
|
|
.label('capital', 'Vienna'))
|
|
.child(note("Czech Republic")
|
|
.label('capital', 'Prague'))
|
|
);
|
|
|
|
const parsingContext = new ParsingContext();
|
|
|
|
let searchResults = await searchService.findNotesWithQuery('# note.labels.capital=Prague', parsingContext);
|
|
expect(searchResults.length).toEqual(1);
|
|
expect(findNoteByTitle(searchResults, "Czech Republic")).toBeTruthy();
|
|
});
|
|
|
|
it("numeric label comparison", async () => {
|
|
rootNote
|
|
.child(note("Europe")
|
|
.label('country', '', true)
|
|
.child(note("Austria")
|
|
.label('population', '8859000'))
|
|
.child(note("Czech Republic")
|
|
.label('population', '10650000'))
|
|
);
|
|
|
|
const parsingContext = new ParsingContext();
|
|
|
|
const searchResults = await searchService.findNotesWithQuery('#country #population >= 10000000', parsingContext);
|
|
expect(searchResults.length).toEqual(1);
|
|
expect(findNoteByTitle(searchResults, "Czech Republic")).toBeTruthy();
|
|
});
|
|
|
|
it("numeric label comparison fallback to string comparison", async () => {
|
|
rootNote
|
|
.child(note("Europe")
|
|
.label('country', '', true)
|
|
.child(note("Austria")
|
|
.label('established', '1955-07-27'))
|
|
.child(note("Czech Republic")
|
|
.label('established', '1993-01-01'))
|
|
.child(note("Hungary")
|
|
.label('established', '..wrong..'))
|
|
);
|
|
|
|
const parsingContext = new ParsingContext();
|
|
|
|
const searchResults = await searchService.findNotesWithQuery('#established < 1990', parsingContext);
|
|
expect(searchResults.length).toEqual(1);
|
|
expect(findNoteByTitle(searchResults, "Austria")).toBeTruthy();
|
|
});
|
|
|
|
it("logical or", async () => {
|
|
rootNote
|
|
.child(note("Europe")
|
|
.label('country', '', true)
|
|
.child(note("Austria")
|
|
.label('languageFamily', 'germanic'))
|
|
.child(note("Czech Republic")
|
|
.label('languageFamily', 'slavic'))
|
|
.child(note("Hungary")
|
|
.label('languageFamily', 'finnougric'))
|
|
);
|
|
|
|
const parsingContext = new ParsingContext();
|
|
|
|
const searchResults = await searchService.findNotesWithQuery('#languageFamily = slavic OR #languageFamily = germanic', parsingContext);
|
|
expect(searchResults.length).toEqual(2);
|
|
expect(findNoteByTitle(searchResults, "Czech Republic")).toBeTruthy();
|
|
expect(findNoteByTitle(searchResults, "Austria")).toBeTruthy();
|
|
});
|
|
|
|
it("fuzzy attribute search", async () => {
|
|
rootNote
|
|
.child(note("Europe")
|
|
.label('country', '', true)
|
|
.child(note("Austria")
|
|
.label('languageFamily', 'germanic'))
|
|
.child(note("Czech Republic")
|
|
.label('languageFamily', 'slavic')));
|
|
|
|
let parsingContext = new ParsingContext({fuzzyAttributeSearch: false});
|
|
|
|
let searchResults = await searchService.findNotesWithQuery('#language', parsingContext);
|
|
expect(searchResults.length).toEqual(0);
|
|
|
|
searchResults = await searchService.findNotesWithQuery('#languageFamily=ger', parsingContext);
|
|
expect(searchResults.length).toEqual(0);
|
|
|
|
parsingContext = new ParsingContext({fuzzyAttributeSearch: true});
|
|
|
|
searchResults = await searchService.findNotesWithQuery('#language', parsingContext);
|
|
expect(searchResults.length).toEqual(2);
|
|
|
|
searchResults = await searchService.findNotesWithQuery('#languageFamily=ger', parsingContext);
|
|
expect(searchResults.length).toEqual(1);
|
|
expect(findNoteByTitle(searchResults, "Austria")).toBeTruthy();
|
|
});
|
|
|
|
it("filter by note property", async () => {
|
|
rootNote
|
|
.child(note("Europe")
|
|
.child(note("Austria"))
|
|
.child(note("Czech Republic")));
|
|
|
|
const parsingContext = new ParsingContext();
|
|
|
|
const searchResults = await searchService.findNotesWithQuery('# note.title =* czech', parsingContext);
|
|
expect(searchResults.length).toEqual(1);
|
|
expect(findNoteByTitle(searchResults, "Czech Republic")).toBeTruthy();
|
|
});
|
|
|
|
it("filter by note's parent", async () => {
|
|
rootNote
|
|
.child(note("Europe")
|
|
.child(note("Austria"))
|
|
.child(note("Czech Republic")))
|
|
.child(note("Asia")
|
|
.child(note('Taiwan')));
|
|
|
|
const parsingContext = new ParsingContext();
|
|
|
|
let searchResults = await searchService.findNotesWithQuery('# note.parents.title = Europe', parsingContext);
|
|
expect(searchResults.length).toEqual(2);
|
|
expect(findNoteByTitle(searchResults, "Austria")).toBeTruthy();
|
|
expect(findNoteByTitle(searchResults, "Czech Republic")).toBeTruthy();
|
|
|
|
searchResults = await searchService.findNotesWithQuery('# note.parents.title = Asia', parsingContext);
|
|
expect(searchResults.length).toEqual(1);
|
|
expect(findNoteByTitle(searchResults, "Taiwan")).toBeTruthy();
|
|
});
|
|
|
|
it("filter by note's child", async () => {
|
|
rootNote
|
|
.child(note("Europe")
|
|
.child(note("Austria"))
|
|
.child(note("Czech Republic")))
|
|
.child(note("Oceania")
|
|
.child(note('Australia')));
|
|
|
|
const parsingContext = new ParsingContext();
|
|
|
|
let searchResults = await searchService.findNotesWithQuery('# note.children.title =* Aust', parsingContext);
|
|
expect(searchResults.length).toEqual(2);
|
|
expect(findNoteByTitle(searchResults, "Europe")).toBeTruthy();
|
|
expect(findNoteByTitle(searchResults, "Oceania")).toBeTruthy();
|
|
|
|
searchResults = await searchService.findNotesWithQuery('# note.children.title =* Aust AND note.children.title *= republic', parsingContext);
|
|
expect(searchResults.length).toEqual(1);
|
|
expect(findNoteByTitle(searchResults, "Europe")).toBeTruthy();
|
|
});
|
|
|
|
it("filter by relation's note properties using short syntax", async () => {
|
|
const austria = note("Austria");
|
|
const portugal = note("Portugal");
|
|
|
|
rootNote
|
|
.child(note("Europe")
|
|
.child(austria)
|
|
.child(note("Czech Republic")
|
|
.relation('neighbor', austria.note))
|
|
.child(portugal)
|
|
.child(note("Spain")
|
|
.relation('neighbor', portugal.note))
|
|
);
|
|
|
|
const parsingContext = new ParsingContext();
|
|
|
|
let searchResults = await searchService.findNotesWithQuery('# ~neighbor.title = Austria', parsingContext);
|
|
expect(searchResults.length).toEqual(1);
|
|
expect(findNoteByTitle(searchResults, "Czech Republic")).toBeTruthy();
|
|
|
|
searchResults = await searchService.findNotesWithQuery('# ~neighbor.title = Portugal', parsingContext);
|
|
expect(searchResults.length).toEqual(1);
|
|
expect(findNoteByTitle(searchResults, "Spain")).toBeTruthy();
|
|
});
|
|
|
|
it("filter by relation's note properties using long syntax", async () => {
|
|
const austria = note("Austria");
|
|
const portugal = note("Portugal");
|
|
|
|
rootNote
|
|
.child(note("Europe")
|
|
.child(austria)
|
|
.child(note("Czech Republic")
|
|
.relation('neighbor', austria.note))
|
|
.child(portugal)
|
|
.child(note("Spain")
|
|
.relation('neighbor', portugal.note))
|
|
);
|
|
|
|
const parsingContext = new ParsingContext();
|
|
|
|
const searchResults = await searchService.findNotesWithQuery('# note.relations.neighbor.title = Austria', parsingContext);
|
|
expect(searchResults.length).toEqual(1);
|
|
expect(findNoteByTitle(searchResults, "Czech Republic")).toBeTruthy();
|
|
});
|
|
});
|
|
|
|
/** @return {Note} */
|
|
function findNoteByTitle(searchResults, title) {
|
|
return searchResults
|
|
.map(sr => noteCache.notes[sr.noteId])
|
|
.find(note => note.title === title);
|
|
}
|
|
|
|
class NoteBuilder {
|
|
constructor(note) {
|
|
this.note = note;
|
|
}
|
|
|
|
label(name, value, isInheritable = false) {
|
|
new Attribute(noteCache, {
|
|
attributeId: id(),
|
|
noteId: this.note.noteId,
|
|
type: 'label',
|
|
isInheritable,
|
|
name,
|
|
value
|
|
});
|
|
|
|
return this;
|
|
}
|
|
|
|
relation(name, targetNote) {
|
|
new Attribute(noteCache, {
|
|
attributeId: id(),
|
|
noteId: this.note.noteId,
|
|
type: 'relation',
|
|
name,
|
|
value: targetNote.noteId
|
|
});
|
|
|
|
return this;
|
|
}
|
|
|
|
child(childNoteBuilder, prefix = "") {
|
|
new Branch(noteCache, {
|
|
branchId: id(),
|
|
noteId: childNoteBuilder.note.noteId,
|
|
parentNoteId: this.note.noteId,
|
|
prefix
|
|
});
|
|
|
|
return this;
|
|
}
|
|
}
|
|
|
|
function id() {
|
|
return randtoken.generate(10);
|
|
}
|
|
|
|
function note(title) {
|
|
const note = new Note(noteCache, {noteId: id(), title});
|
|
|
|
return new NoteBuilder(note);
|
|
}
|