# Source: https://docs.vespa.ai/en/ranking/ranking-expressions-features.html.md

# Ranking Expressions and Features

 

Read the [ranking introduction](../basics/ranking.html) first. This guide is about [ranking expressions](../reference/ranking/ranking-expressions.html)and [rank features](../reference/ranking/rank-features.html), find guides and examples below.

## Ranking expressions

Vespa uses [ranking expressions](../reference/ranking/ranking-expressions.html)to rank documents matching a query, computing a rank score per document. A ranking expression is stored in a _rank profile_.

Ranking expressions are mathematical functions. The function may contain anything from a single reference to a built-in feature to a machine-learned model. Ranking expressions support the usual operators and functions, as well as an _if_ function - enabling decision trees and conditional business logic. They support a comprehensive set of [tensor functions](../reference/ranking/tensor.html), which allows expressing machine-learned functions such as deep neural nets. Refer to [multivalue query operators](multivalue-query-operators.html)for details on using dot products, tensors and wand.

Ranking is most often the resource driver - this is where is application's logic is implemented. Use [two-phase ranking](phased-ranking.html) to optimize, using an inexpensive _first-phase_ ranking to eliminate the lowest ranked candidates, then focus the resources on a strong _second-phase_ ranking.

Ranking expressions can be _handwritten_ - works well if the ranking is well-defined enough to be easily mappable into a ranking expression. Alternatively, make the ranking expression automatically using _machine learning_. Ranking expressions can be large, and can be imported using[file:filename](../reference/schemas/schemas.html#expression).

## Rank features

The primitive values used in ranking expressions are called [rank features](../reference/ranking/rank-features.html). Rank features can be [tensors](tensor-user-guide.html),[multivalue fields](../searching-multi-valued-fields) or scalars, and one of:

- Constants set in the application package
- Values sent with the query or set in the document
- Computed by Vespa from the query and the document to provide information about how well the query matched the document

Vespa's [rank feature set](../reference/ranking/rank-features.html)contains a large set of low level features, as well as some higher level features. If automated training is used, all features can often just be handed to the training algorithm to let it choose which ones to use. Depending on the algorithm, it can be a good idea to leave out the un-normalized features to avoid spending learning power on having to learn to normalize these features and determine that they really represent the same information as some of the normalized features.

Include [default rank features](https://github.com/vespa-engine/system-test/blob/master/tests/search/rankfeatures/dump.txt)in query results by adding[ranking.listFeatures](../reference/api/query.html#ranking.listfeatures) to the query. This is useful for tasks like recording the rank feature values for automated training - learn more in the [tutorial](../learn/tutorials/text-search-ml). If more rank features than is available in the default set is wanted, they can be added to the set in the [rank profile](../reference/schemas/schemas.html#rank-features):

```
rank-features: feature1 feature2 …
```

It is also possible to control which features to dump - add this to the rank profile:

```
ignore-default-rank-features
```

This will make the explicitly listed rank features the only ones dumped when requesting rankfeatures in the query.

### Normalization

The rank features provided includes both features normalized to the range 0-1, and un-normalized features like counts and positions. Whenever possible, prefer the normalized features. They capture the same information (and more), but are easier to use because they can be combined more easily with other features. In addition, try to write ranking expressions such that the combined rank score is also normalized, for example by taking averages not sums. The resulting normalized rank scores makes it possible to implement relevance based blending, search assistance triggering when there are no good hits, and so on.

### Configuration

Some features, most notably the [fieldMatch](../reference/ranking/rank-features.html#field-match-features-normalized) features, have configuration parameters that enables the feature calculation to be tweaked per field for performance or relevance. Feature configuration values are set by adding to the rank profile:

```
rank-properties {
    featureName.configurationProperty: "value"
}
```

The values are set per field, like:

```
rank-properties {
    fieldMatch(title).maxAlternativeSegmentations: 10
    fieldMatch(title).maxOccurrences: 5
    fieldMatch(description).maxOccurrences: 20
}
```

Refer to the[rank feature configuration](../reference/ranking/rank-feature-configuration.html) reference.

### Feature contribution functions

Vespa ranking features are linear. For example, the [earliness](../reference/ranking/rank-features.html#fieldMatch(name).earliness) feature is 0 if the match is at the end of the field, 1 if the match is at the start of the field, and 0.5 if the match is exactly in the middle of the field. In many cases, the contribution of a feature should not be linear with its "goodness". For example, _earliness_ could decay quickly in the beginning and slowly at the end of the field. This from the intuition that it matters more if the match is of the first or the twentieth word in the field, but it doesn't matter as much if the match is at the thousands or thousand-and-twentieths.

To achieve this, pass the feature value through a function which turns the line into a curve matching the intent. This is easiest with normalized fields. The function begins and ends in the same point, f(0)=0 and f(1)=1, but which curves in between. To get the effect described above, a curve which starts almost flat and ends steep works - example:

```
pow(0-x,2)
```

The second number decides how pronounced the curving is. A larger number will make changes to higher x values even more important relative to the same change to lower x values.

### Dumping rank features for specific documents

For a training set containing judgements for certain documents, it is useful to select those documents in the query by adding a term matching the document id, but without impacting the values of any rank features. To do this, add that term with [ranked](../reference/querying/yql.html#ranked) set to false:

```
select * from mydocumenttype where myidfield contains ({ranked: false}"mydocumentid" and ...)
```

### Accessing feature/function values in results

Any feature can be returned in the hit producing it by adding it to the list of [summary-features](../reference/schemas/schemas.html#summary-features) of the rank profile. As all functions are features this allows the result of any computation to be accessible in results. Example:

```
rank-profile test {

    summary-features: tensor_join join_sum

    function tensor_join() {
        expression: attribute(my_tensor_field) * query(my_query_tensor)
    }

    function join_sum() {
        expression: sum(tensor_join())
    }

}
```

The results of these functions will be available in the Hits of the result as follows:

```
```
import com.yahoo.search.result.FeatureData;

    FeatureData featureData = (FeatureData)hit.getField("summaryfeatures");
    Tensor tensor_join_value = featureData.getTensor("rankingExpression(tensor_join)");
    double join_sum_value = featureData.getDouble("rankingExpression(join_sum)");
```
```

Do further computation on the returned tensors, such as e.g `Tensor larger = tensor_join_value.map((value) -> 3 * value)`.

If also leveraging [multiphase searching](../applications/searchers.html#multiphase-searching), it is possible to get rank features returned in the first phase using [match-features](../reference/schemas/schemas.html#match-features). This pre-populates the [matchfeatures](../reference/querying/default-result-format.html#matchfeatures) field. The effects which can be observed in the results are the same, so this may seem like the same functionality, but the performance trade-off is different:

- The expressions in _match-features_ must be computed for all hits returned in the first phase, before selecting which hits to _fill_. But that also means it's possible to use the `matchfeatures` field to select which hits to keep and which to remove before calling `fill()` at all.
- The expressions in _summary-features_ are not available before the _fill_ phase, but only need to be calculated for those hits that are actually filled.

The difference is most pronounced when the corpus is divided onto many content nodes. Consider a case with 7 content nodes, fetching 100 matches from each. These are merged (by relevance score) into a list of 700 hits, and the 100 with the best relevance are selected and _filled_. If you use _match-features_, they need to be calculated for all 700 hits. Compare with _summary-features_, where only the final 100 hits need to be considered for calculating those.

## Conditional expressions

Ranking expressions support conditional logic to choose between different sub-expressions based on document attributes, query parameters, or other features. This enables ranking to adapt to different document types, user segments, or business rules within a single rank profile.

### The if function

`if` can be used for other purposes than encoding MLR trained decision trees. One use is to choose different ranking functions for different types of documents in the same search. Ranking expressions are able to do string equality tests, so to choose between different ranking sub-functions based on the value of a string attribute (say, "category"), use an expression like:

```
if (attribute(category)=="restaurant",_…restaurant function_, if (attribute(category)=="hotel",_…hotel function_, …))
```

This method is also used automatically when multiple schemas are deployed to the same cluster, and all is included in the same query to choose the ranking expression from the correct schema for each document.

By using `if` functions, one can also implement strict tiering, ensuring that documents having some criterion always gets a higher score than the other documents. Example:

```
if (fieldMatch(business).fieldCompleteness==1, 0.8+document.distance*0.2,
                                               if (attribute(category)=="shop", 0.6+fieldMatch(title)*0.2,
                                                                                 match*attribute(popularity)*0.6 )
```

This function puts all exact matches on business names first, sorted by geographical distance, followed by all shops sorted by title match, followed by everything else sorted by the overall match quality and popularity.

Also see [pin results](multivalue-query-operators.html#pin-results-example) for a comprehensive examples of using a tiered ranking function to pin queries and results.

### The switch function

When comparing many values against the same discriminant value, the `switch` function provides a more readable alternative to deeply nested `if` statements. For example, the nested if expression above can be written more clearly as:

```
switch (attribute(category)) {
    case "restaurant":_…restaurant function_,
    case "hotel":_…hotel function_,
    default:_…default function_}
```

Use `switch` when testing a single expression for equality against multiple values. Continue using `if` for different comparison operators or when each condition tests different expressions, such as in the tiering example above. See the [switch function reference](../reference/ranking/ranking-expressions.html#the-switch-function) for details.

## Using constants

Ranking expressions can refer to constants defined in a `constants` clause:

```
first-phase {
    expression: myConst1 + myConst2
}
constants {
    myConst1: 1.5
    myConst2: 2.5
    ...
}
```

Constants lists are inherited and can be overridden in sub-profiles. This is useful to create a set of rank profiles that use the same broad ranking but differs by constants values.

For performance, always prefer constants to query variables (see below) whenever the constant values to use can be enumerated in a set of rank profiles. Constants are applied to ranking expressions at configuration time, and the resulting constant parts of expressions calculated, which may lead to reduced execution cost, especially with tensor constants.

## Using query variables

As ranking expressions can refer to any feature by name, one can use [query features](../reference/ranking/rank-features.html#feature-list) as ranking variables. These variables can be used for example to allow the query to specify the degree of importance to various parts of a ranking expression, or to quickly search large parameter spaces to find a good ranking, by trying different values in each query. These variables can be assigned default values in the [rank profile](../reference/schemas/schemas.html#rank-profile)by adding:

```
inputs {
    query(myvalue) double: 0.5
}
```

to the rank profile. These variables can then be overridden in the query by adding:

```
input.query(myvalue)=0.1
```

to it - see the [Query API](../reference/api/query.html#ranking.features).

## Query feature types

The default type of all features are scalar. To use query feature _tensors_ we must[define their type in the rank profile](../reference/schemas/schemas.html#inputs).

Without the correct tensor type, a passed query feature is handled as a string to be converted to a scalar, which will _not give an error but will produce incorrect results_.

Tensors can be passed in requests using the [tensor literal form](../reference/ranking/tensor.html#tensor-literal-form), for example:

```
input.query(user_profile)=%7B%7Bcat%3Apop%7D%3A0.8%2C%7Bcat%3Arock%7D%3A0.2%2C%7Bcat%3Ajazz%7D%3A0.1%7D
```

However, it is usually preferable instead to create them in a [Searcher](../applications/searchers.html). Set the tensor value using the[RankFeatures](https://github.com/vespa-engine/vespa/blob/master/container-search/src/main/java/com/yahoo/search/query/ranking/RankFeatures.java) instance associated with[Query](https://github.com/vespa-engine/vespa/blob/master/container-search/src/main/java/com/yahoo/search/Query.java) instance. This example makes a tensor with a single cell with value 500:

```
```
package com.yahoo.example;

import com.yahoo.search.Query;
import com.yahoo.search.Result;
import com.yahoo.search.Searcher;
import com.yahoo.search.searchchain.Execution;
import com.yahoo.tensor.Tensor;
import com.yahoo.tensor.TensorType;

public class TensorInQuerySearcher extends Searcher {
    @Override
    public Result search(Query query, Execution execution) {
        // The Tensor type must also be in the application package as described in the above section
        query.getRanking().getFeatures().put("query(tensor_feature)",
              Tensor.Builder.of(TensorType.fromSpec("tensor<float>(x{})")).cell().label("x", "foo").value(500).build());
        return execution.search(query);
    }
}
```
```
Refer to the [Tensor](https://javadoc.io/doc/com.yahoo.vespa/vespajlib/latest/com/yahoo/tensor/Tensor.html)Java API for details on how to construct tensors programmatically.
## Function snippets

When using machine learned ranking, we are searching a function space which is much more limited than the space of functions supported by ranking expressions. We can increase the space of functions available to MLR because the primitive features used in MLR training do not need to be primitive features in Vespa - they can just as well be ranking expression snippets. If there are certain mathematical combinations of features believed to be useful in an application, these can be pre-calculated from the actual primitive features of Vespa and given to MLR as primitives. Such primitives can then be replaced textually by the corresponding ranking expression snippet, before the learned expression is deployed on Vespa.

Vespa supports [expression functions](../reference/schemas/schemas.html#function-rank). Functions having zero arguments can be used as summary- and rank-features. For example, the function "myfeature":

```
rank-profile myrankprofile inherits default {
    function myfeature() {
      expression: fieldMatch(title).completeness * pow(0 - fieldMatch(title).earliness, 2)
    }
}
```

becomes available as a feature as follows:

```
summary-features {
    myfeature
}
```

## Tracking relevance variations over time

Vespa comes with a few simple metrics for relevance that enables applications to see how relevance changes over time, either as a result of changes to how relevance is computed, changes to query construction, changes to the content ingested, or as a result of changing user behavior.

The relevance metrics are `relevance.at_1`,`relevance.at_3` and `relevance.at_10`. See [metrics](../operations/metrics.html) for more information.

## Examples

If the user is underage, assign 0 to adult content and use the average of match quality in the title field and popularity among kids. If the user is not, use the match quality in the title field:

```
if ( query(userage) < 18,
     if ( attribute(adultness) > 0.1, 0 , (fieldMatch(title)+attribute(kidspopularity)) / 2 ),
     fieldMatch(title) )
```

Use a weighted average of the match quality in some fields, multiplied by 1-exp of the document age:

```
( 10*fieldMatch(title) + 5*fieldMatch(description) +
  7*attributeMatch(tags).normalizedWeight ) /22 * ( 1 - age(creationtime) )
```

 Copyright © 2026 - [Cookie Preferences](#)

### On this page:

- [Ranking expressions](#ranking-expressions)
- [Rank features](#rank-features)
- [Normalization](#normalization)
- [Configuration](#configuration)
- [Feature contribution functions](#feature-contribution-functions)
- [Dumping rank features for specific documents](#dumping-rank-features-for-specific-documents)
- [Accessing feature/function values in results](#accessing-feature-function-values-in-results)
- [Conditional expressions](#conditional-expressions)
- [The if function](#the-if-function)
- [The switch function](#the-switch-function)
- [Using constants](#using-constants)
- [Using query variables](#using-query-variables)
- [Query feature types](#query-feature-types)
- [Function snippets](#function-snippets)
- [Tracking relevance variations over time](#tracking-relevance-variations-over-time)
- [Examples](#examples)