# Multiple target vectors

In a multi-target vector search, Weaviate searches multiple target vector spaces concurrently. These results are combined using a ["join strategy"](#available-join-strategies) to produce a single set of search results.

There are multiple ways to specify the target vectors and query vectors, such as:

- [Specify target vector names only](#specify-target-vector-names-only)
- [Specify query vectors](#specify-query-vectors)
- [Specify target vector names and join strategy](#specify-target-vector-names-and-join-strategy)
- [Weight raw vector distances](#weight-raw-vector-distances)
- [Weight normalized vector distances](#weight-normalized-vector-distances)

<!-- TODO: Move most of the description/prose to a new "vector.md" page under concepts/search. -->

Multi-target vector search is available for `near_xxx` queries (from `v1.26`), as well as `hybrid` queries (from `v1.27`).

### Available join strategies.

- **minimum** (_default_) Use the minimum of all vector distances.
- **sum** Use the sum of the vector distances.
- **average** Use the average of the vector distances.
- **manual weights** Use the sum of weighted distances, where the weight is provided for each target vector.
- **relative score** Use the sum of weighted normalized distances, where the weight is provided for each target vector.

## Specify target vector names only

As a minimum, specify the target vector names as an array of named vectors. This will use the [default join strategy](#available-join-strategies).

:::::tabs{sync="languages"}
:::tab{title="Python"}
```python {8}
from weaviate.classes.query import MetadataQuery

collection = client.collections.use("JeopardyTiny")

response = collection.query.near_text(
    query="a wild animal",
    limit=2,
    target_vector=["jeopardy_questions_vector", "jeopardy_answers_vector"],  # Specify the target vectors
    return_metadata=MetadataQuery(distance=True)
)

for o in response.objects:
    print(o.properties)
    return_metadata=MetadataQuery(distance=True)
```
:::

:::tab{title="JavaScript/TypeScript"}
```typescript {5}
jeopardy = client.collections.use('JeopardyTiny');

result = await jeopardy.query.nearText('a wild animal', {
  limit: 2,
  targetVector: ['jeopardy_questions_vector', 'jeopardy_answers_vector'],
  returnMetadata: ['distance'],
});

result.objects.forEach((item) => {
  console.log(JSON.stringify(item.properties, null, 2));
  console.log(JSON.stringify(item.metadata?.distance, null, 2));
});
```
:::

::::tab{title="Go"}
```go
concepts := []string{"a wild animal"}
nearText := client.GraphQL().NearTextArgBuilder().
  WithConcepts(concepts).
  WithTargetVectors("jeopardy_questions_vector", "jeopardy_answers_vector")

ctx := context.Background()

result, err := client.GraphQL().Get().
WithClassName(className).
WithFields(graphql.Field{Name: "question"}, graphql.Field{Name: "answer"}).
WithNearText(nearText).
Do(ctx)
```

:::accordion{title="Complete code"}
```go
package main

import (
  "context"
  "fmt"
  "os"

  "github.com/weaviate/weaviate-go-client/v5/weaviate"
  "github.com/weaviate/weaviate-go-client/v5/weaviate/graphql"
)

func main() {
  cfg := weaviate.Config{
    Host:   "localhost:8080",
    Scheme: "http",
    Headers: map[string]string{
      "X-Openai-Api-Key": os.Getenv("OPENAI_API_KEY"),
    },
  }

  client, err := weaviate.NewClient(cfg)
  if err != nil {
    panic(err)
  }

  className := "JeopardyTiny"
```
:::
::::

:::tab{title="Java"}
```java {5-7}
CollectionHandle<Map<String, Object>> collection =
    client.collections.use(COLLECTION_NAME);

var response = collection.query.nearText(
    // In Java, a plain list of target vectors implies an "average" strategy
    Target.average("a wild animal", "jeopardy_questions_vector",
        "jeopardy_answers_vector"),
    q -> q.limit(2).returnMetadata(Metadata.DISTANCE));

for (var o : response.objects()) {
  System.out.println(objectMapper.writerWithDefaultPrettyPrinter()
      .writeValueAsString(o.properties()));
  System.out.println("Distance: " + o.queryMetadata().distance());
}
```
:::

:::tab{title="C#"}
```csharp {6}
var collection = client.Collections.Use(CollectionName);

var response = await collection.Query.NearText(
    query =>
        query(["a wild animal"])
            .TargetVectorsMinimum("jeopardy_questions_vector", "jeopardy_answers_vector"), // Specify the target vectors
    limit: 2,
    returnMetadata: MetadataOptions.Distance
);

foreach (var o in response.Objects)
{
    Console.WriteLine(JsonSerializer.Serialize(o.Properties));
    Console.WriteLine(o.Metadata.Distance);
}
```
:::
:::::

## Specify query vectors

You can specify multiple query vectors in the search query with a `nearVector` search. This allows use of a different query vector for each corresponding target vector.

:::code-group{sync="languages"}
```python title="Python" {6-10}
from weaviate.classes.query import MetadataQuery

collection = client.collections.use("JeopardyTiny")

response = collection.query.near_vector(
    # Specify the query vectors for each target vector
    near_vector={
        "jeopardy_questions_vector": v1,
        "jeopardy_answers_vector": v2,
    },
    limit=2,
    target_vector=["jeopardy_questions_vector", "jeopardy_answers_vector"],  # Specify the target vectors
    return_metadata=MetadataQuery(distance=True)
)

for o in response.objects:
    print(o.properties)
    return_metadata=MetadataQuery(distance=True)
```

```typescript title="JavaScript/TypeScript" {4-5,8}
jeopardy = client.collections.use('JeopardyTiny');

result = await jeopardy.query.nearVector({
  'jeopardy_questions_vector': v1,
  'jeopardy_answers_vector': v2
}, {
  limit: 2,
  targetVector: ['jeopardy_questions_vector', 'jeopardy_answers_vector'],
  returnMetadata: ['distance'],
});

result.objects.forEach((item) => {
  console.log(JSON.stringify(item.properties, null, 2));
  console.log(JSON.stringify(item.metadata?.distance, null, 2));
});
```

```java title="Java" {2-5}
var response = collection.query.nearVector(
    // Specify the query vectors for each target vector using Target objects
    // The default combination strategy is "average"
    Target.average(Target.vector("jeopardy_questions_vector", v1),
        Target.vector("jeopardy_answers_vector", v2)),
    q -> q.limit(2).returnMetadata(Metadata.DISTANCE));

for (var o : response.objects()) {
  System.out.println(objectMapper.writerWithDefaultPrettyPrinter()
      .writeValueAsString(o.properties()));
  System.out.println("Distance: " + o.queryMetadata().distance());
}
```

```csharp title="C#" {2-7}
var response = await collection.Query.NearVector(
    // Specify the query vectors for each target vector
    vectors: new Vectors
    {
        { "jeopardy_questions_vector", v1 },
        { "jeopardy_answers_vector", v2 },
    },
    limit: 2,
    returnMetadata: MetadataOptions.Distance
);

foreach (var o in response.Objects)
{
    Console.WriteLine(JsonSerializer.Serialize(o.Properties));
    Console.WriteLine(o.Metadata.Distance);
}
```
:::

You can also specify the query vectors as an array of vectors. The array will be parsed according to the order of the specified target vectors.

### Specify array(s) of query vectors

You can also specify the same target vector multiple times with different query vectors. In other words, you can use multiple query vectors for the same target vector.

The query vectors in this case are specified as an array of vectors. There are multiple ways to specify the target vectors in this case:

#### Target vector names only

The target vectors can be specified as an array as shown here.

:::code-group{sync="languages"}
```python title="Python" {6-10}
from weaviate.classes.query import MetadataQuery

collection = client.collections.use("JeopardyTiny")

response = collection.query.near_vector(
    # Specify the query vectors for each target vector
    near_vector={
        "jeopardy_questions_vector": v1,
        "jeopardy_answers_vector": [v2, v3]
    },
    limit=2,
    # Specify the target vectors as a list
    target_vector=[
        "jeopardy_questions_vector",
        "jeopardy_answers_vector",
    ],
    return_metadata=MetadataQuery(distance=True)
)

for o in response.objects:
    print(o.properties)
    return_metadata=MetadataQuery(distance=True)
```

```typescript title="JavaScript/TypeScript" {4-6}
jeopardy = client.collections.use('JeopardyTiny');

result = await jeopardy.query.nearVector({
  // Specify the query vectors for each target vector. where v1, v2.. are vectors
  'jeopardy_questions_vector': v1,
  'jeopardy_answers_vector': [v2, v3]
}, {
  limit: 2,
  // Specify the target vectors as a list
  targetVector: ['jeopardy_questions_vector', 'jeopardy_answers_vector'],
  returnMetadata: ['distance'],
});

result.objects.forEach((item) => {
  console.log(JSON.stringify(item.properties, null, 2));
  console.log(JSON.stringify(item.metadata?.distance, null, 2));
});
```

```java title="Java" {2-6}
var responseV1 = collection.query.nearVector(
    // Pass multiple Target.vector objects with the same name
    // The default combination strategy is "average"
    Target.average(Target.vector("jeopardy_questions_vector", v1),
        Target.vector("jeopardy_answers_vector", v2),
        Target.vector("jeopardy_answers_vector", v3)),
    q -> q.limit(2).returnMetadata(Metadata.DISTANCE));

for (var o : responseV1.objects()) {
  System.out.println(objectMapper.writerWithDefaultPrettyPrinter()
      .writeValueAsString(o.properties()));
  System.out.println("Distance: " + o.queryMetadata().distance());
}
```

```csharp title="C#" {2-8}
var response = await collection.Query.NearVector(
    // Use NearVectorInput to pass multiple vectors naturally
    vectors: v =>
        v.TargetVectorsSum(
            ("jeopardy_questions_vector", v1),
            ("jeopardy_answers_vector", v2),
            ("jeopardy_answers_vector", v3)
        ),
    limit: 2,
    returnMetadata: MetadataOptions.Distance
);
```
:::

#### Target vectors and weights

If you want to provide weights for each target vector you can do it as shown here.

:::code-group{sync="languages"}
```python title="Python" {6-10}
from weaviate.classes.query import TargetVectors, MetadataQuery

collection = client.collections.use("JeopardyTiny")

response = collection.query.near_vector(
    # Specify the query vectors for each target vector
    near_vector={
        "jeopardy_questions_vector": v1,
        "jeopardy_answers_vector": [v2, v3]
    },
    limit=2,
    # Specify the target vectors and weights
    target_vector=TargetVectors.manual_weights({
        "jeopardy_questions_vector": 10,
        "jeopardy_answers_vector": [30, 30],  # Matches the order of the vectors above
    }),
    return_metadata=MetadataQuery(distance=True)
)

for o in response.objects:
    print(o.properties)
    return_metadata=MetadataQuery(distance=True)
```

```typescript title="JavaScript/TypeScript" {4-12}
jeopardy = client.collections.use('JeopardyTiny');

result = await jeopardy.query.nearVector({
  'jeopardy_questions_vector': v1,
  'jeopardy_answers_vector': [v2, v3]
}, {
  limit: 2,
  // Specify the target vectors as a list
  targetVector: jeopardy.multiTargetVector.manualWeights({
    "jeopardy_questions_vector": 10,
    "jeopardy_answers_vector": [30, 30], // Matches the order of the vectors above
  }),
  returnMetadata: ['distance'],
});

result.objects.forEach((item) => {
  console.log(JSON.stringify(item.properties, null, 2));
  console.log(JSON.stringify(item.metadata?.distance, null, 2));
});
```

```java title="Java" {2-7}
var responseV2 = collection.query.nearVector(
    // Specify weights for each vector
    Target.manualWeights(
        Target.vector("jeopardy_questions_vector", 10f, v1),
        Target.vector("jeopardy_answers_vector", 30f, v2),
        Target.vector("jeopardy_answers_vector", 30f, v3) // Weights match the vectors
    ),
    q -> q.limit(2).returnMetadata(Metadata.DISTANCE));

for (var o : responseV2.objects()) {
  System.out.println(objectMapper.writerWithDefaultPrettyPrinter()
      .writeValueAsString(o.properties()));
  System.out.println("Distance: " + o.queryMetadata().distance());
}
```

```csharp title="C#" {2-7}
var responseV2 = await collection.Query.NearVector(
    vectors: v =>
        v.TargetVectorsManualWeights(
            ("jeopardy_questions_vector", 10, v1),
            ("jeopardy_answers_vector", 30, v2),
            ("jeopardy_answers_vector", 30, v3)
        ),
    limit: 2,
    returnMetadata: MetadataOptions.Distance
);
```
:::

## Specify target vector names and join strategy

Specify target vectors as an array of named vectors and how to join the result sets.

The `sum`, `average`, `minimum` join strategies only require the name of the strategy and the target vectors.

:::code-group{sync="languages"}
```python title="Python" {8-9}
from weaviate.classes.query import TargetVectors, MetadataQuery

collection = client.collections.use("JeopardyTiny")

response = collection.query.near_text(
    query="a wild animal",
    limit=2,
    target_vector=TargetVectors.average(["jeopardy_questions_vector", "jeopardy_answers_vector"]),  # Specify the target vectors and the join strategy
    # .sum(), .minimum(), .manual_weights(), .relative_score() also available
    return_metadata=MetadataQuery(distance=True)
)

for o in response.objects:
    print(o.properties)
    print(o.metadata.distance)
```

```typescript title="JavaScript/TypeScript" {5}
jeopardy = client.collections.use('JeopardyTiny');

result = await jeopardy.query.nearText('a wild animal', {
  limit: 2,
  targetVector: jeopardy.multiTargetVector.average(['jeopardy_questions_vector', 'jeopardy_answers_vector']),
  returnMetadata: ['distance'],
});

result.objects.forEach((item) => {
  console.log(JSON.stringify(item.properties, null, 2));
  console.log(JSON.stringify(item.metadata?.distance, null, 2));
});
```

```java title="Java" {5-7}
CollectionHandle<Map<String, Object>> collection =
    client.collections.use(COLLECTION_NAME);

var response = collection.query.nearText(
    Target.average("a wild animal", "jeopardy_questions_vector",
        "jeopardy_answers_vector"), // Specify the target vectors and the join strategy
    // .sum(), .min(), .manualWeights(), .relativeScore() also available
    q -> q.limit(2).returnMetadata(Metadata.DISTANCE));

for (var o : response.objects()) {
  System.out.println(objectMapper.writerWithDefaultPrettyPrinter()
      .writeValueAsString(o.properties()));
  System.out.println("Distance: " + o.queryMetadata().distance());
}
```

```csharp title="C#" {6-8}
var collection = client.Collections.Use(CollectionName);

var response = await collection.Query.NearText(
    query =>
        query(["a wild animal"])
            // Specify the target vectors and the join strategy
            // Available: Sum, Minimum, Average, ManualWeights, RelativeScore
            .TargetVectorsAverage("jeopardy_questions_vector", "jeopardy_answers_vector"),
    limit: 2,
    returnMetadata: MetadataOptions.Distance
);

foreach (var o in response.Objects)
{
    Console.WriteLine(JsonSerializer.Serialize(o.Properties));
    Console.WriteLine(o.Metadata.Distance);
}
```
:::

## Weight raw vector distances

Search by sums of weighted, **raw** distances to each target vector.

:::accordion{title="The weighting in detail"}
Each distance between the query vector and the target vector is multiplied by the specified weight, then the resulting weighted distances are summed for each object to produce a combined distance. The search results are sorted by this combined distance.
:::

:::code-group{sync="languages"}
```python title="Python" {8-11}
from weaviate.classes.query import TargetVectors, MetadataQuery

collection = client.collections.use("JeopardyTiny")

response = collection.query.near_text(
    query="a wild animal",
    limit=2,
    target_vector=TargetVectors.manual_weights({
        "jeopardy_questions_vector": 10,
        "jeopardy_answers_vector": 50
    }),
    return_metadata=MetadataQuery(distance=True)
)

for o in response.objects:
    print(o.properties)
    print(o.metadata.distance)
```

```typescript title="JavaScript/TypeScript" {5-8}
jeopardy = client.collections.use('JeopardyTiny');

result = await jeopardy.query.nearText('a wild animal', {
  limit: 2,
  targetVector: jeopardy.multiTargetVector.manualWeights({
    jeopardy_questions_vector: 10,
    jeopardy_answers_vector: 50,
  }),
  returnMetadata: ['distance'],
});

result.objects.forEach((item) => {
  console.log(JSON.stringify(item.properties, null, 2));
  console.log(JSON.stringify(item.metadata?.distance, null, 2));
});
```

```java title="Java" {5-7}
CollectionHandle<Map<String, Object>> collection =
    client.collections.use(COLLECTION_NAME);

var response = collection.query.nearText(
    Target.manualWeights("a wild animal",
        Target.weight("jeopardy_questions_vector", 10f),
        Target.weight("jeopardy_answers_vector", 50f)),
    q -> q.limit(2).returnMetadata(Metadata.DISTANCE));

for (var o : response.objects()) {
  System.out.println(objectMapper.writerWithDefaultPrettyPrinter()
      .writeValueAsString(o.properties()));
  System.out.println("Distance: " + o.queryMetadata().distance());
}
```

```csharp title="C#" {6-9}
var collection = client.Collections.Use(CollectionName);

var response = await collection.Query.NearText(
    query =>
        query(["a wild animal"])
            .TargetVectorsManualWeights(
                ("jeopardy_questions_vector", 10),
                ("jeopardy_answers_vector", 50)
            ),
    limit: 2,
    returnMetadata: MetadataOptions.Distance
);

foreach (var o in response.Objects)
{
    Console.WriteLine(JsonSerializer.Serialize(o.Properties));
    Console.WriteLine(o.Metadata.Distance);
}
```
:::

## Weight normalized vector distances

Search by sums of weighted, **normalized** distances to each target vector.

:::accordion{title="The weighting in detail"}
Each distance is normalized against other results for that target vector. Each normalized distance between the query vector and the target vector is multiplied by the specified weight. The resulting weighted distances are summed for each object to produce a combined distance. The search results are sorted by this combined distance.

For a more detailed explanation of how scores are normalized, see the blog post on [hybrid relative score fusion](https://weaviate.io/blog/hybrid-search-fusion-algorithms#relative-score-fusion)
:::

:::code-group{sync="languages"}
```python title="Python" {8-11}
from weaviate.classes.query import TargetVectors, MetadataQuery

collection = client.collections.use("JeopardyTiny")

response = collection.query.near_text(
    query="a wild animal",
    limit=2,
    target_vector=TargetVectors.relative_score({
        "jeopardy_questions_vector": 10,
        "jeopardy_answers_vector": 10
    }),
    return_metadata=MetadataQuery(distance=True)
)

for o in response.objects:
    print(o.properties)
    print(o.metadata.distance)
```

```typescript title="JavaScript/TypeScript" {5-8}
jeopardy = client.collections.use('JeopardyTiny');

result = await jeopardy.query.nearText('a wild animal', {
  limit: 2,
  targetVector: jeopardy.multiTargetVector.relativeScore({
    jeopardy_questions_vector: 10,
    jeopardy_answers_vector: 10,
  }),
  returnMetadata: ['distance'],
});

result.objects.forEach((item) => {
  console.log(JSON.stringify(item.properties, null, 2));
  console.log(JSON.stringify(item.metadata?.distance, null, 2));
});
```

```java title="Java" {5-7}
CollectionHandle<Map<String, Object>> collection =
    client.collections.use(COLLECTION_NAME);

var response = collection.query.nearText(
    Target.relativeScore("a wild animal",
        Target.weight("jeopardy_questions_vector", 10f),
        Target.weight("jeopardy_answers_vector", 10f)),
    q -> q.limit(2).returnMetadata(Metadata.DISTANCE));

for (var o : response.objects()) {
  System.out.println(objectMapper.writerWithDefaultPrettyPrinter()
      .writeValueAsString(o.properties()));
  System.out.println("Distance: " + o.queryMetadata().distance());
}
```

```csharp title="C#" {6-9}
var collection = client.Collections.Use(CollectionName);

var response = await collection.Query.NearText(
    query =>
        query(["a wild animal"])
            .TargetVectorsRelativeScore(
                ("jeopardy_questions_vector", 10),
                ("jeopardy_answers_vector", 10)
            ),
    returnMetadata: MetadataOptions.Distance
);

foreach (var o in response.Objects)
{
    Console.WriteLine(JsonSerializer.Serialize(o.Properties));
    Console.WriteLine(o.Metadata.Distance);
}
```
:::

## Related pages

- [Connect to Weaviate](../connect-to-weaviate/index.md)

## Questions and feedback

Have a question or feedback? Here's how to reach us.

::::card-grid
:::card{title="Community Forum" href="https://forum.weaviate.io/c/support" icon="messages-square"}
Ask questions and connect with other developers on our **Community forum**.
:::

:::card{title="Support" href="/guides/support-overview" icon="life-buoy"}
Weaviate Cloud user or customer? Find the right channel on the **Support page**.
:::
::::

## Related pages

- [Agents](./agents-index.md)
- [AI-assisted Weaviate code generation](./ai-assisted-vibe-coding-index.md)
- [APIs](./apis-index.md)
- [Authorization and authentication](./authorization-and-authentication-index.md)
- [Benchmarks](./benchmarks-index.md)
- [Best practices](./best-practices-index.md)
- [Client libraries](./clients-index.md)
- [Client Libraries / SDKs](./client-libraries-index.md)
- [Cloud](./cloud-index.md)
- [Cloud account management](./cloud-account-management-index.md)

# Agent Instructions

This portal answers questions programmatically. To receive a synthesized,
source-cited answer instead of crawling page by page, append the `?ask=`
query parameter to any page URL on this site:

    /guides/quickstart?ask=how+do+I+authenticate

Optional parameters:

- `&goal=<what-you-are-trying-to-do>` steers the answer toward your
  objective (e.g. `&goal=write+a+python+client`).
- `&version=<label>` scopes the answer to a mounted version when the
  portal publishes more than one.

The response is `text/markdown`: the answer followed by a `# Sources` list
of the portal pages it was grounded in. Status codes are the contract:

- `200` — the answer; `402` — the portal owner’s plan or answer credits are
  exhausted (surface this to your operator; do NOT retry); `429` — you are
  rate-limited; back off for the `Retry-After` seconds; `503` — the answer
  lane is temporarily unavailable; fall back to crawling the `.md` pages.

For the full corpus map read `llms.txt` at the site root; for the tool
surface (search + page fetch as MCP tools) see `/mcp`.
