Multi-path search runs up to N independent queries in parallel within a single request, then merges and uniformly ranks the results. Each query path can have its own retrieval count and priority, and you can trace which path contributed each result in the final output.
Use this feature when a single query strategy cannot cover your retrieval needs — for example, combining an exact-match keyword search with a semantic vector search, or merging results from different indexes or ranking profiles.
Prerequisites
Before you begin, make sure you have:
An OpenSearch Industry Algorithm Edition application
A Resource Access Management (RAM) user with permissions granted to the
AliyunServiceRoleForOpenSearchrole. For details, see AliyunServiceRoleForOpenSearch and Access authorization rules.An AccessKey pair for the RAM user. To create one, see Create an AccessKey pair.
Use a RAM user's AccessKey pair instead of your Alibaba Cloud account's AccessKey pair. An account-level AccessKey pair grants access to all API operations. Do not embed AccessKey pairs in source code or other locations where others can access them.
Configure environment variables
Set ALIBABA_CLOUD_ACCESS_KEY_ID and ALIBABA_CLOUD_ACCESS_KEY_SECRET to the AccessKey ID and AccessKey secret of your RAM user.
Linux and macOS
export ALIBABA_CLOUD_ACCESS_KEY_ID=<access_key_id> export ALIBABA_CLOUD_ACCESS_KEY_SECRET=<access_key_secret>Windows
Create an environment variable file and add
ALIBABA_CLOUD_ACCESS_KEY_IDandALIBABA_CLOUD_ACCESS_KEY_SECRETwith their respective values.Restart Windows for the variables to take effect.
Run a multi-path search
The following Go example sends a multi-path search request with five query paths to /v3/openapi/apps/{appName}/multi-path-search. All paths run in parallel, and the results are merged using the unified ranking profile specified in unified_rank_name.
Key request parameters
| Parameter | Description |
|---|---|
path | A unique identifier for the query path (for example, main, sub, sub1). Use this label to trace which path contributed each result when trace is set to "info". |
priority | Determines the order in which paths are processed when resources are constrained. Paths with a higher priority value are processed first. |
quota | The maximum number of candidate results this path can contribute to the merged pool. To give one path more influence over the final ranking, set a higher quota for that path. |
total_rank_size | The number of candidates retrieved in the first-rank stage for this path. |
total_rerank_size | The number of candidates passed to the second-rank (reranking) stage for this path. |
trace | Set to "info" to include path attribution in the response, so you can see which query path each result came from. |
unified_rank_name | The ranking profile used to re-rank the merged candidates from all paths. |
Sample code
package main
import (
"fmt"
util "github.com/alibabacloud-go/tea-utils/service"
"github.com/alibabacloud-go/tea/tea"
opensearch "main/client"
"os"
)
func main() {
// Initialize the client with your endpoint and credentials from environment variables.
config := &opensearch.Config{
Endpoint: tea.String("<Endpoint>"),
AccessKeyId: tea.String(os.Getenv("ALIBABA_CLOUD_ACCESS_KEY_ID")),
AccessKeySecret: tea.String(os.Getenv("ALIBABA_CLOUD_ACCESS_KEY_SECRET")),
}
client, _clientErr := opensearch.NewClient(config)
if _clientErr != nil {
fmt.Println(_clientErr)
return
}
// Build the request body with five parallel query paths.
// Each path runs an independent query; results are merged and re-ranked
// using the unified ranking profile defined in unified_rank_name.
requestBody := map[string]interface{}{
"queries": []map[string]interface{}{
{
// Path "sub": exact-match query on two keywords.
"query": "default:'black and white' AND default:'breeches'",
"first_rank_name": "default",
"second_rank_name": "sys_second_default",
"total_rank_size": 10000,
"total_rerank_size": 1000,
"path": "sub",
"priority": 1,
"quota": 100,
"qp": "sys_default",
},
{
// Path "sub1": phrase query for a specific product type.
"query": "default:'men's casual retro basketball shoes'",
"first_rank_name": "default",
"second_rank_name": "default",
"total_rank_size": 10000,
"total_rerank_size": 1000,
"path": "sub1",
"priority": 1,
"quota": 100,
"qp": "sys_default",
},
{
// Path "sub2": broad thematic query.
"query": "default:'themed style'",
"first_rank_name": "default",
"second_rank_name": "sys_second_default",
"total_rank_size": 10000,
"total_rerank_size": 1000,
"path": "sub2",
"priority": 1,
"quota": 100,
"qp": "sys_default",
},
{
// Path "sub3": category-level fallback query.
"query": "default:'clothing'",
"first_rank_name": "default",
"second_rank_name": "sys_second_default",
"total_rank_size": 10000,
"total_rerank_size": 1000,
"path": "sub3",
"priority": 1,
"quota": 100,
"qp": "sys_default",
},
{
// Path "main": primary brand-name query, sorted by primary key.
"query": "default:'Nike sports shoes'",
"first_rank_name": "default",
"second_rank_name": "sys_second_default",
"total_rank_size": 10000,
"total_rerank_size": 1000,
"path": "main",
"priority": 1,
"quota": 100,
"sort": "+pk",
"qp": "sys_default",
},
},
"raw_query": "Nike sports shoes",
"start": 0,
"hit": 10,
"format": "fulljson",
// Set trace to "info" to see which path each result came from.
"trace": "info",
"rank_trace": "info",
"user_id": "1865410598556969",
"vector_search": map[string]interface{}{
"vector": map[string]interface{}{
"top_n": 100,
"namespaces": []string{},
"threshold": 0.5,
"search_params": map[string]interface{}{
"qc_scan_ratio": 0.01,
},
},
},
// unified_rank_name specifies the ranking profile applied across all merged candidates.
"unified_rank_type": "cava_script",
"unified_rank_size": 500,
"unified_rank_name": "ctr",
}
// Configure connection pool and timeout settings.
runtime := &util.RuntimeOptions{
ConnectTimeout: tea.Int(5000),
ReadTimeout: tea.Int(10000),
Autoretry: tea.Bool(false),
IgnoreSSL: tea.Bool(false),
MaxIdleConns: tea.Int(50),
}
// Replace <appName> with your OpenSearch application name.
appName := "<appName>"
response, _requestErr := client.Request(
tea.String("POST"),
tea.String("/v3/openapi/apps/"+appName+"/multi-path-search"),
nil,
nil,
requestBody,
runtime)
if _requestErr != nil {
fmt.Println(_requestErr)
return
}
fmt.Println(response)
}Replace the following placeholders before running the code:
| Placeholder | Description |
|---|---|
<Endpoint> | The domain name of the region where your application is deployed |
<appName> | Your OpenSearch application name |
Trace path attribution in the response
When trace is set to "info", each result in the response includes a path label identifying which query path it came from. Use this to verify that your quota and priority settings are producing the expected result distribution across paths such as main, sub, sub1, sub2, and sub3.
What's next
For the full list of request and response parameters, see the Multi-path search API reference.
To set up a RAM user and grant the required permissions, see Create a RAM user.