Skip to main content

Search & Filtering

Blocker Outdoors implements a dual search architecture combining FacetWP Pro v4.3.5 for faceted filtering and Algolia v2.8.2 for instant search, providing users with powerful product discovery tools.

Architecture Overview

┌─────────────────────────────────────────┐
│ Product Discovery System │
└─────────────────────────────────────────┘

User Query

├──► Faceted Navigation (FacetWP)
│ • Category filters
│ • Price range slider
│ • Brand checkboxes
│ • Availability toggle
│ • Custom attributes
│ │
│ └──► WordPress Query
│ └──► BigCommerce Products

└──► Instant Search (Algolia)
• Real-time autocomplete
• Typo tolerance
• Synonym support
• Result ranking

└──► Algolia Index
└──► Synced Product Data

FacetWP Pro v4.3.5

Faceted search and filtering system for BigCommerce products.

Installation & Setup

# Install FacetWP Pro
wp plugin install facetwp-pro-v4.3.5.zip --activate

# Install BigCommerce integration add-on
wp plugin install facetwp-bigcommerce-v0.2.1.zip --activate

# Activate license
wp option update facetwp_license '{"key":"YOUR_LICENSE_KEY","status":"valid"}'

Configuration

Settings → FacetWP → Settings:

// FacetWP settings array
$facetwp_settings = [
'gmaps_api_key' => 'YOUR_GOOGLE_MAPS_KEY', // For location-based facets
'license_key' => 'YOUR_LICENSE_KEY',
'prefix' => '', // URL prefix (empty for clean URLs)
'ajax_refresh' => true, // AJAX-based filtering
'facet_version' => 'latest',
];

Facet Configuration

1. Category Facet

// Settings → FacetWP → Facets → Add New
[
'name' => 'categories',
'label' => 'Categories',
'type' => 'checkboxes',
'source' => 'tax/bigcommerce_category',
'modifier_type' => 'off',
'modifier_values' => '',
'hierarchical' => 'yes',
'show_expanded' => 'yes',
'parent_term' => 0,
'orderby' => 'count',
'count' => 'yes',
'soft_limit' => 10,
]

2. Price Range Facet

[
'name' => 'price',
'label' => 'Price Range',
'type' => 'slider',
'source' => 'cf/bigcommerce_price',
'prefix' => '$',
'suffix' => '',
'step' => 10,
'format' => 'decimal',
'decimal_separator' => '.',
'thousands_separator' => ',',
]

3. Brand Facet

[
'name' => 'brands',
'label' => 'Brands',
'type' => 'checkboxes',
'source' => 'tax/bigcommerce_brand',
'modifier_type' => 'off',
'modifier_values' => '',
'hierarchical' => 'no',
'show_expanded' => 'no',
'orderby' => 'display_value',
'count' => 'yes',
'soft_limit' => 15,
]

4. Availability Facet

[
'name' => 'availability',
'label' => 'Availability',
'type' => 'radio',
'source' => 'cf/bigcommerce_availability',
'modifier_type' => 'replace',
'modifier_values' => [
'available' => 'In Stock',
'preorder' => 'Pre-Order',
],
]

5. Size Facet (Custom Attribute)

[
'name' => 'size',
'label' => 'Size',
'type' => 'checkboxes',
'source' => 'cf/product_size', // Custom field from variant data
'modifier_type' => 'off',
'orderby' => 'raw', // Maintain size order (S, M, L, XL, 2XL)
'count' => 'yes',
]

6. Color Facet

[
'name' => 'color',
'label' => 'Color/Pattern',
'type' => 'color',
'source' => 'cf/product_color',
'modifier_type' => 'off',
'orderby' => 'count',
'count' => 'yes',
]

Sort Options

[
'name' => 'sort_order',
'label' => 'Sort By',
'type' => 'sort',
'source' => [
'default' => [
'label' => 'Relevance',
'query_args' => [],
],
'price_asc' => [
'label' => 'Price: Low to High',
'query_args' => [
'orderby' => 'meta_value_num',
'meta_key' => 'bigcommerce_price',
'order' => 'ASC',
],
],
'price_desc' => [
'label' => 'Price: High to Low',
'query_args' => [
'orderby' => 'meta_value_num',
'meta_key' => 'bigcommerce_price',
'order' => 'DESC',
],
],
'name_asc' => [
'label' => 'Name: A to Z',
'query_args' => [
'orderby' => 'title',
'order' => 'ASC',
],
],
'name_desc' => [
'label' => 'Name: Z to A',
'query_args' => [
'orderby' => 'title',
'order' => 'DESC',
],
],
'date_desc' => [
'label' => 'Newest First',
'query_args' => [
'orderby' => 'date',
'order' => 'DESC',
],
],
],
]

