Scoring & Ranking¶
Forage provides powerful tools to control how search results are ranked. You can combine Lucene's relevance scoring with business signals to create the perfect ranking for your use case.
Ranking Layers¶
graph TB
A[Base Lucene Score] -->|TF/IDF| B[Relevance Score]
B -->|Query Boosts| C[Boosted Score]
C -->|Function Score| D[Final Score]
D -->|Sorting| E[Ordered Results]
E -->|Minimum Score| F[Filtered Results]
Ranking Components¶
| Component | Purpose | Documentation |
|---|---|---|
| Query Boosts | Emphasize specific query clauses | Boost important fields |
| Function Score | Apply custom scoring functions | Field values, scripts, decay |
| Sorting | Order results by criteria | Score, field values |
| Minimum Score | Filter low-quality results | Quality threshold |
Quick Examples¶
Query-Level Boosting¶
// Title matches are 2x more important than author matches
QueryBuilder.booleanQuery()
.query(QueryBuilder.matchQuery("title", "java").boost(2.0f).build())
.query(QueryBuilder.matchQuery("author", "java").boost(1.0f).build())
.clauseType(ClauseType.SHOULD)
.buildForageQuery()
Rank by Field Value¶
// Rank by rating (highest rated first)
QueryBuilder.functionScoreQuery()
.baseQuery(QueryBuilder.matchAllQuery().build())
.fieldValueFactor("rating")
.buildForageQuery(20)
Combine Relevance with Business Signal¶
// Blend text relevance with rating
QueryBuilder.functionScoreQuery()
.baseQuery(QueryBuilder.matchQuery("title", "programming").build())
.scoreFunction(new ScriptScoreFunction("score * rating"))
.buildForageQuery(10)
Custom Sorting¶
// Sort by score, then by rating as tiebreaker
QueryBuilder.matchQuery("title", "java")
.buildForageQuery(10, Arrays.asList(
SortCriteria.byScore(SortOrder.DESC),
new SortCriteria("rating", SortOrder.DESC)
))
Quality Threshold¶
// Only return results with score >= 0.5
QueryBuilder.matchQuery("title", "programming")
.buildForageQuery(10, null, 0.5f)
Scoring Modes¶
Function scores operate in two modes:
| Mode | Formula | Use Case |
|---|---|---|
| Multiplicative | base_score × function_value |
Scale relevance by weight |
| Direct Value | function_value |
Replace score with field value |
// Multiplicative: WeightedScoreFunction, ScriptScoreFunction
// final_score = relevance_score × weight
QueryBuilder.functionScoreQuery()
.baseQuery(QueryBuilder.matchQuery("title", "java").build())
.scoreFunction(new WeightedScoreFunction(2.0f)) // Score × 2
// Direct Value: FieldValueFactorFunction, ConstantScoreFunction
// final_score = field_value (ignores base relevance)
QueryBuilder.functionScoreQuery()
.baseQuery(QueryBuilder.matchAllQuery().build())
.fieldValueFactor("rating") // Score = rating
Function Score Types¶
| Function | Mode | Description |
|---|---|---|
ConstantScoreFunction |
Direct | All matches get same score |
WeightedScoreFunction |
Multiplicative | Scale base score by weight |
FieldValueFactorFunction |
Direct | Use field value as score |
ScriptScoreFunction |
Multiplicative | Custom JavaScript expression |
RandomScoreFunction |
Direct | Deterministic random order |
DecayFunction |
Direct | Distance-based decay |
Common Ranking Recipes¶
E-Commerce Product Search¶
// Combine relevance + rating + recency
QueryBuilder.functionScoreQuery()
.baseQuery(QueryBuilder.booleanQuery()
.query(QueryBuilder.matchQuery("name", searchTerm).boost(3.0f).build())
.query(QueryBuilder.matchQuery("description", searchTerm).build())
.clauseType(ClauseType.SHOULD)
.build())
.scoreFunction(new ScriptScoreFunction("score * rating * recencyBoost"))
.buildForageQuery(20, Arrays.asList(SortCriteria.byScore()), 0.1f)
Content Discovery (Popularity-Based)¶
// Rank purely by popularity
QueryBuilder.functionScoreQuery()
.baseQuery(QueryBuilder.matchQuery("category", "technology").build())
.fieldValueFactor("viewCount")
.buildForageQuery(10)
Location-Based (Decay)¶
// Prefer items closer to target value
QueryBuilder.functionScoreQuery()
.baseQuery(QueryBuilder.matchAllQuery().build())
.scoreFunction(new DecayFunction(
targetDistance, // origin
10.0, // scale
0.0, // offset
0.5, // decay
DecayType.GAUSS,
"distance"
))
.buildForageQuery(20)
A/B Testing (Random)¶
// Consistent random order for A/B testing
long seed = userId.hashCode();
QueryBuilder.functionScoreQuery()
.baseQuery(QueryBuilder.matchAllQuery().build())
.scoreFunction(new RandomScoreFunction(seed, "id_numeric"))
.buildForageQuery(10)
Next Steps¶
Explore each ranking component:
- Query Boosts - Field and clause boosting
- Function Score - All function types explained
- Sorting - Sort criteria and ordering
- Minimum Score - Quality filtering