0
# Search and Indexing
1
2
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.
3
4
## Capabilities
5
6
### Search Configuration
7
8
Classes for configuring which fields are searchable and how they're indexed.
9
10
```python { .api }
11
class SearchField:
12
"""
13
Configures a field for full-text search indexing.
14
15
Parameters:
16
field_name (str): Name of the model field to index
17
partial_match (bool): Whether to enable partial matching
18
boost (int): Search result boost factor for this field
19
"""
20
def __init__(self, field_name, partial_match=True, boost=1):
21
"""Initialize search field configuration."""
22
23
class AutocompleteField:
24
"""
25
Configures a field for autocomplete search functionality.
26
27
Parameters:
28
field_name (str): Name of the model field for autocomplete
29
"""
30
def __init__(self, field_name):
31
"""Initialize autocomplete field configuration."""
32
33
class FilterField:
34
"""
35
Configures a field for exact match filtering in search results.
36
37
Parameters:
38
field_name (str): Name of the model field for filtering
39
"""
40
def __init__(self, field_name):
41
"""Initialize filter field configuration."""
42
43
class RelatedFields:
44
"""
45
Configures search through related model fields.
46
47
Parameters:
48
relation_name (str): Name of the relation to search through
49
fields (list): List of SearchField objects for related model
50
"""
51
def __init__(self, relation_name, fields):
52
"""Initialize related fields search configuration."""
53
```
54
55
### Search Interface
56
57
Base classes and mixins for making models searchable.
58
59
```python { .api }
60
class Indexed:
61
"""
62
Mixin class that makes Django models searchable.
63
64
Add this mixin to any model to enable search functionality.
65
"""
66
search_fields: list # List of SearchField, AutocompleteField, FilterField objects
67
68
@classmethod
69
def search(cls, query, backend=None):
70
"""
71
Perform a search query on this model.
72
73
Parameters:
74
query (str): Search query string
75
backend (str): Search backend to use (optional)
76
77
Returns:
78
SearchResults: Query results with ranking and filtering
79
"""
80
81
def get_search_backend(backend_name=None):
82
"""
83
Get a search backend instance.
84
85
Parameters:
86
backend_name (str): Name of backend ('default', 'elasticsearch', etc.)
87
88
Returns:
89
SearchBackend: Configured search backend instance
90
"""
91
```
92
93
### Search Backends
94
95
Available search backend implementations with different capabilities.
96
97
```python { .api }
98
class SearchBackend:
99
"""
100
Base class for search backends.
101
102
All search backends inherit from this class.
103
"""
104
def search(self, query, model_or_queryset, fields=None, filters=None, order_by_relevance=True):
105
"""
106
Execute a search query.
107
108
Parameters:
109
query (str): Search query string
110
model_or_queryset: Model class or QuerySet to search
111
fields (list): Fields to search in
112
filters (dict): Additional filters to apply
113
order_by_relevance (bool): Whether to order by search relevance
114
115
Returns:
116
SearchResults: Search results with metadata
117
"""
118
119
def autocomplete(self, query, model_or_queryset, fields=None):
120
"""
121
Get autocomplete suggestions.
122
123
Parameters:
124
query (str): Partial query for autocomplete
125
model_or_queryset: Model class or QuerySet for suggestions
126
fields (list): Fields to use for autocomplete
127
128
Returns:
129
list: List of autocomplete suggestions
130
"""
131
132
class DatabaseSearchBackend(SearchBackend):
133
"""
134
Search backend using Django's database with PostgreSQL full-text search.
135
136
Default backend that works with any Django-supported database.
137
"""
138
139
class ElasticsearchSearchBackend(SearchBackend):
140
"""
141
Search backend using Elasticsearch for advanced search capabilities.
142
143
Provides better performance and advanced features for large datasets.
144
"""
145
```
146
147
### Search Results
148
149
Classes for handling and manipulating search results.
150
151
```python { .api }
152
class SearchResults:
153
"""
154
Container for search results with metadata and filtering capabilities.
155
"""
156
def __init__(self, model_or_queryset, query, backend):
157
"""Initialize search results container."""
158
159
def __iter__(self):
160
"""Iterate over search results."""
161
162
def __len__(self):
163
"""Get number of search results."""
164
165
def __getitem__(self, index):
166
"""Get result at specific index or slice."""
167
168
def filter(self, **kwargs):
169
"""Apply additional filters to search results."""
170
171
def order_by(self, *fields):
172
"""Change the ordering of search results."""
173
174
def annotate_score(self, field_name='_score'):
175
"""Add search relevance score to results."""
176
177
class SearchQuery:
178
"""
179
Represents a search query with parsing and normalization.
180
"""
181
def __init__(self, query_string, operator='and', boost=None):
182
"""
183
Initialize search query.
184
185
Parameters:
186
query_string (str): Raw query string
187
operator (str): Query operator ('and' or 'or')
188
boost (dict): Field boost configuration
189
"""
190
191
def get_terms(self):
192
"""Get individual search terms from query."""
193
194
def get_filters(self):
195
"""Get filter conditions from query."""
196
```
197
198
### Search Utilities
199
200
Utility functions and classes for search functionality.
201
202
```python { .api }
203
def parse_query_string(query_string, operator='and'):
204
"""
205
Parse a query string into structured query components.
206
207
Parameters:
208
query_string (str): Raw search query
209
operator (str): Default operator between terms
210
211
Returns:
212
SearchQuery: Parsed query object
213
"""
214
215
class SearchPromotion:
216
"""
217
Model for promoting specific content in search results.
218
219
Allows administrators to boost certain pages for specific queries.
220
"""
221
query: str
222
page: Page
223
sort_order: int
224
description: str
225
226
@classmethod
227
def get_for_query(cls, query_string):
228
"""Get promotions for a specific query."""
229
230
def rebuild_search_index():
231
"""
232
Rebuild the entire search index for all searchable models.
233
234
Use this management command function to refresh search data.
235
"""
236
237
def update_search_index(model=None):
238
"""
239
Update search index for specific model or all models.
240
241
Parameters:
242
model (Model): Specific model to update (None for all models)
243
"""
244
```
245
246
## Usage Examples
247
248
### Making Models Searchable
249
250
```python
251
from wagtail.models import Page
252
from wagtail.search import index
253
from django.db import models
254
255
class BlogPage(Page):
256
"""Blog page with comprehensive search configuration."""
257
date = models.DateField("Post date")
258
intro = models.CharField(max_length=250)
259
body = models.TextField()
260
tags = models.CharField(max_length=255, blank=True)
261
262
# Configure search fields
263
search_fields = Page.search_fields + [
264
index.SearchField('intro', partial_match=True, boost=2),
265
index.SearchField('body'),
266
index.SearchField('tags', partial_match=True),
267
index.AutocompleteField('title'),
268
index.FilterField('date'),
269
index.FilterField('live'),
270
]
271
272
class Author(models.Model):
273
"""Author model with search fields."""
274
name = models.CharField(max_length=100)
275
bio = models.TextField()
276
email = models.EmailField()
277
278
# Make non-page models searchable
279
search_fields = [
280
index.SearchField('name', partial_match=True, boost=3),
281
index.SearchField('bio'),
282
index.AutocompleteField('name'),
283
]
284
285
class Meta:
286
# Add Indexed mixin functionality
287
# In practice, inherit from index.Indexed
288
pass
289
290
# Add Indexed mixin to existing model
291
Author.__bases__ = (index.Indexed,) + Author.__bases__
292
```
293
294
### Performing Searches
295
296
```python
297
from wagtail.models import Page
298
from wagtail.search.models import Query
299
300
# Basic page search
301
search_results = Page.objects.live().search('django cms')
302
303
# Advanced search with filtering
304
blog_results = BlogPage.objects.live().search(
305
'web development'
306
).filter(
307
date__gte='2023-01-01'
308
).order_by('-date')
309
310
# Search with specific backend
311
from wagtail.search.backends import get_search_backend
312
313
elasticsearch_backend = get_search_backend('elasticsearch')
314
results = elasticsearch_backend.search(
315
'python tutorial',
316
BlogPage.objects.live(),
317
fields=['title', 'intro', 'body']
318
)
319
320
# Autocomplete search
321
suggestions = Page.objects.live().autocomplete('wagtai')
322
323
# Track search queries
324
Query.get('django cms').add_hit() # Record search analytics
325
popular_queries = Query.objects.filter(
326
hits__gte=10
327
).order_by('-hits')
328
```
329
330
### Custom Search Views
331
332
```python
333
from django.shortcuts import render
334
from django.core.paginator import Paginator
335
from wagtail.models import Page
336
from wagtail.search.models import Query
337
338
def search_view(request):
339
"""Custom search view with pagination and analytics."""
340
query_string = request.GET.get('q', '')
341
page_number = request.GET.get('page', 1)
342
343
if query_string:
344
# Perform search
345
search_results = Page.objects.live().search(query_string)
346
347
# Track search query
348
query = Query.get(query_string)
349
query.add_hit()
350
351
# Add pagination
352
paginator = Paginator(search_results, 10)
353
results_page = paginator.get_page(page_number)
354
355
# Get search suggestions
356
suggestions = Page.objects.live().autocomplete(query_string)[:5]
357
else:
358
results_page = None
359
suggestions = []
360
361
return render(request, 'search/search.html', {
362
'query_string': query_string,
363
'results': results_page,
364
'suggestions': suggestions,
365
})
366
367
def faceted_search_view(request):
368
"""Search view with faceted filtering."""
369
query_string = request.GET.get('q', '')
370
content_type = request.GET.get('type', '')
371
date_filter = request.GET.get('date', '')
372
373
if query_string:
374
results = Page.objects.live().search(query_string)
375
376
# Apply facet filters
377
if content_type:
378
results = results.type(eval(content_type))
379
380
if date_filter:
381
results = results.filter(first_published_at__gte=date_filter)
382
383
# Get facet counts
384
facets = {
385
'types': results.facet('content_type'),
386
'dates': results.facet('first_published_at__year'),
387
}
388
else:
389
results = Page.objects.none()
390
facets = {}
391
392
return render(request, 'search/faceted_search.html', {
393
'query_string': query_string,
394
'results': results,
395
'facets': facets,
396
})
397
```
398
399
### Search with Related Fields
400
401
```python
402
from wagtail.models import Page
403
from wagtail.search import index
404
from django.db import models
405
from modelcluster.fields import ParentalKey
406
407
class BlogCategory(models.Model):
408
"""Blog category model."""
409
name = models.CharField(max_length=100)
410
description = models.TextField()
411
412
search_fields = [
413
index.SearchField('name', boost=2),
414
index.SearchField('description'),
415
]
416
417
class BlogPage(Page):
418
"""Blog page with category relationship."""
419
category = models.ForeignKey(
420
BlogCategory,
421
on_delete=models.SET_NULL,
422
null=True,
423
blank=True
424
)
425
body = models.TextField()
426
427
# Search through related fields
428
search_fields = Page.search_fields + [
429
index.SearchField('body'),
430
index.RelatedFields('category', [
431
index.SearchField('name', boost=2),
432
index.SearchField('description'),
433
]),
434
]
435
436
# Search will now find pages by category name/description
437
results = BlogPage.objects.search('python') # Finds pages in "Python" category
438
```
439
440
### Custom Search Backend Configuration
441
442
```python
443
# settings.py
444
WAGTAILSEARCH_BACKENDS = {
445
'default': {
446
'BACKEND': 'wagtail.search.backends.database',
447
'SEARCH_CONFIG': 'english', # PostgreSQL text search config
448
},
449
'elasticsearch': {
450
'BACKEND': 'wagtail.search.backends.elasticsearch7',
451
'URLS': ['http://localhost:9200'],
452
'INDEX': 'wagtail',
453
'TIMEOUT': 5,
454
'OPTIONS': {},
455
'INDEX_SETTINGS': {
456
'settings': {
457
'analysis': {
458
'analyzer': {
459
'ngram_analyzer': {
460
'tokenizer': 'ngram_tokenizer',
461
}
462
}
463
}
464
}
465
}
466
}
467
}
468
469
# Use specific backend
470
from wagtail.search.backends import get_search_backend
471
472
elasticsearch = get_search_backend('elasticsearch')
473
database = get_search_backend('default')
474
475
# Backend-specific search
476
es_results = elasticsearch.search('query', MyModel)
477
db_results = database.search('query', MyModel)
478
```
479
480
### Search Index Management
481
482
```python
483
# Management commands for search index
484
from django.core.management import call_command
485
486
# Rebuild entire search index
487
call_command('update_index')
488
489
# Update specific model
490
call_command('update_index', 'blog.BlogPage')
491
492
# Clear search index
493
call_command('clear_index')
494
495
# Programmatic index updates
496
from wagtail.search.signal_handlers import post_save_signal_handler
497
from wagtail.search import index
498
499
# Manual index update
500
page = BlogPage.objects.get(pk=1)
501
index.insert_or_update_object(page)
502
503
# Remove from index
504
index.remove_object(page)
505
506
# Bulk index operations
507
pages = BlogPage.objects.all()
508
for page in pages:
509
index.insert_or_update_object(page)
510
```