Template Integration

Product Archive with Facets

<?php
/**
* Template Name: Product Archive with Filters
*/

get_header();
?>

<div class="product-archive-layout container mx-auto px-4 py-8">

<div class="archive-header mb-8">
<h1 class="text-4xl font-heading font-bold">
<?php echo is_tax() ? single_term_title( '', false ) : 'All Products'; ?>
</h1>
</div>

<div class="grid grid-cols-1 lg:grid-cols-4 gap-8">

<!-- Sidebar Filters -->
<aside class="filters-sidebar">

<!-- Mobile Filter Toggle -->
<button class="lg:hidden btn btn-outline mb-4 w-full" id="toggle-filters">
<span class="icon-filter"></span>
Filters
</button>

<!-- Facet Container -->
<div class="facet-container" id="facet-container">

<!-- Categories -->
<div class="facet-group mb-6">
<h3 class="facet-heading font-semibold mb-3">Categories</h3>
<?php echo facetwp_display( 'facet', 'categories' ); ?>
</div>

<!-- Price Range -->
<div class="facet-group mb-6">
<h3 class="facet-heading font-semibold mb-3">Price Range</h3>
<?php echo facetwp_display( 'facet', 'price' ); ?>
</div>

<!-- Brands -->
<div class="facet-group mb-6">
<h3 class="facet-heading font-semibold mb-3">Brands</h3>
<?php echo facetwp_display( 'facet', 'brands' ); ?>
</div>

<!-- Size -->
<div class="facet-group mb-6">
<h3 class="facet-heading font-semibold mb-3">Size</h3>
<?php echo facetwp_display( 'facet', 'size' ); ?>
</div>

<!-- Color -->
<div class="facet-group mb-6">
<h3 class="facet-heading font-semibold mb-3">Color/Pattern</h3>
<?php echo facetwp_display( 'facet', 'color' ); ?>
</div>

<!-- Availability -->
<div class="facet-group mb-6">
<h3 class="facet-heading font-semibold mb-3">Availability</h3>
<?php echo facetwp_display( 'facet', 'availability' ); ?>
</div>

<!-- Reset Button -->
<button class="btn btn-outline w-full" onclick="FWP.reset()">
Clear All Filters
</button>

</div>
</aside>

<!-- Product Results -->
<main class="product-results lg:col-span-3">

<!-- Results Header -->
<div class="results-header flex justify-between items-center mb-6">

<!-- Result Count -->
<div class="result-count text-gray-600">
<?php echo facetwp_display( 'counts' ); ?>
</div>

<!-- Sort Dropdown -->
<div class="result-sort">
<?php echo facetwp_display( 'facet', 'sort_order' ); ?>
</div>

</div>

<!-- FacetWP Template -->
<div class="facetwp-template">

<?php
$query_args = [
'post_type' => 'bigcommerce_product',
'posts_per_page' => 24,
'post_status' => 'publish',
'orderby' => 'menu_order',
'order' => 'ASC',
'facetwp' => true, // Enable FacetWP filtering
];

$products = new WP_Query( $query_args );

if ( $products->have_posts() ) :
?>

<div class="product-grid grid grid-cols-2 lg:grid-cols-3 gap-6">
<?php
while ( $products->have_posts() ) :
$products->the_post();
get_template_part( 'template-parts/product-card' );
endwhile;
?>
</div>

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

<?php else : ?>

<div class="no-results text-center py-12">
<h2 class="text-2xl font-bold mb-4">No products found</h2>
<p class="text-gray-600 mb-6">Try adjusting your filters or search terms.</p>
<button class="btn btn-primary" onclick="FWP.reset()">
Clear Filters
</button>
</div>

<?php
endif;
wp_reset_postdata();
?>

</div>

</main>

</div>

</div>

<?php
get_footer();

Custom Indexing

Index product variants for faceting:

