Cava sort scripts let you implement custom scoring logic for fine sort in OpenSearch. Unlike sort expressions, Cava scripts give you full programmatic control — you can call OpenSearch libraries, access document fields, and read query parameters to build any scoring formula your business requires.
Note: Cava sort scripts apply only to fine sort, not rough sort. For an introduction to creating and managing sort scripts, see the API reference for sort scripts. To create scripts using the command-line interface, see Create sort scripts by using SortScript.
How it works
For each search request, OpenSearch runs your sort script in two phases:
Init phase — OpenSearch calls
init()once per search request to set up request-level state: declare the attribute fields your script needs, initialize scoring features, and read query parameters.Score phase — OpenSearch calls
score()once per candidate document in the fine sort to compute that document's score. Documents are then ranked by their scores.
If init() fails, OpenSearch returns an error and terminates the request. If the score phase exceeds memory or loop limits, OpenSearch returns partial results with a warning.
The BasicSimilarityScorer class
All Cava sort scripts must implement the BasicSimilarityScorer class in the users.scorer package. The class name and package name are fixed — changing either causes a compilation error.
package users.scorer;
import com.aliyun.opensearch.cava.framework.OpsScoreParams;
import com.aliyun.opensearch.cava.framework.OpsScorerInitParams;
class BasicSimilarityScorer {
// Declare member variables here (scoring features, query parameters, etc.)
boolean init(OpsScorerInitParams params) {
// Initialize member variables for this search request.
// Called once per request.
return true;
}
double score(OpsScoreParams params) {
double score = 0;
// Compute and return the document score.
return score;
}
};Method reference
| Method | Parameter | When called | Return value | Purpose |
|---|---|---|---|---|
init | OpsScorerInitParams | Once per search request | boolean | Declare attribute fields; initialize scoring features and query parameters |
score | OpsScoreParams | Once per candidate document | double | Compute and return the document score |
The method signatures — parameter types and return types — cannot be modified. Changing them causes a compilation error.
What to put in each method
In
init: declare attribute fields withrequireAttribute(), initialize scoring features, and readkvpairsparameters. All request-level setup belongs here.In
score: read document fields and evaluate scoring features. Avoid reading request-level data inscore— it is called once per document, so redundant initialization wastes memory and processing time.
Example sort script
The following script ranks documents by combining a text relevance score with a numeric document field (shop_margin).
package users.scorer;
import cava.lang.CString;
import com.aliyun.opensearch.cava.framework.OpsScoreParams;
import com.aliyun.opensearch.cava.framework.OpsScorerInitParams;
import com.aliyun.opensearch.cava.framework.OpsRequest;
import com.aliyun.opensearch.cava.framework.OpsKvPairs;
import com.aliyun.opensearch.cava.framework.OpsDoc;
import com.aliyun.opensearch.cava.features.similarity.TextRelevance;
class BasicSimilarityScorer {
TextRelevance _textRelevance; // Scoring feature declared as a member variable
boolean init(OpsScorerInitParams params) {
if (!params.getDoc().requireAttribute("shop_margin")) { // Declare attribute fields used in score()
return false;
}
_textRelevance = TextRelevance.create(params, "default", "name"); // Initialize scoring feature once
return true;
}
double score(OpsScoreParams params) {
float shopMargin = params.getDoc().docFieldFloat("shop_margin"); // Read document field
float textScore = _textRelevance.evaluate(params); // Evaluate scoring feature
double score = textScore * 30.0 + shopMargin;
return score;
}
}Key patterns in this example:
_textRelevanceis a member variable initialized once ininit()and reused in everyscore()call — this avoids redundant initialization.shop_marginis declared ininit()withrequireAttribute()before being read inscore().The final score combines a weighted text relevance signal with a raw document attribute.
Performance considerations
Sort scripts run per-document during fine sort. Two hard limits apply per search request:
| Limit | Value | Behavior when exceeded |
|---|---|---|
| Memory | 40 MB | OpenSearch reports an error and returns partial results. Only some documents are scored based on scores; others may not be scored. |
| For loops and function invocations | 100,000 | OpenSearch reports an error and returns results early. |
To stay within these limits:
Avoid frequent use of `new` in `score()` — object allocation is expensive when called once per document across thousands of candidates.
Minimize string operations in `score()` — strings consume disproportionate memory at high call volumes.
Adjust `rerank_size` — use the
rerank_sizeparameter to control how many documents enter the fine sort stage. Reducingrerank_sizedirectly reduces memory usage and loop count.
Usage notes
Single-file upload, single package — define all classes in the
users.scorerpackage. Only one file can be uploaded, so all your classes must be in that file.Explicit imports only — the wildcard
import com.aliyun.opensearch.cava.framework.*is not supported. Import each class explicitly.Attribute fields must be in the application schema — a field must be defined as an attribute field in the application schema before you can declare it with
requireAttribute()and read it inscore().
What's next
OpsScorerInitParams reference — methods available in
init()OpsScoreParams reference — methods available in
score()Create sort scripts by using SortScript — command-line tool for managing sort scripts
Rank parameter reference — configure
rerank_sizeand other ranking parameters