We have already seen that the default sort order for search results is by relevance, meaning the degree to which they match the query. If one entity matches on two fields, while another has only one field match, then that first entity is the more relevant result.
Hibernate Search allows us to adjust how relevance is calculated, by boosting the relative importance of entities or fields when they are indexed. These adjustments can be static and fixed, or they can be dynamic and driven by the state of the data at runtime.
Fixed boosting, regardless of the actual data, is as
simple as annotating a class or field with @Boost
. This annotation takes a floating-point parameter for its relative weight, with the default weight being 1.0. So for example, @Boost(2.0f)
would double the weight of a class or field relative to non-annotated classes and fields.
Our VAPORware Marketplace application searches on several fields and associations, such as the names of supported devices, and comments posted in customer reviews. However, doesn't it make sense that the text under our control (each app's name and full description) should carry more weight than text coming from outside parties?
To make this adjustment, the chapter4
version starts by annotating the App
class itself:
...
@Boost(2.0f)
public class App implements Serializable {
...
This essentially makes App
twice as relevant as Device
or CustomerReview
. Next, we apply field-level boosting to the name and full description fields:
... @Boost(1.5f) private String name; ... @Boost(1.2f) private String description; ...
We are declaring here that name
carries slightly more weight than description
, and they each carry more weight relative to normal fields.
Be aware that class-level and field-level boosting cascade and combine! When more than one boost factor applies to a given field, they are multiplied to form the total factor.
Here, because a weight of 2.0 was
already applied to the App
class itself, name
has a total effective weight of 3.0 and description
is at 2.4.
For an example of boosting an entity
dynamically based on its data at index-time, let's say that we wanted to give the CustomerReview
objects a bit more weight when the reviewer gives a five-star rating. To do this, we apply a @DynamicBoost
annotation to the class:
...
@DynamicBoost(impl=FiveStarBoostStrategy.class)
public class CustomerReview {
...
This annotation must be passed a class that implements the BoostStrategy
interface, and its defineBoost
method:
public class FiveStarBoostStrategy implements BoostStrategy { public float defineBoost(Object value) { if(value == null || !(value instanceofCustomerReview)) { return 1; } CustomerReviewcustomerReview = (CustomerReview) value; if(customerReview.getStars() == 5) { return 1.5f; } else { return 1; } } }
When the
@DynamicBoost
annotation was applied to a class, the parameter automatically passed to defineBoost
is an instance of that class (a CustomerReview
object in this case). If the annotation had been applied to a particular field, then the automatically-passed parameter would be that field's value.
The float
value returned by defineBoost
becomes the weight of the class or field that was annotated. In this case, we increase a CustomerReview
object's weight to 1.5 when it represents a five-star review. Otherwise, we keep the 1.0 default.