Skip to main content

Extending the plugin

🚧Documentation Under Construction

We are actively working to improve this documentation. The content you see here may be incomplete, subject to change, or may not fully reflect the current state of the feature. We appreciate your understanding as we continue to enhance our docs.

For most use cases, the built-in boost types are sufficient. This guide covers creating custom boost types for specialized requirements.

Architecture overview​

The plugin uses a simple class hierarchy:

1
BoostConfiguration (abstract) — defines how boosts are organized
2
└── CustomBoostConfiguration — user-configurable list of boosts
3
4
Boost (abstract)
5
├── IndexBoost (abstract) — for field-based boosts
6
│ ├── ExactMatchBoost
7
│ ├── PartialMatchBoost
8
│ ├── StartsWithBoost
9
│ ├── NewestBoost
10
│ └── OldestBoost
11
└── TypeBoost (abstract) — for content type-based boosts
12
├── ContentTypeBoost
13
└── SemanticMatchBoost

There are two main extension points:

  • BoostConfiguration — Controls how boosts are organized and applied (e.g., curated presets vs. user-defined lists)
  • Boost — Individual boost rules that modify query relevance

Creating a custom boost​

Option 1: Extend IndexBoost​

Use IndexBoost when your boost operates on an indexed field. This gives you the field selector UI automatically.

1
import com.brightspot.cms.search.boost.IndexBoost;
2
import com.psddev.cms.tool.Search;
3
import com.psddev.dari.db.Query;
4
import com.psddev.dari.db.Recordable;
5
import org.apache.commons.lang3.StringUtils;
6
7
/**
8
* Boosts results where the selected field contains the query as a substring, using case-insensitive matching.
9
*/
10
@Recordable.DisplayName("Contains Match")
11
public class ContainsMatchBoost extends IndexBoost {
12
13
@Override
14
public void updateQuery(Search search, Query<?> query) {
15
String queryString = search.getQueryString();
16
if (StringUtils.isBlank(queryString)) {
17
return;
18
}
19
20
String index = getIndex();
21
if (StringUtils.isBlank(index)) {
22
return;
23
}
24
25
// Boost results where the field contains the query string
26
query.sortRelevant(getBoost(), index + " matchesAll ?", queryString.toLowerCase());
27
}
28
}

Key points:

  • Extend IndexBoost to inherit the field selector UI
  • Use getIndex() to get the selected field name
  • Use getBoost() to get the calculated boost factor from the weight
  • Override getIndexFieldType() to filter available fields (default is "text", use "date" for date fields)

Option 2: Extend TypeBoost​

Use TypeBoost when your boost operates on a content type. This gives you the content type selector UI.

1
import com.brightspot.cms.search.boost.TypeBoost;
2
import com.psddev.cms.tool.Search;
3
import com.psddev.dari.db.ObjectType;
4
import com.psddev.dari.db.Query;
5
import com.psddev.dari.db.Recordable;
6
7
/**
8
* Boosts results of the selected content type only when the search query is longer than a specified minimum length.
9
*/
10
@Recordable.DisplayName("Long Query Type Boost")
11
public class LongQueryTypeBoost extends TypeBoost {
12
13
@Recordable.Minimum(1)
14
private int minimumQueryLength = 3;
15
16
public int getMinimumQueryLength() {
17
return minimumQueryLength;
18
}
19
20
public void setMinimumQueryLength(int minimumQueryLength) {
21
this.minimumQueryLength = minimumQueryLength;
22
}
23
24
@Override
25
public void updateQuery(Search search, Query<?> query) {
26
String queryString = search.getQueryString();
27
28
// Only apply boost for queries meeting minimum length
29
if (queryString == null || queryString.length() < minimumQueryLength) {
30
return;
31
}
32
33
ObjectType objectType = getContentObjectType();
34
if (objectType == null) {
35
return;
36
}
37
38
query.sortRelevant(getBoost(), "_type = ?", objectType.findConcreteTypes());
39
}
40
}

Key points:

  • Extend TypeBoost to inherit the content type selector UI
  • Use getContentObjectType() to get the selected ObjectType
  • Add custom fields with standard Dari annotations

Option 3: Extend Boost Directly​

For completely custom logic that doesn't fit the field or type patterns, extend Boost directly.

