# Vector Operations — Detailed Reference

Target collection must exist and be loaded.

> **Never use fake or placeholder vectors** (e.g., `[0.1, 0.2, ...]`). Use Milvus built-in embedding functions (`aliyun_milvus` provider, **requires ≥ 2.6**) to automatically generate vectors — no manual embedding code needed.

## Embedding Function Setup (aliyun_milvus)

> **Requires Milvus ≥ 2.6.** Embedding functions are not available on earlier versions.

Milvus can automatically generate vector embeddings from scalar fields via `Function`. You do **not** need to install any external embedding library or call any embedding API manually.

```python
from pymilvus import MilvusClient, DataType, Function, FunctionType

PYMILVUS_GRPC_OPTIONS = {
    "grpc.primary_user_agent": "AlibabaCloud-Agent-Skills/alibabacloud-milvus-manage"
}

client = MilvusClient(
    uri="http://<endpoint>:19530",
    token="root:password",
    grpc_options=PYMILVUS_GRPC_OPTIONS,
)

schema = client.create_schema()

# Define fields
schema.add_field("id", DataType.INT64, is_primary=True, auto_id=True)
schema.add_field("text", DataType.VARCHAR, max_length=9000)
schema.add_field("dense", DataType.FLOAT_VECTOR, dim=1024)

# Text embedding function — vectors are auto-generated on insert and search
text_embedding_function = Function(
    name="text_embedding_func",
    function_type=FunctionType.TEXTEMBEDDING,
    input_field_names=["text"],
    output_field_names=["dense"],
    params={
        "provider": "aliyun_milvus",
        "model_name": "text-embedding-v4"
    }
)
schema.add_function(text_embedding_function)

# Create index and collection
index_params = client.prepare_index_params()
index_params.add_index(field_name="dense", index_type="AUTOINDEX", metric_type="COSINE")

client.create_collection(
    collection_name="my_collection",
    schema=schema,
    index_params=index_params
)
```

### Multimodal Embedding Function (Vision-Language)

For multimodal content (text + images), use `qwen3-vl-embedding`. Add a nullable `mm_value` field to carry image/video/audio references:

```python
schema.add_field("mm_value", DataType.VARCHAR, max_length=9000, nullable=True)
schema.add_field("dense_mm", DataType.FLOAT_VECTOR, dim=1024)

mm_embedding_function = Function(
    name="mm_embedding_func",
    function_type=FunctionType.TEXTEMBEDDING,
    input_field_names=["mm_value"],
    output_field_names=["dense_mm"],
    params={
        "provider": "aliyun_milvus",
        "model_name": "qwen3-vl-embedding",
        "dim": "1024"
    }
)
schema.add_function(mm_embedding_function)

index_params.add_index(field_name="dense_mm", index_type="AUTOINDEX", metric_type="COSINE")
```

### Multimodal Content Handling

Different media types require different handling strategies for the `mm_value` field:

| Media Type               | Size | Strategy | mm_value Example |
|--------------------------|------|----------|------------------|
| **Small image** (< 60KB) | Small | Convert to base64 data URI | `"data:image/jpeg;base64,/9j/4AAQ..."` |
| **Large image** (≥ 60KB)  | Large | Upload to OSS (public read), pass URL | `"https://your-bucket.oss-cn-hangzhou.aliyuncs.com/img/large.jpg"` |
| **Video**                | Any | Upload to OSS (public read), pass URL | `"https://your-bucket.oss-cn-hangzhou.aliyuncs.com/video/demo.mp4"` |
| **Audio**                | Any | Upload to OSS (public read), pass URL | `"https://your-bucket.oss-cn-hangzhou.aliyuncs.com/audio/speech.wav"` |

```python
import base64

# Small image → base64
with open("small_photo.jpg", "rb") as f:
    base64_str = base64.b64encode(f.read()).decode("utf-8")
    mm_value_base64 = f"data:image/jpeg;base64,{base64_str}"

# Large video/audio/image → upload to OSS first, then use public-read URL
mm_value_oss_url = "https://your-bucket.oss-cn-hangzhou.aliyuncs.com/video/demo.mp4"
```

### Supported Providers and Models

