refactoring of "some path" WIP

This commit is contained in:
zadam 2023-04-16 09:22:24 +02:00
parent 7aa26580ba
commit a1d4e062ed
6 changed files with 45 additions and 137 deletions

View file

@ -24,49 +24,12 @@ function isNotePathArchived(notePath) {
return false;
}
/**
* This assumes that note is available. "archived" note means that there isn't a single non-archived note-path
* leading to this note.
*
* @param noteId
*/
function isArchived(noteId) {
const notePath = getSomePath(noteId);
return isNotePathArchived(notePath);
}
/**
* @param {string} noteId
* @param {string} ancestorNoteId
* @returns {boolean} - true if given noteId has ancestorNoteId in any of its paths (even archived)
*/
function isInAncestor(noteId, ancestorNoteId) {
if (ancestorNoteId === 'root' || ancestorNoteId === noteId) {
return true;
}
const note = becca.notes[noteId];
if (!note) {
return false;
}
for (const parentNote of note.parents) {
if (isInAncestor(parentNote.noteId, ancestorNoteId)) {
return true;
}
}
return false;
}
function getNoteTitle(childNoteId, parentNoteId) {
const childNote = becca.notes[childNoteId];
const parentNote = becca.notes[parentNoteId];
if (!childNote) {
log.info(`Cannot find note in cache for noteId '${childNoteId}'`);
log.info(`Cannot find note '${childNoteId}'`);
return "[error fetching title]";
}
@ -119,86 +82,15 @@ function getNoteTitleForPath(notePathArray) {
return titles.join(' / ');
}
/**
* Returns notePath for noteId. Note hoisting is respected.
* Archived (and hidden) notes are also returned, but non-archived paths are preferred if available
* - this means that archived paths is returned only if there's no non-archived path
* - you can check whether returned path is archived using isArchived
*
* @param {BNote} note
* @param {string[]} path
*/
function getSomePath(note, path = []) {
// first try to find note within hoisted note, otherwise take any existing note path
return getSomePathInner(note, path, true)
|| getSomePathInner(note, path, false);
}
/**
* @param {BNote} note
* @param {string[]} parentPath
* @param {boolean} respectHoisting
* @returns {string[]|false}
*/
function getSomePathInner(note, parentPath, respectHoisting) {
const childPath = [...parentPath, note.noteId];
if (note.isRoot()) {
childPath.reverse();
if (respectHoisting && !childPath.includes(cls.getHoistedNoteId())) {
return false;
}
return childPath;
}
const parents = note.parents;
if (parents.length === 0) {
console.log(`Note '${note.noteId}' - '${note.title}' has no parents.`);
return false;
}
const completeNotePaths = parents.map(parentNote => getSomePathInner(parentNote, childPath, respectHoisting));
if (completeNotePaths.length === 0) {
return false;
} else if (completeNotePaths.length === 1) {
return completeNotePaths[0];
} else {
completeNotePaths.sort((a, b) => {
if (a.isInHoistedSubTree !== b.isInHoistedSubTree) {
return a.isInHoistedSubTree ? -1 : 1;
} else if (a.isSearch !== b.isSearch) {
return a.isSearch ? 1 : -1;
} else if (a.isArchived !== b.isArchived) {
return a.isArchived ? 1 : -1;
} else if (a.isHidden !== b.isHidden) {
return a.isHidden ? 1 : -1;
} else {
return a.notePath.length - b.notePath.length;
}
});
// if there are multiple valid paths, take the shortest one
const shortestNotePath = completeNotePaths.reduce((shortestPath, nextPath) =>
nextPath.length < shortestPath.length
? nextPath
: shortestPath, completeNotePaths[0]);
return shortestNotePath;
}
}
function getNotePath(noteId) {
const note = becca.notes[noteId];
if (!note) {
console.trace(`Cannot find note '${noteId}' in cache.`);
console.trace(`Cannot find note '${noteId}'.`);
return;
}
const retPath = getSomePath(note);
const retPath = note.getBestNotePath();
if (retPath) {
const noteTitle = getNoteTitleForPath(retPath);
@ -223,23 +115,9 @@ function getNotePath(noteId) {
}
}
/**
* @param noteId
* @returns {boolean} - true if note exists (is not deleted) and is available in current note hoisting
*/
function isAvailable(noteId) {
const notePath = getNotePath(noteId);
return !!notePath;
}
module.exports = {
getSomePath,
getNotePath,
getNoteTitle,
getNoteTitleForPath,
isAvailable,
isArchived,
isInAncestor,
isNotePathArchived
};

View file

@ -748,6 +748,21 @@ class BNote extends AbstractBeccaEntity {
return this.hasAttribute('label', 'archived');
}
areAllNotePathsArchived() {
// there's a slight difference between note being itself archived and all its note paths being archived
// - note is archived when it itself has an archived label or inherits it
// - note does not have or inherit archived label, but each note paths contains a note with (non-inheritable)
// archived label
const bestNotePathRecord = this.getSortedNotePathRecords()[0];
if (!bestNotePathRecord) {
throw new Error(`No note path available for note '${this.noteId}'`);
}
return bestNotePathRecord.isArchived;
}
hasInheritableArchivedLabel() {
for (const attr of this.getAttributes()) {
if (attr.name === 'archived' && attr.type === LABEL && attr.isInheritable) {

View file

@ -404,7 +404,7 @@ async function findSimilarNotes(noteId) {
let score = computeScore(candidateNote);
if (score >= 1.5) {
const notePath = beccaService.getSomePath(candidateNote);
const notePath = candidateNote.getBestNotePath();
// this takes care of note hoisting
if (!notePath) {

View file

@ -3,14 +3,14 @@
const sql = require('../../services/sql');
const protectedSessionService = require('../../services/protected_session');
const noteService = require('../../services/notes');
const beccaService = require('../../becca/becca_service');
const becca = require("../../becca/becca");
function getRecentChanges(req) {
const {ancestorNoteId} = req.params;
let recentChanges = [];
const noteRevisions = sql.getRows(`
const noteRevisionRows = sql.getRows(`
SELECT
notes.noteId,
notes.isDeleted AS current_isDeleted,
@ -24,16 +24,18 @@ function getRecentChanges(req) {
note_revisions
JOIN notes USING(noteId)`);
for (const noteRevision of noteRevisions) {
if (beccaService.isInAncestor(noteRevision.noteId, ancestorNoteId)) {
recentChanges.push(noteRevision);
for (const noteRevisionRow of noteRevisionRows) {
const note = becca.getNote(noteRevisionRow.noteId);
if (note?.hasAncestor(ancestorNoteId)) {
recentChanges.push(noteRevisionRow);
}
}
// now we need to also collect date points not represented in note revisions:
// 1. creation for all notes (dateCreated)
// 2. deletion for deleted notes (dateModified)
const notes = sql.getRows(`
const noteRows = sql.getRows(`
SELECT
notes.noteId,
notes.isDeleted AS current_isDeleted,
@ -57,9 +59,11 @@ function getRecentChanges(req) {
FROM notes
WHERE notes.isDeleted = 1`);
for (const note of notes) {
if (beccaService.isInAncestor(note.noteId, ancestorNoteId)) {
recentChanges.push(note);
for (const noteRow of noteRows) {
const note = becca.getNote(noteRow.noteId);
if (note?.hasAncestor(ancestorNoteId)) {
recentChanges.push(noteRow);
}
}

View file

@ -24,7 +24,7 @@ class NoteFlatTextExp extends Expression {
*/
function searchDownThePath(note, tokens, path) {
if (tokens.length === 0) {
const retPath = beccaService.getSomePath(note, path);
const retPath = this.getNotePath(note, path);
if (retPath) {
const noteId = retPath[retPath.length - 1];
@ -131,6 +131,17 @@ class NoteFlatTextExp extends Expression {
return resultNoteSet;
}
getNotePath(note, path) {
if (path.length === 0) {
return note.getBestNotePath();
} else {
const closestNoteId = path[0];
const closestNoteBestNotePath = becca.getNote(closestNoteId).getBestNotePathString();
return [...closestNoteBestNotePath, ...path.slice(1)];
}
}
/**
* Returns noteIds which have at least one matching tokens
*

View file

@ -157,7 +157,7 @@ function findResultsWithExpression(expression, searchContext) {
const searchResults = noteSet.notes
.filter(note => !note.isDeleted)
.map(note => {
const notePathArray = executionContext.noteIdToNotePath[note.noteId] || beccaService.getSomePath(note);
const notePathArray = executionContext.noteIdToNotePath[note.noteId] || note.getBestNotePath();
if (!notePathArray) {
throw new Error(`Can't find note path for note ${JSON.stringify(note.getPojo())}`);