Définir le TTL de la collection

Milvus peut automatiquement faire expirer des entités par le biais d'une politique de durée de vie (TTL). Les entités expirées cessent immédiatement d'apparaître dans les résultats des requêtes et des recherches et sont physiquement retirées du stockage lors du prochain cycle de compactage, généralement dans les 24 heures.

Il existe deux modes de TTL :

  • TTL au niveau de la collection - une fenêtre de rétention partagée par chaque entité, définie par la propriété collection.ttl.seconds.

  • TTL au niveau de l'entité - chaque entité porte son propre délai d'expiration absolu dans un champ TIMESTAMPTZ dédié, marqué comme champ TTL par le biais de la propriété ttl_field.

Cette fonctionnalité ne s'applique qu'aux collections gérées.

Limites

  • Les deux modes de TTL s'excluent mutuellement. Une collection ne peut pas avoir à la fois collection.ttl.seconds et ttl_field. Pour passer d'un mode à l'autre, voir Migrer entre les deux modes.

  • Le TTL au niveau de la collection applique une fenêtre à l'ensemble de la collection. Si une seule ligne a besoin d'une durée de vie différente, utilisez le TTL au niveau de l'entité.

  • Le champ du TTL au niveau de l'entité doit être TIMESTAMPTZ. Les autres types sont rejetés.

  • Un champ TTL par collection. Le schéma peut contenir plusieurs champs TIMESTAMPTZ, mais un seul peut être nommé dans ttl_field.

  • L'abandon de ttl_field ne fait pas réapparaître les entités expirées. Pour restaurer une entité expirée, il faut la réinsérer avec une date d'expiration NULL ou future.

Vue d'ensemble

Développez

Quand utiliser le TTL ?

Le TTL est le bon outil lorsque la rétention est une politique - vous savez à l'avance que certaines entités doivent disparaître, et vous voulez que le cluster l'applique sans que vous ayez à écrire une tâche cron.

Scénarios typiques :

  • Jeux de données à fenêtre temporelle. Ne conservez que les N derniers jours de logs, de métriques, d'événements ou de caches de fonctionnalités à courte durée de vie.

  • Collections multi-locataires. Différents locataires ont des fenêtres de conservation différentes dans la même collection.

  • Politiques de conservation par enregistrement. Durée de vie d'un document dans les pipelines IoT, les magasins de documents ou les magasins de fonctionnalités MLOps.

  • Mélange de données chaudes et froides. Des entités à courte durée de vie coexistent avec des entités à long terme dans la même collection.

  • Expiration axée sur la conformité. Minimisation des données de type GDPR où chaque enregistrement porte sa propre date de suppression.

  • Expiration en fonction de l'activité. Une entité représente un enregistrement qui n'est valable que jusqu'à un moment donné (fin d'une campagne, expiration d'une session).

Les entités expirées n'apparaîtront pas dans les résultats des recherches ou des requêtes. Toutefois, elles peuvent rester dans le stockage jusqu'au compactage des données, qui doit être effectué dans les 24 heures suivantes.

Vous pouvez contrôler le moment du déclenchement du compactage des données en définissant l'élément de configuration dataCoord.compaction.expiry.tolerance dans votre fichier de configuration Milvus.

Cet élément de configuration a pour valeur par défaut -1, ce qui indique que l'intervalle de compactage des données existant s'applique. Toutefois, lorsque vous modifiez sa valeur en un nombre entier positif, comme 12, le compactage des données sera déclenché le nombre d'heures spécifié après l'expiration de toute entité.

Modes TTL

Les deux modes répondent à des questions différentes en matière de conservation :

  • LeTTL au niveau de la collection applique une durée de conservation unique à chaque entité. Chaque entité expire à l'adresse insert_ts + ttl_seconds.

  • LeTTL au niveau de l'entité permet à chaque entité de stocker son propre délai d'expiration absolu dans un champ TIMESTAMPTZ. Un NULL dans ce champ signifie que l'entité n'expire jamais.

Une collection n' utilise qu'un seul mode à la fois - les deux s'excluent mutuellement. Le passage d'un mode à l'autre est une opération en plusieurs étapes ; voir Migrer entre les deux modes.

Utilisez ce tableau pour choisir un mode :

Si votre situation est...

Utiliser

Chaque entité de la collection doit suivre la même fenêtre de conservation.

TTL au niveau de la collection

