or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

admin-interface.mdapi.mdcontent-fields.mdcontrib.mdindex.mdmedia.mdpage-models.mdsearch.mdsystem-integration.mdtemplates.mdworkflows.md

search.mddocs/

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

```