added caching to search suggestions on client side

This commit is contained in:
partisan 2024-10-31 11:32:28 +01:00
parent f7e6c34722
commit 2f65d04dda

View file

@ -1,5 +1,5 @@
/* /*
This script is responsible for fetching search suggestions when the user types in the search bar. It also shows and hides the search suggestions wrapper. This script fetches and caches search suggestions to reduce server requests with an LRU cache.
*/ */
document.addEventListener('DOMContentLoaded', () => { document.addEventListener('DOMContentLoaded', () => {
const searchInput = document.getElementById('search-input'); const searchInput = document.getElementById('search-input');
@ -7,14 +7,30 @@ document.addEventListener('DOMContentLoaded', () => {
const resultsWrapper = document.querySelector('.autocomplete'); const resultsWrapper = document.querySelector('.autocomplete');
let suggestions = []; let suggestions = [];
let currentIndex = -1; // Keep track of the currently selected suggestion let currentIndex = -1;
const cacheLimit = 10000; // This should take about 3.6 MB of memory
const suggestionCache = new Map(); // Map to store suggestions with LRU capability
// Fetch suggestions from the server // Fetch suggestions from server or cache
async function getSuggestions(query) { async function getSuggestions(query) {
if (suggestionCache.has(query)) {
// Move the recently accessed item to the end of Map to mark it as most recently used
const cachedResult = suggestionCache.get(query);
suggestionCache.delete(query);
suggestionCache.set(query, cachedResult);
return cachedResult;
}
try { try {
const response = await fetch(`/suggestions?q=${encodeURIComponent(query)}`); const response = await fetch(`/suggestions?q=${encodeURIComponent(query)}`);
const data = await response.json(); const data = await response.json();
return data[1]; // Return only the array of suggestion strings // Add result to cache and enforce cache limit
suggestionCache.set(query, data[1]);
if (suggestionCache.size > cacheLimit) {
// Remove the oldest (first) item in the Map
const firstKey = suggestionCache.keys().next().value;
suggestionCache.delete(firstKey);
}
return data[1];
} catch (error) { } catch (error) {
console.error('Error fetching suggestions:', error); console.error('Error fetching suggestions:', error);
return []; return [];
@ -63,29 +79,20 @@ document.addEventListener('DOMContentLoaded', () => {
const items = resultsWrapper.querySelectorAll('li'); const items = resultsWrapper.querySelectorAll('li');
if (event.key === 'ArrowUp' || event.key === 'ArrowDown' || event.key === 'Tab') { if (event.key === 'ArrowUp' || event.key === 'ArrowDown' || event.key === 'Tab') {
event.preventDefault(); event.preventDefault();
if (items[currentIndex]) items[currentIndex].classList.remove('selected');
// Remove 'selected' class from the current item currentIndex = event.key === 'ArrowUp'
if (items[currentIndex]) { ? (currentIndex > 0 ? currentIndex - 1 : items.length - 1)
items[currentIndex].classList.remove('selected'); : (currentIndex + 1) % items.length;
}
if (event.key === 'ArrowUp') {
currentIndex = (currentIndex > 0) ? currentIndex - 1 : items.length - 1;
} else {
currentIndex = (currentIndex + 1) % items.length;
}
// Add 'selected' class to the new item and update the input value
if (items[currentIndex]) { if (items[currentIndex]) {
items[currentIndex].classList.add('selected'); items[currentIndex].classList.add('selected');
searchInput.value = items[currentIndex].textContent; searchInput.value = items[currentIndex].textContent;
} }
} else if (event.key === 'Enter') { } else if (event.key === 'Enter' && currentIndex > -1 && items[currentIndex]) {
if (currentIndex > -1 && items[currentIndex]) {
event.preventDefault(); event.preventDefault();
selectSuggestion(items[currentIndex]); selectSuggestion(items[currentIndex]);
} }
}
}); });
// Function to handle suggestion selection // Function to handle suggestion selection
@ -94,14 +101,8 @@ document.addEventListener('DOMContentLoaded', () => {
searchInput.value = query; searchInput.value = query;
resultsWrapper.innerHTML = ''; resultsWrapper.innerHTML = '';
searchWrapper.classList.remove('wrapper-searching'); searchWrapper.classList.remove('wrapper-searching');
// Submit the form or navigate to search results
const form = searchInput.closest('form'); const form = searchInput.closest('form');
if (form) { form ? form.submit() : window.location.href = `/search?q=${encodeURIComponent(query)}&t=web`;
form.submit();
} else {
window.location.href = `/search?q=${encodeURIComponent(query)}&t=web`;
}
} }
// Handle clicks on search suggestions // Handle clicks on search suggestions