Advanced Filtering

Build dynamic pages that filter content by multiple tags or criteria

2 min read 422 words

Standard tag pages list content for one tag (e.g.,/tags/python/). But sometimes users want to find content that matches multiple criteria, like "Tutorials" AND "Python" AND "Beginner".

This guide shows how to build an advanced filter using Kida template set logic.

The Logic: Set Intersections

Kida (and Jinja2) allows us to treat lists of pages as mathematical sets.

  • Union: All unique items in A or B.
  • Intersection: Items that are in BOTH A and B.
  • Difference: Items in A but not in B.

Example: Building a "Recipe Finder"

Imagine a documentation site where you want to find "API Guides" for "v2.0".

  1. 1

    Access the data

    Bengal exposessite.taxonomieswhich gives us lists of pages for each tag.

    {% let api_pages = site.taxonomies.tags["api"].pages %}
    {% let v2_pages = site.taxonomies.tags["v2"].pages %}
    
  2. 2

    Find the intersection

    Use the|> intersect()filter to find pages in both lists:

    {% let results = api_pages |> intersect(v2_pages) %}
    
    <h2>API v2 Guides ({{ results | length }})</h2>
    <ul>
      {% for page in results %}
        <li><a href="{{ page.href }}">{{ page.title }}</a></li>
      {% end %}
    </ul>
    

Tip

For more complex filtering, use|> where()with operators:

{% let results = site.pages
  |> where('tags', 'api', 'in')
  |> where('tags', 'v2', 'in') %}

Advanced: The "Filter Page" Layout

For a dynamic filter page (e.g.,/search/), we typically rely on client-side JavaScript because pre-rendering every combination of tags is expensive (combinatorial explosion).

However, for specific, high-value combinations, you can create dedicated pages.

  1. 1

    Create the Page

    site/content/guides/python-tutorials.md:

    ---
    title: Python Tutorials
    variant: filter_page
    filter_tags: ["python", "tutorial"]
    ---
    Here are all our Python tutorials.
    
  2. 2

    Create the Layout

    templates/filter_page.html:

    {% extends "base.html" %}
    
    {% block content %}
      <h1>{{ page.title }}</h1>
    
      {# Start with all pages, then filter by each required tag #}
      {% let matches = site.pages %}
    
      {% for tag in page.metadata.filter_tags %}
        {% let tag_pages = site.pages |> where('tags', tag, 'in') %}
        {% let matches = matches |> intersect(tag_pages) %}
      {% end %}
    
      {# Render Results #}
      <div class="results">
        {% for post in matches %}
          {% include "partials/card.html" %}
        {% else %}
          <p>No matches found.</p>
        {% end %}
      </div>
    
    {% end %}
    

Summary

Use Set Intersections to build powerful "Topic Pages" that aggregate content from multiple dimensions without manually curating lists.

Seealso