react-minisearch
react-minisearch copied to clipboard
Allow passing pre-built search index to useMiniSearch
As we discussed in an issue in the main MiniSearch repo, in some use cases it makes sense to pre-build a search index, so that one can provide and load this index. However, the useMiniSearch hook only allows providing an array of documents currently.
I concede that this is not necessarily a common use case, and my team and I can work around it easily, so don't feel obligated to push this out in a hurry - I just wanted to leave an issue for future reference 🙂
Hi @Zloka , thanks for opening this issue here.
The main obstacles to implement such feature is to come up with a consistent and reasonably simple API. Currently, useMiniSearch is designed to facilitate a common case when using MiniSearch, but as it optimizes for this common use case, it leaves other cases out of scope. In particular, useMiniSearch assumes that you want full documents as search results (not just document IDs and stored fields, like when directly using MiniSearch). For this purpose, it internally maintains a map of documents by ID.
Now, to illustrate the issue, a possible hook to load a pre-built serialized index could look like this:
const { search, searchResults } = useMiniSearchLoadJSON(jsonString, options)
This version won't work though, because the serialized index in jsonString does not include the original documents, so it would be impossible to build a map of document by id. Therefore, there would be two possible alternatives:
- either also take the document list as an argument, as in
useMiniSearchLoadJSON(jsonString, documents, options). This works, but introduces a cumbersome dependency between arguments: the document list MUST be the same list of documents from which the index was created - or change the contract of
useMiniSearchFromJSONcompletely with respect touseMiniSearch, returning document IDs as results instead of documents.
The first options looks a bit more acceptable. It could throw an error at runtime if a search result cannot be found among the provided documents. Unfortunately, it is still a bit cumbersome.
So far, I assumed that react-minisearch should optimize only for the most basic case, and let developers take control by using MiniSearch directly when departing from the basic case. This assumption might change, but I still would want to keep the API very simple, as otherwise there would be no reason to use react-minisearch over working directly with MiniSearch.
For reference, here is how one would implement the loadJSON case without react-minisearch:
const MyComponent = ({ jsonIndex, documents, options }) => {
const miniSearchRef = useRef(MiniSearch.loadJSON(jsonIndex, options))
const documentByIdRef = useRef(documents.reduce((byId, doc) => {
byId[doc.id] = doc
return byId
}, {}))
const [rawResults, setRawResults] = useState(null)
const [searchResults, setSearchResults] = useState(null)
const search = (query, searchOptions) => {
const results = miniSearchRef.current.search(query, searchOptions)
setRawResults(results)
setSearchResults(results.map(({ id }) => documentByIdRef.current[id])
}
const handleSearchChange = (event) => {
search(event.target.value, { prefix: true, fuzzy: 2 })
}
return (
<form>
<input type="search" onChange={handleSearchChange} />
<ol>
{
searchResults && searchResults.map((result, i) => {
return <li key={i}>result.title</li>
})
}
</ol>
</form>
)
}