TIMESTAMPTZ フィールド
地域をまたがる時間を追跡するアプリケーション(例:eコマースシステム、コラボレーションツール、分散ログ)では、タイムゾーン付きタイムスタンプの正確な処理が必要です。Zilliz Cloud の TIMESTAMPTZ データ型は、タイムゾーンに関連付けられたタイムスタンプを保存することで、この機能を提供します。
TIMESTAMPTZ フィールドとは何ですか?
TIMESTAMPTZ フィールドは、Zilliz Cloud でのスキーマ定義データ型(データType.TIMESTAMPTZ)であり、タイムゾーン対応の入力を処理し、すべての時点を内部的に UTC 絶対時間として保存します:
-
許容される入力形式:
TIMESTAMPTZフィールドは ISO 8601 互換のタイムスタンプ文字列を受け入れます。以下を含みます:-
"2024-12-31 22:00:00" -
"2024-12-31T22:00:00" -
"2024-12-31T22:00:00+08:00" -
"2024-12-31T22:00:00Z"
-
-
タイムスタンプのパースルール: タイムスタンプの解釈方法は、入力文字列がタイムゾーンを明示的に指定しているかどうかによって異なります:
-
入力にタイムゾーンオフセットが含まれている場合(例:+08:00 または Z)、絶対時点として扱われます。
-
入力にタイムゾーンオフセットが含まれていない場合、コレクションの設定されたタイムゾーンを使用して解釈されます。例えば、コレクションのタイムゾーンが Asia/Shanghai の場合:
-
"2024-12-31 22:00:00"は 2024-12-31T22:00:00+08:00 として解釈されます -
"2024-12-31T22:00:00"は 2024-12-31T22:00:00Z として解釈され、これは 2025-01-01T06:00:00+08:00 に相当します
-
-
-
内部ストレージ: すべての
TIMESTAMPTZ値は正規化され、協定世界時(UTC)で保存されます。 -
比較とフィルタリング: TIMESTAMPTZ フィールドに対するすべての比較、フィルタリング、および順序付け操作は、UTC 正規化された値に対して実行されるため、異なるタイムゾーン間で一貫した動作が保証されます。
TIMESTAMPTZフィールドに対してnullable=Trueを設定することで、欠損値を許容できます。default_value属性を使用して、ISO 8601 形式でデフォルトのタイムスタンプ値を指定できます。
詳細については、NULL許容 & デフォルト を参照してください。
基本操作
TIMESTAMPTZ フィールドを使用する基本的なワークフローは、Zilliz Cloud の他のスカラーフィールドと同様です:フィールドを定義する → データを挿入する → クエリ/フィルタリングする。
ステップ 1: TIMESTAMPTZ フィールドを定義する
TIMESTAMPTZ フィールドを使用するには、コレクションの作成時にコレクションスキーマで明示的に定義します。以下の例は、データType.TIMESTAMPTZ 型の tsz フィールドを持つコレクションを作成する方法を示しています。
- Python
- Java
- NodeJS
- Go
- cURL
import time
from pymilvus import MilvusClient, DataType
import datetime
import pytz
server_address = "YOUR_CLUSTER_ENDPOINT"
collection_name = "timestamptz_test123"
client = MilvusClient(uri=server_address)
if client.has_collection(collection_name):
client.drop_collection(collection_name)
schema = client.create_schema()
# Add a primary key field
schema.add_field("id", DataType.INT64, is_primary=True)
# Add a TIMESTAMPTZ field that allows null values
schema.add_field("tsz", DataType.TIMESTAMPTZ, nullable=True)
# Add a vector field
schema.add_field("vec", DataType.FLOAT_VECTOR, dim=4)
client.create_collection(collection_name, schema=schema, consistency_level="Session")
print(f"Collection '{collection_name}' with a TimestampTz field created successfully.")
import io.milvus.v2.common.DataType;
import io.milvus.v2.client.ConnectConfig;
import io.milvus.v2.client.MilvusClientV2;
import io.milvus.v2.service.collection.request.AddFieldReq;
import io.milvus.v2.service.collection.request.CreateCollectionReq;
String CLUSTER_ENDPOINT = "YOUR_CLUSTER_ENDPOINT";
String TOKEN = "YOUR_CLUSTER_TOKEN";
// 1. Connect to Milvus server
ConnectConfig connectConfig = ConnectConfig.builder()
.uri(CLUSTER_ENDPOINT)
.token(TOKEN)
.build();
MilvusClientV2 client = new MilvusClientV2(connectConfig);
CreateCollectionReq.CollectionSchema schema = CreateCollectionReq.CollectionSchema.builder()
.build();
schema.addField(AddFieldReq.builder()
.fieldName("id")
.dataType(DataType.Int64)
.isPrimaryKey(true)
.build());
schema.addField(AddFieldReq.builder()
.fieldName("tsz")
.dataType(DataType.Timestamptz)
.isNullable(true)
.build());
schema.addField(AddFieldReq.builder()
.fieldName("vec")
.dataType(DataType.FloatVector)
.dimension(4)
.build());
String collectionName = "timestamptz_test123";
CreateCollectionReq requestCreate = CreateCollectionReq.builder()
.collectionName(collectionName)
.collectionSchema(schema)
.consistencyLevel(ConsistencyLevel.SESSION)
.build();
client.createCollection(requestCreate);
const { MilvusClient, DataType } = require('@zilliz/milvus2-sdk-node');
const serverAddress = 'YOUR_CLUSTER_ENDPOINT';
const collectionName = 'timestamptz_test123';
const client = new MilvusClient({
address: serverAddress,
});
await client.createCollection({
collection_name: collectionName,
fields: [
{
name: 'id',
data_type: DataType.Int64,
is_primary_key: true,
},
{
name: 'tsz',
data_type: DataType.TimestampTZ,
nullable: true,
},
{
name: 'vec',
data_type: DataType.FloatVector,
dim: 4,
},
]
});
// go
curl --request POST \
--url YOUR_CLUSTER_ENDPOINT/v2/vectordb/collections/create \
--header 'Authorization: Bearer YOUR_CLUSTER_TOKEN' \
--header 'Content-Type: application/json' \
--data '{
"collectionName": "timestamptz_test123",
"schema": {
"autoId": false,
"fields": [
{ "fieldName": "id", "dataType": "Int64", "isPrimary": true },
{ "fieldName": "tsz", "dataType": "Timestamptz", "nullable": true },
{ "fieldName": "vec", "dataType": "FloatVector", "elementTypeParams": { "dim": "4" } }
]
},
"indexParams": [
{
"fieldName": "vec",
"indexName": "vector_index",
"metricType": "L2",
"indexConfig": { "index_type": "AUTOINDEX" }
}
],
"consistencyLevel": "Session"
}'
Step 2: Insert data
タイムゾーンオフセット付きの ISO 8601 文字列を含むエンティティを挿入します。
以下の例では、サンプルデータの 8,193 行をコレクションに挿入します。各行には以下が含まれます:
-
ユニークな ID
-
タイムゾーン情報を含むタイムスタンプ(上海時間)
-
単純な 4 次元ベクトル
- Python
- Java
- NodeJS
- Go
- cURL
data_size = 10
# Get the Asia/Shanghai time zone using the pytz library
# You can use any valid IANA time zone identifier such as:
# "Asia/Tokyo", "America/New_York", "Europe/London", "UTC", etc.
# To view all available values:
# import pytz; print(pytz.all_timezones)
# Reference:
# IANA database – https://www.iana.org/time-zones
# Wikipedia – https://en.wikipedia.org/wiki/List_of_tz_database_time_zones
shanghai_tz = pytz.timezone("Asia/Shanghai")
data = [
{
"id": i + 1,
"tsz": shanghai_tz.localize(
datetime.datetime(2025, 1, 1, 0, 0, 0) + datetime.timedelta(days=i)
).isoformat(),
"vec": [float(i) / 10 for i in range(4)],
}
for i in range(data_size)
]
client.insert(collection_name, data)
print("Data inserted successfully.")
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
import io.milvus.v2.service.vector.request.InsertReq;
public static List<Float> generateFloatVector(int dimension) {
Random ran = new Random();
List<Float> vector = new ArrayList<>();
for (int i = 0; i < dimension; ++i) {
vector.add(ran.nextFloat());
}
return vector;
}
int rowCount = 10;
ZoneId zone = ZoneId.of("Asia/Shanghai");
DateTimeFormatter formatter = DateTimeFormatter.ISO_OFFSET_DATE_TIME;
List<JsonObject> rows = new ArrayList<>();
Gson gson = new Gson();
for (long i = 0L; i < rowCount; ++i) {
JsonObject row = new JsonObject();
row.addProperty("id", i);
row.add("vec", gson.toJsonTree(CommonUtils.generateFloatVector(4)));
LocalDateTime tt = LocalDateTime.of(2025, 1, 1, 0, 0, 0).plusDays(i);
ZonedDateTime zt = tt.atZone(zone);
row.addProperty("tsz", zt.format(formatter));
rows.add(row);
}
client.insert(InsertReq.builder()
.collectionName(collectionName)
.data(rows)
.build());
const dataSize = 10;
const formatDateWithTimezone = (year, month, day, hour, minute, second, timezoneOffset = '+08:00') => {
const monthStr = String(month).padStart(2, '0');
const dayStr = String(day).padStart(2, '0');
const hourStr = String(hour).padStart(2, '0');
const minuteStr = String(minute).padStart(2, '0');
const secondStr = String(second).padStart(2, '0');
return \`${year}-${monthStr}-${dayStr}T${hourStr}:${minuteStr}:${secondStr}${timezoneOffset}\`;
};
const data = [];
for (let i = 0; i < dataSize; i++) {
const baseDate = new Date(2025, 0, 1 + i, 0, 0, 0);
const year = baseDate.getFullYear();
const month = baseDate.getMonth() + 1;
const day = baseDate.getDate();
const isoString = formatDateWithTimezone(year, month, day, 0, 0, 0, '+08:00');
data.push({
id: i + 1,
tsz: isoString,
vec: Array.from({ length: 4 }, (_, j) => i / 10),
});
}
await client.insert({
collection_name: collectionName,
data: data,
});
// go
curl --request POST \ --url YOUR_CLUSTER_ENDPOINT/v2/vectordb/entities/insert \ --header 'Authorization: Bearer YOUR_CLUSTER_TOKEN' \ --header 'Content-Type: application/json' \ --data '{ "collectionName": "timestamptz_test123", "data": [ { "id": 1, "tsz": "2026-01-14T19:50:00Z", "vec": [0.1, 0.2, 0.3, 0.4] }, { "id": 2, "tsz": "2026-01-14T12:00:00+08:00", "vec": [0.5, 0.6, 0.7, 0.8] }, { "id": 3, "vec": [0.9, 0.0, 0.1, 0.2] } ] }'
ステップ 3: フィルタリング操作
TIMESTAMPTZ はスカラー比較、間隔演算、および時間コンポーネントの抽出をサポートしています。
TIMESTAMPTZ フィールドに対してフィルタリング操作を実行する前に、以下の点を確認してください。
-
各ベクターフィールドにインデックスを作成済みであること。
-
コレクションがメモリにロード済みであること。
コード例を表示
- Python
- Java
- NodeJS
- Go
- cURL
# Create index on vector field
index_params = client.prepare_index_params()
index_params.add_index(
field_name="vec",
index_type="AUTOINDEX",
index_name="vec_index",
metric_type="COSINE"
)
client.create_index(collection_name, index_params)
print("Index created successfully.")
# Load the collection
client.load_collection(collection_name)
print(f"Collection '{collection_name}' loaded successfully.")
import io.milvus.v2.common.IndexParam;
import io.milvus.v2.service.index.request.CreateIndexReq;
List<IndexParam> indexes = new ArrayList<>();
indexes.add(IndexParam.builder()
.fieldName("vec")
.indexName("vec_index")
.indexType(IndexParam.IndexType.AUTOINDEX)
.metricType(IndexParam.MetricType.COSINE)
.build());
client.createIndex(CreateIndexReq.builder()
.collectionName(collectionName)
.indexParams(indexes)
.build());
// nodejs
await client.createIndex({
collection_name: collection_name,
field_name: "vec",
index_type: "AUTOINDEX",
index_name: "vec_index",
metric_type: "COSINE"
});
await client.loadCollection({
collection_name: collection_name,
});
// go
curl --request POST \ --url YOUR_CLUSTER_ENDPOINT/v2/vectordb/collections/load \ --header 'Authorization: Bearer YOUR_CLUSTER_TOKEN' \ --header 'Content-Type: application/json' \ --data '{ "collectionName": "timestamptz_test123" }'
タイムスタンプフィルタリングを使用したクエリ
==、!=、<、>、<=、>= などの算術演算子を使用します。Zilliz Cloud で使用可能な算術演算子の完全なリストについては、算術演算子 を参照してください。
連鎖した範囲式(例: lower_bound < tsz < upper_bound)はサポートされていません。
代わりに論理積を使用してください: tsz > lower_bound AND tsz < upper_bound。
以下の例では、タイムスタンプ(tsz)が 2025-01-03T00:00:00+08:00 と等しくないエンティティをフィルタリングします:
- Python
- Java
- NodeJS
- Go
- cURL
# Query for entities where tsz is not equal to '2025-01-03T00:00:00+08:00'
expr = "tsz != ISO '2025-01-03T00:00:00+08:00'"
results = client.query(
collection_name=collection_name,
filter=expr,
output_fields=["id", "tsz"],
limit=10
)
print("Query result: ", results)
# Expected output:
# Query result: data: ["{'id': 1, 'tsz': '2024-12-31T16:00:00Z'}", "{'id': 2, 'tsz': '2025-01-01T16:00:00Z'}", "{'id': 4, 'tsz': '2025-01-03T16:00:00Z'}", "{'id': 5, 'tsz': '2025-01-04T16:00:00Z'}", "{'id': 6, 'tsz': '2025-01-05T16:00:00Z'}", "{'id': 7, 'tsz': '2025-01-06T16:00:00Z'}", "{'id': 8, 'tsz': '2025-01-07T16:00:00Z'}", "{'id': 9, 'tsz': '2025-01-08T16:00:00Z'}", "{'id': 10, 'tsz': '2025-01-09T16:00:00Z'}", "{'id': 11, 'tsz': '2025-01-10T16:00:00Z'}"]
import io.milvus.v2.service.vector.request.QueryReq;
import io.milvus.v2.service.vector.response.QueryResp;
String filter = "tsz != ISO '2025-01-03T00:00:00+08:00'";
QueryResp queryRet = client.query(QueryReq.builder()
.collectionName(collectionName)
.filter(filter)
.outputFields(Arrays.asList("id", "tsz"))
.limit(10)
.build());
List<QueryResp.QueryResult> records = queryRet.getQueryResults();
for (QueryResp.QueryResult record : records) {
System.out.println(record.getEntity());
}
const expr = "tsz != ISO '2025-01-03T00:00:00+08:00'"
const results = await client.query({
collection_name,
filter: expr,
output_fields: ["id", "tsz"],
limit: 10
});
console.log(results);
// go
curl --request POST \
--url YOUR_CLUSTER_ENDPOINT/v2/vectordb/entities/query \
--header 'Authorization: Bearer YOUR_CLUSTER_TOKEN' \
--header 'Content-Type: application/json' \
--data '{
"collectionName": "timestamptz_test123",
"filter": "tsz != ISO '\''2025-01-03T00:00:00+08:00'\''",
"outputFields": ["id", "tsz"],
"limit": 10
}'
上記の例では、
-
tszはスキーマで定義されたTIMESTAMPTZフィールド名です。 -
ISO '2025-01-03T00:00:00+08:00'は ISO 8601 形式のタイムスタンプリテラルで、タイムゾーンオフセットを含みます。 -
!=はフィールド値とそのリテラルを比較します。他にサポートされている演算子には==、<、<=、>、>=があります。
Interval operations
ISO 8601 duration format の INTERVAL 値を使用して、TIMESTAMPTZ フィールドに対する演算を実行できます。これにより、データをフィルタリングする際に、タイムスタンプに日、時間、分などの期間を加算または減算できます。
たとえば、次のクエリは、タイムスタンプ (tsz) にゼロ日を加算した値が 2025-01-03T00:00:00+08:00 と等しくないエンティティをフィルタリングします。
- Python
- Java
- NodeJS
- Go
- cURL
expr = "tsz + INTERVAL 'P0D' != ISO '2025-01-03T00:00:00+08:00'"
results = client.query(
collection_name,
filter=expr,
output_fields=["id", "tsz"],
limit=10
)
print("Query result: ", results)
# Expected output:
# Query result: data: ["{'id': 1, 'tsz': '2024-12-31T16:00:00Z'}", "{'id': 2, 'tsz': '2025-01-01T16:00:00Z'}", "{'id': 4, 'tsz': '2025-01-03T16:00:00Z'}", "{'id': 5, 'tsz': '2025-01-04T16:00:00Z'}", "{'id': 6, 'tsz': '2025-01-05T16:00:00Z'}", "{'id': 7, 'tsz': '2025-01-06T16:00:00Z'}", "{'id': 8, 'tsz': '2025-01-07T16:00:00Z'}", "{'id': 9, 'tsz': '2025-01-08T16:00:00Z'}", "{'id': 10, 'tsz': '2025-01-09T16:00:00Z'}", "{'id': 11, 'tsz': '2025-01-10T16:00:00Z'}"]
String filter = "tsz + INTERVAL 'P0D' != ISO '2025-01-03T00:00:00+08:00'";
QueryResp queryRet = client.query(QueryReq.builder()
.collectionName(collectionName)
.filter(filter)
.outputFields(Arrays.asList("id", "tsz"))
.limit(10)
.build());
List<QueryResp.QueryResult> records = queryRet.getQueryResults();
for (QueryResp.QueryResult record : records) {
System.out.println(record.getEntity());
}
const expr = "tsz + INTERVAL 'P0D' != ISO '2025-01-03T00:00:00+08:00'";
const results = await client.query({
collection_name,
filter: expr,
output_fields: ["id", "tsz"],
limit: 10
});
console.log(results);
// go
curl --request POST \ --url YOUR_CLUSTER_ENDPOINT/v2/vectordb/entities/query \ --header 'Authorization: Bearer YOUR_CLUSTER_TOKEN' \ --header 'Content-Type: application/json' \ --data '{ "collectionName": "timestamptz_test123", "filter": "tsz + INTERVAL '\''P0D'\'' != ISO '\''2025-01-03T00:00:00+08:00'\''", "outputFields": ["id", "tsz"], "limit": 10 }'
INTERVAL 値は ISO 8601 duration 構文 に従います。例:
P1D→ 1 日PT3H→ 3 時間P2DT6H→ 2 日と 6 時間
フィルター式内で直接 INTERVAL 演算を使用できます。例:
tsz + INTERVAL 'P3D'→ 3 日を加算tsz - INTERVAL 'PT2H'→ 2 時間を減算
タイムスタンプによるフィルタリングを伴う検索
TIMESTAMPTZ フィルタリングとベクトル類似性検索を組み合わせて、時間と類似性の両方で結果を絞り込むことができます。
- Python
- Java
- NodeJS
- Go
- cURL
# Define a time-based filter expression
filter = "tsz > ISO '2025-01-05T00:00:00+08:00'"
res = client.search(
collection_name=collection_name, # Collection name
data=[[0.1, 0.2, 0.3, 0.4]], # Query vector (must match collection's vector dim)
limit=5, # Max. number of results to return
filter=filter, # Filter expression using TIMESTAMPTZ
output_fields=["id", "tsz"], # Fields to include in the search results
)
print("Search result: ", res)
# Expected output:
# Search result: data: [[{'id': 10, 'distance': 0.9759000539779663, 'entity': {'tsz': '2025-01-09T16:00:00Z', 'id': 10}}, {'id': 9, 'distance': 0.9759000539779663, 'entity': {'tsz': '2025-01-08T16:00:00Z', 'id': 9}}, {'id': 8, 'distance': 0.9759000539779663, 'entity': {'tsz': '2025-01-07T16:00:00Z', 'id': 8}}, {'id': 7, 'distance': 0.9759000539779663, 'entity': {'tsz': '2025-01-06T16:00:00Z', 'id': 7}}, {'id': 6, 'distance': 0.9759000539779663, 'entity': {'tsz': '2025-01-05T16:00:00Z', 'id': 6}}]]
import io.milvus.v2.service.vector.request.SearchReq;
import io.milvus.v2.service.vector.response.SearchResp;
String filter = "tsz > ISO '2025-01-05T00:00:00+08:00'";
SearchResp searchR = client.search(SearchReq.builder()
.collectionName(collectionName)
.data(Collections.singletonList(new FloatVec(new float[]{0.1f, 0.2f, 0.3f, 0.4f})))
.limit(5)
.filter(filter)
.outputFields(Arrays.asList("id", "tsz"))
.build());
List<List<SearchResp.SearchResult>> searchResults = searchR.getSearchResults();
for (List<SearchResp.SearchResult> results : searchResults) {
for (SearchResp.SearchResult result : results) {
System.out.printf("ID: %d, Score: %f, %s\n", (long) result.getId(), result.getScore(), result.getEntity().toString());
}
}
const expr = "tsz > ISO '2025-01-05T00:00:00+08:00'";
const results = await client.search({
collection_name,
data=[[0.1, 0.2, 0.3, 0.4]], // Query vector (must match collection's vector dim)
filter: expr,
output_fields: ["id", "tsz"],
limit: 5
});
console.log(results);
// go
curl --request POST \ --url YOUR_CLUSTER_ENDPOINT/v2/vectordb/entities/search \ --header 'Authorization: Bearer YOUR_CLUSTER_TOKEN' \ --header 'Content-Type: application/json' \ --data '{ "collectionName": "timestamptz_test123", "data": [[0.1, 0.2, 0.3, 0.4]], "limit": 5, "filter": "tsz > ISO '\''2025-01-05T00:00:00+08:00'\''", "outputFields": ["id", "tsz"] }'
コレクションに2つ以上のベクトルフィールドがある場合、タイムスタンプフィルタリングを使用したハイブリッド検索操作を実行できます。詳細については、マルチベクトルハイブリッド検索を参照してください。
高度な使用方法
高度な使用方法として、異なるレベル(データベース、コレクション、クエリなど)でタイムゾーンを管理したり、TIMESTAMPTZ フィールドにインデックスを作成してクエリの高速化を行うことができます。
異なるレベルでのタイムゾーン管理
コレクションレベルまたはクエリ/検索レベルで TIMESTAMPTZ フィールドのタイムゾーンを制御できます。
レベル | パラメータ | スコープ | 優先度 |
|---|---|---|---|
コレクション |
| そのコレクションのデータベースデフォルトのタイムゾーン設定を上書き | 中 |
クエリ/検索/ハイブリッド検索 |
| 1回の特定の操作に対する一時的な上書き | 最高 |
ステップバイステップの手順とコードサンプルについては、専用のページを参照してください:
クエリの高速化
デフォルトでは、インデックスのない TIMESTAMPTZ フィールドに対するクエリは、すべての行をフルスキャンして実行されるため、大規模なデータセットでは遅くなる可能性があります。タイムスタンプクエリを高速化するには、TIMESTAMPTZ フィールドに AUTOINDEX インデックスを作成してください。
詳細については、スカラーフィールドのインデックス作成を参照してください。