La rétention est "à partir du moment de l'insertion, conserver N secondes".

TTL au niveau de la collection

Différentes entités ont besoin de durées de vie différentes dans la même collection (par locataire, chaud/froid, par document).

TTL au niveau de l'entité

La conservation est une heure absolue (par exemple, 2027-01-01T00:00:00Z).

TTL au niveau de l'entité

La conservation est pilotée par un horodatage commercial, et non par l'horodatage de l'insertion.

TTL au niveau de l'entité

Vous souhaitez rafraîchir ou prolonger la durée de vie d'une entité après son insertion.

TTL au niveau de l'entité

Certaines entités ne doivent jamais expirer alors que d'autres doivent le faire

TTL au niveau de l'entité (utiliser NULL pour les entités immortelles)

Définir un TTL au niveau de la collection

Utiliser le TTL au niveau de la collection lorsque chaque entité de la collection doit suivre la même fenêtre de rétention.

Activer sur une nouvelle collection

Passer collection.ttl.seconds (entier, en secondes) à travers la carte properties au moment de la création.

from pymilvus import MilvusClient, DataType

client = MilvusClient(uri="http://localhost:19530")

schema = client.create_schema(auto_id=False, enable_dynamic_field=False)
schema.add_field("id", DataType.INT64, is_primary=True, auto_id=False)
schema.add_field("vector", DataType.FLOAT_VECTOR, dim=128)

index_params = client.prepare_index_params()
index_params.add_index(
    field_name="vector", index_type="AUTOINDEX", metric_type="COSINE"
)

client.create_collection(
    collection_name="my_collection",
    schema=schema,
    index_params=index_params,
    properties={
        "collection.ttl.seconds": 1209600  # 14 days
    },
)
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;

import io.milvus.v2.client.ConnectConfig;
import io.milvus.v2.client.MilvusClientV2;
import io.milvus.v2.common.DataType;
import io.milvus.v2.common.IndexParam;
import io.milvus.v2.service.collection.request.AddFieldReq;
import io.milvus.v2.service.collection.request.CreateCollectionReq;

MilvusClientV2 client = new MilvusClientV2(ConnectConfig.builder()
        .uri("http://localhost:19530")
        .build());

CreateCollectionReq.CollectionSchema schema = CreateCollectionReq.CollectionSchema.builder().build();
schema.addField(AddFieldReq.builder().fieldName("id").dataType(DataType.Int64)
        .isPrimaryKey(true).autoID(false).build());
schema.addField(AddFieldReq.builder().fieldName("vector").dataType(DataType.FloatVector)
        .dimension(128).build());

IndexParam indexParam = IndexParam.builder().fieldName("vector")
        .indexType(IndexParam.IndexType.AUTOINDEX)
        .metricType(IndexParam.MetricType.COSINE).build();

Map<String, String> properties = new HashMap<>();
properties.put("collection.ttl.seconds", "1209600"); // 14 days

client.createCollection(CreateCollectionReq.builder()
        .collectionName("my_collection")
        .collectionSchema(schema)
        .indexParams(Collections.singletonList(indexParam))
        .properties(properties)
        .build());
const { MilvusClient, DataType } = require("@zilliz/milvus2-sdk-node");

const client = new MilvusClient({ address: "http://localhost:19530" });

await client.createCollection({
  collection_name: "my_collection",
  fields: [
    { name: "id", data_type: DataType.Int64, is_primary_key: true, autoID: false },
    { name: "vector", data_type: DataType.FloatVector, dim: 128 },
  ],
  index_params: [
    { field_name: "vector", index_type: "AUTOINDEX", metric_type: "COSINE" },
  ],
  properties: {
    "collection.ttl.seconds": 1209600, // 14 days
  },
});
err = client.CreateCollection(ctx, milvusclient.NewCreateCollectionOption("my_collection", schema).
    WithProperty(common.CollectionTTLConfigKey, 1209600)) //  TTL in seconds
if err != nil {
    fmt.Println(err.Error())
    // handle error
}
export params='{
    "ttlSeconds": 1209600
}'

export CLUSTER_ENDPOINT="http://localhost:19530"
export TOKEN="root:Milvus"

curl --request POST \
--url "${CLUSTER_ENDPOINT}/v2/vectordb/collections/create" \
--header "Authorization: Bearer ${TOKEN}" \
--header "Content-Type: application/json" \
--header "Request-Timeout: 10" \
-d "{
    \"collectionName\": \"my_collection\",
    \"schema\": $schema,
    \"params\": $params
}"

