mirror of
https://github.com/zadam/trilium.git
synced 2025-06-06 18:08:33 +02:00
added ancestor depth search criteria
This commit is contained in:
parent
a1f67e830d
commit
fbabdef272
@ -3,12 +3,43 @@ import noteAutocompleteService from "../../services/note_autocomplete.js";
|
||||
|
||||
const TPL = `
|
||||
<tr>
|
||||
<td class="title-column" title="Matched notes must be within subtree of given note.">
|
||||
Ancestor:
|
||||
</td>
|
||||
<td>
|
||||
<div class="input-group">
|
||||
<input class="ancestor form-control" placeholder="search for note by its name">
|
||||
<td colspan="2">
|
||||
<div style="display: flex; align-items: center;">
|
||||
<div style="margin-right: 10px">Ancestor:</div>
|
||||
<div class="input-group" style="flex-shrink: 2">
|
||||
<input class="ancestor form-control" placeholder="search for note by its name">
|
||||
</div>
|
||||
|
||||
<div style="margin-left: 10px; margin-right: 10px">depth:</div>
|
||||
|
||||
<select name="depth" class="form-control d-inline ancestor-depth" style="flex-shrink: 3">
|
||||
<option value="">doesn't mattter</option>
|
||||
<option value="eq1">is exactly 1 (direct children)</option>
|
||||
<option value="eq2">is exactly 2</option>
|
||||
<option value="eq3">is exactly 3</option>
|
||||
<option value="eq4">is exactly 4</option>
|
||||
<option value="eq5">is exactly 5</option>
|
||||
<option value="eq6">is exactly 6</option>
|
||||
<option value="eq7">is exactly 7</option>
|
||||
<option value="eq8">is exactly 8</option>
|
||||
<option value="eq9">is exactly 9</option>
|
||||
<option value="gt1">is greater than 1</option>
|
||||
<option value="gt2">is greater than 2</option>
|
||||
<option value="gt3">is greater than 3</option>
|
||||
<option value="gt4">is greater than 4</option>
|
||||
<option value="gt5">is greater than 5</option>
|
||||
<option value="gt6">is greater than 6</option>
|
||||
<option value="gt7">is greater than 7</option>
|
||||
<option value="gt8">is greater than 8</option>
|
||||
<option value="gt9">is greater than 9</option>
|
||||
<option value="lt3">is less than 3</option>
|
||||
<option value="lt4">is less than 4</option>
|
||||
<option value="lt5">is less than 5</option>
|
||||
<option value="lt6">is less than 6</option>
|
||||
<option value="lt7">is less than 7</option>
|
||||
<option value="lt8">is less than 8</option>
|
||||
<option value="lt9">is less than 9</option>
|
||||
</select>
|
||||
</div>
|
||||
</td>
|
||||
<td class="button-column">
|
||||
@ -27,6 +58,7 @@ export default class Ancestor extends AbstractSearchOption {
|
||||
doRender() {
|
||||
const $option = $(TPL);
|
||||
const $ancestor = $option.find('.ancestor');
|
||||
const $ancestorDepth = $option.find('.ancestor-depth');
|
||||
noteAutocompleteService.initNoteAutocomplete($ancestor);
|
||||
|
||||
$ancestor.on('autocomplete:closed', async () => {
|
||||
@ -37,12 +69,35 @@ export default class Ancestor extends AbstractSearchOption {
|
||||
}
|
||||
});
|
||||
|
||||
$ancestorDepth.on('change', async () => {
|
||||
const ancestorDepth = $ancestorDepth.val();
|
||||
|
||||
if (ancestorDepth) {
|
||||
await this.setAttribute('label', 'ancestorDepth', ancestorDepth);
|
||||
}
|
||||
else {
|
||||
await this.deleteAttribute('label', 'ancestorDepth');
|
||||
}
|
||||
});
|
||||
|
||||
const ancestorNoteId = this.note.getRelationValue('ancestor');
|
||||
|
||||
if (ancestorNoteId !== 'root') {
|
||||
$ancestor.setNote(ancestorNoteId);
|
||||
}
|
||||
|
||||
const ancestorDepth = this.note.getLabelValue('ancestorDepth');
|
||||
|
||||
if (ancestorDepth) {
|
||||
$ancestorDepth.val(ancestorDepth);
|
||||
}
|
||||
|
||||
return $option;
|
||||
}
|
||||
|
||||
async deleteOption() {
|
||||
await this.deleteAttribute('label', 'ancestorDepth');
|
||||
|
||||
await super.deleteOption();
|
||||
}
|
||||
}
|
||||
|
@ -18,6 +18,7 @@ async function search(note) {
|
||||
const searchContext = new SearchContext({
|
||||
fastSearch: note.hasLabel('fastSearch'),
|
||||
ancestorNoteId: note.getRelationValue('ancestor'),
|
||||
ancestorDepth: note.getLabelValue('ancestorDepth'),
|
||||
includeArchivedNotes: note.hasLabel('includeArchivedNotes'),
|
||||
orderBy: note.getLabelValue('orderBy'),
|
||||
orderDirection: note.getLabelValue('orderDirection'),
|
||||
|
@ -368,6 +368,20 @@ class Note {
|
||||
return arr;
|
||||
}
|
||||
|
||||
getDistanceToAncestor(ancestorNoteId) {
|
||||
if (this.noteId === ancestorNoteId) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
let minDistance = 999_999;
|
||||
|
||||
for (const parent of this.parents) {
|
||||
minDistance = Math.min(minDistance, parent.getDistanceToAncestor(ancestorNoteId) + 1);
|
||||
}
|
||||
|
||||
return minDistance;
|
||||
}
|
||||
|
||||
decrypt() {
|
||||
if (this.isProtected && !this.isDecrypted && protectedSessionService.isProtectedSessionAvailable()) {
|
||||
this.title = protectedSessionService.decryptString(this.title);
|
||||
|
@ -6,10 +6,11 @@ const log = require('../../log');
|
||||
const noteCache = require('../../note_cache/note_cache');
|
||||
|
||||
class AncestorExp extends Expression {
|
||||
constructor(ancestorNoteId) {
|
||||
constructor(ancestorNoteId, ancestorDepth) {
|
||||
super();
|
||||
|
||||
this.ancestorNoteId = ancestorNoteId;
|
||||
this.ancestorDepthComparator = this.getComparator(ancestorDepth);
|
||||
}
|
||||
|
||||
execute(inputNoteSet, executionContext) {
|
||||
@ -21,7 +22,45 @@ class AncestorExp extends Expression {
|
||||
return new NoteSet([]);
|
||||
}
|
||||
|
||||
return new NoteSet(ancestorNote.subtreeNotes).intersection(inputNoteSet);
|
||||
const subTreeNoteSet = new NoteSet(ancestorNote.subtreeNotes).intersection(inputNoteSet);
|
||||
|
||||
if (!this.ancestorDepthComparator) {
|
||||
return subTreeNoteSet;
|
||||
}
|
||||
|
||||
const depthConformingNoteSet = new NoteSet([]);
|
||||
|
||||
for (const note of subTreeNoteSet.notes) {
|
||||
const distance = note.getDistanceToAncestor(ancestorNote.noteId);
|
||||
|
||||
if (this.ancestorDepthComparator(distance)) {
|
||||
depthConformingNoteSet.add(note);
|
||||
}
|
||||
}
|
||||
|
||||
return depthConformingNoteSet;
|
||||
}
|
||||
|
||||
getComparator(depthCondition) {
|
||||
if (!depthCondition) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const comparedDepth = parseInt(depthCondition.substr(2));
|
||||
|
||||
if (depthCondition.startsWith("eq")) {
|
||||
return depth => depth === comparedDepth;
|
||||
}
|
||||
else if (depthCondition.startsWith("gt")) {
|
||||
return depth => depth > comparedDepth;
|
||||
}
|
||||
else if (depthCondition.startsWith("lt")) {
|
||||
return depth => depth < comparedDepth;
|
||||
}
|
||||
else {
|
||||
log.error(`Unrecognized depth condition value ${depthCondition}`);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -4,6 +4,7 @@ class SearchContext {
|
||||
constructor(params = {}) {
|
||||
this.fastSearch = !!params.fastSearch;
|
||||
this.ancestorNoteId = params.ancestorNoteId;
|
||||
this.ancestorDepth = params.ancestorDepth;
|
||||
this.includeArchivedNotes = !!params.includeArchivedNotes;
|
||||
this.orderBy = params.orderBy;
|
||||
this.orderDirection = params.orderDirection;
|
||||
|
@ -410,7 +410,7 @@ function getExpression(tokens, searchContext, level = 0) {
|
||||
function parse({fulltextTokens, expressionTokens, searchContext}) {
|
||||
let exp = AndExp.of([
|
||||
searchContext.includeArchivedNotes ? null : new PropertyComparisonExp(searchContext, "isarchived", buildComparator("=", "false")),
|
||||
searchContext.ancestorNoteId ? new AncestorExp(searchContext.ancestorNoteId) : null,
|
||||
searchContext.ancestorNoteId ? new AncestorExp(searchContext.ancestorNoteId, searchContext.ancestorDepth) : null,
|
||||
getFulltext(fulltextTokens, searchContext),
|
||||
getExpression(expressionTokens, searchContext)
|
||||
]);
|
||||
|
Loading…
x
Reference in New Issue
Block a user