Skip to content

Function Score

Function Score queries allow you to modify document scores using custom functions, field values, mathematical expressions, or decay functions.

Basic Usage

QueryBuilder.functionScoreQuery()
    .baseQuery(QueryBuilder.matchQuery("title", "java").build())
    .fieldValueFactor("rating")
    .buildForageQuery(10)

Function Types

Function Mode Score Calculation
ConstantScoreFunction Direct score = constant
WeightedScoreFunction Multiplicative score = base_score × weight
FieldValueFactorFunction Direct score = field_value × factor
ScriptScoreFunction Multiplicative score = base_score × expression_result
RandomScoreFunction Direct score = random(seed, field)
DecayFunction Direct score = decay(distance)

Constant Score Function

Assigns a fixed score to all matching documents, ignoring the base query's relevance.

QueryBuilder.functionScoreQuery()
    .baseQuery(QueryBuilder.matchQuery("category", "fiction").build())
    .constantScore(5.0f)  // All matches get score = 5.0
    .buildForageQuery()

Use case: When relevance doesn't matter, only matching does.

// All fiction books get the same score
var fictionBooks = engine.search(
    QueryBuilder.functionScoreQuery()
        .baseQuery(QueryBuilder.matchQuery("category", "fiction").build())
        .constantScore(1.0f)
        .buildForageQuery(100)
);

Weighted Score Function

Multiplies the base query score by a weight factor.

QueryBuilder.functionScoreQuery()
    .baseQuery(QueryBuilder.matchQuery("title", "programming").build())
    .scoreFunction(new WeightedScoreFunction(2.0f))  // Score × 2
    .buildForageQuery()

Use case: Scale relevance scores without changing ranking order.

// Double all scores (useful when combining with other scoring)
QueryBuilder.functionScoreQuery()
    .baseQuery(QueryBuilder.matchQuery("title", "java").build())
    .scoreFunction(new WeightedScoreFunction(2.0f))
    .buildForageQuery()

Field Value Factor Function

Uses a numeric field's value as the score, optionally multiplied by a factor.

// Score = rating
QueryBuilder.functionScoreQuery()
    .baseQuery(QueryBuilder.matchAllQuery().build())
    .fieldValueFactor("rating")
    .buildForageQuery()

// Score = rating × 1.5
QueryBuilder.functionScoreQuery()
    .baseQuery(QueryBuilder.matchAllQuery().build())
    .fieldValueFactor("rating", 1.5f)
    .buildForageQuery()

Use case: Rank by a business metric (rating, popularity, sales).

// Top rated books
var topRated = engine.search(
    QueryBuilder.functionScoreQuery()
        .baseQuery(QueryBuilder.matchAllQuery().build())
        .fieldValueFactor("rating")
        .buildForageQuery(20)
);

// Results ordered: 4.9, 4.8, 4.7, 4.5, ...

Script Score Function

Executes a JavaScript expression that can reference the base score and document fields.

// Score = base_relevance × rating
QueryBuilder.functionScoreQuery()
    .baseQuery(QueryBuilder.matchQuery("title", "programming").build())
    .scoreFunction(new ScriptScoreFunction("score * rating"))
    .buildForageQuery()

Available Variables

Variable Description
score Base query relevance score
<fieldName> Any numeric DocValues field

Expression Examples

// Blend relevance with rating
new ScriptScoreFunction("score * rating")

// Weighted combination
new ScriptScoreFunction("score * 0.7 + rating * 0.3")

// Complex formula
new ScriptScoreFunction("score * (1 + ln(popularity + 1))")

// Multiple fields
new ScriptScoreFunction("score * rating * freshness")

Use case: Create custom ranking formulas combining multiple signals.

// E-commerce: blend relevance, rating, and recency
var products = engine.search(
    QueryBuilder.functionScoreQuery()
        .baseQuery(QueryBuilder.matchQuery("name", searchTerm).build())
        .scoreFunction(new ScriptScoreFunction("score * rating * recencyBoost"))
        .buildForageQuery(20)
);

Random Score Function

Generates deterministic pseudo-random scores for consistent shuffling.

QueryBuilder.functionScoreQuery()
    .baseQuery(QueryBuilder.matchAllQuery().build())
    .scoreFunction(new RandomScoreFunction(
        42L,           // seed (same seed = same order)
        "id_numeric"   // field for randomization
    ))
    .buildForageQuery(10)

Use case: A/B testing, showing variety, avoiding bias.

