mirror of
https://github.com/zadam/trilium.git
synced 2025-03-01 14:22:32 +01:00
added ancestor
This commit is contained in:
parent
8ce2afff8a
commit
9ede77aead
@ -3,6 +3,7 @@ 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 dateUtils = require('../src/services/date_utils');
|
||||
const noteCache = require('../src/services/note_cache/note_cache');
|
||||
const randtoken = require('rand-token').generator({source: 'crypto'});
|
||||
|
||||
@ -131,6 +132,48 @@ describe("Search", () => {
|
||||
expect(findNoteByTitle(searchResults, "Czech Republic")).toBeTruthy();
|
||||
});
|
||||
|
||||
it("smart date comparisons", async () => {
|
||||
// dates should not be coerced into numbers which would then give wrong numbers
|
||||
|
||||
rootNote
|
||||
.child(note("My note")
|
||||
.label('year', new Date().getFullYear().toString())
|
||||
.label('month', dateUtils.localNowDate().substr(0, 7))
|
||||
.label('date', dateUtils.localNowDate())
|
||||
.label('dateTime', dateUtils.localNowDateTime())
|
||||
);
|
||||
|
||||
const parsingContext = new ParsingContext();
|
||||
|
||||
async function test(query, expectedResultCount) {
|
||||
const searchResults = await searchService.findNotesWithQuery(query, parsingContext);
|
||||
expect(searchResults.length).toEqual(expectedResultCount);
|
||||
|
||||
if (expectedResultCount === 1) {
|
||||
expect(findNoteByTitle(searchResults, "My note")).toBeTruthy();
|
||||
}
|
||||
}
|
||||
|
||||
await test("#year = YEAR", 1);
|
||||
await test("#year >= YEAR", 1);
|
||||
await test("#year <= YEAR", 1);
|
||||
await test("#year < YEAR+1", 1);
|
||||
await test("#year > YEAR+1", 0);
|
||||
|
||||
await test("#month = MONTH", 1);
|
||||
|
||||
await test("#date = TODAY", 1);
|
||||
await test("#date > TODAY", 0);
|
||||
await test("#date > TODAY-1", 1);
|
||||
await test("#date < TODAY+1", 1);
|
||||
await test("#date < 'TODAY + 1'", 1);
|
||||
|
||||
await test("#dateTime <= NOW+10", 1);
|
||||
await test("#dateTime < NOW-10", 0);
|
||||
await test("#dateTime >= NOW-10", 1);
|
||||
await test("#dateTime < NOW-10", 0);
|
||||
});
|
||||
|
||||
it("logical or", async () => {
|
||||
rootNote
|
||||
.child(note("Europe")
|
||||
@ -217,6 +260,29 @@ describe("Search", () => {
|
||||
expect(findNoteByTitle(searchResults, "Prague")).toBeTruthy();
|
||||
});
|
||||
|
||||
it("filter by note's ancestor", async () => {
|
||||
rootNote
|
||||
.child(note("Europe")
|
||||
.child(note("Austria"))
|
||||
.child(note("Czech Republic")
|
||||
.child(note("Prague").label('city')))
|
||||
)
|
||||
.child(note("Asia")
|
||||
.child(note('Taiwan')
|
||||
.child(note('Taipei').label('city')))
|
||||
);
|
||||
|
||||
const parsingContext = new ParsingContext();
|
||||
|
||||
let searchResults = await searchService.findNotesWithQuery('#city AND note.ancestors.title = Europe', parsingContext);
|
||||
expect(searchResults.length).toEqual(1);
|
||||
expect(findNoteByTitle(searchResults, "Prague")).toBeTruthy();
|
||||
|
||||
searchResults = await searchService.findNotesWithQuery('#city AND note.ancestors.title = Asia', parsingContext);
|
||||
expect(searchResults.length).toEqual(1);
|
||||
expect(findNoteByTitle(searchResults, "Taipei")).toBeTruthy();
|
||||
});
|
||||
|
||||
it("filter by note's child", async () => {
|
||||
rootNote
|
||||
.child(note("Europe")
|
||||
@ -411,7 +477,7 @@ class NoteBuilder {
|
||||
this.note = note;
|
||||
}
|
||||
|
||||
label(name, value, isInheritable = false) {
|
||||
label(name, value = '', isInheritable = false) {
|
||||
new Attribute(noteCache, {
|
||||
attributeId: id(),
|
||||
noteId: this.note.noteId,
|
||||
|
@ -53,6 +53,9 @@ class Note {
|
||||
if (protectedSessionService.isProtectedSessionAvailable()) {
|
||||
this.decrypt();
|
||||
}
|
||||
|
||||
/** @param {Note[]|null} */
|
||||
this.ancestorCache = null;
|
||||
}
|
||||
|
||||
/** @return {Attribute[]} */
|
||||
@ -164,6 +167,7 @@ class Note {
|
||||
|
||||
this.attributeCache = null;
|
||||
this.inheritableAttributeCache = null;
|
||||
this.ancestorCache = null;
|
||||
}
|
||||
|
||||
invalidateSubtreeCaches() {
|
||||
@ -258,6 +262,29 @@ class Note {
|
||||
return this.attributes.length;
|
||||
}
|
||||
|
||||
get ancestors() {
|
||||
if (!this.ancestorCache) {
|
||||
const noteIds = new Set();
|
||||
this.ancestorCache = [];
|
||||
|
||||
for (const parent of this.parents) {
|
||||
if (!noteIds.has(parent.noteId)) {
|
||||
this.ancestorCache.push(parent);
|
||||
noteIds.add(parent.noteId);
|
||||
}
|
||||
|
||||
for (const ancestorNote of parent.ancestors) {
|
||||
if (!noteIds.has(ancestorNote.noteId)) {
|
||||
this.ancestorCache.push(ancestorNote);
|
||||
noteIds.add(ancestorNote.noteId);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return this.ancestorCache;
|
||||
}
|
||||
|
||||
/** @return {Note[]} - returns only notes which are templated, does not include their subtrees
|
||||
* in effect returns notes which are influenced by note's non-inheritable attributes */
|
||||
get templatedNotes() {
|
||||
|
30
src/services/search/expressions/descendant_of.js
Normal file
30
src/services/search/expressions/descendant_of.js
Normal file
@ -0,0 +1,30 @@
|
||||
"use strict";
|
||||
|
||||
const Expression = require('./expression');
|
||||
const NoteSet = require('../note_set');
|
||||
|
||||
class DescendantOfExp extends Expression {
|
||||
constructor(subExpression) {
|
||||
super();
|
||||
|
||||
this.subExpression = subExpression;
|
||||
}
|
||||
|
||||
execute(inputNoteSet, searchContext) {
|
||||
const resNoteSet = new NoteSet();
|
||||
|
||||
for (const note of inputNoteSet.notes) {
|
||||
const subInputNoteSet = new NoteSet(note.ancestors);
|
||||
|
||||
const subResNoteSet = this.subExpression.execute(subInputNoteSet, searchContext);
|
||||
|
||||
if (subResNoteSet.notes.length > 0) {
|
||||
resNoteSet.add(note);
|
||||
}
|
||||
}
|
||||
|
||||
return resNoteSet;
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = DescendantOfExp;
|
@ -2,6 +2,7 @@
|
||||
|
||||
class NoteSet {
|
||||
constructor(notes = []) {
|
||||
/** @type {Note[]} */
|
||||
this.notes = notes;
|
||||
}
|
||||
|
||||
|
@ -4,6 +4,7 @@ const AndExp = require('./expressions/and');
|
||||
const OrExp = require('./expressions/or');
|
||||
const NotExp = require('./expressions/not');
|
||||
const ChildOfExp = require('./expressions/child_of');
|
||||
const DescendantOfExp = require('./expressions/descendant_of');
|
||||
const ParentOfExp = require('./expressions/parent_of');
|
||||
const RelationWhereExp = require('./expressions/relation_where');
|
||||
const PropertyComparisonExp = require('./expressions/property_comparison');
|
||||
@ -64,6 +65,12 @@ function getExpression(tokens, parsingContext) {
|
||||
return new ParentOfExp(parseNoteProperty());
|
||||
}
|
||||
|
||||
if (tokens[i] === 'ancestors') {
|
||||
i += 1;
|
||||
|
||||
return new DescendantOfExp(parseNoteProperty());
|
||||
}
|
||||
|
||||
if (tokens[i] === 'labels') {
|
||||
if (tokens[i + 1] !== '.') {
|
||||
parsingContext.addError(`Expected "." to separate field path, god "${tokens[i + 1]}"`);
|
||||
|
Loading…
x
Reference in New Issue
Block a user