Skip to content

Filters

Loupe Search exposes eight filters for changing what gets indexed, how it is weighted, and how results are returned.

Filters use the loupe_search_* prefix. The old wp_loupe_* names still work as deprecated aliases — see Renamed from WP Loupe.

  1. Where to put filter code
  2. Quick reference
  3. Indexing scope
  4. Field content
  5. Schema
  6. Sortability
  7. Results
  8. Recipes
  9. After changing a filter

Put these in a small mu-plugin (wp-content/mu-plugins/loupe-search-tweaks.php) rather than a theme’s functions.php. Indexing also runs from WP-CLI and from wp-cron, where the active theme may not be loaded — a filter that only exists in functions.php can therefore apply on the front end but silently not apply during a CLI reindex, producing an index that disagrees with your settings.

Filters that affect indexing must be registered before indexing runs, so register them at load time (not inside an init callback that runs late).

FilterPassedUse it to
loupe_search_post_typesarray $post_typesChoose which post types are indexed
loupe_search_index_protectedbool $should_indexIndex password-protected posts
loupe_search_db_pathstring $pathMove the index directory
loupe_search_field_{$field_name}mixed $valueClean a core post field before indexing
loupe_search_schema_{$post_type}array $schemaAdd/remove fields, set weights and sorting
loupe_search_is_safely_sortable_{$post_type}bool $sortable, string $field_nameForce a core field to be sortable
loupe_search_is_safely_sortable_meta_{$post_type}bool $sortable, string $field_nameCorrect meta-field sortability detection
loupe_search_posts_per_pageint $per_pageChange results per page

Which post types are indexed. Defaults to the post types selected on the settings screen, falling back to [ 'post', 'page' ].

add_filter( 'loupe_search_post_types', function ( array $post_types ): array {
$post_types[] = 'book';
return $post_types;
} );

Append rather than replace unless you intend to override the settings screen — returning a hard-coded array makes the admin checkboxes have no effect.

Whether the current post should be indexed. The value passed in is empty( $post->post_password ), so password-protected posts arrive as false and everything else as true.

// Index password-protected posts too.
add_filter( 'loupe_search_index_protected', '__return_true' );

The filter receives no post object, so it cannot make a per-post decision — it is an on/off switch. Note that indexed protected posts become findable by their content, which is usually not what a password is for.

Where the Loupe index files live. Defaults to WP_CONTENT_DIR . '/loupe-search-db'.

add_filter( 'loupe_search_db_path', function ( string $path ): string {
return WP_CONTENT_DIR . '/uploads/loupe-search-db';
} );

Use an absolute path that PHP can write to, and exclude it from version control. Changing this after indexing points the plugin at an empty directory — reindex afterwards.

Transforms a field’s value just before it is indexed. {$field_name} is the field name, e.g. loupe_search_field_post_content.

// Strip HTML (including block comments) from post_content before indexing.
add_filter( 'loupe_search_field_post_content', 'wp_strip_all_tags' );

This only fires for properties that exist on the WP_Post objectpost_content, post_title, post_excerpt, and so on. It does not fire for custom fields or taxonomy values, so loupe_search_field_my_meta_key will never run. To change how meta is indexed, use the schema filter.

Return a string or other scalar; returning an array will not index usefully.

The schema controls which fields are indexed and how they behave. The array you receive is the baseline schema merged with whatever is configured on the settings screen, so read before you write.

Each field accepts:

KeyTypeDefaultMeaning
weightfloat1.0Relevance multiplier in search results
indexableboolfalseInclude the field in the index
filterableboolfalseAllow filtering and terms facets on the field
sortableboolfalseAllow sorting by the field
sort_directionstring'desc'Default direction, 'asc' or 'desc'

sortable is also gated by the sortability checks below — setting it to true on a field that holds arrays will be overridden.

add_filter( 'loupe_search_schema_book', function ( array $schema ): array {
// Add a custom field.
$schema['book_isbn'] = [
'weight' => 2.0,
'indexable' => true,
'filterable' => true,
];
// Make books sort by publication date, newest first.
$schema['book_published'] = [
'weight' => 1.0,
'indexable' => true,
'sortable' => true,
'sort_direction' => 'desc',
];
// Boost titles for this post type.
$schema['post_title']['weight'] = 3.0;
// Stop indexing excerpts.
unset( $schema['post_excerpt'] );
return $schema;
} );

