mirror of
				https://github.com/zadam/trilium.git
				synced 2025-11-04 04:16:17 +01:00 
			
		
		
		
	To prevent search lag when there are a large number of notes
This commit is contained in:
		@@ -10,6 +10,18 @@ const SELECTED_NOTE_PATH_KEY = "data-note-path";
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
const SELECTED_EXTERNAL_LINK_KEY = "data-external-link";
 | 
					const SELECTED_EXTERNAL_LINK_KEY = "data-external-link";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// To prevent search lag when there are a large number of notes, set a delay based on the number of notes to avoid jitter.
 | 
				
			||||||
 | 
					const notesCount = await server.get<number>(`stats/notesCount`);
 | 
				
			||||||
 | 
					let debounceTimeoutId: ReturnType<typeof setTimeout>;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					function getSearchDelay(notesCount: number): number {
 | 
				
			||||||
 | 
					    const maxNotes = 20000; 
 | 
				
			||||||
 | 
					    const maxDelay = 1000;  
 | 
				
			||||||
 | 
					    const delay = Math.min(maxDelay, (notesCount / maxNotes) * maxDelay);
 | 
				
			||||||
 | 
					    return delay;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					let searchDelay = getSearchDelay(notesCount);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export interface Suggestion {
 | 
					export interface Suggestion {
 | 
				
			||||||
    noteTitle?: string;
 | 
					    noteTitle?: string;
 | 
				
			||||||
    externalLink?: string;
 | 
					    externalLink?: string;
 | 
				
			||||||
@@ -72,10 +84,9 @@ async function autocompleteSource(term: string, cb: (rows: Suggestion[]) => void
 | 
				
			|||||||
    const activeNoteId = appContext.tabManager.getActiveContextNoteId();
 | 
					    const activeNoteId = appContext.tabManager.getActiveContextNoteId();
 | 
				
			||||||
    const length = term.trim().length;
 | 
					    const length = term.trim().length;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    let results: Suggestion[] = [];
 | 
					    let results = await server.get<Suggestion[]>(`autocomplete?query=${encodeURIComponent(term)}&activeNoteId=${activeNoteId}&fastSearch=${fastSearch}`);
 | 
				
			||||||
    if (length >= 3) {
 | 
					
 | 
				
			||||||
        results = await server.get<Suggestion[]>(`autocomplete?query=${encodeURIComponent(term)}&activeNoteId=${activeNoteId}&fastSearch=${fastSearch}`);
 | 
					    options.fastSearch = true;
 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (length >= 1 && options.allowCreatingNotes) {
 | 
					    if (length >= 1 && options.allowCreatingNotes) {
 | 
				
			||||||
        results = [
 | 
					        results = [
 | 
				
			||||||
@@ -112,6 +123,7 @@ async function autocompleteSource(term: string, cb: (rows: Suggestion[]) => void
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function clearText($el: JQuery<HTMLElement>) {
 | 
					function clearText($el: JQuery<HTMLElement>) {
 | 
				
			||||||
 | 
					    searchDelay = 0;
 | 
				
			||||||
    $el.setSelectedNotePath("");
 | 
					    $el.setSelectedNotePath("");
 | 
				
			||||||
    $el.autocomplete("val", "").trigger("change");
 | 
					    $el.autocomplete("val", "").trigger("change");
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@@ -122,6 +134,7 @@ function setText($el: JQuery<HTMLElement>, text: string) {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function showRecentNotes($el: JQuery<HTMLElement>) {
 | 
					function showRecentNotes($el: JQuery<HTMLElement>) {
 | 
				
			||||||
 | 
					    searchDelay = 0;
 | 
				
			||||||
    $el.setSelectedNotePath("");
 | 
					    $el.setSelectedNotePath("");
 | 
				
			||||||
    $el.autocomplete("val", "");
 | 
					    $el.autocomplete("val", "");
 | 
				
			||||||
    $el.autocomplete("open");
 | 
					    $el.autocomplete("open");
 | 
				
			||||||
@@ -137,11 +150,8 @@ function fullTextSearch($el: JQuery<HTMLElement>, options: Options) {
 | 
				
			|||||||
    options.fastSearch = false;
 | 
					    options.fastSearch = false;
 | 
				
			||||||
    $el.autocomplete("val", "");
 | 
					    $el.autocomplete("val", "");
 | 
				
			||||||
    $el.setSelectedNotePath("");
 | 
					    $el.setSelectedNotePath("");
 | 
				
			||||||
 | 
					    searchDelay = 0;
 | 
				
			||||||
    $el.autocomplete("val", searchString);
 | 
					    $el.autocomplete("val", searchString);
 | 
				
			||||||
    // Set a delay to avoid resetting to true before full text search (await server.get) is called.
 | 
					 | 
				
			||||||
    setTimeout(() => {
 | 
					 | 
				
			||||||
        options.fastSearch = true;
 | 
					 | 
				
			||||||
    }, 100);
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function initNoteAutocomplete($el: JQuery<HTMLElement>, options?: Options) {
 | 
					function initNoteAutocomplete($el: JQuery<HTMLElement>, options?: Options) {
 | 
				
			||||||
@@ -154,6 +164,15 @@ function initNoteAutocomplete($el: JQuery<HTMLElement>, options?: Options) {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    options = options || {};
 | 
					    options = options || {};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Used to track whether the user is performing character composition with an input method (such as Chinese Pinyin, Japanese, Korean, etc.) and to avoid triggering a search during the composition process.
 | 
				
			||||||
 | 
					    let isComposingInput = false;
 | 
				
			||||||
 | 
					    $el.on("compositionstart", () => {
 | 
				
			||||||
 | 
					        isComposingInput = true; 
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					    $el.on("compositionend", () => {
 | 
				
			||||||
 | 
					        isComposingInput = false; 
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    $el.addClass("note-autocomplete-input");
 | 
					    $el.addClass("note-autocomplete-input");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    const $clearTextButton = $("<a>").addClass("input-group-text input-clearer-button bx bxs-tag-x").prop("title", t("note_autocomplete.clear-text-field"));
 | 
					    const $clearTextButton = $("<a>").addClass("input-group-text input-clearer-button bx bxs-tag-x").prop("title", t("note_autocomplete.clear-text-field"));
 | 
				
			||||||
@@ -226,7 +245,19 @@ function initNoteAutocomplete($el: JQuery<HTMLElement>, options?: Options) {
 | 
				
			|||||||
        },
 | 
					        },
 | 
				
			||||||
        [
 | 
					        [
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                source: (term, cb) => autocompleteSource(term, cb, options),
 | 
					                source: (term, cb) => {
 | 
				
			||||||
 | 
					                    clearTimeout(debounceTimeoutId);
 | 
				
			||||||
 | 
					                    debounceTimeoutId = setTimeout(() => {
 | 
				
			||||||
 | 
					                        if (isComposingInput) {
 | 
				
			||||||
 | 
					                            return;
 | 
				
			||||||
 | 
					                        }
 | 
				
			||||||
 | 
					                        autocompleteSource(term, cb, options);
 | 
				
			||||||
 | 
					                    }, searchDelay); 
 | 
				
			||||||
 | 
					                    
 | 
				
			||||||
 | 
					                    if (searchDelay === 0) {
 | 
				
			||||||
 | 
					                        searchDelay = getSearchDelay(notesCount);
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                },
 | 
				
			||||||
                displayKey: "notePathTitle",
 | 
					                displayKey: "notePathTitle",
 | 
				
			||||||
                templates: {
 | 
					                templates: {
 | 
				
			||||||
                    suggestion: (suggestion) => suggestion.highlightedNotePathTitle
 | 
					                    suggestion: (suggestion) => suggestion.highlightedNotePathTitle
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -48,7 +48,16 @@ function getSubtreeSize(req: Request) {
 | 
				
			|||||||
    };
 | 
					    };
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Get the total number of notes
 | 
				
			||||||
 | 
					function getNotesCount(req: Request) {
 | 
				
			||||||
 | 
					    const notesCount = sql.getRow(
 | 
				
			||||||
 | 
					        `SELECT COUNT(*) AS count FROM notes WHERE isDeleted = 0;`,
 | 
				
			||||||
 | 
					    ) as { count: number };
 | 
				
			||||||
 | 
					    return notesCount.count;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export default {
 | 
					export default {
 | 
				
			||||||
    getNoteSize,
 | 
					    getNoteSize,
 | 
				
			||||||
    getSubtreeSize
 | 
					    getSubtreeSize,
 | 
				
			||||||
 | 
					    getNotesCount
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -361,6 +361,7 @@ function register(app: express.Application) {
 | 
				
			|||||||
    apiRoute(GET, "/api/similar-notes/:noteId", similarNotesRoute.getSimilarNotes);
 | 
					    apiRoute(GET, "/api/similar-notes/:noteId", similarNotesRoute.getSimilarNotes);
 | 
				
			||||||
    apiRoute(GET, "/api/backend-log", backendLogRoute.getBackendLog);
 | 
					    apiRoute(GET, "/api/backend-log", backendLogRoute.getBackendLog);
 | 
				
			||||||
    apiRoute(GET, "/api/stats/note-size/:noteId", statsRoute.getNoteSize);
 | 
					    apiRoute(GET, "/api/stats/note-size/:noteId", statsRoute.getNoteSize);
 | 
				
			||||||
 | 
					    apiRoute(GET, "/api/stats/notesCount", statsRoute.getNotesCount);
 | 
				
			||||||
    apiRoute(GET, "/api/stats/subtree-size/:noteId", statsRoute.getSubtreeSize);
 | 
					    apiRoute(GET, "/api/stats/subtree-size/:noteId", statsRoute.getSubtreeSize);
 | 
				
			||||||
    apiRoute(PST, "/api/delete-notes-preview", notesApiRoute.getDeleteNotesPreview);
 | 
					    apiRoute(PST, "/api/delete-notes-preview", notesApiRoute.getDeleteNotesPreview);
 | 
				
			||||||
    route(GET, "/api/fonts", [auth.checkApiAuthOrElectron], fontsRoute.getFontCss);
 | 
					    route(GET, "/api/fonts", [auth.checkApiAuthOrElectron], fontsRoute.getFontCss);
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user