Activer sur une collection existante

Appeler alter_collection_properties avec collection.ttl.seconds dans la carte properties pour appliquer le TTL à une collection déjà utilisée.

from pymilvus import MilvusClient, DataType

client = MilvusClient(uri="http://localhost:19530")

# Assumes "my_collection" was created earlier without TTL
schema = client.create_schema(auto_id=False, enable_dynamic_field=False)
schema.add_field("id", DataType.INT64, is_primary=True, auto_id=False)
schema.add_field("vector", DataType.FLOAT_VECTOR, dim=128)

index_params = client.prepare_index_params()
index_params.add_index(
    field_name="vector", index_type="AUTOINDEX", metric_type="COSINE"
)

if not client.has_collection("my_collection"):
    client.create_collection(
        collection_name="my_collection",
        schema=schema,
        index_params=index_params,
    )

client.alter_collection_properties(
    collection_name="my_collection",
    properties={"collection.ttl.seconds": 1209600},
)
import java.util.HashMap;
import java.util.Map;

import io.milvus.v2.client.ConnectConfig;
import io.milvus.v2.client.MilvusClientV2;
import io.milvus.v2.service.collection.request.AlterCollectionPropertiesReq;

MilvusClientV2 client = new MilvusClientV2(ConnectConfig.builder()
        .uri("http://localhost:19530")
        .build());

// Assumes "my_collection" was created earlier without TTL.

Map<String, String> properties = new HashMap<>();
properties.put("collection.ttl.seconds", "1209600");

client.alterCollectionProperties(AlterCollectionPropertiesReq.builder()
        .collectionName("my_collection")
        .properties(properties)
        .build());
const { MilvusClient } = require("@zilliz/milvus2-sdk-node");

const client = new MilvusClient({ address: "http://localhost:19530" });

// Assumes "my_collection" was created earlier without TTL.
await client.alterCollectionProperties({
  collection_name: "my_collection",
  properties: { "collection.ttl.seconds": 1209600 },
});
err = client.AlterCollectionProperties(ctx, milvusclient.NewAlterCollectionPropertiesOption("my_collection").
    WithProperty(common.CollectionTTLConfigKey, 60))
if err != nil {
    fmt.Println(err.Error())
    // handle error
}
curl --request POST \
--url "${CLUSTER_ENDPOINT}/v2/vectordb/collections/alter_properties" \
--header "Authorization: Bearer ${TOKEN}" \
--header "Content-Type: application/json" \
--header "Request-Timeout: 10" \
-d "{
    \"collectionName\": \"my_collection\",
    \"properties\": {
        \"collection.ttl.seconds\": 1209600
    }
}"

Abandonner le paramètre TTL

Si vous décidez de conserver indéfiniment les données d'une collection, vous pouvez simplement supprimer le paramètre TTL de cette collection.

from pymilvus import MilvusClient

client = MilvusClient(uri="http://localhost:19530")

client.drop_collection_properties(
    collection_name="my_collection",
    property_keys=["collection.ttl.seconds"],
)
import java.util.Collections;

import io.milvus.v2.client.ConnectConfig;
import io.milvus.v2.client.MilvusClientV2;
import io.milvus.v2.service.collection.request.DropCollectionPropertiesReq;

MilvusClientV2 client = new MilvusClientV2(ConnectConfig.builder()
        .uri("http://localhost:19530")
        .build());

client.dropCollectionProperties(DropCollectionPropertiesReq.builder()
        .collectionName("my_collection")
        .propertyKeys(Collections.singletonList("collection.ttl.seconds"))
        .build());
const { MilvusClient } = require("@zilliz/milvus2-sdk-node");

const client = new MilvusClient({ address: "http://localhost:19530" });

await client.dropCollectionProperties({
  collection_name: "my_collection",
  properties: ["collection.ttl.seconds"],
});
err = client.DropCollectionProperties(ctx, milvusclient.NewDropCollectionPropertiesOption("my_collection", common.CollectionTTLConfigKey))
if err != nil {
    fmt.Println(err.Error())
    // handle error
}
curl --request POST \
--url "${CLUSTER_ENDPOINT}/v2/vectordb/collections/drop_properties" \
--header "Authorization: Bearer ${TOKEN}" \
--header "Content-Type: application/json" \
--header "Request-Timeout: 10" \
-d "{
    \"collectionName\": \"my_collection\",
    \"propertyKeys\": [
        \"collection.ttl.seconds\"
    ]
}"