Only post_date is present before settings are applied:

[
'post_date' => [
'weight' => 1.0,
'indexable' => true,
'filterable' => true,
'sortable' => true,
'sort_direction' => 'desc',
],
]

Every other field — post_title, post_content, post_excerpt, post_author, permalink, and any custom fields — is added from the settings screen. Do not assume a field exists in $schema; check first when modifying:

add_filter( 'loupe_search_schema_post', function ( array $schema ): array {
if ( isset( $schema['post_title'] ) ) {
$schema['post_title']['weight'] = 4.0;
}
return $schema;
} );

Before a field is registered as sortable, the plugin checks that its values are scalar — Loupe cannot sort on arrays. These two filters let you override that verdict.

loupe_search_is_safely_sortable_{$post_type}

Section titled “loupe_search_is_safely_sortable_{$post_type}”

Runs for core post fields that are not already classified as sortable or non-sortable. Receives false by default plus the field name.

add_filter( 'loupe_search_is_safely_sortable_book', function ( bool $sortable, string $field_name ): bool {
return 'menu_order' === $field_name ? true : $sortable;
}, 10, 2 );

loupe_search_is_safely_sortable_meta_{$post_type}

Section titled “loupe_search_is_safely_sortable_meta_{$post_type}”

Runs for meta fields. The incoming value comes from sampling the meta value of up to five posts: true if all sampled values were scalar (or geo-points).

That sample can be wrong. If only the sixth post stores an array, the field is wrongly marked sortable and sorting misbehaves; if the first five posts have no value, a perfectly sortable field is marked unsortable. Use this filter to state the answer explicitly:

add_filter( 'loupe_search_is_safely_sortable_meta_book', function ( bool $sortable, string $field_name ): bool {
// This one is always a single number, regardless of what sampling found.
if ( 'book_price' === $field_name ) {
return true;
}
// This one sometimes holds an array — never sort on it.
if ( 'book_contributors' === $field_name ) {
return false;
}
return $sortable;
}, 10, 2 );

Number of results per page for front-end searches. Defaults to the query’s posts_per_page, falling back to Settings → Reading → “Blog pages show at most”.

add_filter( 'loupe_search_posts_per_page', function ( int $per_page ): int {
return 20;
} );

This affects the WordPress search loop only. The REST API takes its page size from the request — see Search API.

Index a custom post type with its custom fields

Section titled “Index a custom post type with its custom fields”
add_filter( 'loupe_search_post_types', function ( array $post_types ): array {
$post_types[] = 'recipe';
return $post_types;
} );
add_filter( 'loupe_search_schema_recipe', function ( array $schema ): array {
$schema['recipe_ingredients'] = [
'weight' => 2.0,
'indexable' => true,
'filterable' => true,
];
$schema['recipe_time'] = [
'weight' => 1.0,
'indexable' => true,
'filterable' => true,
'sortable' => true,
'sort_direction' => 'asc',
];
return $schema;
} );
add_filter( 'loupe_search_is_safely_sortable_meta_recipe', function ( bool $sortable, string $field_name ): bool {
return 'recipe_time' === $field_name ? true : $sortable;
}, 10, 2 );

Strip shortcodes as well as HTML from indexed content

Section titled “Strip shortcodes as well as HTML from indexed content”
add_filter( 'loupe_search_field_post_content', function ( $value ) {
return wp_strip_all_tags( strip_shortcodes( (string) $value ) );
} );

Keep a post type out of the index temporarily

Section titled “Keep a post type out of the index temporarily”
add_filter( 'loupe_search_post_types', function ( array $post_types ): array {
return array_values( array_diff( $post_types, [ 'page' ] ) );
} );

Filters that affect what or how content is indexed — post types, schema, field content, sortability, protected posts — only take effect for content indexed afterwards. Reindex so the existing index matches:

Terminal window
wp loupe-search reindex

Filters that only affect reading, such as loupe_search_posts_per_page, take effect immediately.