CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/pypi-wagtail

A Django content management system with a user-friendly interface and powerful features for building websites and applications.

Overview
Eval results
Files

search.mddocs/

Search and Indexing

Full-text search capabilities with configurable backends, search field configuration, and powerful query interfaces. Wagtail provides comprehensive search functionality that integrates with multiple search engines and supports complex queries.

Capabilities

Search Configuration

Classes for configuring which fields are searchable and how they're indexed.

class SearchField:
    """
    Configures a field for full-text search indexing.
    
    Parameters:
        field_name (str): Name of the model field to index
        partial_match (bool): Whether to enable partial matching
        boost (int): Search result boost factor for this field
    """
    def __init__(self, field_name, partial_match=True, boost=1):
        """Initialize search field configuration."""

class AutocompleteField:
    """
    Configures a field for autocomplete search functionality.
    
    Parameters:
        field_name (str): Name of the model field for autocomplete
    """
    def __init__(self, field_name):
        """Initialize autocomplete field configuration."""

class FilterField:
    """
    Configures a field for exact match filtering in search results.
    
    Parameters:
        field_name (str): Name of the model field for filtering
    """
    def __init__(self, field_name):
        """Initialize filter field configuration."""

class RelatedFields:
    """
    Configures search through related model fields.
    
    Parameters:
        relation_name (str): Name of the relation to search through
        fields (list): List of SearchField objects for related model
    """
    def __init__(self, relation_name, fields):
        """Initialize related fields search configuration."""

Search Interface

Base classes and mixins for making models searchable.

class Indexed:
    """
    Mixin class that makes Django models searchable.
    
    Add this mixin to any model to enable search functionality.
    """
    search_fields: list  # List of SearchField, AutocompleteField, FilterField objects
    
    @classmethod
    def search(cls, query, backend=None):
        """
        Perform a search query on this model.
        
        Parameters:
            query (str): Search query string
            backend (str): Search backend to use (optional)
            
        Returns:
            SearchResults: Query results with ranking and filtering
        """

def get_search_backend(backend_name=None):
    """
    Get a search backend instance.
    
    Parameters:
        backend_name (str): Name of backend ('default', 'elasticsearch', etc.)
        
    Returns:
        SearchBackend: Configured search backend instance
    """

Search Backends

Available search backend implementations with different capabilities.

class SearchBackend:
    """
    Base class for search backends.
    
    All search backends inherit from this class.
    """
    def search(self, query, model_or_queryset, fields=None, filters=None, order_by_relevance=True):
        """
        Execute a search query.
        
        Parameters:
            query (str): Search query string
            model_or_queryset: Model class or QuerySet to search
            fields (list): Fields to search in
            filters (dict): Additional filters to apply
            order_by_relevance (bool): Whether to order by search relevance
            
        Returns:
            SearchResults: Search results with metadata
        """
    
    def autocomplete(self, query, model_or_queryset, fields=None):
        """
        Get autocomplete suggestions.
        
        Parameters:
            query (str): Partial query for autocomplete
            model_or_queryset: Model class or QuerySet for suggestions
            fields (list): Fields to use for autocomplete
            
        Returns:
            list: List of autocomplete suggestions
        """

class DatabaseSearchBackend(SearchBackend):
    """
    Search backend using Django's database with PostgreSQL full-text search.
    
    Default backend that works with any Django-supported database.
    """

class ElasticsearchSearchBackend(SearchBackend):
    """
    Search backend using Elasticsearch for advanced search capabilities.
    
    Provides better performance and advanced features for large datasets.
    """

Search Results

Classes for handling and manipulating search results.

class SearchResults:
    """
    Container for search results with metadata and filtering capabilities.
    """
    def __init__(self, model_or_queryset, query, backend):
        """Initialize search results container."""
    
    def __iter__(self):
        """Iterate over search results."""
    
    def __len__(self):
        """Get number of search results."""
    
    def __getitem__(self, index):
        """Get result at specific index or slice."""
    
    def filter(self, **kwargs):
        """Apply additional filters to search results."""
    
    def order_by(self, *fields):
        """Change the ordering of search results."""
    
    def annotate_score(self, field_name='_score'):
        """Add search relevance score to results."""