Définir un TTL au niveau de l'entitéCompatible with Milvus 3.0.x

Le TTL au niveau de l'entité permet à chaque entité d'avoir son propre délai d'expiration absolu. Le temps est stocké dans une colonne dédiée TIMESTAMPTZ que vous déclarez dans le schéma, et vous marquez cette colonne comme champ TTL par le biais de la propriété de collection ttl_field.

Activation sur une nouvelle collection

L'activation du TTL au niveau de l'entité au moment de la création nécessite deux ajouts dans le même appel create_collection: un champ TIMESTAMPTZ dans le schéma et la propriété ttl_field qui pointe vers ce champ.

from pymilvus import MilvusClient, DataType

client = MilvusClient(uri="http://localhost:19530")

schema = client.create_schema(enable_dynamic_field=False)
schema.add_field("id", DataType.INT64, is_primary=True, auto_id=False)
schema.add_field("expire_at", DataType.TIMESTAMPTZ, nullable=True)
schema.add_field("vector", DataType.FLOAT_VECTOR, dim=128)

index_params = client.prepare_index_params()
index_params.add_index(field_name="vector", index_type="AUTOINDEX",
                       metric_type="COSINE")

client.create_collection(
    collection_name="my_collection",
    schema=schema,
    index_params=index_params,
    properties={"ttl_field": "expire_at"},
)
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;

import io.milvus.v2.client.ConnectConfig;
import io.milvus.v2.client.MilvusClientV2;
import io.milvus.v2.common.DataType;
import io.milvus.v2.common.IndexParam;
import io.milvus.v2.service.collection.request.AddFieldReq;
import io.milvus.v2.service.collection.request.CreateCollectionReq;

MilvusClientV2 client = new MilvusClientV2(ConnectConfig.builder()
        .uri("http://localhost:19530")
        .build());

CreateCollectionReq.CollectionSchema schema = CreateCollectionReq.CollectionSchema.builder().build();
schema.addField(AddFieldReq.builder().fieldName("id").dataType(DataType.Int64)
        .isPrimaryKey(true).autoID(false).build());
schema.addField(AddFieldReq.builder().fieldName("expire_at").dataType(DataType.Timestamptz)
        .isNullable(true).build());
schema.addField(AddFieldReq.builder().fieldName("vector").dataType(DataType.FloatVector)
        .dimension(128).build());

IndexParam indexParam = IndexParam.builder().fieldName("vector")
        .indexType(IndexParam.IndexType.AUTOINDEX)
        .metricType(IndexParam.MetricType.COSINE).build();

Map<String, String> properties = new HashMap<>();
properties.put("ttl_field", "expire_at");

client.createCollection(CreateCollectionReq.builder()
        .collectionName("my_collection")
        .collectionSchema(schema)
        .indexParams(Collections.singletonList(indexParam))
        .properties(properties)
        .build());
const { MilvusClient, DataType } = require("@zilliz/milvus2-sdk-node");

const client = new MilvusClient({ address: "http://localhost:19530" });

await client.createCollection({
  collection_name: "my_collection",
  fields: [
    { name: "id", data_type: DataType.Int64, is_primary_key: true, autoID: false },
    { name: "expire_at", data_type: DataType.Timestamptz, nullable: true },
    { name: "vector", data_type: DataType.FloatVector, dim: 128 },
  ],
  index_params: [
    { field_name: "vector", index_type: "AUTOINDEX", metric_type: "COSINE" },
  ],
  properties: { ttl_field: "expire_at" },
});
// go
# restful

Une fois que la collection existe, insérer des entités avec des chaînes d'horodatage ISO 8601.

import random
from pymilvus import MilvusClient

client = MilvusClient(uri="http://localhost:19530")

# Assumes "my_collection" was created earlier with `ttl_field`: "expire_at"
rows = [
    # Never expires
    {"id": 1, "expire_at": None,
     "vector": [random.random() for _ in range(128)]},
    # Expires at 2026-12-31 UTC midnight
    {"id": 2, "expire_at": "2026-12-31T00:00:00Z",
     "vector": [random.random() for _ in range(128)]},
    # Shanghai local time — normalized to UTC internally
    {"id": 3, "expire_at": "2027-01-01T00:00:00+08:00",
     "vector": [random.random() for _ in range(128)]},
]