1
import com.brightspot.cms.search.boost.Boost;
2
import com.psddev.cms.tool.Search;
3
import com.psddev.dari.db.Query;
4
import com.psddev.dari.db.Recordable;
5
6
/**
7
* Boosts results that are marked as "featured" in the CMS. This boost applies regardless of the search query.
8
*/
9
@Recordable.DisplayName("Featured Content")
10
public class FeaturedContentBoost extends Boost {
11
12
@Override
13
public void updateQuery(Search search, Query<?> query) {
14
// Boost any content where the 'featured' field is true
15
query.sortRelevant(getBoost(), "featured = ?", true);
16
}
17
18
@Override
19
public String getLabel() {
20
return "Featured Content (" + getWeight() + ")";
21
}
22
}

Key points:

  • You're responsible for all configuration fields and UI
  • Override getLabel() to provide a meaningful display name
  • The boost automatically appears in the boost type dropdown

Creating a custom BoostConfiguration​

While CustomBoostConfiguration lets users build their own boost lists, you can extend BoostConfiguration to create curated presets with hardcoded boost rules. This is useful for:

  • Providing optimized configurations for specific use cases (e.g., "News Site", "E-Commerce")
  • Enforcing consistent boost rules across sites
  • Simplifying configuration for non-technical users

Example: Curated news configuration​

1
import com.brightspot.cms.search.boost.BoostConfiguration;
2
import com.psddev.cms.tool.Search;
3
import com.psddev.dari.db.Query;
4
import com.psddev.dari.db.Recordable;
5
6
/**
7
* Pre-configured boost settings optimized for news sites. Prioritizes recent content and exact headline matches.
8
*/
9
@Recordable.DisplayName("News Site (Optimized)")
10
public class NewsBoostConfiguration extends BoostConfiguration {
11
12
@Minimum(0)
13
@Maximum(100)
14
private int weightMultiplier = 50;
15
16
@Override
17
public void updateQuery(Search search, Query<?> query) {
18
String queryString = search.getQueryString();
19
double baseBoost = calculateBoost(weightMultiplier);
20
21
// Strong boost for exact headline matches
22
query.sortRelevant(baseBoost * 2, "headline = ?", queryString);
23
24
// Moderate boost for partial headline matches
25
for (String term : queryString.split("\\s+")) {
26
query.sortRelevant(baseBoost, "headline matches ?", "*" + term + "*");
27
}
28
29
// Boost recent content (within last 7 days gets highest boost)
30
query.sortNewest(baseBoost * 1.5, "cms.content.publishDate");
31
}
32
33
private double calculateBoost(int weight) {
34
return Math.pow(10, weight / 10.0);
35
}
36
37
public int getWeightMultiplier() {
38
return weightMultiplier;
39
}
40
41
public void setWeightMultiplier(int weightMultiplier) {
42
this.weightMultiplier = weightMultiplier;
43
}
44
}

Key points:

  • Extend BoostConfiguration directly
  • Override updateQuery() to apply your hardcoded boost logic
  • Use @Recordable.DisplayName to give it a friendly name in the dropdown
  • Optionally add configurable fields to allow some customization (like the weight multiplier in the example)

When to use custom BoostConfiguration​

Use CaseApproach
Users need full control over boost rulesUse built-in CustomBoostConfiguration
Provide optimized presets users can selectCreate custom BoostConfiguration subclasses
Mix of preset + customizationCreate custom BoostConfiguration with configurable fields
Enforce specific boost rules programmaticallyCreate custom BoostConfiguration with hardcoded logic

Key APIs​

Query methods​

The Query class provides several methods for applying boosts:

1
// Boost by a condition
2
query.sortRelevant(boostFactor, "field = ?", value);
3
4
// Boost by date (newer first)
5
query.sortNewest(boostFactor, "dateField");
6
7
// Boost by date (older first)
8
query.sortOldest(boostFactor, "dateField");

Search context​

The Search object provides access to:

1
search.getQueryString() // The raw search query entered by the user

Weight conversion​

The getBoost() method converts the 0-100 weight to a logarithmic scale:

  • Weight 0 = boost factor 1.0
  • Weight 50 = boost factor ~1,000
  • Weight 100 = boost factor ~1 billion

Registration​

Custom boosts and configurations are automatically discovered and appear in the CMS UI. Just ensure your class:

  1. Extends Boost, IndexBoost, TypeBoost, or BoostConfiguration
  2. Has the @Recordable.DisplayName annotation for a friendly name