Adding Search Functionality to a Next.js Blog
TL;DR
I wanted search functionality after all, so I implemented it using morphological analysis and flexsearch. The sensitivity still feels a bit off, so it might be better to combine it with n-grams.
Basic Strategy
Since I want full AMP compatibility, queries are received in getServerSideProps and passed through flexsearch. The search targets are cached in a data.js file before uploading to the server, and that file is referenced.
The form is created like this, using GET to send the user to the search page with something like /search?q=word.
For AMP forms, the target attribute is required and must be either _top or _blank. _top navigates in the same tab, while _blank opens a new tab.
Morphological Analysis
I used kuromojin. First, fetch all blog posts, convert the markdown to text, run morphological analysis, and save the results in a format like cache/data.js. I used strip-markdown for the markdown-to-text conversion.
From the morphological analysis results, I removed parts that might interfere with regex (thinking it might cause issues with highlight functionality), and kept nouns, verbs, and adjectives that are useful for search. I also kept only words with a length of 2 or more. These settings should probably be tuned further. For now, only the title and body are searched, but adding more fields is just a matter of including them.
The general flow is: getAllPostsPath fetches all posts, gray-matter reads the content, filterToken extracts the desired tokens, and all extracted tokens are saved as words.
makeCache.js
pre-commit
Since I'd inevitably forget to run this every time, I used a package called husky.
Modify the build scripts section of package.json like this:
package.json
This way, makeCache.js runs automatically with every commit, which is convenient.
When you don't want the pre-commit hook configured via husky to run:
This will skip it.
Search Page
Now let's build the search page. getServerSideProps receives a ctx variable that contains most of the information. This time we only need the query result, so we'll use ctx.query.
Since words are saved as space-separated strings during the makeCache.js step, the flexsearch tokenizer uses a custom one (which simply splits on whitespace to create an array). Everything else follows the flexsearch documentation. The id serves as the URL path, so it's passed through directly.
So that's how I implemented in-site article search for now. Google really is amazing.