client.insert("my_collection", rows)
import java.util.ArrayList;
import java.util.List;
import java.util.Random;

import com.google.gson.Gson;
import com.google.gson.JsonNull;
import com.google.gson.JsonObject;

import io.milvus.v2.client.ConnectConfig;
import io.milvus.v2.client.MilvusClientV2;
import io.milvus.v2.service.vector.request.InsertReq;

MilvusClientV2 client = new MilvusClientV2(ConnectConfig.builder()
        .uri("http://localhost:19530")
        .build());

// Assumes "my_collection" was created earlier with `ttl_field`: "expire_at".
Gson gson = new Gson();
Random rng = new Random();

List<Float> vector = new ArrayList<>();
for (int i = 0; i < 128; i++) vector.add(rng.nextFloat());

List<JsonObject> rows = new ArrayList<>();

// Never expires
JsonObject r1 = new JsonObject();
r1.addProperty("id", 1);
r1.add("expire_at", JsonNull.INSTANCE);
r1.add("vector", gson.toJsonTree(vector));
rows.add(r1);

// Expires at 2026-12-31 UTC midnight
JsonObject r2 = new JsonObject();
r2.addProperty("id", 2);
r2.addProperty("expire_at", "2026-12-31T00:00:00Z");
r2.add("vector", gson.toJsonTree(vector));
rows.add(r2);

// Shanghai local time — normalized to UTC internally
JsonObject r3 = new JsonObject();
r3.addProperty("id", 3);
r3.addProperty("expire_at", "2027-01-01T00:00:00+08:00");
r3.add("vector", gson.toJsonTree(vector));
rows.add(r3);

client.insert(InsertReq.builder()
        .collectionName("my_collection")
        .data(rows)
        .build());
const { MilvusClient } = require("@zilliz/milvus2-sdk-node");

const client = new MilvusClient({ address: "http://localhost:19530" });

const vector = Array.from({ length: 128 }, () => Math.random());

// Assumes "my_collection" was created earlier with `ttl_field`: "expire_at".
await client.insert({
  collection_name: "my_collection",
  data: [
    // Never expires
    { id: 1, expire_at: null, vector },
    // Expires at 2026-12-31 UTC midnight
    { id: 2, expire_at: "2026-12-31T00:00:00Z", vector },
    // Shanghai local time — normalized to UTC internally
    { id: 3, expire_at: "2027-01-01T00:00:00+08:00", vector },
  ],
});
// go
# restful

À chaque requête et recherche vectorielle, le serveur injecte automatiquement le filtre TTL - vous n'avez pas à en écrire un vous-même, et les entités expirées n'apparaissent jamais dans les résultats :

from pymilvus import MilvusClient

client = MilvusClient(uri="http://localhost:19530")

client.load_collection("my_collection")

# Expired rows are filtered out automatically
results = client.query(
    collection_name="my_collection",
    filter="id >= 0",
    output_fields=["id", "expire_at"],
    limit=10,
)
print(results)
import java.util.Arrays;

import io.milvus.v2.client.ConnectConfig;
import io.milvus.v2.client.MilvusClientV2;
import io.milvus.v2.service.collection.request.LoadCollectionReq;
import io.milvus.v2.service.vector.request.QueryReq;
import io.milvus.v2.service.vector.response.QueryResp;

MilvusClientV2 client = new MilvusClientV2(ConnectConfig.builder()
        .uri("http://localhost:19530")
        .build());

client.loadCollection(LoadCollectionReq.builder()
        .collectionName("my_collection")
        .build());

// Expired rows are filtered out automatically
QueryResp results = client.query(QueryReq.builder()
        .collectionName("my_collection")
        .filter("id >= 0")
        .outputFields(Arrays.asList("id", "expire_at"))
        .limit(10L)
        .build());
System.out.println(results.getQueryResults());
const { MilvusClient } = require("@zilliz/milvus2-sdk-node");

const client = new MilvusClient({ address: "http://localhost:19530" });

await client.loadCollection({ collection_name: "my_collection" });

// Expired rows are filtered out automatically
const results = await client.query({
  collection_name: "my_collection",
  filter: "id >= 0",
  output_fields: ["id", "expire_at"],
  limit: 10,
});
console.log(results.data);
// go
# restful

Le même filtre automatique s'applique à client.search().

