Skip to main content

Search & Filtering

FacetWP Pro v4.x for faceted filtering and WP Search with Algolia v2.8.2 for instant search provide advanced product discovery.

Overview

StealthCam uses a dual search architecture:

  1. FacetWP Pro - Faceted filtering on category/catalog pages with AJAX-powered refinement
  2. Algolia - Instant search with autocomplete and typo tolerance
  3. BigCommerce Integration - Custom addon bridges FacetWP with BC product data

Both systems work together seamlessly to provide comprehensive product discovery.


FacetWP Integration

Plugin: FacetWP Pro v4.x
Location: wp-content/plugins/facetwp/
License: Commercial (FacetWP, LLC)
Documentation: https://facetwp.com/documentation/

Purpose

Provide faceted filtering for product catalogs with dynamic refinement based on BigCommerce product attributes like brand, category, price range, features, and custom fields.

Core Features

  • Multiple Facet Types - Checkboxes, dropdowns, sliders, radio buttons, search, etc.
  • AJAX Refresh - No page reload on filter changes
  • URL Parameters - SEO-friendly filter URLs (/products/?_brand=stealth-cam&_price=100-200)
  • Result Count - Display number of matching products
  • Pagination - Integrated with filtered results
  • Template Overrides - Custom result displays
  • Indexing Engine - Fast query performance
  • Mobile Responsive - Touch-optimized interfaces

Add-On Plugins

FacetWP Cache

Location: wp-content/plugins/facetwp-cache/

Caches facet data and query results for improved performance. Automatically purges cache when products are updated.

Configuration:

  • Cache method: Object cache (Redis/Memcached) or transients
  • Cache duration: 24 hours (default)
  • Auto-purge on product save: Enabled

FacetWP Range List

Location: wp-content/plugins/facetwp-range-list/

Custom facet type for displaying numeric ranges as selectable list items instead of sliders. Useful for discrete sizes, quantities, or price tiers.

Example: Display shoe sizes as buttons instead of a slider.

FacetWP BigCommerce Integration

Location: wp-content/plugins/facetwp-bigcommerce/
Version: v0.2.1

Purpose: Custom integration that allows FacetWP to query and filter BigCommerce product data synced to WordPress.

Features:

  • BC product data source
  • BC custom field support
  • Variant filtering
  • Brand filtering
  • Category filtering
  • Price range filtering
  • Stock status filtering

Facet Configuration

Admin Location: Settings → FacetWP → Facets

Example Facet Setup

Brand Facet

Facet Settings:

  • Name: brand
  • Label: Brand
  • Type: Checkboxes
  • Data Source: BigCommerce Product / Brand
  • Sort: Alphabetical
  • Show expanded: Yes
  • Show search box: Yes (when 10+ brands)
Price Facet

Facet Settings:

  • Name: price
  • Label: Price Range
  • Type: Slider
  • Data Source: BigCommerce Product / Price
  • Range: Auto-detect min/max
  • Prefix: $
  • Format: Number with commas
Category Facet

Facet Settings:

  • Name: category
  • Label: Categories
  • Type: Hierarchy
  • Data Source: BigCommerce Category
  • Show expanded: No
  • Preserve Hierarchy: Yes
Features Facet (Custom Fields)

Facet Settings:

  • Name: features
  • Label: Features
  • Type: Checkboxes
  • Data Source: Custom Field / bc_features
  • Show count: Yes

Template Configuration

Admin Location: Settings → FacetWP → Templates

Product Grid Template

Template Name: products
Query Type: Post Type
Post Types: bigcommerce_product
Posts Per Page: 24
Order By: Title (A-Z)

Template Code:

<div class="facetwp-template">
<div class="product-grid">
<?php
if (have_posts()) :
while (have_posts()) : the_post();
$product = new \BigCommerce\Post_Types\Product\Product(get_the_ID());
?>
<div class="product-card">
<a href="<?php the_permalink(); ?>" class="product-link">
<div class="product-image">
<?php the_post_thumbnail('product-card'); ?>
</div>
<div class="product-info">
<h3 class="product-title"><?php the_title(); ?></h3>
<div class="product-brand">
<?php echo esc_html($product->brand()); ?>
</div>
<div class="product-price">
<?php echo $product->price_range_html(); ?>
</div>
</div>
</a>
</div>
<?php
endwhile;
else :
?>
<div class="no-results">
<p>No products found matching your filters.</p>
</div>
<?php
endif;
?>
</div>
</div>

Display Facets in Theme

Location: Product archive pages, category pages

