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