mirror of
				https://github.com/zadam/trilium.git
				synced 2025-10-31 02:16:05 +01:00 
			
		
		
		
	note cache breakup into classes, WIP
This commit is contained in:
		
							
								
								
									
										233
									
								
								src/services/note_cache/note_cache_service.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										233
									
								
								src/services/note_cache/note_cache_service.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,233 @@ | ||||
| function isNotePathArchived(notePath) { | ||||
|     const noteId = notePath[notePath.length - 1]; | ||||
|     const note = noteCache.notes[noteId]; | ||||
|  | ||||
|     if (note.isArchived) { | ||||
|         return true; | ||||
|     } | ||||
|  | ||||
|     for (let i = 0; i < notePath.length - 1; i++) { | ||||
|         const note = noteCache.notes[notePath[i]]; | ||||
|  | ||||
|         // this is going through parents so archived must be inheritable | ||||
|         if (note.hasInheritableOwnedArchivedLabel) { | ||||
|             return true; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     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 | ||||
|  * @return {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 = noteCache.notes[noteId]; | ||||
|  | ||||
|     for (const parentNote of note.parents) { | ||||
|         if (isInAncestor(parentNote.noteId, ancestorNoteId)) { | ||||
|             return true; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     return false; | ||||
| } | ||||
|  | ||||
| function getNoteTitle(childNoteId, parentNoteId) { | ||||
|     const childNote = noteCache.notes[childNoteId]; | ||||
|     const parentNote = noteCache.notes[parentNoteId]; | ||||
|  | ||||
|     let title; | ||||
|  | ||||
|     if (childNote.isProtected) { | ||||
|         title = protectedSessionService.isProtectedSessionAvailable() ? childNote.title : '[protected]'; | ||||
|     } | ||||
|     else { | ||||
|         title = childNote.title; | ||||
|     } | ||||
|  | ||||
|     const branch = parentNote ? getBranch(childNote.noteId, parentNote.noteId) : null; | ||||
|  | ||||
|     return ((branch && branch.prefix) ? `${branch.prefix} - ` : '') + title; | ||||
| } | ||||
|  | ||||
| function getNoteTitleArrayForPath(notePathArray) { | ||||
|     const titles = []; | ||||
|  | ||||
|     if (notePathArray[0] === hoistedNoteService.getHoistedNoteId() && notePathArray.length === 1) { | ||||
|         return [ getNoteTitle(hoistedNoteService.getHoistedNoteId()) ]; | ||||
|     } | ||||
|  | ||||
|     let parentNoteId = 'root'; | ||||
|     let hoistedNotePassed = false; | ||||
|  | ||||
|     for (const noteId of notePathArray) { | ||||
|         // start collecting path segment titles only after hoisted note | ||||
|         if (hoistedNotePassed) { | ||||
|             const title = getNoteTitle(noteId, parentNoteId); | ||||
|  | ||||
|             titles.push(title); | ||||
|         } | ||||
|  | ||||
|         if (noteId === hoistedNoteService.getHoistedNoteId()) { | ||||
|             hoistedNotePassed = true; | ||||
|         } | ||||
|  | ||||
|         parentNoteId = noteId; | ||||
|     } | ||||
|  | ||||
|     return titles; | ||||
| } | ||||
|  | ||||
| function getNoteTitleForPath(notePathArray) { | ||||
|     const titles = getNoteTitleArrayForPath(notePathArray); | ||||
|  | ||||
|     return titles.join(' / '); | ||||
| } | ||||
|  | ||||
| /** | ||||
|  * Returns notePath for noteId from cache. Note hoisting is respected. | ||||
|  * Archived 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() | ||||
|  */ | ||||
| function getSomePath(note, path = []) { | ||||
|     if (note.noteId === 'root') { | ||||
|         path.push(note.noteId); | ||||
|         path.reverse(); | ||||
|  | ||||
|         if (!path.includes(hoistedNoteService.getHoistedNoteId())) { | ||||
|             return false; | ||||
|         } | ||||
|  | ||||
|         return path; | ||||
|     } | ||||
|  | ||||
|     const parents = note.parents; | ||||
|     if (parents.length === 0) { | ||||
|         return false; | ||||
|     } | ||||
|  | ||||
|     for (const parentNote of parents) { | ||||
|         const retPath = getSomePath(parentNote, path.concat([note.noteId])); | ||||
|  | ||||
|         if (retPath) { | ||||
|             return retPath; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     return false; | ||||
| } | ||||
|  | ||||
| function getNotePath(noteId) { | ||||
|     const note = noteCache.notes[noteId]; | ||||
|     const retPath = getSomePath(note); | ||||
|  | ||||
|     if (retPath) { | ||||
|         const noteTitle = getNoteTitleForPath(retPath); | ||||
|         const parentNote = note.parents[0]; | ||||
|  | ||||
|         return { | ||||
|             noteId: noteId, | ||||
|             branchId: getBranch(noteId, parentNote.noteId).branchId, | ||||
|             title: noteTitle, | ||||
|             notePath: retPath, | ||||
|             path: retPath.join('/') | ||||
|         }; | ||||
|     } | ||||
| } | ||||
|  | ||||
| function evaluateSimilarity(sourceNote, candidateNote, results) { | ||||
|     let coeff = stringSimilarity.compareTwoStrings(sourceNote.flatText, candidateNote.flatText); | ||||
|  | ||||
|     if (coeff > 0.4) { | ||||
|         const notePath = getSomePath(candidateNote); | ||||
|  | ||||
|         // this takes care of note hoisting | ||||
|         if (!notePath) { | ||||
|             return; | ||||
|         } | ||||
|  | ||||
|         if (isNotePathArchived(notePath)) { | ||||
|             coeff -= 0.2; // archived penalization | ||||
|         } | ||||
|  | ||||
|         results.push({coeff, notePath, noteId: candidateNote.noteId}); | ||||
|     } | ||||
| } | ||||
|  | ||||
| /** | ||||
|  * Point of this is to break up long running sync process to avoid blocking | ||||
|  * see https://snyk.io/blog/nodejs-how-even-quick-async-functions-can-block-the-event-loop-starve-io/ | ||||
|  */ | ||||
| function setImmediatePromise() { | ||||
|     return new Promise((resolve) => { | ||||
|         setTimeout(() => resolve(), 0); | ||||
|     }); | ||||
| } | ||||
|  | ||||
| async function findSimilarNotes(noteId) { | ||||
|     const results = []; | ||||
|     let i = 0; | ||||
|  | ||||
|     const origNote = noteCache.notes[noteId]; | ||||
|  | ||||
|     if (!origNote) { | ||||
|         return []; | ||||
|     } | ||||
|  | ||||
|     for (const note of Object.values(notes)) { | ||||
|         if (note.isProtected && !note.isDecrypted) { | ||||
|             continue; | ||||
|         } | ||||
|  | ||||
|         evaluateSimilarity(origNote, note, results); | ||||
|  | ||||
|         i++; | ||||
|  | ||||
|         if (i % 200 === 0) { | ||||
|             await setImmediatePromise(); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     results.sort((a, b) => a.coeff > b.coeff ? -1 : 1); | ||||
|  | ||||
|     return results.length > 50 ? results.slice(0, 50) : results; | ||||
| } | ||||
|  | ||||
| /** | ||||
|  * @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 = { | ||||
|     getNotePath, | ||||
|     getNoteTitleForPath, | ||||
|     isAvailable, | ||||
|     isArchived, | ||||
|     isInAncestor, | ||||
|     findSimilarNotes | ||||
| }; | ||||
		Reference in New Issue
	
	Block a user