class SearchQuery:
    """
    Represents a search query with parsing and normalization.
    """
    def __init__(self, query_string, operator='and', boost=None):
        """
        Initialize search query.
        
        Parameters:
            query_string (str): Raw query string
            operator (str): Query operator ('and' or 'or')
            boost (dict): Field boost configuration
        """
    
    def get_terms(self):
        """Get individual search terms from query."""
    
    def get_filters(self):
        """Get filter conditions from query."""

Search Utilities

Utility functions and classes for search functionality.

def parse_query_string(query_string, operator='and'):
    """
    Parse a query string into structured query components.
    
    Parameters:
        query_string (str): Raw search query
        operator (str): Default operator between terms
        
    Returns:
        SearchQuery: Parsed query object
    """

class SearchPromotion:
    """
    Model for promoting specific content in search results.
    
    Allows administrators to boost certain pages for specific queries.
    """
    query: str
    page: Page
    sort_order: int
    description: str
    
    @classmethod
    def get_for_query(cls, query_string):
        """Get promotions for a specific query."""

def rebuild_search_index():
    """
    Rebuild the entire search index for all searchable models.
    
    Use this management command function to refresh search data.
    """

def update_search_index(model=None):
    """
    Update search index for specific model or all models.
    
    Parameters:
        model (Model): Specific model to update (None for all models)
    """

Usage Examples

Making Models Searchable

from wagtail.models import Page
from wagtail.search import index
from django.db import models

class BlogPage(Page):
    """Blog page with comprehensive search configuration."""
    date = models.DateField("Post date")
    intro = models.CharField(max_length=250)
    body = models.TextField()
    tags = models.CharField(max_length=255, blank=True)
    
    # Configure search fields
    search_fields = Page.search_fields + [
        index.SearchField('intro', partial_match=True, boost=2),
        index.SearchField('body'),
        index.SearchField('tags', partial_match=True),
        index.AutocompleteField('title'),
        index.FilterField('date'),
        index.FilterField('live'),
    ]

class Author(models.Model):
    """Author model with search fields."""
    name = models.CharField(max_length=100)
    bio = models.TextField()
    email = models.EmailField()
    
    # Make non-page models searchable
    search_fields = [
        index.SearchField('name', partial_match=True, boost=3),
        index.SearchField('bio'),
        index.AutocompleteField('name'),
    ]
    
    class Meta:
        # Add Indexed mixin functionality
        # In practice, inherit from index.Indexed
        pass

# Add Indexed mixin to existing model
Author.__bases__ = (index.Indexed,) + Author.__bases__

Performing Searches

from wagtail.models import Page
from wagtail.search.models import Query

# Basic page search
search_results = Page.objects.live().search('django cms')

# Advanced search with filtering
blog_results = BlogPage.objects.live().search(
    'web development'
).filter(
    date__gte='2023-01-01'
).order_by('-date')

# Search with specific backend
from wagtail.search.backends import get_search_backend

elasticsearch_backend = get_search_backend('elasticsearch')
results = elasticsearch_backend.search(
    'python tutorial',
    BlogPage.objects.live(),
    fields=['title', 'intro', 'body']
)

# Autocomplete search
suggestions = Page.objects.live().autocomplete('wagtai')

# Track search queries
Query.get('django cms').add_hit()  # Record search analytics
popular_queries = Query.objects.filter(
    hits__gte=10
).order_by('-hits')

Custom Search Views

from django.shortcuts import render
from django.core.paginator import Paginator
from wagtail.models import Page
from wagtail.search.models import Query

