mirror of
				https://github.com/zadam/trilium.git
				synced 2025-11-04 05:28:59 +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 Branch = require('../src/services/note_cache/entities/branch');
 | 
				
			||||||
const Attribute = require('../src/services/note_cache/entities/attribute');
 | 
					const Attribute = require('../src/services/note_cache/entities/attribute');
 | 
				
			||||||
const ParsingContext = require('../src/services/search/parsing_context');
 | 
					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 noteCache = require('../src/services/note_cache/note_cache');
 | 
				
			||||||
const randtoken = require('rand-token').generator({source: 'crypto'});
 | 
					const randtoken = require('rand-token').generator({source: 'crypto'});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -131,6 +132,48 @@ describe("Search", () => {
 | 
				
			|||||||
        expect(findNoteByTitle(searchResults, "Czech Republic")).toBeTruthy();
 | 
					        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 () => {
 | 
					    it("logical or", async () => {
 | 
				
			||||||
        rootNote
 | 
					        rootNote
 | 
				
			||||||
            .child(note("Europe")
 | 
					            .child(note("Europe")
 | 
				
			||||||
@ -217,6 +260,29 @@ describe("Search", () => {
 | 
				
			|||||||
        expect(findNoteByTitle(searchResults, "Prague")).toBeTruthy();
 | 
					        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 () => {
 | 
					    it("filter by note's child", async () => {
 | 
				
			||||||
        rootNote
 | 
					        rootNote
 | 
				
			||||||
            .child(note("Europe")
 | 
					            .child(note("Europe")
 | 
				
			||||||
@ -411,7 +477,7 @@ class NoteBuilder {
 | 
				
			|||||||
        this.note = note;
 | 
					        this.note = note;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    label(name, value, isInheritable = false) {
 | 
					    label(name, value = '', isInheritable = false) {
 | 
				
			||||||
        new Attribute(noteCache, {
 | 
					        new Attribute(noteCache, {
 | 
				
			||||||
            attributeId: id(),
 | 
					            attributeId: id(),
 | 
				
			||||||
            noteId: this.note.noteId,
 | 
					            noteId: this.note.noteId,
 | 
				
			||||||
 | 
				
			|||||||
@ -53,6 +53,9 @@ class Note {
 | 
				
			|||||||
        if (protectedSessionService.isProtectedSessionAvailable()) {
 | 
					        if (protectedSessionService.isProtectedSessionAvailable()) {
 | 
				
			||||||
            this.decrypt();
 | 
					            this.decrypt();
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        /** @param {Note[]|null} */
 | 
				
			||||||
 | 
					        this.ancestorCache = null;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /** @return {Attribute[]} */
 | 
					    /** @return {Attribute[]} */
 | 
				
			||||||
@ -164,6 +167,7 @@ class Note {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        this.attributeCache = null;
 | 
					        this.attributeCache = null;
 | 
				
			||||||
        this.inheritableAttributeCache = null;
 | 
					        this.inheritableAttributeCache = null;
 | 
				
			||||||
 | 
					        this.ancestorCache = null;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    invalidateSubtreeCaches() {
 | 
					    invalidateSubtreeCaches() {
 | 
				
			||||||
@ -258,6 +262,29 @@ class Note {
 | 
				
			|||||||
        return this.attributes.length;
 | 
					        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
 | 
					    /** @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 */
 | 
					     *                     in effect returns notes which are influenced by note's non-inheritable attributes */
 | 
				
			||||||
    get templatedNotes() {
 | 
					    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 {
 | 
					class NoteSet {
 | 
				
			||||||
    constructor(notes = []) {
 | 
					    constructor(notes = []) {
 | 
				
			||||||
 | 
					        /** @type {Note[]} */
 | 
				
			||||||
        this.notes = notes;
 | 
					        this.notes = notes;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -4,6 +4,7 @@ const AndExp = require('./expressions/and');
 | 
				
			|||||||
const OrExp = require('./expressions/or');
 | 
					const OrExp = require('./expressions/or');
 | 
				
			||||||
const NotExp = require('./expressions/not');
 | 
					const NotExp = require('./expressions/not');
 | 
				
			||||||
const ChildOfExp = require('./expressions/child_of');
 | 
					const ChildOfExp = require('./expressions/child_of');
 | 
				
			||||||
 | 
					const DescendantOfExp = require('./expressions/descendant_of');
 | 
				
			||||||
const ParentOfExp = require('./expressions/parent_of');
 | 
					const ParentOfExp = require('./expressions/parent_of');
 | 
				
			||||||
const RelationWhereExp = require('./expressions/relation_where');
 | 
					const RelationWhereExp = require('./expressions/relation_where');
 | 
				
			||||||
const PropertyComparisonExp = require('./expressions/property_comparison');
 | 
					const PropertyComparisonExp = require('./expressions/property_comparison');
 | 
				
			||||||
@ -64,6 +65,12 @@ function getExpression(tokens, parsingContext) {
 | 
				
			|||||||
            return new ParentOfExp(parseNoteProperty());
 | 
					            return new ParentOfExp(parseNoteProperty());
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (tokens[i] === 'ancestors') {
 | 
				
			||||||
 | 
					            i += 1;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            return new DescendantOfExp(parseNoteProperty());
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if (tokens[i] === 'labels') {
 | 
					        if (tokens[i] === 'labels') {
 | 
				
			||||||
            if (tokens[i + 1] !== '.') {
 | 
					            if (tokens[i + 1] !== '.') {
 | 
				
			||||||
                parsingContext.addError(`Expected "." to separate field path, god "${tokens[i + 1]}"`);
 | 
					                parsingContext.addError(`Expected "." to separate field path, god "${tokens[i + 1]}"`);
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user