Faceted Navigation & Filtering

Documentation > WEB CRAWLER-Solr API > Faceted Navigation & Filtering

Faceted Navigation & Filtering

Faceted navigation lets users refine their search results by clicking on filter options like language, domain, date range, content type, or price. Think of how Amazon lets you filter by brand, rating, and price range — that is faceted navigation.

Your Opensolr Web Crawler index supports faceting out of the box on multiple fields.


How Faceting Works

When you add faceting parameters to your search query, Solr returns the normal search results plus a count of how many results exist for each unique value of the faceted field.

For example, faceting on meta_detected_language might return:

  • English: 834 results
  • German: 245 results
  • French: 123 results
  • Spanish: 89 results

The user can then click "German" to filter and see only the 245 German-language results.


Facet Parameters

Add these to your search query:

facet=true
facet.field=meta_detected_language
facet.field=meta_domain
facet.field=content_type
facet.mincount=1
facet.sort=count
Parameter Description
facet=true Enable faceting
facet.field Which field to facet on (can specify multiple)
facet.mincount Minimum count to include a facet value. Set to 1 to hide empty values.
facet.sort count (most results first) or index (alphabetical)
facet.limit Maximum number of facet values to return (default: 100). Set to -1 for unlimited.

Best Fields to Facet On

Field What It Shows Use Case
meta_detected_language Language of each page (en, de, fr, es...) "Filter by Language" sidebar
meta_domain Source domain name "Filter by Site" when crawling multiple domains
content_type MIME type (text/html, application/pdf...) "Filter by Type" (Web pages, PDFs, Documents)
category Content category "Filter by Category"
currency_s Currency code (USD, EUR...) "Filter by Currency" for e-commerce

Note: You can only facet on string or numeric fields with docValues=true. Text fields (like title or description) cannot be used for faceting because they are analyzed/tokenized.


Applying a Facet Filter

When the user clicks on a facet value, you add a filter query (fq) to your search:

fq=meta_detected_language:en

This narrows results to only English pages. The important thing about fq is that it does not affect the relevancy score — it just filters. And because Solr caches filter queries, applying and removing filters is very fast.

You can combine multiple filters:

fq=meta_detected_language:en
fq=meta_domain:yoursite.com
fq=content_type:text/html

Multiple fq parameters are AND-ed together.


Date Range Faceting

For date-based filtering, you can use range faceting to create time buckets:

facet=true
facet.range=creation_date
facet.range.start=NOW-1YEAR
facet.range.end=NOW
facet.range.gap=+1MONTH

This returns counts for each month in the past year:

"facet_ranges": {
  "creation_date": {
    "counts": [
      "2025-03-01T00:00:00Z", 45,
      "2025-04-01T00:00:00Z", 67,
      "2025-05-01T00:00:00Z", 89,
      ...
      "2026-02-01T00:00:00Z", 123
    ]
  }
}

You can then render this as a timeline or date filter.


Price Range Faceting

For e-commerce indexes with price data:

facet=true
facet.range=price_f
facet.range.start=0
facet.range.end=500
facet.range.gap=50

Returns: 0-50 (23 items), 50-100 (45 items), 100-150 (12 items), etc.


JavaScript Implementation

Here is a complete example of rendering a language facet sidebar and handling clicks:

function renderFacets(data) {
    var facetDiv = document.getElementById('facets');
    var facets = data.facet_counts.facet_fields;

    var html = '';

    // Language facet
    if (facets.meta_detected_language) {
        html += '<h4>Language</h4><ul class="facet-list">';
        var langs = facets.meta_detected_language;
        for (var i = 0; i < langs.length; i += 2) {
            var lang = langs[i];
            var count = langs[i + 1];
            if (count === 0) continue;
            var label = getLanguageName(lang); // "en" -> "English"
            var active = currentFilters.lang === lang ? ' class="active"' : '';
            html += '<li' + active + '>'
                 +  '<a href="#" onclick="toggleFilter(\'meta_detected_language\', \'  ' + lang + ' \')">'
                 +  label + ' <span class="count">(' + count + ')</span>'
                 +  '</a></li>';
        }
        html += '</ul>';
    }

    // Domain facet
    if (facets.meta_domain) {
        html += '<h4>Source</h4><ul class="facet-list">';
        var domains = facets.meta_domain;
        for (var i = 0; i < domains.length; i += 2) {
            var domain = domains[i];
            var count = domains[i + 1];
            if (count === 0) continue;
            html += '<li><a href="#" onclick="toggleFilter(\'meta_domain\', \'  ' + domain + ' \')">'
                 +  domain + ' <span class="count">(' + count + ')</span>'
                 +  '</a></li>';
        }
        html += '</ul>';
    }

    facetDiv.innerHTML = html;
}

function toggleFilter(field, value) {
    // Add or remove the fq parameter and re-run the search
    if (currentFilters[field] === value) {
        delete currentFilters[field];
    } else {
        currentFilters[field] = value;
    }
    doSearch();
}

function getLanguageName(code) {
    var names = { en: 'English', de: 'German', fr: 'French', es: 'Spanish', it: 'Italian', nl: 'Dutch', pt: 'Portuguese', ro: 'Romanian', ja: 'Japanese', zh: 'Chinese', ko: 'Korean', ar: 'Arabic', ru: 'Russian' };
    return names[code] || code.toUpperCase();
}

Facet Sidebar CSS

.facet-list { list-style: none; padding: 0; margin: 0 0 20px; }
.facet-list li { margin: 0; }
.facet-list li a { display: block; padding: 6px 0; color: #333; text-decoration: none; font-size: 14px; }
.facet-list li a:hover { color: #e8650a; }
.facet-list li.active a { font-weight: bold; color: #e8650a; }
.facet-list .count { color: #999; font-size: 12px; }

Tips

  • Always include facet counts in your UI — showing "English (834)" is much more useful than just "English". Users can see at a glance how many results each filter will give them.
  • Use facet.mincount=1 — Do not show facet values with zero results. It is confusing for users.
  • Recalculate facets on every search — When the user changes their query or applies a filter, the facet counts change. Always send facet=true with every search request.
  • Active filters should be visually highlighted — Make it obvious which filters are currently applied, and provide a way to remove them (click again or an "x" button).