2026-08-04 - false
With the index populated and ready to go, we're reading to fullfill a search request. The code presented in this section is steps 5, 6 and 8 from the worker sequence diagram (click "Worker sequence diagram" below for another look at the sequence diagram).
Worker sequence diagram
sequenceDiagram
autonumber
actor User
participant client as client<br>(search.js)
participant worker as worker<br>(search-worker.js)
note over client, worker: index created <br>when page loads
client ->> worker: FlexSearch.CREATE_INDEX
activate worker
create participant index
worker ->> index: create index
worker->> client: FlexSearch.INDEX_READY
note over client, worker: 5K docs indexed in 2/3 of second!
deactivate worker
User ->> client: send search request
client ->> worker: FlexSearch.SEARCH_INDEX
activate worker
worker ->> index: search index
index ->> worker: search results
worker ->> client: FlexSearch.SEARCH_RESULTS
deactivate worker
client ->> User: render search results
FlexSearch Web worker sequence diagram
The steps to perform a search are:
- The user enters search value.
search.jscaptures the search value and issues aFlexSearch.SEARCH_INDEXmessage (which includes the search arguments FlexSearch needs).- Upon receiving the
FlexSearch.SEARCH_INDEX, the worker uses the search data in the message to perform the search and issues a FlexSearch.SEARCH_RESULTS message (which includes the search results). - Upon receiving the
FlexSearch.SEARCH_RESULTSmessage, using the search results sent with the message,search.jsrenders the search results for the user to see.
let indexReady = false;
let searchDebounceTimeoutId = null;
/*
* Watch for worker messages.
*/
worker.addEventListener("message", (event) => {
const { type, data } = event.data;
switch (type) {
case FlexSearch.INDEX_READY:
indexReady = true;
case FlexSearch.SEARCH_RESULTS:
renderSearchResults(data);
}
});
/*
* Value to search for changed.
*/
input.addEventListener("input", () => {
const query = input.value.trim();
if (!query || !indexReady) return;
clearTimeout(searchDebounceTimeoutId);
searchDebounceTimeoutId = setTimeout(() => {
submitSearchRequest();
}, 300); // 300ms debounce
});
function submitSearchRequest() {
const query = input.value.trim();
const searchArgs = {
query,
enrich: true,
};
worker.postMessage({ type: FlexSearch.SEARCH_INDEX, data: { searchArgs } });
}
// Render search results.
function renderSearchResults(docs) {
for (const doc of docs) {
const excludeTags = ["posts", "links"];
const divStart = '<div class="tags-wrapper">';
const divEnd = "</div>";
const tags = doc.tags.split(",");
let tagsString = [];
tags.forEach((tag) => {
if (!excludeTags.includes(tag)) {
tagsString.push(`<span class="tags-list">${tag}</span>`);
}
});
const html = `<li class="search-result-row">${divStart}<a href="${doc.url}">${doc.id} - ${doc.title}</a>${tagsString.join("")}${divEnd}</li>`;
results.insertAdjacentHTML("beforeend", html);
}
}