# Hybrid Search

Hybrid search combines multiple vector similarity searches across different vector fields and merges the results using a reranking strategy. This is useful when your data has multiple vector representations (e.g., dense + sparse vectors, or text + image embeddings).

## Overview

In the Milvus Node.js SDK, `hybridSearch()` is an alias for `search()`. When you pass a `HybridSearchReq` (with `data` as an array of individual search requests), the SDK automatically performs a hybrid search. You can also call `search()` directly with the same parameter shape.

## Basic Usage

```javascript
const results = await client.hybridSearch({
  collection_name: 'my_collection',
  data: [
    {
      data: [0.1, 0.2, 0.3, ...],  // dense vector
      anns_field: 'dense_vector',
      params: { nprobe: 10 },
    },
    {
      data: { 1: 0.5, 100: 0.3, 500: 0.8 },  // sparse vector
      anns_field: 'sparse_vector',
    },
  ],
  rerank: {
    strategy: 'rrf',
    params: { k: 60 },
  },
  limit: 10,
  output_fields: ['id', 'text'],
});
```

## Search Request Parameters

Each item in the `data` array is a `HybridSearchSingleReq`:

| Parameter | Type | Description |
|-----------|------|-------------|
| `data` | `number[]` or `object` | The search vector (dense array or sparse dict) |
| `anns_field` | `string` | The vector field to search |
| `expr` | `string` | Optional filter expression |
| `exprValues` | `object` | Optional template values for filter expression |
| `params` | `object` | Optional search parameters (e.g., `{ nprobe: 10 }`) |
| `group_by_field` | `string` | Optional field to group results by |

## Reranking Strategies

The `rerank` parameter controls how results from multiple searches are merged.

### RRF (Reciprocal Rank Fusion)

Combines results based on their rank positions. Higher-ranked results from any search get more weight.

```javascript
rerank: {
  strategy: 'rrf',
  params: { k: 60 },  // k controls rank smoothing (default: 60)
}
```

### Weighted Ranker

Assigns explicit weights to each search request's results.

```javascript
rerank: {
  strategy: 'weighted',
  params: { weights: [0.7, 0.3] },  // weights correspond to each search in data[]
}
```

### FunctionScore

Advanced reranking using custom scoring functions.

```javascript
rerank: {
  functions: [
    {
      name: 'my_scorer',
      type: FunctionType.RERANK,
      input_field_names: ['text'],
      output_field_names: ['score'],
      params: { key: 'value' },
    },
  ],
  params: {},
}
```

## Top-Level Parameters

| Parameter | Type | Description |
|-----------|------|-------------|
| `collection_name` | `string` | The collection to search |
| `data` | `HybridSearchSingleReq[]` | Array of individual search requests |
| `rerank` | `RerankerObj \| FunctionScore` | Reranking strategy |
| `limit` | `number` | Maximum number of results to return |
| `output_fields` | `string[]` | Fields to include in results |
| `consistency_level` | `string` | Optional consistency level |
| `partition_names` | `string[]` | Optional partitions to search |

## End-to-End Example

```javascript
import { MilvusClient, DataType } from '@zilliz/milvus2-sdk-node';

const client = new MilvusClient({ address: 'localhost:19530' });

// 1. Create a collection with two vector fields
await client.createCollection({
  collection_name: 'hybrid_demo',
  fields: [
    { name: 'id', data_type: DataType.Int64, is_primary_key: true, autoID: true },
    { name: 'text', data_type: DataType.VarChar, max_length: 512 },
    { name: 'dense_vector', data_type: DataType.FloatVector, dim: 4 },
    { name: 'sparse_vector', data_type: DataType.SparseFloatVector },
  ],
});

// 2. Create indexes for both vector fields
await client.createIndex({
  collection_name: 'hybrid_demo',
  index_params: [
    { field_name: 'dense_vector', index_type: 'AUTOINDEX', metric_type: 'COSINE' },
    { field_name: 'sparse_vector', index_type: 'SPARSE_INVERTED_INDEX', metric_type: 'IP' },
  ],
});

// 3. Load collection
await client.loadCollectionSync({ collection_name: 'hybrid_demo' });

// 4. Insert data
await client.insert({
  collection_name: 'hybrid_demo',
  data: [
    {
      text: 'machine learning fundamentals',
      dense_vector: [0.1, 0.2, 0.3, 0.4],
      sparse_vector: { 10: 0.5, 20: 0.3 },
    },
    {
      text: 'deep learning neural networks',
      dense_vector: [0.5, 0.6, 0.7, 0.8],
      sparse_vector: { 15: 0.8, 25: 0.2 },
    },
  ],
});

// 5. Hybrid search
const results = await client.hybridSearch({
  collection_name: 'hybrid_demo',
  data: [
    {
      data: [0.1, 0.2, 0.3, 0.4],
      anns_field: 'dense_vector',
    },
    {
      data: { 10: 0.5, 20: 0.3 },
      anns_field: 'sparse_vector',
    },
  ],
  rerank: {
    strategy: 'rrf',
    params: { k: 60 },
  },
  limit: 10,
  output_fields: ['text'],
});

console.log('Results:', results.results);

// 6. Cleanup
await client.dropCollection({ collection_name: 'hybrid_demo' });
```

## Next Steps

- Learn about [Full-Text Search](/advanced/full-text-search) for text-based hybrid search
- Explore [Iterators](/operations/iterators) for paginating large result sets
- Check [Query & Search](/operations/data-operations-query) for standard search operations