def search_view(request):
    """Custom search view with pagination and analytics."""
    query_string = request.GET.get('q', '')
    page_number = request.GET.get('page', 1)
    
    if query_string:
        # Perform search
        search_results = Page.objects.live().search(query_string)
        
        # Track search query
        query = Query.get(query_string)
        query.add_hit()
        
        # Add pagination
        paginator = Paginator(search_results, 10)
        results_page = paginator.get_page(page_number)
        
        # Get search suggestions
        suggestions = Page.objects.live().autocomplete(query_string)[:5]
    else:
        results_page = None
        suggestions = []
    
    return render(request, 'search/search.html', {
        'query_string': query_string,
        'results': results_page,
        'suggestions': suggestions,
    })

def faceted_search_view(request):
    """Search view with faceted filtering."""
    query_string = request.GET.get('q', '')
    content_type = request.GET.get('type', '')
    date_filter = request.GET.get('date', '')
    
    if query_string:
        results = Page.objects.live().search(query_string)
        
        # Apply facet filters
        if content_type:
            results = results.type(eval(content_type))
        
        if date_filter:
            results = results.filter(first_published_at__gte=date_filter)
        
        # Get facet counts
        facets = {
            'types': results.facet('content_type'),
            'dates': results.facet('first_published_at__year'),
        }
    else:
        results = Page.objects.none()
        facets = {}
    
    return render(request, 'search/faceted_search.html', {
        'query_string': query_string,
        'results': results,
        'facets': facets,
    })

Search with Related Fields

from wagtail.models import Page
from wagtail.search import index
from django.db import models
from modelcluster.fields import ParentalKey

class BlogCategory(models.Model):
    """Blog category model."""
    name = models.CharField(max_length=100)
    description = models.TextField()
    
    search_fields = [
        index.SearchField('name', boost=2),
        index.SearchField('description'),
    ]

class BlogPage(Page):
    """Blog page with category relationship."""
    category = models.ForeignKey(
        BlogCategory,
        on_delete=models.SET_NULL,
        null=True,
        blank=True
    )
    body = models.TextField()
    
    # Search through related fields
    search_fields = Page.search_fields + [
        index.SearchField('body'),
        index.RelatedFields('category', [
            index.SearchField('name', boost=2),
            index.SearchField('description'),
        ]),
    ]

# Search will now find pages by category name/description
results = BlogPage.objects.search('python')  # Finds pages in "Python" category

Custom Search Backend Configuration

# settings.py
WAGTAILSEARCH_BACKENDS = {
    'default': {
        'BACKEND': 'wagtail.search.backends.database',
        'SEARCH_CONFIG': 'english',  # PostgreSQL text search config
    },
    'elasticsearch': {
        'BACKEND': 'wagtail.search.backends.elasticsearch7',
        'URLS': ['http://localhost:9200'],
        'INDEX': 'wagtail',
        'TIMEOUT': 5,
        'OPTIONS': {},
        'INDEX_SETTINGS': {
            'settings': {
                'analysis': {
                    'analyzer': {
                        'ngram_analyzer': {
                            'tokenizer': 'ngram_tokenizer',
                        }
                    }
                }
            }
        }
    }
}

# Use specific backend
from wagtail.search.backends import get_search_backend

elasticsearch = get_search_backend('elasticsearch')
database = get_search_backend('default')

# Backend-specific search
es_results = elasticsearch.search('query', MyModel)
db_results = database.search('query', MyModel)

Search Index Management

# Management commands for search index
from django.core.management import call_command

# Rebuild entire search index
call_command('update_index')

# Update specific model
call_command('update_index', 'blog.BlogPage')

# Clear search index
call_command('clear_index')

# Programmatic index updates
from wagtail.search.signal_handlers import post_save_signal_handler
from wagtail.search import index

# Manual index update
page = BlogPage.objects.get(pk=1)
index.insert_or_update_object(page)

# Remove from index
index.remove_object(page)

# Bulk index operations
pages = BlogPage.objects.all()
for page in pages:
    index.insert_or_update_object(page)

Install with Tessl CLI

npx tessl i tessl/pypi-wagtail

docs

admin-interface.md

api.md

content-fields.md

contrib.md

index.md

media.md

page-models.md

search.md

system-integration.md

templates.md

workflows.md

tile.json