<!-- Facet Sidebar -->
<div class="facet-sidebar">
<!-- Brand Filter -->
<div class="facet-group">
<h4 class="facet-title">Brand</h4>
<?php echo facetwp_display('facet', 'brand'); ?>
</div>

<!-- Price Filter -->
<div class="facet-group">
<h4 class="facet-title">Price</h4>
<?php echo facetwp_display('facet', 'price'); ?>
</div>

<!-- Category Filter -->
<div class="facet-group">
<h4 class="facet-title">Categories</h4>
<?php echo facetwp_display('facet', 'category'); ?>
</div>

<!-- Features Filter -->
<div class="facet-group">
<h4 class="facet-title">Features</h4>
<?php echo facetwp_display('facet', 'features'); ?>
</div>

<!-- Active Filters -->
<div class="active-filters">
<h4 class="facet-title">Active Filters</h4>
<?php echo facetwp_display('selections'); ?>
<button class="reset-filters" onclick="FWP.reset()">Clear All</button>
</div>
</div>

<!-- Product Grid -->
<div class="facet-content">
<!-- Result Count & Sort -->
<div class="facet-controls">
<div class="result-count">
<?php echo facetwp_display('counts'); ?>
</div>
<div class="sort-dropdown">
<?php echo facetwp_display('facet', 'sort'); ?>
</div>
</div>

<!-- Product Results -->
<?php echo facetwp_display('template', 'products'); ?>

<!-- Pagination -->
<div class="facet-pagination">
<?php echo facetwp_display('pager'); ?>
</div>
</div>

JavaScript Integration

FacetWP Events

// After facets refresh and results load
document.addEventListener('facetwp-loaded', function() {
console.log('FacetWP loaded');

// Custom code after results load
// Re-initialize any JS components
initProductCards();

// Track analytics
if (typeof gtag !== 'undefined') {
gtag('event', 'facet_interaction', {
facets: FWP.facets
});
}
});

// Before facets refresh
document.addEventListener('facetwp-refresh', function() {
console.log('FacetWP refreshing');
// Show loading state
document.querySelector('.facetwp-template').classList.add('loading');
});

Custom Facet Behavior

// Reset filters button
document.querySelector('.reset-filters').addEventListener('click', function(e) {
e.preventDefault();
FWP.reset();
});

// Programmatically set facet value
FWP.facets['brand'] = ['stealth-cam'];
FWP.refresh();

// Get current facet values
console.log(FWP.facets);

Theme Integration

Location: wp-content/themes/suma-elementor/inc/integrations/class-facetwp.php

<?php
namespace Suma\Integrations;

class FacetWP {
public function __construct() {
add_filter('facetwp_render_output', [$this, 'custom_facet_html'], 10, 2);
add_filter('facetwp_facet_html', [$this, 'facet_html_override'], 10, 2);
add_action('wp_enqueue_scripts', [$this, 'enqueue_assets']);
}

/**
* Custom facet HTML wrapper
*/
public function custom_facet_html($output, $params) {
// Add custom classes, wrapper elements
return '<div class="suma-facet-wrapper">' . $output . '</div>';
}

/**
* Override specific facet HTML
*/
public function facet_html_override($html, $params) {
if ('brand' === $params['facet']['name']) {
// Custom brand facet HTML
}
return $html;
}

/**
* Enqueue FacetWP custom styles
*/
public function enqueue_assets() {
if (class_exists('FacetWP')) {
wp_enqueue_style(
'suma-facetwp',
get_template_directory_uri() . '/assets/dist/css/facetwp.css'
);
}
}
}

new FacetWP();

Mobile Optimization

Filter Drawer: Mobile devices show filters in a drawer/modal instead of sidebar.

<!-- Mobile Filter Button -->
<button class="filter-toggle mobile-only" id="filter-toggle">
<span class="icon"></span>
<span class="text">Filters</span>
<span class="count"><?php echo facetwp_display('counts'); ?></span>
</button>

<!-- Filter Drawer -->
<div class="filter-drawer" id="filter-drawer">
<div class="filter-drawer-header">
<h3>Filters</h3>
<button class="close-drawer">&times;</button>
</div>
<div class="filter-drawer-body">
<!-- Facets here -->
</div>
<div class="filter-drawer-footer">
<button class="btn-apply" onclick="document.getElementById('filter-drawer').classList.remove('is-open')">
Apply Filters
</button>
<button class="btn-reset" onclick="FWP.reset()">
Clear All
</button>
</div>
</div>
// Mobile filter drawer toggle
document.getElementById('filter-toggle').addEventListener('click', function() {
document.getElementById('filter-drawer').classList.add('is-open');
});