/**
* Index product variant attributes for FacetWP
*/
add_filter( 'facetwp_index_row', function( $params, $class ) {

if ( 'bigcommerce_product' === $params['post_id_type'] ) {

$product_id = $params['post_id'];
$bc_id = get_post_meta( $product_id, 'bigcommerce_id', true );

// Fetch variant data from custom table
global $wpdb;
$variants = $wpdb->get_results( $wpdb->prepare(
"SELECT * FROM {$wpdb->prefix}bigcommerce_variants WHERE product_id = %d",
$product_id
) );

// Index each variant option as a facet value
foreach ( $variants as $variant ) {
$option_values = json_decode( $variant->option_values, true );

foreach ( $option_values as $option ) {
// Index size
if ( $option['label'] === 'Size' ) {
$class->insert( [
'post_id' => $product_id,
'facet_name' => 'size',
'facet_value' => $option['value'],
'facet_display_value' => $option['value'],
] );
}

// Index color/pattern
if ( $option['label'] === 'Color' || $option['label'] === 'Pattern' ) {
$class->insert( [
'post_id' => $product_id,
'facet_name' => 'color',
'facet_value' => $option['value'],
'facet_display_value' => $option['value'],
] );
}
}
}
}

return $params;

}, 10, 2 );

Performance Optimization

// Enable FacetWP caching
add_filter( 'facetwp_cache_enable', '__return_true' );

// Cache duration (seconds)
add_filter( 'facetwp_cache_lifetime', function() {
return 3600; // 1 hour
} );

// Preload index for specific post types
add_filter( 'facetwp_indexer_post_types', function( $post_types ) {
return [ 'bigcommerce_product' ];
} );

Algolia v2.8.2

Instant search with autocomplete, typo tolerance, and advanced ranking.

Installation & Setup

# Install WP Search with Algolia
wp plugin install wp-search-with-algolia-v2.8.2.zip --activate

# Configure via wp-config.php

wp-config.php:

// Algolia credentials
define( 'ALGOLIA_APP_ID', 'XXXXXXXXXX' );
define( 'ALGOLIA_SEARCH_KEY', 'xxxxxxxxxxxxxxxxxxxxx' ); // Search-only API key
define( 'ALGOLIA_ADMIN_KEY', 'xxxxxxxxxxxxxxxxxxxxx' ); // Admin API key (secure!)

Index Configuration

Settings → Algolia Search → Indices:

// Primary products index
$indices_config = [
[
'index_id' => 'products',
'label' => 'Products',
'enabled' => true,
'post_types' => [ 'bigcommerce_product' ],
'taxonomies' => [
'bigcommerce_category',
'bigcommerce_brand',
'product_tag',
],
'searchable_fields' => [
'post_title',
'post_content',
'taxonomies',
'meta.bigcommerce_sku',
'meta.bigcommerce_brand',
],
'custom_ranking' => [
'desc(meta.bigcommerce_featured)',
'desc(post_date)',
'asc(post_title)',
],
'distinct' => true,
],
];

Searchable Attributes Configuration

/**
* Configure Algolia index settings
*/
add_filter( 'algolia_posts_products_settings', function( $settings ) {

// Searchable attributes (ordered by priority)
$settings['searchableAttributes'] = [
'unordered(post_title)',
'unordered(taxonomies.bigcommerce_category)',
'unordered(taxonomies.bigcommerce_brand)',
'unordered(meta.bigcommerce_sku)',
'post_content',
'unordered(taxonomies.product_tag)',
];

// Attributes for faceting
$settings['attributesForFaceting'] = [
'searchable(taxonomies.bigcommerce_category)',
'searchable(taxonomies.bigcommerce_brand)',
'filterOnly(meta.bigcommerce_availability)',
'meta.bigcommerce_price',
];

// Custom ranking
$settings['customRanking'] = [
'desc(meta.bigcommerce_featured)',
'desc(record_index)', // Boost newer products
'asc(post_title)',
];

// Typo tolerance
$settings['typoTolerance'] = 'strict';

// Remove words if no results
$settings['removeWordsIfNoResults'] = 'lastWords';

// Distinct products (avoid duplicate variants)
$settings['distinct'] = true;
$settings['attributeForDistinct'] = 'meta.bigcommerce_id';

// Highlighting
$settings['highlightPreTag'] = '<mark>';
$settings['highlightPostTag'] = '</mark>';

// Snippeting
$settings['attributesToSnippet'] = [
'post_content:30',
];

// Pagination
$settings['hitsPerPage'] = 20;
$settings['maxValuesPerFacet'] = 100;

return $settings;

} );

