-
Notifications
You must be signed in to change notification settings - Fork 179
Adding Site-Wide Search to Ignite #819
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
Conceptually the idea of site-wide search is great, and would be a welcome addition. I have my usual concerns about adding a third-party dependency, particularly for filtering based on only three properties, but putting that to one side this feels like an odd API shape. If this were a regular Ignite page type (alongside |
The implementation uses only three properties for standard pages—and five properties for articles—to prevent performance issues. A search index is a JSON file hosted on the website. Say a website has 100 searchable pages: indexing three properties that naturally have the highest concentration of keywords creates a much smaller index file than indexing the entire body of each page—which would create an index file that is essentially 100 pages in size. This "tradeoff" isn't unique to lunr.js, but to client-side search in general. That said, I think the solution—encouraging folks to write highly searchable titles and descriptions—is a good thing.
I had considered making a The view used for a search result needs to be defined by itself, because it creates a reusable Something like this isn't possible: struct MySearchPage: SearchPage {
var body: some HTML {
Text("Search Results")
ForEach(searchResults) {
result.title
}
}
} Because we need to generate HTML that looks like this for the search result: <template>
<p class="result-title"></p>
</template> But we don't know where the reusable template starts and ends in the view tree of To make a struct MySearchPage: SearchPage {
func resultView(result: SearchResult) → some HTML {
result.title
.font(.title4)
.fontWeight(.bold)
.foregroundStyle(.bootstrapPurple)
result.description
.foregroundStyle(.black)
.lineSpacing(1.2)
}
var body: some HTML {
ForEach(searchResults) { // implicit property, but this property could also be used in the above method, which shouldn't have access to it
result.title
}
}
} Or this: struct MySearchPage: SearchPage {
func resultView(result: SearchResult) -> some HTML {
result.title
.font(.title4)
.fontWeight(.bold)
.foregroundStyle(.bootstrapPurple)
result.description
.foregroundStyle(.black)
.lineSpacing(1.2)
}
func body(searchResults: [SearchResultView]) -> some HTML {
ForEach(searchResults) {
result.title
}
}
func noResultsView() -> some HTML {
Text("No results...")
}
} Those API shapes look like nothing else in Ignite. What's more, the current API allows people to create different search pages for each instance of |
Could we perhaps specialise This would be somewhat transparent to the user while giving us all the necessary control required for this sort of behaviour 🤔 |
Why is that |
I believe the reason is simply because the results & html elements are generated at runtime and need to be populated dynamically? We can define the |
@MrSkwiggs is correct. The |
Unfortunately not. We can specialize a type based only on its children, not its parent. |
If we wanted to use a struct MySearchPage: SearchPage {
var body: some HTML {
Text("Search Results")
ForEach(searchResults) {
SearchResult {
result.title
}
}
}
} |
What about the "override its declaration in We could do something like: public extension SearchPage {
struct ForEach {
// Use regular ForEach under the hood but wrap contents with the extra <template> anchors
}
} |
Unfortunately you can't nest types inside protocols in Swift. But that idea has put us on the right path! What we could do is create a custom initializer for |
This PR offers a possible implementation of site-wide search leveraging lunr.js, which integrates with Ignite with a single JS file, much like Prism. During publishing, we use the
title
,description
, andurl
properties of all pages—and also thetags
anddate
properties of articles—to build a search index that lunr queries via its JS.Pages that lack a description will be ignored and emit a warning during publishing.
The API looks like this:
And looks like this in action:
You can see site-wide search in action on this branch of IgniteSamples.