document.querySelector('.close-drawer').addEventListener('click', function() {
document.getElementById('filter-drawer').classList.remove('is-open');
});

Plugin: WP Search with Algolia v2.8.2
Location: wp-content/plugins/wp-search-with-algolia/
Service: Algolia SaaS (cloud-hosted search)

Purpose

Provide instant, typo-tolerant search with autocomplete for products, content, FAQs, and dealers.

Features

  • Instant Results - Search-as-you-type with sub-100ms response times
  • Typo Tolerance - Handles misspellings automatically
  • Autocomplete - Suggest products and content while typing
  • Faceted Search - Filter results by attributes
  • Highlighting - Highlight matching terms in results
  • Ranking - Customizable relevance ranking
  • Analytics - Search analytics and insights
  • Multi-Index - Search across multiple content types

Configuration

Admin Location: Settings → Algolia Search

Application Settings

  • Application ID: StealthCam Algolia app ID
  • Search API Key: Public search-only key
  • Admin API Key: Backend indexing key (keep secure)

Indexed Content Types

Posts: bigcommerce_product, post, page, suma_dealer, suma_faq, suma_download

Taxonomies: bigcommerce_category, bigcommerce_brand, download_category, faq_category

Index Configuration

Products Index: stealthcam_products

Searchable Attributes (ranked):

  1. Product title
  2. Product description
  3. Brand
  4. SKU
  5. Categories
  6. Custom fields

Attributes for Faceting:

  • Brand
  • Category
  • Price (numeric)
  • Features
  • Stock status

Custom Ranking:

  1. Featured products (desc)
  2. Review count (desc)
  3. Sales count (desc)
  4. Price (asc)

Search Box Integration

Global Search (Header)

Location: Theme header via Elementor widget

<div class="algolia-search-box" id="algolia-search-global">
<input
type="search"
id="algolia-search-input"
placeholder="Search products, articles, FAQs..."
autocomplete="off"
/>
<button type="submit" class="search-button">
<svg><!-- Search icon --></svg>
</button>
</div>

<!-- Autocomplete Dropdown -->
<div class="algolia-autocomplete" id="algolia-autocomplete">
<!-- Populated by Algolia InstantSearch.js -->
</div>

JavaScript Initialization

Location: assets/src/js/modules/algolia-search.js

import algoliasearch from 'algoliasearch/lite';
import instantsearch from 'instantsearch.js';
import { searchBox, hits, refinementList, rangeSlider } from 'instantsearch.js/es/widgets';

// Initialize Algolia client
const searchClient = algoliasearch(
'YOUR_APP_ID',
'YOUR_SEARCH_API_KEY'
);

// Create search instance
const search = instantsearch({
indexName: 'stealthcam_products',
searchClient,
routing: true // Enable URL routing
});

// Add search box widget
search.addWidgets([
searchBox({
container: '#algolia-search-input',
placeholder: 'Search products...',
showSubmit: true,
showReset: true,
showLoadingIndicator: true,
})
]);

// Add hits (results) widget
search.addWidgets([
hits({
container: '#algolia-hits',
templates: {
item: `
<div class="algolia-hit">
<a href="{{permalink}}">
<img src="{{image_url}}" alt="{{name}}">
<div class="hit-content">
<h3>{{#helpers.highlight}}{ "attribute": "name" }{{/helpers.highlight}}</h3>
<p class="hit-brand">{{brand}}</p>
<p class="hit-price">${{price}}</p>
</div>
</a>
</div>
`,
empty: '<div class="no-results">No results found for <strong>{{query}}</strong></div>'
}
})
]);

// Add refinement widgets (filters)
search.addWidgets([
refinementList({
container: '#brand-filter',
attribute: 'brand',
limit: 10,
showMore: true,
searchable: true,
searchablePlaceholder: 'Search brands...'
}),

rangeSlider({
container: '#price-filter',
attribute: 'price',
pips: false,
tooltips: {
format(value) {
return `$${Math.round(value)}`;
}
}
})
]);

// Start search
search.start();

Autocomplete Integration

Library: Algolia Autocomplete.js

import { autocomplete } from '@algolia/autocomplete-js';