Pour prolonger la durée de vie d'une entité avant que le compactage ne la supprime physiquement, il faut l'insérer avec un horodatage d'expiration ultérieur - ou None - pour la réintégrer dans l'ensemble interrogeable.

import random
from pymilvus import MilvusClient

client = MilvusClient(uri="http://localhost:19530")

client.upsert("my_collection", [
    {"id": 2,
     "vector": [random.random() for _ in range(128)],
     "expire_at": "2028-01-01T00:00:00Z"},
])
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Random;

import com.google.gson.Gson;
import com.google.gson.JsonObject;

import io.milvus.v2.client.ConnectConfig;
import io.milvus.v2.client.MilvusClientV2;
import io.milvus.v2.service.vector.request.UpsertReq;

MilvusClientV2 client = new MilvusClientV2(ConnectConfig.builder()
        .uri("http://localhost:19530")
        .build());

Gson gson = new Gson();
Random rng = new Random();
List<Float> vector = new ArrayList<>();
for (int i = 0; i < 128; i++) vector.add(rng.nextFloat());

JsonObject row = new JsonObject();
row.addProperty("id", 2);
row.add("vector", gson.toJsonTree(vector));
row.addProperty("expire_at", "2028-01-01T00:00:00Z");

client.upsert(UpsertReq.builder()
        .collectionName("my_collection")
        .data(Collections.singletonList(row))
        .build());
const { MilvusClient } = require("@zilliz/milvus2-sdk-node");

const client = new MilvusClient({ address: "http://localhost:19530" });

const vector = Array.from({ length: 128 }, () => Math.random());

await client.upsert({
  collection_name: "my_collection",
  data: [
    { id: 2, vector, expire_at: "2028-01-01T00:00:00Z" },
  ],
});
// go
# restful

Activation sur une collection existante

Si la collection existe déjà et n'a pas l'ensemble collection.ttl.seconds, ajoutez une colonne TIMESTAMPTZ avec add_collection_field, puis marquez-la comme champ TTL avec alter_collection_properties. Si vous le souhaitez, ajoutez des lignes historiques pour remplir leur date d'expiration - les lignes que vous ne remplissez pas gardent NULL et n'expirent jamais.

import random
from pymilvus import MilvusClient, DataType

client = MilvusClient(uri="http://localhost:19530")

# Step 1 — add a TIMESTAMPTZ column to the schema
client.add_collection_field(
    collection_name="my_collection",
    field_name="expire_at",
    data_type=DataType.TIMESTAMPTZ,
    nullable=True,
)

# Step 2 — mark the new column as the TTL field
client.alter_collection_properties(
    collection_name="my_collection",
    properties={"ttl_field": "expire_at"},
)

# Step 3 (optional) — backfill expiration timestamps for historical rows
client.upsert("my_collection", [
    {"id": 1,
     "vector": [random.random() for _ in range(128)],
     "expire_at": "2026-12-31T00:00:00Z"},
])
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Random;

import com.google.gson.Gson;
import com.google.gson.JsonObject;

import io.milvus.v2.client.ConnectConfig;
import io.milvus.v2.client.MilvusClientV2;
import io.milvus.v2.common.DataType;
import io.milvus.v2.service.collection.request.AddCollectionFieldReq;
import io.milvus.v2.service.collection.request.AlterCollectionPropertiesReq;
import io.milvus.v2.service.vector.request.UpsertReq;

MilvusClientV2 client = new MilvusClientV2(ConnectConfig.builder()
        .uri("http://localhost:19530")
        .build());

// Step 1 — add a TIMESTAMPTZ column to the schema
client.addCollectionField(AddCollectionFieldReq.builder()
        .collectionName("my_collection")
        .fieldName("expire_at")
        .dataType(DataType.Timestamptz)
        .isNullable(true)
        .build());

// Step 2 — mark the new column as the TTL field
Map<String, String> properties = new HashMap<>();
properties.put("ttl_field", "expire_at");
client.alterCollectionProperties(AlterCollectionPropertiesReq.builder()
        .collectionName("my_collection")
        .properties(properties)
        .build());

// Step 3 (optional) — backfill expiration timestamps for historical rows
Gson gson = new Gson();
Random rng = new Random();
List<Float> vector = new ArrayList<>();
for (int i = 0; i < 128; i++) vector.add(rng.nextFloat());