| Provider | Model Name | Description |
|----------|------------|-------------|
| `aliyun_milvus` | `text-embedding-v4` | Alibaba Cloud text embedding |
| `aliyun_milvus` | `text-embedding-v3` | Alibaba Cloud text embedding |
| `aliyun_milvus` | `text-embedding-v2` | Alibaba Cloud text embedding |
| `aliyun_milvus` | `qwen3-vl-embedding` | Alibaba Cloud multimodal (vision-language) embedding, requires `dim` param |

## Insert

When a collection has embedding functions, only provide the scalar input fields — vector fields are auto-generated by the function:

```python
# Text-only insert (vector auto-generated from "text" field)
data = [
    {"id": 1, "text": "AI advances in 2024"},
    {"id": 2, "text": "ML basics for beginners"},
]
res = client.insert(collection_name="my_collection", data=data)
# Returns: {"insert_count": 2, "ids": [1, 2]}

# With multimodal content (mm_value is nullable)
import base64

# Small image → base64 data URI
with open("small_photo.jpg", "rb") as f:
    base64_str = base64.b64encode(f.read()).decode("utf-8")
    mm_base64 = f"data:image/jpeg;base64,{base64_str}"

data_with_mm = [
    {"id": 3, "text": "A cat sitting on a sofa", "mm_value": mm_base64},
    {"id": 4, "text": "Product demo video", "mm_value": "https://your-bucket.oss-cn-hangzhou.aliyuncs.com/video/demo.mp4"},
    {"id": 5, "text": "Pure text document"},  # mm_value is nullable, can be omitted
]
res = client.insert(collection_name="my_collection", data=data_with_mm)
```

## Upsert (insert or update if PK exists)

```python
data = [
    {"id": 1, "text": "Updated: AI advances in 2025"},
    {"id": 2, "text": "Updated: Deep learning fundamentals"},
]
res = client.upsert(collection_name="my_collection", data=data)
# Returns: {"upsert_count": 2}
```

## Search (vector similarity)

When a collection has embedding functions, search `data` accepts raw text or URLs instead of vectors:

```python
# Text search — pass raw text, embedding is auto-generated
results = client.search(
    collection_name="my_collection",
    data=["What is artificial intelligence?"],  # Raw text, not vector
    anns_field="dense",                 # Vector field name (output of text embedding function)
    limit=10,                           # Top-K
    output_fields=["text", "id"],       # Fields to return
    filter='age > 20 and status == "active"',  # Optional scalar filter
)
# Returns: List[List[dict]]
# Each hit: {"id": 1, "distance": 0.95, "entity": {"text": "AI advances..."}}

# Multimodal search — pass image URL or base64
results = client.search(
    collection_name="my_collection",
    data=["https://example.com/image.jpeg"],  # Image URL, not vector
    anns_field="dense_mm",              # Vector field name (output of multimodal embedding function)
    limit=5,
    output_fields=["text", "mm_value"],
)
```

## Hybrid Search (multi-vector with reranking)

When using embedding functions, pass raw text/URLs as `data` in `AnnSearchRequest`:

```python
from pymilvus import AnnSearchRequest, RRFRanker, WeightedRanker

# Dense text search — raw text, auto-embedded by aliyun_milvus function
req1 = AnnSearchRequest(
    data=["What is artificial intelligence?"],  # Raw text
    anns_field="dense",
    param={"metric_type": "COSINE"},
    limit=10
)

# Sparse BM25 full-text search — raw text, auto-tokenized by BM25 function
req2 = AnnSearchRequest(
    data=["artificial intelligence"],    # Raw text for BM25
    anns_field="sparse",
    param={"metric_type": "BM25"},
    limit=10
)

# RRF reranking
results = client.hybrid_search(
    collection_name="my_collection",
    reqs=[req1, req2],
    ranker=RRFRanker(k=60),
    limit=10,
    output_fields=["text"]
)

# Or weighted reranking
results = client.hybrid_search(
    collection_name="my_collection",
    reqs=[req1, req2],
    ranker=WeightedRanker(0.7, 0.3),
    limit=10
)
```

## Full-Text Search

Full-text search uses Milvus's built-in BM25 tokenizer to convert text into sparse vectors automatically.

### Setup: Collection with Full-Text Search

