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', () => {
const searchInput = document.getElementById('search-input');
@ -7,14 +7,30 @@ document.addEventListener('DOMContentLoaded', () => {
const resultsWrapper = document.querySelector('.autocomplete');
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) {
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 {
const response = await fetch(`/suggestions?q=${encodeURIComponent(query)}`);
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) {
console.error('Error fetching suggestions:', error);
return [];
@ -63,29 +79,20 @@ document.addEventListener('DOMContentLoaded', () => {
const items = resultsWrapper.querySelectorAll('li');
if (event.key === 'ArrowUp' || event.key === 'ArrowDown' || event.key === 'Tab') {
event.preventDefault();
if (items[currentIndex]) items[currentIndex].classList.remove('selected');
// Remove 'selected' class from the current item
if (items[currentIndex]) {
items[currentIndex].classList.remove('selected');
}
currentIndex = event.key === 'ArrowUp'
? (currentIndex > 0 ? currentIndex - 1 : items.length - 1)
: (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]) {
items[currentIndex].classList.add('selected');
searchInput.value = items[currentIndex].textContent;
}
} else if (event.key === 'Enter') {
if (currentIndex > -1 && items[currentIndex]) {
} else if (event.key === 'Enter' && currentIndex > -1 && items[currentIndex]) {
event.preventDefault();
selectSuggestion(items[currentIndex]);
}
}
});
// Function to handle suggestion selection
@ -94,14 +101,8 @@ document.addEventListener('DOMContentLoaded', () => {
searchInput.value = query;
resultsWrapper.innerHTML = '';
searchWrapper.classList.remove('wrapper-searching');
// Submit the form or navigate to search results
const form = searchInput.closest('form');
if (form) {
form.submit();
} else {
window.location.href = `/search?q=${encodeURIComponent(query)}&t=web`;
}
form ? form.submit() : window.location.href = `/search?q=${encodeURIComponent(query)}&t=web`;
}
// Handle clicks on search suggestions