JsonObject row = new JsonObject();
row.addProperty("id", 1);
row.add("vector", gson.toJsonTree(vector));
row.addProperty("expire_at", "2026-12-31T00:00:00Z");

client.upsert(UpsertReq.builder()
        .collectionName("my_collection")
        .data(Collections.singletonList(row))
        .build());
const { MilvusClient, DataType } = require("@zilliz/milvus2-sdk-node");

const client = new MilvusClient({ address: "http://localhost:19530" });

const vector = Array.from({ length: 128 }, () => Math.random());

// Step 1 — add a TIMESTAMPTZ column to the schema
await client.addCollectionField({
  collection_name: "my_collection",
  field: { name: "expire_at", data_type: DataType.Timestamptz, nullable: true },
});

// Step 2 — mark the new column as the TTL field
await client.alterCollectionProperties({
  collection_name: "my_collection",
  properties: { ttl_field: "expire_at" },
});

// Step 3 (optional) — backfill expiration timestamps for historical rows
await client.upsert({
  collection_name: "my_collection",
  data: [
    { id: 1, vector, expire_at: "2026-12-31T00:00:00Z" },
  ],
});
// go
# restful

Supprimer le paramètre TTL

Appelez drop_collection_properties avec ttl_field dans property_keys pour arrêter l'expiration par entité. La colonne TIMESTAMPTZ elle-même reste dans le schéma - vous pouvez toujours l'interroger comme un champ normal.

from pymilvus import MilvusClient

client = MilvusClient(uri="http://localhost:19530")

client.drop_collection_properties(
    collection_name="my_collection",
    property_keys=["ttl_field"],
)
import java.util.Collections;

import io.milvus.v2.client.ConnectConfig;
import io.milvus.v2.client.MilvusClientV2;
import io.milvus.v2.service.collection.request.DropCollectionPropertiesReq;

MilvusClientV2 client = new MilvusClientV2(ConnectConfig.builder()
        .uri("http://localhost:19530")
        .build());

client.dropCollectionProperties(DropCollectionPropertiesReq.builder()
        .collectionName("my_collection")
        .propertyKeys(Collections.singletonList("ttl_field"))
        .build());
const { MilvusClient } = require("@zilliz/milvus2-sdk-node");

const client = new MilvusClient({ address: "http://localhost:19530" });

await client.dropCollectionProperties({
  collection_name: "my_collection",
  properties: ["ttl_field"],
});
// go
# restful

L'abandon de ttl_field désactive le filtre automatique pour les requêtes futures, mais les entités qui ont déjà expiré ne sont pas automatiquement remises à la surface. Pour rendre visible une entité précédemment expirée, il faut la réinsérer avec une date d'expiration None ou future - c'est le seul moyen de restaurer l'accès aux lignes expirées au cours de la même session de chargement.

Migrer entre les deux modes

Les deux modes de TTL s'excluant mutuellement, le passage de l'un à l'autre se fait en plusieurs étapes.

Passer d'un TTL au niveau de la collection à un TTL au niveau de l'entité

Si votre collection a été créée avec collection.ttl.seconds et que vous souhaitez passer à une expiration par entité, suivez ces quatre étapes. Si vous sautez l'étape 1, l'étape 3 échouera avec collection TTL is already set, cannot be set ttl field.

import random
from pymilvus import MilvusClient, DataType

client = MilvusClient(uri="http://localhost:19530")

# Assumes "my_collection" already exists with `collection.ttl.seconds` set.
# Step 1 — disable collection-level TTL (mandatory; the two modes are mutually exclusive)
client.drop_collection_properties(
    collection_name="my_collection",
    property_keys=["collection.ttl.seconds"],
)

# Step 2 — add a TIMESTAMPTZ column to the schema
client.add_collection_field(
    collection_name="my_collection",
    field_name="expire_at",
    data_type=DataType.TIMESTAMPTZ,
    nullable=True,
)

# Step 3 — set the ttl_field property on the column you just added
client.alter_collection_properties(
    collection_name="my_collection",
    properties={"ttl_field": "expire_at"},
)

# Step 4 (optional) — backfill expiration timestamps for historical entities
client.upsert("my_collection", [
    {"id": 1,
     "vector": [random.random() for _ in range(128)],
     "expire_at": "2026-12-31T00:00:00Z"},
])
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Random;

import com.google.gson.Gson;
import com.google.gson.JsonObject;