Synonyms Configuration

/**
* Add product synonyms for better search
*/
add_action( 'algolia_after_push_settings_products', function( $index ) {

$synonyms = [
[
'objectID' => 'hunting-apparel',
'type' => 'synonym',
'synonyms' => [ 'hunting clothes', 'camo', 'camouflage', 'hunting gear' ],
],
[
'objectID' => 'jacket-synonyms',
'type' => 'synonym',
'synonyms' => [ 'jacket', 'coat', 'parka', 'outerwear' ],
],
[
'objectID' => 'pants-synonyms',
'type' => 'synonym',
'synonyms' => [ 'pants', 'trousers', 'bottoms' ],
],
[
'objectID' => 'scent-control',
'type' => 'synonym',
'synonyms' => [ 'scent blocker', 'scent control', 'odor elimination' ],
],
];

$index->saveSynonyms( $synonyms, [ 'replaceExistingSynonyms' => true ] );

} );

Autocomplete Implementation

Frontend JavaScript:

import algoliasearch from 'algoliasearch/lite';
import { autocomplete, getAlgoliaResults } from '@algolia/autocomplete-js';
import '@algolia/autocomplete-theme-classic';

// Initialize Algolia client
const searchClient = algoliasearch(
sumaConfig.algolia.appId,
sumaConfig.algolia.searchKey
);

// Initialize autocomplete
autocomplete({
container: '#autocomplete',
placeholder: 'Search for products...',
openOnFocus: true,
detachedMediaQuery: '(max-width: 1024px)',
getSources({ query }) {
return [
{
sourceId: 'products',
getItems() {
return getAlgoliaResults({
searchClient,
queries: [
{
indexName: 'blocker_products',
query,
params: {
hitsPerPage: 8,
attributesToSnippet: ['post_content:30'],
snippetEllipsisText: '…',
filters: 'meta.bigcommerce_availability:available',
},
},
],
});
},
templates: {
item({ item, components, html }) {
return html`
<a href="${item.permalink}" class="aa-ItemLink">
<div class="aa-ItemContent">
${item.images && item.images.thumbnail ? html`
<div class="aa-ItemIcon">
<img src="${item.images.thumbnail.url}" alt="${item.post_title}" />
</div>
` : ''}
<div class="aa-ItemContentBody">
<div class="aa-ItemContentTitle">
${components.Highlight({ hit: item, attribute: 'post_title' })}
</div>
<div class="aa-ItemContentDescription">
${item.taxonomies?.bigcommerce_brand?.[0] || ''}
</div>
${item.meta?.bigcommerce_price ? html`
<div class="aa-ItemContentPrice">
$${parseFloat(item.meta.bigcommerce_price).toFixed(2)}
</div>
` : ''}
</div>
</div>
</a>
`;
},
noResults() {
return 'No products found.';
},
},
},
];
},
});

Indexing Automation

// Reindex on product save/update
add_action( 'save_post_bigcommerce_product', function( $post_id ) {
// Trigger Algolia reindex for this product
do_action( 'algolia_reindex_post', $post_id );
}, 100 );

// Scheduled full reindex (daily at 4 AM)
if ( ! wp_next_scheduled( 'algolia_daily_reindex' ) ) {
wp_schedule_event( strtotime( '04:00:00' ), 'daily', 'algolia_daily_reindex' );
}

add_action( 'algolia_daily_reindex', function() {
do_action( 'algolia_reindex_all', 'products' );
} );

// Manual reindex via WP-CLI
// wp algolia reindex products --clear

Search Analytics

Track search queries and click-through rates:

// Track search query
window.dataLayer.push({
'event': 'search',
'search_term': query,
'search_results': resultsCount,
});

// Track search result click
window.dataLayer.push({
'event': 'select_item',
'item_list_name': 'Search Results',
'items': [{
'item_id': productId,
'item_name': productName,
'index': position,
}],
});

Troubleshooting

FacetWP Issues

# Rebuild FacetWP index
wp facetwp index

# Check index status
wp facetwp status

# Clear cache
wp facetwp flush-cache

Algolia Issues

# Reindex all products
wp algolia reindex products --clear

# Check index status
wp algolia indices list

# Test search query
wp algolia search "scent blocker" --index=products