// Show random selection to user (consistent per session)
long sessionSeed = session.getId().hashCode();
var randomPicks = engine.search(
    QueryBuilder.functionScoreQuery()
        .baseQuery(QueryBuilder.matchQuery("category", "featured").build())
        .scoreFunction(new RandomScoreFunction(sessionSeed, "id_numeric"))
        .buildForageQuery(5)
);

Decay Function

Scores decrease based on distance from an origin value. Three decay types available.

QueryBuilder.functionScoreQuery()
    .baseQuery(QueryBuilder.matchAllQuery().build())
    .scoreFunction(new DecayFunction(
        origin,      // Optimal value (score = 1.0 at origin)
        scale,       // Distance at which score = decay
        offset,      // No decay within this distance
        decay,       // Score at scale distance (e.g., 0.5)
        decayType,   // GAUSS, EXP, or LINEAR
        field        // Numeric field name
    ))
    .buildForageQuery()

Decay Types

graph LR
    subgraph Decay Types
        A[GAUSS] -->|Bell curve| B[Smooth falloff]
        C[EXP] -->|Exponential| D[Rapid initial decay]
        E[LINEAR] -->|Straight line| F[Constant decay rate]
    end
Type Formula Character
GAUSS Gaussian bell curve Smooth, natural falloff
EXP Exponential decay Rapid initial drop
LINEAR Linear decrease Constant rate

Decay Examples

// Prefer books around 300 pages (± 50 pages is OK)
new DecayFunction(
    300.0,          // origin: ideal page count
    100.0,          // scale: score = 0.5 at 100 pages away
    50.0,           // offset: no decay within 50 pages
    0.5,            // decay: score at scale distance
    DecayType.GAUSS,
    "numPages"
)

// Prefer recent items (date as epoch seconds)
new DecayFunction(
    System.currentTimeMillis() / 1000.0,  // now
    86400 * 7,                             // scale: 7 days
    86400,                                 // offset: 1 day grace period
    0.5,
    DecayType.EXP,
    "publishedAt"
)

Use case: Location-based search, time-sensitive content, optimal value matching.

// Find medium-length books (prefer ~300 pages)
var mediumBooks = engine.search(
    QueryBuilder.functionScoreQuery()
        .baseQuery(QueryBuilder.matchQuery("category", "programming").build())
        .scoreFunction(new DecayFunction(300.0, 100.0, 0.0, 0.5, DecayType.GAUSS, "numPages"))
        .buildForageQuery(10)
);

Top-Level Boost

Apply a final multiplier to the function score result:

QueryBuilder.functionScoreQuery()
    .baseQuery(QueryBuilder.matchQuery("title", "java").build())
    .fieldValueFactor("rating")
    .boost(1.5f)  // Final score × 1.5
    .buildForageQuery()

Complete Examples

E-Commerce Product Ranking

// Combine text relevance with business signals
ForageQuery productQuery = QueryBuilder.functionScoreQuery()
    .baseQuery(QueryBuilder.booleanQuery()
        .query(QueryBuilder.matchQuery("name", searchTerm).boost(3.0f).build())
        .query(QueryBuilder.matchQuery("brand", searchTerm).boost(2.0f).build())
        .query(QueryBuilder.matchQuery("description", searchTerm).build())
        .clauseType(ClauseType.SHOULD)
        .build())
    .scoreFunction(new ScriptScoreFunction("score * rating * (1 + ln(salesCount + 1))"))
    .boost(1.0f)
    .buildForageQuery(20, Arrays.asList(SortCriteria.byScore()), 0.1f);

Content Freshness

// Boost recent articles
long now = Instant.now().getEpochSecond();
ForageQuery freshContent = QueryBuilder.functionScoreQuery()
    .baseQuery(QueryBuilder.matchQuery("content", searchTerm).build())
    .scoreFunction(new DecayFunction(
        now,           // origin: now
        86400 * 30,    // scale: 30 days
        86400 * 7,     // offset: 7 day grace
        0.5,           // decay to 0.5
        DecayType.EXP,
        "publishedAt"
    ))
    .buildForageQuery(15);

Popularity-Based Discovery

// Browse by popularity only
ForageQuery popular = QueryBuilder.functionScoreQuery()
    .baseQuery(QueryBuilder.matchQuery("category", "technology").build())
    .fieldValueFactor("viewCount")
    .buildForageQuery(10);

Field Requirements

Function scoring requires numeric DocValues fields:

// In your document creation
new ForageDocument(item.getId(), item, Arrays.asList(
    // These can be used in function scoring:
    new FloatField("rating", new float[]{item.getRating()}),
    new IntField("popularity", new int[]{item.getPopularity()}),
    new FloatField("price", new float[]{item.getPrice()})
));