const autocompleteSearch = autocomplete({
container: '#algolia-search-input',
placeholder: 'Search...',
getSources({ query }) {
return [
{
sourceId: 'products',
getItems() {
return getAlgoliaResults({
searchClient,
queries: [
{
indexName: 'stealthcam_products',
query,
params: {
hitsPerPage: 5
}
}
]
});
},
templates: {
item({ item, components }) {
return `
<div class="autocomplete-product">
<img src="${item.image_url}" alt="${item.name}">
<div>
<div class="autocomplete-product-name">
${components.Highlight({ hit: item, attribute: 'name' })}
</div>
<div class="autocomplete-product-price">$${item.price}</div>
</div>
</div>
`;
}
}
},
{
sourceId: 'articles',
getItems() {
return getAlgoliaResults({
searchClient,
queries: [
{
indexName: 'stealthcam_posts',
query,
params: {
hitsPerPage: 3
}
}
]
});
}
}
];
}
});

Search Page Template

Location: wp-content/themes/suma-elementor/search.php

Full-featured search results page with filters, sorting, and pagination.

<?php get_header(); ?>

<div class="algolia-search-page">
<aside class="search-sidebar">
<h3>Refine Results</h3>

<!-- Brand Filter -->
<div id="brand-filter"></div>

<!-- Price Filter -->
<div id="price-filter"></div>

<!-- Category Filter -->
<div id="category-filter"></div>

<!-- Clear Filters -->
<button id="clear-filters">Clear All Filters</button>
</aside>

<main class="search-content">
<!-- Search Stats -->
<div id="algolia-stats"></div>

<!-- Sort Dropdown -->
<div id="algolia-sort"></div>

<!-- Search Results -->
<div id="algolia-hits"></div>

<!-- Pagination -->
<div id="algolia-pagination"></div>
</main>
</div>

<?php get_footer(); ?>

Custom Ranking Formula

Algolia Dashboard: Index Settings → Ranking

1. Typo (default)
2. Geo (default)
3. Words (default)
4. Filters (default)
5. Proximity (default)
6. Attribute (default)
7. Exact (default)
8. Custom: featured_product (desc)
9. Custom: review_count (desc)
10. Custom: sales_count (desc)

Indexing Schedule

Cron Job: Hourly product reindex

// Schedule Algolia reindex
add_action('suma_algolia_reindex', function() {
if (class_exists('Algolia_Plugin')) {
$plugin = Algolia_Plugin::get_instance();
$plugin->get_indices_manager()->reindex_post_type('bigcommerce_product');
}
});

// Register cron schedule
if (!wp_next_scheduled('suma_algolia_reindex')) {
wp_schedule_event(time(), 'hourly', 'suma_algolia_reindex');
}

Theme Integration

Location: wp-content/themes/suma-elementor/inc/integrations/class-algolia.php

<?php
namespace Suma\Integrations;

class Algolia {
public function __construct() {
add_filter('algolia_searchable_post_shared_attributes', [$this, 'add_custom_attributes'], 10, 2);
add_filter('algolia_should_index_post', [$this, 'should_index_product'], 10, 2);
}

/**
* Add custom attributes to Algolia index
*/
public function add_custom_attributes($attributes, $post) {
if ('bigcommerce_product' === $post->post_type) {
$product = new \BigCommerce\Post_Types\Product\Product($post->ID);

// Add BigCommerce data
$attributes['bc_id'] = $product->bc_id();
$attributes['brand'] = $product->brand();
$attributes['sku'] = $product->sku();
$attributes['stock_level'] = $product->inventory_level();
$attributes['featured_product'] = get_post_meta($post->ID, 'featured', true);
$attributes['review_count'] = $product->review_count();
$attributes['average_rating'] = $product->average_rating();

// Price data
$prices = $product->price_range();
$attributes['price'] = $prices['calculated']['min'] ?? 0;
}

return $attributes;
}

/**
* Control which products to index
*/
public function should_index_product($should_index, $post) {
if ('bigcommerce_product' === $post->post_type) {
// Don't index draft or hidden products
if ('publish' !== $post->post_status) {
return false;
}
}
return $should_index;
}
}

new Algolia();

Performance Optimization

FacetWP Caching

  • Object Cache: Redis-backed cache for facet queries
  • Transient Cache: Fallback for shared hosting
  • Auto Purge: Clears cache on product updates

Algolia Optimization

  • Geo-Distributed: Servers in multiple regions for low latency
  • Query Suggestions: Pre-computed suggestions for common queries
  • Index Replicas: Separate indices for different sort orders
  • Conditional Indexing: Skip indexing for certain content

Combined Strategy

Use FacetWP for category pages with predictable filters, and Algolia for global search and complex queries. This balances server load with search quality.