```python
from pymilvus import MilvusClient, DataType, Function, FunctionType

PYMILVUS_GRPC_OPTIONS = {
    "grpc.primary_user_agent": "AlibabaCloud-Agent-Skills/alibabacloud-milvus-manage"
}

client = MilvusClient(
    uri="<USER_URI>",
    token="<USER_TOKEN>",
    grpc_options=PYMILVUS_GRPC_OPTIONS,
)

schema = client.create_schema()
schema.add_field("id", DataType.INT64, is_primary=True, auto_id=True)
schema.add_field("text", DataType.VARCHAR, max_length=1000, enable_analyzer=True)
schema.add_field("sparse", DataType.SPARSE_FLOAT_VECTOR)

# Define BM25 function to auto-convert text -> sparse vector
bm25_function = Function(
    name="text_bm25",
    input_field_names=["text"],
    output_field_names=["sparse"],
    function_type=FunctionType.BM25,
)
schema.add_function(bm25_function)

index_params = client.prepare_index_params()
index_params.add_index(field_name="sparse", index_type="AUTOINDEX", metric_type="BM25")

client.create_collection(collection_name="full_text_col", schema=schema, index_params=index_params)
```

### Search with Text

```python
results = client.search(
    collection_name="full_text_col",
    data=["machine learning algorithms"],   # Raw text query
    anns_field="sparse",
    limit=10,
    output_fields=["text"]
)
```

## Search Iterator (paginated search over large results)

```python
iterator = client.search_iterator(
    collection_name="my_collection",
    data=["search query text"],         # Raw text, auto-embedded by aliyun_milvus function
    anns_field="dense",
    batch_size=100,
    limit=10000,
    output_fields=["text"],
    search_params={"metric_type": "COSINE"}
)

results = []
while True:
    batch = iterator.next()
    if not batch:
        break
    results.extend(batch)

iterator.close()
```

## Query Iterator (paginated filter-based retrieval)

```python
iterator = client.query_iterator(
    collection_name="my_collection",
    filter='age > 20',
    output_fields=["text", "age"],
    batch_size=100,
    limit=10000
)

results = []
while True:
    batch = iterator.next()
    if not batch:
        break
    results.extend(batch)

iterator.close()
```

## Query (filter-based retrieval)

```python
results = client.query(
    collection_name="my_collection",
    filter='id in [1, 2, 3]',
    output_fields=["text"],
    limit=100
)
```

## Get (by primary key)

```python
results = client.get(
    collection_name="my_collection",
    ids=[1, 2, 3],
    output_fields=["text"]
)
```

## Delete

```python
# By primary keys
client.delete(collection_name="my_collection", ids=[1, 2, 3])

# By filter expression
client.delete(collection_name="my_collection", filter='status == "obsolete"')
```

## Filter Expression Syntax

| Expression | Example |
|---|---|
| Comparison | `age > 20` |
| Equality | `status == "active"` |
| IN list | `id in [1, 2, 3]` |
| AND/OR | `age > 20 and status == "active"` |
| String match | `text like "hello%"` |
| Array contains | `ARRAY_CONTAINS(tags, "ml")` |
| JSON field | `json_field["key"] > 100` |
| Match all | `id > 0` |

## Guidance

- **Never use fake or placeholder vectors.** Use `aliyun_milvus` embedding functions to auto-generate vectors from text or multimodal content.
- When a collection has embedding functions, pass **raw text or URLs** as `data` in search — do not manually compute vectors.
- For **text embedding**, use `provider: "aliyun_milvus"` with `model_name: "text-embedding-v4"`.
- For **multimodal embedding** (images/video/audio), use `model_name: "qwen3-vl-embedding"` with `dim` param.
- **Small images** (< 60KB): convert to base64 data URI (`data:image/jpeg;base64,...`) and pass directly in the `mm_value` field.
- **Large images / video / audio**: upload to OSS with **public-read** access, then pass the URL in the `mm_value` field.
- For full-text search, pass raw text strings as `data` — Milvus handles tokenization via BM25.
- For large inserts, batch data into chunks (e.g., 1000 rows per batch).
- Always specify `output_fields` to control which fields are returned.
- For large result sets, use `search_iterator` or `query_iterator` instead of increasing `limit`.
- Always call `iterator.close()` when done to release server resources.
