In the interest of saving money, less complication and patching, less maintenance, I have moved my travel and photography blog from an AWS server running Ubuntu to a static site hosted in an S3 bucket. It is still based on WordPress, but I host that internally at home on a box and generate a static export of the site and upload it to the S3 bucket when I have an update.
One function I wanted to retain was the ability to search the site. I have posts going back over 10 years (never read your own cringe writing from 10 years ago, never) and sometimes I want to find a particular reference for something and search is the quickest way to do it.
I tried adding a Custom Google Programmable thing but failed miserably, and really wanted something that didn’t rely on anything else, other than the hosted files. It took me a little bit of time, and trial and error, but I got something working I am happy with.
Step 1. Create an index
First thing you need is an index, something a JavaScript search app can go through. I found a WordPress Plugin designed to export the content of a blog to a JSON file, Exporting Blog Posts to JSON which was a great starting point, but then I updated it for me needs - specifically changing the format of the output to something that can be read by Lunr. I just zipped the PHP file and uploaded it to WordPress as a new plugin.
This plugin generates a JSON file of all the (written) content in the form of:
1
2
3
4
5
6
{
"id": “<internal WP post ID>”,
"title": “Title”,
"content": “ALL THE WORDS...”,
"url": "https://<internal non-public server>/2024/03/Title/“
},
When exporting the JSON file it has a URL which is based on my internal network and the hosted WordPress server I used for writing and updates - a quick find and replace in a text editor fixes that:
1
2
3
4
5
6
{
"id": “<internal WP post ID>”,
"title": “Title”,
"content": “ALL THE WORDS...”,
"url": "https://<public-host>/2024/03/Title/“
},
Now you have an index that can be easily searched by something, you need something. The something I found that worked for me is Lunr.
Step 2. Search Scripts
For my site I added a /search/
URL with a “WordPress page” that was pretty much blank. I had tried adding the HTML to the page as an “HTML” block, but Gutenberg and WordPress just kept destroying the code by adding random tags through it all. It did not understand it was supposed to be some scripts.
I also included lunr.js
as a file hosted in my S3 bucket.
Outside of the WordPress I edit the index.html
that is generated by the static export process to include the following code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
<p><strong>Search the site:</strong></p>
<p><input type="text" id="searchQuery" placeholder="Search..." class="wp-block-search__input">
<button onclick="performSearch()">Search</button></p>
<p><b>Search Results</b></p>
<ul id="results" style="list-style: disc;list-style-position: inside;"></ul>
<script src="https://<static-host>/search/lunr.js"></script>
<script>
// Load the JSON index and create Lunr index
let idx, documents;
async function loadIndex() {
const response = await fetch('https://<static-host>/search/blog-json-all.json'); // URL to your JSON index from step 1
documents = await response.json();
// Create Lunr index
idx = lunr(function () {
this.ref('id');
this.field('title');
this.field('content');
documents.forEach(function (doc) {
this.add(doc);
}, this);
});
}
// Perform the search
async function performSearch() {
const query = document.getElementById('searchQuery').value;
if (!idx) {
await loadIndex(); // Ensure index is loaded
}
const results = idx.search(query);
// Display the search results
const resultsContainer = document.getElementById('results');
resultsContainer.innerHTML = ''; // Clear previous results
if (results.length === 0) {
resultsContainer.innerHTML = '<li>No results found<\/li>';
return;
}
results.forEach(result => {
const doc = documents.find(d => d.id === result.ref);
const listItem = document.createElement('li');
listItem.innerHTML = `<a href="${doc.url}">${doc.title}<\/a>`;
resultsContainer.appendChild(listItem);
});
}
// Load the index when the page loads
document.addEventListener('DOMContentLoaded', loadIndex);
</script>
</div>
(Please excuse the slightly odd syntax highlighting, ‘javascript’ was the only way to get that display right at all.)
This gives a plain and simple search function that works across all browsers I have tested it with, and looks kinda like this: Cheese
Yes, I am VERY aware of the irony of posting about creating a static search function for a static WordPress blog on something generated with Jekyll.