import io.milvus.v2.client.ConnectConfig;
import io.milvus.v2.client.MilvusClientV2;
import io.milvus.v2.common.DataType;
import io.milvus.v2.service.collection.request.AddCollectionFieldReq;
import io.milvus.v2.service.collection.request.AlterCollectionPropertiesReq;
import io.milvus.v2.service.collection.request.DropCollectionPropertiesReq;
import io.milvus.v2.service.vector.request.UpsertReq;

MilvusClientV2 client = new MilvusClientV2(ConnectConfig.builder()
        .uri("http://localhost:19530")
        .build());

// Assumes "my_collection" already exists with `collection.ttl.seconds` set.
// Step 1 — disable collection-level TTL (mandatory; the two modes are mutually exclusive)
client.dropCollectionProperties(DropCollectionPropertiesReq.builder()
        .collectionName("my_collection")
        .propertyKeys(Collections.singletonList("collection.ttl.seconds"))
        .build());

// Step 2 — add a TIMESTAMPTZ column to the schema
client.addCollectionField(AddCollectionFieldReq.builder()
        .collectionName("my_collection")
        .fieldName("expire_at")
        .dataType(DataType.Timestamptz)
        .isNullable(true)
        .build());

// Step 3 — set the ttl_field property on the column you just added
Map<String, String> ttlField = new HashMap<>();
ttlField.put("ttl_field", "expire_at");
client.alterCollectionProperties(AlterCollectionPropertiesReq.builder()
        .collectionName("my_collection")
        .properties(ttlField)
        .build());

// Step 4 (optional) — backfill expiration timestamps for historical entities
Gson gson = new Gson();
Random rng = new Random();
List<Float> vector = new ArrayList<>();
for (int i = 0; i < 128; i++) vector.add(rng.nextFloat());

JsonObject row = new JsonObject();
row.addProperty("id", 1);
row.add("vector", gson.toJsonTree(vector));
row.addProperty("expire_at", "2026-12-31T00:00:00Z");

client.upsert(UpsertReq.builder()
        .collectionName("my_collection")
        .data(Collections.singletonList(row))
        .build());
// nodejs
// go
# restful

Les entités historiques pour lesquelles vous ne remplissez pas expire_at auront NULL dans cette colonne, ce qui signifie qu'elles n'expirent jamais. Ne remplissez que les lignes qui doivent avoir une durée de vie limitée.

Passer d'un TTL au niveau de l'entité à un TTL au niveau de la collection

Pour aller dans l'autre sens, abandonnez ttl_field et définissez collection.ttl.seconds:

from pymilvus import MilvusClient

client = MilvusClient(uri="http://localhost:19530")

# Assumes "my_collection" already exists with `ttl_field` set.
client.drop_collection_properties(
    collection_name="my_collection",
    property_keys=["ttl_field"],
)
client.alter_collection_properties(
    collection_name="my_collection",
    properties={"collection.ttl.seconds": 1209600},  # 14 days
)
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;

import io.milvus.v2.client.ConnectConfig;
import io.milvus.v2.client.MilvusClientV2;
import io.milvus.v2.service.collection.request.AlterCollectionPropertiesReq;
import io.milvus.v2.service.collection.request.DropCollectionPropertiesReq;

MilvusClientV2 client = new MilvusClientV2(ConnectConfig.builder()
        .uri("http://localhost:19530")
        .build());

// Assumes "my_collection" already exists with `ttl_field` set.
client.dropCollectionProperties(DropCollectionPropertiesReq.builder()
        .collectionName("my_collection")
        .propertyKeys(Collections.singletonList("ttl_field"))
        .build());

Map<String, String> properties = new HashMap<>();
properties.put("collection.ttl.seconds", "1209600"); // 14 days
client.alterCollectionProperties(AlterCollectionPropertiesReq.builder()
        .collectionName("my_collection")
        .properties(properties)
        .build());
// nodejs
// go
# restful

FAQ

Quand les données expirent-elles en raison des paramètres TTL ?

Actuellement, les données expirent en fonction du moment où elles ont été insérées ou réinsérées. Les données expirées ne seront pas affichées dans les résultats de recherche. Pour plus de détails, voir Exemples.

Quand les données expirées seront-elles physiquement supprimées ?

Une fois que les données expirent, elles ne seront plus incluses dans les résultats de recherche. Toutefois, elles ne seront physiquement supprimées qu'après le compactage ultérieur du système, conformément aux politiques de compactage de votre cluster.