# Template Functions Reference URL: /docs/reference/template-functions/ Section: reference Tags: reference, templates, filters, jinja2, hugo -------------------------------------------------------------------------------- Bengal provides powerful template filters for querying, filtering, and transforming content collections. Many filters are inspired by Hugo for easy migration. Collection Filters These filters work with lists of pages, dictionaries, or any iterable. where Filter items where a key matches a value. Supports Hugo-like comparison operators. Basic Usage: 1 2 3 4 5{# Filter by exact value (default) #} {% set tutorials = site.pages | where('category', 'tutorial') %} {# Filter by nested attribute #} {% set track_pages = site.pages | where('metadata.track_id', 'getting-started') %} With Comparison Operators: Operator Description Example eq Equal (default) where('status', 'published', 'eq') ne Not equal where('status', 'draft', 'ne') gt Greater than where('date', one_year_ago, 'gt') gte Greater than or equal where('priority', 5, 'gte') lt Less than where('weight', 100, 'lt') lte Less than or equal where('order', 10, 'lte') in Value in list where('tags', 'python', 'in') not_in Value not in list where('status', ['archived'], 'not_in') Operator Examples: 1 2 3 4 5 6 7 8 9 10 11 12 13 14{# Pages newer than a year ago #} {% set recent = site.pages | where('date', one_year_ago, 'gt') %} {# Pages with priority 5 or higher #} {% set important = site.pages | where('metadata.priority', 5, 'gte') %} {# Pages tagged with 'python' #} {% set python_posts = site.pages | where('tags', 'python', 'in') %} {# Pages with specific statuses #} {% set active = site.pages | where('status', ['active', 'featured'], 'in') %} {# Exclude archived pages #} {% set live = site.pages | where('status', ['archived', 'draft'], 'not_in') %} where_not Filter items where a key does NOT equal a value. Shorthand for where(key, value, 'ne'). 1 2 3 4 5{# Exclude drafts #} {% set published = site.pages | where_not('draft', true) %} {# Exclude archived items #} {% set active = users | where_not('status', 'archived') %} sort_by Sort items by a key, with optional reverse order. 1 2 3 4 5 6 7 8{# Sort by date, newest first #} {% set recent = site.pages | sort_by('date', reverse=true) %} {# Sort alphabetically by title #} {% set alphabetical = site.pages | sort_by('title') %} {# Sort by weight (ascending) #} {% set ordered = sections | sort_by('weight') %} group_by Group items by a key value, returning a dictionary. 1 2 3 4 5 6 7 8 9 10{% set by_category = site.pages | group_by('category') %} {% for category, pages in by_category.items() %} <h2>{{ category }}</h2> <ul> {% for page in pages %} <li><a href="{{ page.url }}">{{ page.title }}</a></li> {% endfor %} </ul> {% endfor %} limit Take the first N items from a list. 1 2 3 4 5{# Latest 5 posts #} {% set latest = site.pages | sort_by('date', reverse=true) | limit(5) %} {# Top 3 featured items #} {% set featured = items | where('featured', true) | limit(3) %} offset Skip the first N items from a list. 1 2 3 4 5{# Skip first 10 items (pagination page 2) #} {% set page_2 = items | offset(10) | limit(10) %} {# Skip the featured post #} {% set rest = posts | offset(1) %} first Get the first item from a list, or None if empty. 1 2 3 4 5 6 7 8{# Get the featured post #} {% set featured = site.pages | where('metadata.featured', true) | first %} {% if featured %} <div class="hero"> <h1>{{ featured.title }}</h1> </div> {% endif %} last Get the last item from a list, or None if empty. 1 2 3 4 5{# Get the oldest post #} {% set oldest = site.pages | sort_by('date') | last %} {# Get the final step #} {% set final_step = steps | last %} reverse Reverse a list (returns a new list, original unchanged). 1 2 3 4 5{# Oldest first #} {% set chronological = site.pages | sort_by('date') %} {# Newest first (reversed) #} {% set newest_first = chronological | reverse %} uniq Remove duplicate items while preserving order. 1 2 3 4 5 6{# Get unique tags from all posts #} {% set all_tags = [] %} {% for page in site.pages %} {% set all_tags = all_tags + page.tags %} {% endfor %} {% set unique_tags = all_tags | uniq %} flatten Flatten nested lists into a single list. 1 2 3{# Combine all tags from all pages #} {% set nested_tags = site.pages | map(attribute='tags') | list %} {% set all_tags = nested_tags | flatten | uniq %} Set Operations Perform set operations on lists of pages or items. union Combine two lists, removing duplicates. 1 2 3 4{# Combine featured and recent posts #} {% set featured = site.pages | where('metadata.featured', true) %} {% set recent = site.pages | sort_by('date', reverse=true) | limit(5) %} {% set combined = featured | union(recent) %} intersect Get items that appear in both lists. 1 2 3 4{# Posts that are both featured AND tagged 'python' #} {% set featured = site.pages | where('metadata.featured', true) %} {% set python = site.pages | where('tags', 'python', 'in') %} {% set featured_python = featured | intersect(python) %} complement Get items in the first list that are NOT in the second list. 1 2 3 4{# All posts except featured ones #} {% set all_posts = site.pages | where('type', 'post') %} {% set featured = site.pages | where('metadata.featured', true) %} {% set regular = all_posts | complement(featured) %} Chaining Filters Filters can be chained for powerful queries: 1 2 3 4 5 6 7{# Recent Python tutorials, sorted by date #} {% set result = site.pages | where('category', 'tutorial') | where('tags', 'python', 'in') | where('draft', false) | sort_by('date', reverse=true) | limit(10) %} Hugo Migration Guide Bengal's template functions are designed for easy migration from Hugo. Here's how common Hugo patterns translate: Filtering Pages Hugo: 1 2{{ $posts := where .Site.RegularPages "Section" "blog" }} {{ $recent := where .Site.RegularPages ".Date" ">" (now.AddDate -1 0 0) }} Bengal: 1 2{% set posts = site.pages | where('section', 'blog') %} {% set recent = site.pages | where('date', one_year_ago, 'gt') %} Sorting Hugo: 1 2{{ range .Site.RegularPages.ByDate.Reverse }} {{ range sort .Site.RegularPages "Title" }} Bengal: 1 2{% for page in site.pages | sort_by('date', reverse=true) %} {% for page in site.pages | sort_by('title') %} First/Last Hugo: 1 2{{ $featured := (where .Site.RegularPages "Params.featured" true).First }} {{ $oldest := .Site.RegularPages.ByDate.Last }} Bengal: 1 2{% set featured = site.pages | where('metadata.featured', true) | first %} {% set oldest = site.pages | sort_by('date') | last %} Limiting Hugo: {{ range first 5 .Site.RegularPages }} Bengal: {% for page in site.pages | limit(5) %} Set Operations Hugo: 1 2 3{{ $both := intersect $list1 $list2 }} {{ $combined := union $list1 $list2 }} {{ $diff := complement $list1 $list2 }} Bengal: 1 2 3{% set both = list1 | intersect(list2) %} {% set combined = list1 | union(list2) %} {% set diff = list1 | complement(list2) %} Tag Filtering Hugo: {{ $tagged := where .Site.RegularPages "Params.tags" "intersect" (slice "python" "web") }} Bengal: 1 2 3 4 5{# Check if page has 'python' tag #} {% set tagged = site.pages | where('tags', 'python', 'in') %} {# Check if page has any of these tags #} {% set tagged = site.pages | where('tags', ['python', 'web'], 'in') %} Complex Queries Hugo: {{ $result := where (where .Site.RegularPages "Section" "blog") ".Params.featured" true }} Bengal: {% set result = site.pages | where('section', 'blog') | where('metadata.featured', true) %} Quick Reference Filter Purpose Example where(key, val) Filter by value pages \| where('type', 'post') where(key, val, 'gt') Greater than pages \| where('date', cutoff, 'gt') where(key, val, 'in') Value in list pages \| where('tags', 'python', 'in') where_not(key, val) Exclude value pages \| where_not('draft', true) sort_by(key) Sort ascending pages \| sort_by('title') sort_by(key, reverse=true) Sort descending pages \| sort_by('date', reverse=true) group_by(key) Group by value pages \| group_by('category') limit(n) Take first N pages \| limit(5) offset(n) Skip first N pages \| offset(10) first First item pages \| first last Last item pages \| last reverse Reverse order pages \| reverse uniq Remove duplicates tags \| uniq flatten Flatten nested lists nested \| flatten union(list2) Combine lists list1 \| union(list2) intersect(list2) Common items list1 \| intersect(list2) complement(list2) Difference list1 \| complement(list2) Navigation Functions These global functions simplify common navigation patterns. get_section Get a section by its path. Cleaner alternative to site.get_section_by_path(). 1 2 3 4 5 6 7{% set docs = get_section('docs') %} {% if docs %} <h2>{{ docs.title }}</h2> {% for page in docs.pages | sort_by('weight') %} <a href="{{ page.url }}">{{ page.title }}</a> {% endfor %} {% endif %} section_pages Get pages from a section directly. Combines get_section() with .pages access. 1 2 3 4 5 6 7 8 9{# Non-recursive (direct children only) #} {% for page in section_pages('docs') | sort_by('weight') %} <a href="{{ page.url }}">{{ page.title }}</a> {% endfor %} {# Recursive (include all nested pages) #} {% for page in section_pages('docs', recursive=true) %} <a href="{{ page.url }}">{{ page.title }}</a> {% endfor %} page_exists Check if a page exists without loading it. More efficient than get_page() for conditional rendering. 1 2 3 4 5 6 7{% if page_exists('guides/advanced') %} <a href="/guides/advanced/">Advanced Guide Available</a> {% endif %} {# Works with or without .md extension #} {% if page_exists('docs/getting-started.md') %}...{% endif %} {% if page_exists('docs/getting-started') %}...{% endif %} String Filters word_count Count words in text, stripping HTML first. Uses same logic as reading_time. 1 2 3 4{{ page.content | word_count }} words {# Combined with reading time #} <span>{{ page.content | word_count }} words ยท {{ page.content | reading_time }} min read</span> Also available as wordcount (Jinja naming convention). -------------------------------------------------------------------------------- Metadata: - Author: lbliii - Word Count: 1567 - Reading Time: 8 minutes