Upstream Pagefind/pagefind state of the touched files before 5b9dce52fe

This commit is contained in:
upstream committed 2026-10-03 01:37:49 +02:00
1 parent 573bb96204
commit ebcea5d0cb
3 files changed
+1623

No files matched your search

+182
View File
@@ -0,0 +1,182 @@
---
title: "Searching multiple sites"
nav_title: "Searching multiple sites"
nav_section: Searching
weight: 80
---
Pagefind can be configured to search across multiple sites, merging results and filters into a single response. Multisite search configuration happens entirely in the browser, by pointing one Pagefind instance at multiple search bundles.
The following examples reflect Pagefind running on a website at `blog.example.com` that wants to include pages from `docs.example.com` in the search results. _(Note that in a matching setup across domains, you will need to configure CORS headers. See [Cross origin indexes](#cross-origin-indexes) below.)_
## Searching additional sites from the Component UI
When configuring the Component UI, include a `mergeIndex` option with an array of additional indexes to merge into the main index. The URL should be the path to a pagefind bundle folder.
{{< diffcode >}}
```js
// Running on blog.example.com
const { configureInstance } = window.PagefindComponents;
+configureInstance("default", {
+ mergeIndex: [{
+ bundlePath: "https://docs.example.com/pagefind"
+ }]
+});
```
{{< /diffcode >}}
Pagefind options can be passed to the additional indexes alongside the `bundlePath`:
{{< diffcode >}}
```js
// Running on blog.example.com
const { configureInstance } = window.PagefindComponents;
configureInstance("default", {
+ // ... options for the blog.example.com index
mergeIndex: [{
bundlePath: "https://docs.example.com/pagefind",
+ // ... options for the docs.example.com index
}]
});
```
{{< /diffcode >}}
## Searching additional sites from the Pagefind JS API
Using an initialized instance of Pagefind, await the `mergeIndex` function to add an additional index. The URL should be the path to a pagefind bundle folder.
{{< diffcode >}}
```js
// Running on blog.example.com
const pagefind = await import("/pagefind/pagefind.js");
+await pagefind.mergeIndex("https://docs.example.com/pagefind");
```
{{< /diffcode >}}
Pagefind options can be passed to the additional index as a second argument:
{{< diffcode >}}
```js
// Running on blog.example.com
const pagefind = await import("/pagefind/pagefind.js");
+await pagefind.options({/* ... options for the blog.example.com index */});
await pagefind.mergeIndex(
"https://docs.example.com/pagefind",
+ {/* ... options for the docs.example.com index */}
);
```
{{< /diffcode >}}
## Changing the weighting of individual indexes
When searching across multiple sites you may want to rank each index higher or lower than the others. This can be achieved by passing an `indexWeight` option for each index:
{{< diffcode >}}
```js
// Component UI:
const { configureInstance } = window.PagefindComponents;
configureInstance("default", {
+ indexWeight: 2,
mergeIndex: [{
bundlePath: "https://docs.example.com/pagefind",
+ indexWeight: 0.5
}]
});
// JS API:
const pagefind = await import("/pagefind/pagefind.js");
+await pagefind.options({ indexWeight: 2 });
await pagefind.mergeIndex("https://docs.example.com/pagefind", {
+ indexWeight: 0.5
});
```
{{< /diffcode >}}
## Filtering results by index
When searching across multiple sites you may want to filter to each index, without having to tag every page on each site with the filter. This can be achieved with the `mergeFilter` option on each index:
{{< diffcode >}}
```js
// Component UI:
const { configureInstance } = window.PagefindComponents;
configureInstance("default", {
+ mergeFilter: {
+ resource: "Blog"
+ },
mergeIndex: [{
bundlePath: "https://docs.example.com/pagefind",
+ mergeFilter: {
+ resource: "Documentation"
+ }
}]
});
// JS API:
const pagefind = await import("/pagefind/pagefind.js");
+await pagefind.options({ mergeFilter: { resource: "Blog" } });
await pagefind.mergeIndex("https://docs.example.com/pagefind", {
+ mergeFilter: {
+ resource: "Documentation"
+ }
});
```
{{< /diffcode >}}
## Merging a specific language index
Pagefind will attempt to grab a matching language when merging an index, falling back to the default language for that index. You can change this behavior by passing a `language` option:
{{< diffcode >}}
```js
// Component UI:
const { configureInstance } = window.PagefindComponents;
configureInstance("default", {
mergeIndex: [{
bundlePath: "https://docs.example.com/pagefind",
+ language: "pt-br"
}]
});
// JS API:
const pagefind = await import("/pagefind/pagefind.js");
await pagefind.mergeIndex("https://docs.example.com/pagefind", {
+ language: "pt-br"
});
```
{{< /diffcode >}}
## Notes
### Cross origin indexes
Due to index merging happening in the browser, your additional search indexes must be configured with [Cross-Origin Resource Sharing (CORS)](https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS) headers if they span separate domains. For example, to configure these headers in a CloudCannon `routing.json` file:
```json
{
"headers": [
{
"match": "/pagefind/.*",
"headers": [
{
"name": "Access-Control-Allow-Origin",
"value": "*"
}
]
}
]
}
```
### Merging multiple languages
Merged indexes will be searched using the WebAssembly module from your main instance. This means that merging an index from another language will use the language support from your main Pagefind instance.
File diff suppressed because it is too large. Load diff
+435
View File
@@ -0,0 +1,435 @@
export {};
declare global {
/** Global index options that can be passed to pagefind.options() */
type PagefindIndexOptions = {
/** Overrides the URL path that Pagefind uses to load its search bundle */
basePath?: string;
/** Appends the given baseURL to all search results. May be a path, or a full domain */
baseUrl?: string;
/** The maximum length of excerpts that Pagefind should generate for search results. Default to 30 */
excerptLength?: number;
/** If set, Pagefind will wrap excerpts with this string on any end that was truncated. Defaults to none */
excerptEllipsis?: string;
/**
* Multiply all rankings for this index by the given weight.
*
* Only applies in multisite setups, where one site should rank higher or lower than others.
*/
indexWeight?: number;
/**
* Merge this filter object into all search queries in this index.
*
* Only applies in multisite setups.
*/
mergeFilter?: Object;
/**
* If set, will add the search term as a query parameter under this key, for use with Pagefind's highlighting script.
*/
highlightParam?: string;
language?: string;
/**
* Whether an instance of Pagefind is the primary index or not (for multisite).
*
* This is set for you automatically, so it is unlikely you should set this directly.
*/
primary?: boolean;
/**
* Provides the ability to fine tune Pagefind's ranking algorithm to better suit your dataset.
*/
ranking?: PagefindRankingWeights;
/**
* If set, diacritics are treated as fully distinct words.
* This means searching for "café" will only match pages containing "café", not "cafe".
* When false (default), diacritics are normalized and all variants match,
* with ranking.diacriticSimilarity applied to favor close matches.
*/
exactDiacritics?: boolean;
/**
* Force Pagefind to run on the main thread instead of using a web worker.
*
* By default, Pagefind will use a web worker for search operations when available,
* falling back to the main thread if workers are not supported or fail to initialize.
*/
noWorker?: boolean;
/**
* If set, replaces the default cache-busting timestamp on the metadata request
* with a fixed string. Useful for PWAs and offline-capable sites so that
* a service worker can cache the request. Change this value each time
* you rebuild your site.
*
* The search index itself is unaffected and can always be cached.
*/
metaCacheTag?: string;
};
type PagefindRankingWeights = {
/**
Controls page ranking based on similarity of terms to the search query (in length).
Increasing this number means pages rank higher when they contain words very close to the query,
e.g. if searching for `part` then `party` will boost a page higher than one containing `partition`.
Minimum value is 0.0, where `party` and `partition` would be viewed equally.
*/
termSimilarity?: Number;
/**
Controls how much effect the average page length has on ranking.
Maximum value is 1.0, where ranking will strongly favour pages that are shorter than the average page on the site.
Minimum value is 0.0, where ranking will exclusively look at term frequency, regardless of how long a document is.
*/
pageLength?: Number;
/**
Controls how quickly a term saturates on the page and reduces impact on the ranking.
Maximum value is 2.0, where pages will take a long time to saturate, and pages with very high term frequencies will take over.
As this number trends to 0, it does not take many terms to saturate and allow other parameters to influence the ranking.
Minimum value is 0.0, where terms will saturate immediately and results will not distinguish between one term and many.
*/
termSaturation?: Number;
/**
Controls how much ranking uses term frequency versus raw term count.
Maximum value is 1.0, where term frequency fully applies and is the main ranking factor.
Minimum value is 0.0, where term frequency does not apply, and pages are ranked based on the raw sum of words and weights.
Values between 0.0 and 1.0 will interpolate between the two ranking methods.
Reducing this number is a good way to boost longer documents in your search results, as they no longer get penalized for having a low term frequency.
*/
termFrequency?: Number;
/**
Controls how much boost is applied when the search query diacritics match the indexed word exactly.
At 1.0, searching for "café" will boost pages containing "café" by 100% over pages containing "cafe".
At 0.0, no boost is applied and all diacritic variants are treated equally.
Must be >= 0
*/
diacriticSimilarity?: Number;
/**
Controls boost weights for metadata field matches.
Keys are meta field names (e.g., "title", "description").
Increasing values boost scores when a search term matches in that meta field.
Default: {"title": 5.0} meaning title matches get 5x boost.
Provided weights are merged with defaults.
Example: { description: 2.0 } — adds description boost while keeping the default title boost.
*/
metaWeights?: Record<string, number>;
};
/** Options that can be passed to pagefind.search() */
type PagefindSearchOptions = {
/** If set, this call will load all assets but return before searching. Prefer using pagefind.preload() instead */
preload?: boolean;
/** Add more verbose console logging for this search query */
verbose?: boolean;
/** The set of filters to execute with this search. Input type is extremely flexible, see the filtering docs for details */
filters?: Object;
/** The set of sorts to use for this search, instead of relevancy */
sort?: Object;
};
/** Filter counts returned from pagefind.filters(), and alongside results from pagefind.search() */
type PagefindFilterCounts = Record<string, Record<string, number>>;
/** The main results object returned from a call to pagefind.search() */
type PagefindSearchResults = {
/** All pages that match the search query and filters provided */
results: PagefindSearchResult[];
/** How many results would there have been if you had omitted the filters */
unfilteredResultCount: number;
/** Given the query and filters provided, how many remaining results are there under each filter? */
filters: PagefindFilterCounts;
/** If the searched filters were removed, how many total results for each filter are there? */
totalFilters: PagefindFilterCounts;
/** Information on how long it took Pagefind to execute this query */
timings: {
preload: number;
search: number;
total: number;
};
/** Verbose information on stemming returned in the Pagefind Playground */
search_keywords?: string[];
/** Verbose query term IDF breakdown returned in the Pagefind Playground */
query_term_idfs?: PagefindQueryTermIdf[];
};
/** Query term IDF info for playground mode */
type PagefindQueryTermIdf = {
/** The search term (stemmed form) */
term: string;
/** IDF value for this term */
idf: number;
};
/** The main results object returned from a call to pagefind.search() */
type PagefindIndexesSearchResults = {
/** All pages that match the search query and filters provided */
results: PagefindSearchResult[];
/** How many results would there have been if you had omitted the filters */
unfilteredResultCount: number;
/** Given the query and filters provided, how many remaining results are there under each filter? */
filters: PagefindFilterCounts;
/** If the searched filters were removed, how many total results for each filter are there? */
totalFilters: PagefindFilterCounts;
/** Information on how long it took Pagefind to execute this query */
timings: {
preload: number;
search: number;
total: number;
}[];
/** Verbose information on stemming returned in the Pagefind Playground */
search_keywords?: string[];
/** Verbose query term IDF breakdown returned in the Pagefind Playground */
query_term_idfs?: PagefindQueryTermIdf[];
/** Verbose information on what environment the Pagefind search was executed in [worker, mainthread] */
search_environment?: string;
};
/** A single result from a search query, before actual data has been loaded */
type PagefindSearchResult = {
/** Pagefind's internal ID for this page, unique across the site */
id: string;
/** Pagefind's internal score for your query matching this page, that is used when ranking these results */
score: number;
/** The locations of all matching words in this page */
words: number[];
/** Verbose information returned in the Pagefind playground mode */
params?: PagefindTermParams;
/** Verbose information returned in the Pagefind playground mode */
scores?: PagefindTermScore[];
/**
* Which metadata fields (e.g., "title", "author") matched the search query.
* Present when search terms were found in metadata fields, allowing consumers to
* indicate matches came from metadata rather than body content.
*/
matchedMetaFields?: string[];
/** Verbose metadata scoring returned in the Pagefind Playground */
verbose_meta_scores?: PagefindMetaScore[];
/**
* Calling data() loads the final data fragment needed to display this result.
*
* Only call this when you need to display the data, rather than all at once.
* (e.g. one page as a time, or in a scroll listener)
* */
data: () => Promise<PagefindSearchFragment>;
};
export type PagefindTermParams = {
/** Length of this result */
document_length: number;
/** Average page length */
average_page_length: number;
/** Total pages */
total_pages: number;
};
type PagefindTermScore = {
/** Term */
search_term: string;
/** Inverse document frequency for term — how common is this word */
idf: number;
/** Term frequency, saturating */
saturating_tf: number;
/** Term frequency, raw counts */
raw_tf: number;
/** Pagefind output term frequency */
pagefind_tf: number;
/** Final score for term */
score: number;
/** Input parameters used for the score */
params: PagefindTermScoreParams;
};
export type PagefindTermScoreParams = {
/** Weighted term frequency */
weighted_term_frequency: number;
/** Pages containing term */
pages_containing_term: number;
/** Length bonus */
length_bonus: number;
};
/** Verbose metadata scoring info returned in the Pagefind Playground */
type PagefindMetaScore = {
/** Metadata field name */
field_name: string;
/** Configured weight for this field */
field_weight: number;
/** Which search terms matched in this field */
matched_terms: string[];
/** Sum of IDF for matched terms */
matched_idf: number;
/** Total IDF across all query terms */
query_total_idf: number;
/** Coverage ratio (matched_idf / query_total_idf) */
coverage: number;
/** Final boost contribution from this field */
coverage_boost: number;
};
/** The useful data Pagefind provides for a search result */
type PagefindSearchFragment = {
/** Pagefind's processed URL for this page. Will include the baseUrl if configured */
url: string;
/** Pagefind's unprocessed URL for this page */
raw_url?: string;
/** The full processed content text of this page */
content: string;
/** Internal type — ignore for now */
raw_content?: string;
/** The processed excerpt for this result, with matching terms wrapping in `<mark>` elements */
excerpt: string;
/** The processed excerpt for this result, without any `<mark>` elements wrapping matching terms */
plain_excerpt: string;
/**
* What regions of the page matched this search query?
*
* Precalculates based on h1->6 tags with IDs, using the text between each.
*/
sub_results: PagefindSubResult[];
/** How many total words are there on this page? */
word_count: number;
/** The locations of all matching words in this page */
locations: number[];
/**
* The locations of all matching words in this page,
* paired with data about their weight and relevance to this query
*/
weighted_locations: PagefindWordLocation[];
/** The filter keys and values this page was tagged with */
filters: Record<string, string[]>;
/** The metadata keys and values this page was tagged with */
meta: Record<string, string>;
/**
* The raw anchor data that Pagefind used to generate sub_results.
*
* Contains _all_ elements that had IDs on the page, so can be used to
* implement your own sub result calculations with different semantics.
*/
anchors: PagefindSearchAnchor[];
};
/** Data for a matched section within a page */
type PagefindSubResult = {
/**
* Title of this sub result — derived from the heading content.
*
* If this is a result for the section of the page before any headings with IDs,
* this will be the same as the page's meta.title value.
*/
title: string;
/**
* Direct URL to this sub result, comprised of the page's URL plus the hash string of the heading.
*
* If this is a result for the section of the page before any headings with IDs,
* this will be the same as the page URL.
*/
url: string;
/** The locations of all matching words in this segment */
locations: number[];
/**
* The locations of all matching words in this segment,
* paired with data about their weight and relevance to this query
*/
weighted_locations: PagefindWordLocation[];
/** The processed excerpt for this segment, with matching terms wrapping in `<mark>` elements */
excerpt: string;
/** The processed excerpt for this segment, without any `<mark>` elements wrapping matching terms */
plain_excerpt: string;
/**
* Raw data about the anchor element associated with this sub result.
*
* The omission of this field means this sub result is for text found on the page
* before the first heading that had an ID.
*/
anchor?: PagefindSearchAnchor;
};
/** Information about a matching word on a page */
type PagefindWordLocation = {
/** The weight that this word was originally tagged as */
weight: number;
/**
* An internal score that Pagefind calculated for this word.
*
* The absolute value is somewhat meaningless, but the value can be used
* in comparison to other values in this set of search results to perform custom ranking.
*/
balanced_score: number;
/**
* The index of this word in the result content.
*
* Splitting the content key by whitespacing and indexing by this number
* will yield the correct word.
*/
location: number;
/**
* Verbose word information returned when running Pagefind in playground mode.
*/
verbose?: PagefindVerboseWordLocation;
};
/** Verbose playground information about a matching word on a page */
type PagefindVerboseWordLocation = {
/**
* The indexed string for this word, usually stemmed.
*/
word_string: string;
/**
* The scoring bonus this word received based on length similarity to a search term.
*/
length_bonus: number;
};
/**
* An independent Pagefind instance returned by createInstance().
* Each instance has its own configuration and search state.
* All instances share a single web worker and WASM module internally.
*/
type PagefindInstance = {
/** Update options for this instance */
options: (opts: PagefindIndexOptions) => Promise<void>;
/** Wait for this instance to finish initializing (WASM load, etc.) */
init: () => Promise<void>;
/** Destroy this instance and terminate its worker */
destroy: () => Promise<void>;
/** Merge an additional index into this instance */
mergeIndex: (
indexPath: string,
options: PagefindIndexOptions,
) => Promise<void>;
/** Search this instance's index */
search: (
term: string,
options?: PagefindSearchOptions,
) => Promise<PagefindIndexesSearchResults>;
/** Debounced search — returns null if superseded by a newer call */
debouncedSearch: (
term: string,
options?: PagefindSearchOptions,
debounceTimeoutMs?: number,
) => Promise<PagefindIndexesSearchResults | null>;
/** Preload index chunks for a search term without returning results */
preload: (term: string, options?: PagefindSearchOptions) => Promise<void>;
/** Retrieve all available filter values and counts */
filters: () => Promise<PagefindFilterCounts>;
};
/** Raw data about elements with IDs that Pagefind encountered when indexing the page */
type PagefindSearchAnchor = {
/** What element type was this anchor? e.g. `h1`, `div` */
element: string;
/** The raw id="..." attribute contents of the element */
id: string;
/** An explicit sub-result title supplied by a data-pagefind-title attribute */
title?: string;
/**
* The text content of this element.
*
* In order to prevent repeating most of the page data for every anchor,
* Pagefind will only take top level text nodes, or text nodes nested within
* inline elements such as <a> and <span>.
*/
text?: string;
/**
* The position of this anchor in the result content.
* Splitting the content key by whitespacing and indexing by this number
* will yield the first word indexed after this element's ID was found.
*/
location: number;
};
}