본문 바로가기
WIKI 기술 지식 베이스

TIMESTAMPTZ 필드 (TIMESTAMPTZ Field)

원문 보기 위키 갱신

Milvus 2.6.6+와 호환돼요.

전자상거래 시스템, 협업 도구, 분산 로깅처럼 지역을 넘나들며 시간을 추적하는 애플리케이션은 시간대가 포함된 타임스탬프를 정밀하게 처리해야 해요. Milvus의 TIMESTAMPTZ 데이터 타입은 타임스탬프와 관련 시간대를 함께 저장해 이 기능을 제공해요.

출처: Milvus 문서

본문

TIMESTAMPTZ 필드란? (What is a TIMESTAMPTZ field?)

TIMESTAMPTZ 필드는 Milvus의 스키마 정의 데이터 타입(DataType.TIMESTAMPTZ)으로, 시간대 인식 입력을 처리하고 모든 시점을 내부적으로 UTC 절대 시간으로 저장해요.

  • 허용되는 입력 형식: 시간대 오프셋이 있는 ISO 8601 문자열(예: "2025-05-01T23:59:59+08:00"은 2025년 5월 1일 오후 11시 59분 59초(UTC+08:00)를 나타냄).
  • 내부 저장: 모든 TIMESTAMPTZ 값은 정규화되어 Coordinated Universal Time(UTC)으로 저장돼요.
  • 비교와 필터링: 모든 필터링과 정렬 연산은 UTC로 수행되어 서로 다른 시간대에서도 일관되고 예측 가능한 결과를 보장해요.
  • TIMESTAMPTZ 필드에 nullable=True를 설정해 누락 값을 허용할 수 있어요.
  • ISO 8601 형식의 default_value 속성을 사용해 기본 타임스탬프 값을 지정할 수 있어요.

자세한 내용은 Nullable & Default를 참고하세요.

기본 연산 (Basic operations)

TIMESTAMPTZ 필드를 사용하는 기본 워크플로우는 Milvus의 다른 스칼라 필드와 같아요. 필드 정의 → 데이터 삽입 → 쿼리/필터링.

1단계: TIMESTAMPTZ 필드 정의 (Step 1)

TIMESTAMPTZ 필드를 사용하려면 컬렉션을 만들 때 컬렉션 스키마에 명시적으로 정의해야 해요. 다음 예시는 DataType.TIMESTAMPTZ 타입의 tsz 필드가 있는 컬렉션을 만드는 방법을 보여 줘요.

import time
from pymilvus import MilvusClient, DataType
import datetime
import pytz

server_address = "http://localhost:19530"
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.")

2단계: 데이터 삽입 (Step 2)

시간대 오프셋이 있는 ISO 8601 문자열을 포함하는 엔티티를 삽입해요.

아래 예시는 8,193행의 샘플 데이터를 컬렉션에 삽입해요. 각 행은 다음을 포함해요.

  • 고유 ID.
  • 시간대 인식 타임스탬프(상하이 시간).
  • 간단한 4차원 벡터.
data_size = 8193

# 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.")

3단계: 필터링 연산 (Step 3)

TIMESTAMPTZ는 스칼라 비교, 간격 산술, 시간 구성 요소 추출을 지원해요.

TIMESTAMPTZ 필드에서 필터링 연산을 수행하기 전에 다음을 확인하세요.

  • 각 벡터 필드에 인덱스를 만들었는지.
  • 컬렉션이 메모리에 로드되었는지.

예시 코드 보기

# 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.")
타임스탬프 필터링으로 쿼리 (Query with timestamp filtering)

==, !=, <, > 같은 산술 연산자를 사용해요. Milvus에서 사용할 수 있는 산술 연산자의 전체 목록은 Arithmetic operators를 참고하세요.

아래 예시는 2025-01-03T00:00:00+08:00과 같지 않은 타임스탬프(tsz)를 가진 엔티티를 필터링해요.

# 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'}"]

위 예시에서,

  • tsz는 스키마에 정의된 TIMESTAMPTZ 필드 이름이에요.
  • ISO '2025-01-03T00:00:00+08:00'은 시간대 오프셋을 포함한 ISO 8601 형식의 타임스탬프 리터럴이에요.
  • !=는 필드 값을 해당 리터럴과 비교해요. 지원되는 다른 연산자에는 ==, <, >=가 있어요.
간격 연산 (Interval operations)

ISO 8601 지속 형식의 INTERVAL 값으로 TIMESTAMPTZ 필드에 산술을 수행할 수 있어요. 이를 통해 데이터를 필터링할 때 타임스탬프에서 일(days), 시(hours), 분(minutes) 같은 기간을 더하거나 뺄 수 있어요.

예를 들어 다음 쿼리는 타임스탬프(tsz)에 0일을 더한 값이 2025-01-03T00:00:00+08:00과 같지 않은 엔티티를 필터링해요.

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'}"]

INTERVAL 값은 ISO 8601 지속 구문을 따르요. 예를 들어:

  • P1D → 1일.
  • PT3H → 3시간.
  • P2DT6H → 2일 6시간.

필터 표현식에서 직접 INTERVAL 산술을 사용할 수 있어요. 예를 들어:

  • tsz + INTERVAL 'P3D' → 3일 더하기.
  • tsz - INTERVAL 'PT2H' → 2시간 빼기.
타임스탬프 필터링으로 검색 (Search with timestamp filtering)

TIMESTAMPTZ 필터링을 벡터 유사도 검색과 결합해 시간과 유사성 모두로 결과를 좁힐 수 있어요.

# 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}}]]

컬렉션에 벡터 필드가 두 개 이상 있으면 타임스탬프 필터링을 사용해 하이브리드 검색 연산을 수행할 수 있어요. 자세한 내용은 Multi-Vector Hybrid Search를 참고하세요.

고급 사용 (Advanced usage)

고급 사용을 위해 서로 다른 수준(예: 데이터베이스, 컬렉션, 쿼리)에서 시간대를 관리하거나 인덱스를 사용해 TIMESTAMPTZ 필드의 쿼리를 가속화할 수 있어요.

서로 다른 수준에서 시간대 관리 (Manage time zones at different levels)

TIMESTAMPTZ 필드의 시간대를 데이터베이스, 컬렉션, 또는 쿼리/검색 수준에서 제어할 수 있어요.

수준 파라미터 범위 우선순위
데이터베이스 timezone 데이터베이스의 모든 컬렉션에 대한 기본값 가장 낮음
컬렉션 timezone 해당 컬렉션의 데이터베이스 기본 시간대 설정을 재정의 중간
쿼리/검색/하이브리드 검색 timezone 한 특정 연산에 대한 임시 재정의 가장 높음

단계별 지침과 코드 샘플은 전용 페이지를 참고하세요.

쿼리 가속화 (Accelerate queries)

기본적으로 인덱스가 없는 TIMESTAMPTZ 필드의 쿼리는 모든 행에 대한 전체 스캔을 수행하며, 대규모 데이터셋에서는 느릴 수 있어요. 타임스탬프 쿼리를 가속화하려면 TIMESTAMPTZ 필드에 STL_SORT 인덱스를 만들어요.

자세한 내용은 STL_SORT를 참고하세요.

더 알아보기 (Learn more)