From df8706026e499c8312fa468210ded3b4b9b1b00e Mon Sep 17 00:00:00 2001 From: zadam Date: Wed, 6 Oct 2021 19:59:29 +0200 Subject: [PATCH] caching initial noteset to improve search times --- src/becca/becca.js | 32 ++++++++++++++++++++++++++ src/becca/becca_loader.js | 2 ++ src/becca/entities/attribute.js | 2 +- src/becca/entities/branch.js | 4 ++-- src/becca/entities/note.js | 4 ++-- src/services/search/services/search.js | 8 +------ 6 files changed, 40 insertions(+), 12 deletions(-) diff --git a/src/becca/becca.js b/src/becca/becca.js index b86e1b5e0..e99eb1f9e 100644 --- a/src/becca/becca.js +++ b/src/becca/becca.js @@ -3,6 +3,7 @@ const sql = require("../services/sql.js"); const NoteRevision = require("./entities/note_revision.js"); const RecentNote = require("./entities/recent_note.js"); +const NoteSet = require("../services/search/note_set"); class Becca { constructor() { @@ -51,6 +52,11 @@ class Becca { } } + addNote(noteId, note) { + this.notes[noteId] = note; + this.dirtyNoteSetCache(); + } + getNote(noteId) { return this.notes[noteId]; } @@ -127,6 +133,32 @@ class Becca { return rows.map(row => new NoteRevision(row)); } + + /** Should be called when the set of all non-skeleton notes changes (added/removed) */ + dirtyNoteSetCache() { + this.allNoteSetCache = null; + } + + getAllNoteSet() { + // caching this since it takes 10s of milliseconds to fill this initial NoteSet for many notes + if (!this.allNoteSetCache) { + const allNotes = []; + + for (const noteId in becca.notes) { + const note = becca.notes[noteId]; + + // in the process of loading data sometimes we create "skeleton" note instances which are expected to be filled later + // in case of inconsistent data this might not work and search will then crash on these + if (note.type !== undefined) { + allNotes.push(note); + } + } + + this.allNoteSet = new NoteSet(allNotes); + } + + return this.allNoteSet; + } } const becca = new Becca(); diff --git a/src/becca/becca_loader.js b/src/becca/becca_loader.js index 8b0c3ff1a..7018110a0 100644 --- a/src/becca/becca_loader.js +++ b/src/becca/becca_loader.js @@ -113,6 +113,8 @@ eventService.subscribe([eventService.ENTITY_DELETED, eventService.ENTITY_DELETE_ function noteDeleted(noteId) { delete becca.notes[noteId]; + + becca.dirtyNoteSetCache(); } function branchDeleted(branchId) { diff --git a/src/becca/entities/attribute.js b/src/becca/entities/attribute.js index d498df81f..3fccd27f7 100644 --- a/src/becca/entities/attribute.js +++ b/src/becca/entities/attribute.js @@ -63,7 +63,7 @@ class Attribute extends AbstractEntity { if (!(this.noteId in this.becca.notes)) { // entities can come out of order in sync, create skeleton which will be filled later - this.becca.notes[this.noteId] = new Note({noteId: this.noteId}); + this.becca.addNote(this.noteId, new Note({noteId: this.noteId})); } this.becca.notes[this.noteId].ownedAttributes.push(this); diff --git a/src/becca/entities/branch.js b/src/becca/entities/branch.js index a7ae115ae..e3762dfdd 100644 --- a/src/becca/entities/branch.js +++ b/src/becca/entities/branch.js @@ -81,7 +81,7 @@ class Branch extends AbstractEntity { get childNote() { if (!(this.noteId in this.becca.notes)) { // entities can come out of order in sync, create skeleton which will be filled later - this.becca.notes[this.noteId] = new Note({noteId: this.noteId}); + this.becca.addNote(this.noteId, new Note({noteId: this.noteId})); } return this.becca.notes[this.noteId]; @@ -95,7 +95,7 @@ class Branch extends AbstractEntity { get parentNote() { if (!(this.parentNoteId in this.becca.notes)) { // entities can come out of order in sync, create skeleton which will be filled later - this.becca.notes[this.parentNoteId] = new Note({noteId: this.parentNoteId}); + this.becca.addNote(this.parentNoteId, new Note({noteId: this.parentNoteId})); } return this.becca.notes[this.parentNoteId]; diff --git a/src/becca/entities/note.js b/src/becca/entities/note.js index 9ba73d10a..6a9a1d784 100644 --- a/src/becca/entities/note.js +++ b/src/becca/entities/note.js @@ -95,7 +95,7 @@ class Note extends AbstractEntity { /** @param {Attribute[]} */ this.targetRelations = []; - this.becca.notes[this.noteId] = this; + this.becca.addNote(this.noteId, this); /** @param {Note[]|null} */ this.ancestorCache = null; @@ -1087,7 +1087,7 @@ class Note extends AbstractEntity { beforeSaving() { super.beforeSaving(); - this.becca.notes[this.noteId] = this; + this.becca.addNote(this.noteId, this); this.dateModified = dateUtils.localNowDateTime(); this.utcDateModified = dateUtils.utcNowDateTime(); diff --git a/src/services/search/services/search.js b/src/services/search/services/search.js index e98e54672..030580fcb 100644 --- a/src/services/search/services/search.js +++ b/src/services/search/services/search.js @@ -64,17 +64,11 @@ function loadNeededInfoFromDatabase() { * @return {SearchResult[]} */ function findResultsWithExpression(expression, searchContext) { - let allNotes = Object.values(becca.notes); - if (searchContext.dbLoadNeeded) { loadNeededInfoFromDatabase(); } - // in the process of loading data sometimes we create "skeleton" note instances which are expected to be filled later - // in case of inconsistent data this might not work and search will then crash on these - allNotes = allNotes.filter(note => note.type !== undefined); - - const allNoteSet = new NoteSet(allNotes); + const allNoteSet = becca.getAllNoteSet(); const executionContext = { noteIdToNotePath: {}