A hybrid query combines k-nearest neighbor (kNN) vector search with text keyword search in a single request. Use it when queries mix natural language with exact terms, product names, or IDs — cases where neither pure vector search nor pure text search alone provides sufficient recall and precision.
Query syntax
URL
POST /search
POST /vector-service/searchThe URLs above omit request header parameters, encoding details, and the endpoint used to connect to your OpenSearch instance. See Request parameters for the full parameter reference.
Protocol
HTTP
Request method
POST
Supported format
JSON
Authentication
Sign requests using HTTP Basic authentication. Encode your credentials as Base64 and include the result in the authorization header.
| Parameter | Type | Description |
|---|---|---|
accessUserName | string | Your username. View it in the API Endpoint section of the Instance Details page. |
accessPassWord | string | Your password. Modify it in the API Endpoint section of the Instance Details page. |
The following Java snippet generates the Base64-encoded credential string:
import com.aliyun.darabonba.encode.Encoder;
import com.aliyun.darabonbastring.Client;
public class GenerateAuthorization {
public static void main(String[] args) throws Exception {
String accessUserName = "username";
String accessPassWord = "password";
String realmStr = accessUserName + ":" + accessPassWord;
String authorization = Encoder.base64EncodeToString(Client.toBytes(realmStr, "UTF-8"));
System.out.println(authorization);
}
}The encoded value looks like:
cm9vdDp******mdhbA==Add the Basic prefix when setting the header in your HTTP request:
authorization: Basic cm9vdDp******mdhbA==Request parameters
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
tableName | string | Yes | — | The name of the table to query. |
knn | object | No | — | The kNN query parameters. |
knn.vector | list[float] | Yes (if knn is set) | — | The dense vector to query. |
knn.topk | int | No | — | The number of results to be returned. |
knn.filter | string | No | "" | A filter expression applied to kNN results. |
knn.weight | float | No | 1.0 | The weight of kNN query results. The result of the score multiplied by the weight is used as the sorting score. |
text | object | No | — | The text query parameters. |
text.queryString | string | Yes (if text is set) | — | A query expression using HA3 query clause syntax. Supports AND and OR operators across multiple text indexes. |
text.queryParams | map[string, string] | No | {} | Additional text query parameters. See text.queryParams sub-parameters. |
text.filter | string | No | "" | A filter expression applied to text results. |
text.weight | float | No | 1.0 | The weight of text query results. The result of the score multiplied by the weight is used as the sorting score. |
text.terminateAfter | integer | No | 0 | The maximum number of documents to scan per shard before stopping. 0 means no limit. |
size | integer | No | 100 | The number of results to return. |
from | integer | No | 0 | The offset into the result set. |
outputFields | list[string] | No | [] | The fields to include in the response. |
order | string | No | DESC | The sort order: DESC (descending) or ASC (ascending). |
rank | object | No | {} | The merge policy for combining kNN and text result sets. See Choose a merge strategy. |
text.queryParams sub-parameters
| Sub-parameter | Description | Valid values | Default |
|---|---|---|---|
default_op | The logical operator between terms after tokenization. | AND, OR | AND |
no_token_indexes | Fields whose values are not converted to terms. Normalization and stop word removal still apply. Separate multiple fields with semicolons (;). | Field names | — |
remove_stopwords | Specifies whether to remove stop words during analysis. | true, false | true |
Usage notes
kNN supports single-vector queries only. Query parameters follow the same conventions as standard API queries.
Before combining kNN and text scores, convert raw kNN scores using the formula for your distance metric:
# Euclidean distance (L2) score = 1.0 / (1.0 + l2_distance^2) # Inner product distance score = (1.0 + ip_distance) / 2.0The
orderparameter inside theknnobject has no effect. The top-levelorderparameter controls sort direction.
Choose a merge strategy
Two strategies are available for combining kNN and text result sets:
| Strategy | How it works | Use when |
|---|---|---|
| Weighted score (default) | Multiplies each result's raw score by its weight, then sums scores for documents that appear in both sets. | Your distance metric produces meaningful absolute scores and you want predictable, tunable blending via knn.weight and text.weight. |
| Reciprocal rank fusion (RRF) | Combines results based on rank position rather than raw score. A smoothing constant (rankConstant) controls how much low-ranked results influence the final score. | Raw scores from kNN and text are not directly comparable, or you want rank-based blending that is robust to score-scale differences. |
Weighted score (default)
Leave rank empty or omit it entirely:
{
"rank": {}
}Final score formula:
score(i) = knn_score(i) * knn_weight + text_score(i) * text_weightAdjust knn.weight and text.weight to shift influence between the two result sets. Both default to 1.0 (equal weighting).
Reciprocal rank fusion (RRF)
{
"rank": {
"rrf": {
"rankConstant": 60
}
}
}rankConstant is optional and defaults to 60 (minimum: 1). A larger value gives low-ranked documents more influence on the final score.
RRF score formula:
score = 0.0
if d in result(q):
score += 1.0 / (rankConstant + rank(result(q), d))
return score
# rank(result(q), d) is the 1-based position of document d in result set q.Query example
{
"tableName": "test",
"text": {
"queryString": "title:'Alibaba'",
"queryParams": {
"default_op": "OR"
},
"filter": "count > 0",
"terminateAfter": 100000
},
"knn": {
"vector": [0.1, 0.2, 0.3, 0.4, 0.5],
"namespace": "1",
"topK": 100
},
"order": "DESC",
"size": 10,
"rank": {
"rrf": {
"rankConstant": 1
}
},
"outputFields": ["title"]
}Response
Example response
{
"totalTime": 8.522,
"coveredPercent": 1.0,
"totalCount": 5,
"result": [
{
"__source__": 2,
"score": 0.833333,
"namespace": 1,
"id": 2,
"fields": {
"title": "a b c"
}
},
{
"__source__": 3,
"score": 0.666666,
"namespace": 1,
"id": 1,
"fields": {
"title": "a b"
}
},
{
"__source__": 3,
"score": 0.333333,
"namespace": 2,
"id": 5,
"fields": {
"title": "c"
}
},
{
"__source__": 2,
"score": 0.25,
"namespace": 2,
"id": 4,
"fields": {
"title": "b c"
}
},
{
"__source__": 2,
"score": 0.2,
"namespace": 1,
"id": 0,
"fields": {
"title": "a"
}
}
]
}Response parameters
| Field | Type | Description |
|---|---|---|
id | Schema-configured type | The primary key value. |
namespace | Schema-configured type | The namespace of the kNN vector index queried. Returned only when a namespace is configured. |
vector | list[float] | The vector index value. Returned only when "includeVector": true is set in the kNN query. |
score | float | The final ranking score. |
fields | map[string, field_type] | The returned fields in key-value format. |
totalTime | integer | The response time in milliseconds. |
totalCount | integer | The total number of matched results. |
__source__ | integer | The query source for each result: 1 = kNN only, 2 = text only, 3 = both kNN and text. |
coveredPercent | float | The fraction of shards that returned data successfully. 1.0 means all shards responded. |