Skip to main content
Core.Today
가격

Databases

OpenSearch 기반의 고성능 문서 데이터베이스입니다. 스키마 정의, CRUD, 전문 검색, 벡터 검색을 지원합니다.

주요 기능

  • 스키마 정의: 필드 타입, 인덱싱 옵션, 벡터 필드 설정
  • CRUD 작업: 단일/대량 문서 생성, 조회, 수정, 삭제
  • 전문 검색: OpenSearch Query DSL 지원 (match, term, bool, range)
  • 벡터 검색: k-NN 기반 시맨틱 검색 (Pro 플랜 이상)
  • 집계: terms, date_histogram, 통계 집계

크레딧 비용

데이터베이스 작업은 유형에 따라 크레딧이 차감됩니다. 작업 실패 시 크레딧은 자동 환불됩니다.

작업엔드포인트크레딧
문서 생성POST /documents0.1
문서 조회GET /documents/{id}0.05
문서 수정PUT, PATCH /documents/{id}0.1
문서 삭제DELETE /documents/{id}0.05
대량 생성POST /documents/_bulk5.0 / 100건
대량 삭제POST /documents/_bulk_delete0.05 / 건
대량 수정POST /documents/_bulk_update0.1 / 건
전문 검색POST /search0.5
집계POST /aggregate1.0
벡터 검색POST /search/vector2.0

무료: 데이터베이스 생성/삭제, 목록 조회, 스키마 변경, 통계 조회, 문서 목록 조회, 문서 수 조회(_count)는 크레딧이 차감되지 않습니다.

플랜별 제한

데이터베이스 리소스 한도는 구독 플랜에 따라 다릅니다. 표의 한도는 공용 클러스터 기준이며, 그 이상은 전용 노드로 제공합니다(문의). 클러스터 여유가 부족하면 쓰기가 일시적으로 503 database_capacity로 거부될 수 있습니다.

항목FreeProTeamEnterprise
최대 데이터베이스 수1520100
DB당 최대 문서 수10,000500,0005,000,00020,000,000
최대 스토리지100 MB2 GB10 GB50 GB
최대 문서 크기100 KB1 MB10 MB25 MB
벌크 작업 최대 건수1001,00010,000100,000
벡터 검색-1,536차원2,048차원4,096차원
분당 요청 수 (조회 / 검색 / 쓰기, 팀 단위)300 / 60 / 1201,500 / 300 / 6005,000 / 1,000 / 2,00015,000 / 3,000 / 6,000
1

데이터베이스 생성

스키마와 함께 새 데이터베이스를 생성합니다. 데이터베이스 이름은 팀 내에서 고유해야 합니다.

이름 규칙

  • 소문자 영문으로 시작해야 합니다
  • 소문자 영문, 숫자, 하이픈(-), 언더스코어(_)만 사용 가능
  • 3~50자 이내
  • aiapi_, db_system_, _ 로 시작할 수 없음 (예약 접두사)
curl -X POST https://api.core.today/v1/databases \
  -H "X-API-Key: cdt_your_api_key" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "products",
    "display_name": "Product Catalog",
    "description": "E-commerce product database",
    "schema_fields": {
      "title": {"type": "text", "index": true},
      "description": {"type": "text", "index": true},
      "price": {"type": "float"},
      "category": {"type": "keyword"},
      "tags": {"type": "keyword"},
      "in_stock": {"type": "boolean"},
      "created_at": {"type": "date"}
    }
  }'

응답 예시

{
  "database_uid": "db_abc123",
  "name": "products",
  "display_name": "Product Catalog",
  "description": "E-commerce product database",
  "team_id": "team_xyz",
  "schema_fields": {...},
  "document_count": 0,
  "created_at": "2024-12-26T10:00:00Z"
}

스키마 수정/삭제는 어떻게 하나요?

스키마를 잘못 정의했을 때 자주 받는 질문입니다. 검색 인덱스(OpenSearch 매핑) 특성상 이미 생성된 필드는 변경할 수 없으며, 가능한 작업과 불가능한 작업은 아래와 같습니다.

  • 새 필드 추가: 필드 추가 API(POST /databases/{uid}/fields)로 언제든 가능합니다 (무료). 필드를 빠뜨린 경우라면 삭제 없이 이 방법을 사용하세요.
  • 이름/설명 수정: 표시 이름(display_name)과 설명(description)은 PATCH /databases/{uid}로 수정할 수 있습니다 (무료).
  • 기존 필드 타입 변경: 이미 정의된 필드의 타입(예: text → keyword)은 변경할 수 없습니다.
  • 기존 필드 삭제: 스키마에서 개별 필드만 제거하는 것은 지원되지 않습니다.

스키마를 잘못 만들었다면: 삭제 후 재생성

  1. 보관할 문서가 있다면 검색 API 등으로 먼저 백업(export)하세요. 데이터베이스를 삭제하면 모든 문서가 함께 삭제됩니다.
  2. 올바른 스키마로 새 데이터베이스를 생성하고, 백업한 문서를 대량 생성 API(_bulk)로 다시 넣으세요.
  3. 새 데이터베이스가 정상 동작하는 것을 확인한 뒤 기존 데이터베이스를 삭제하세요. 삭제는 되돌릴 수 없습니다.

데이터베이스 삭제는 콘솔의 팀 워크스페이스 → Databases 페이지에서도 할 수 있습니다. 필드 추가와 이름/설명 수정은 현재 API로만 지원됩니다.

지원 필드 타입

타입설명예시
text전문 검색이 가능한 텍스트. 정렬·집계용 .keyword 서브필드가 자동으로 붙고(정렬/집계에 필드명을 그대로 쓰면 자동 변환), 형태소 분석기는 language(ko=nori · en · ja · zh) 또는 analyzer로 지정"Product description..."
keyword정확한 매칭용 문자열 (필터, 집계)"electronics"
integer정수42
long64비트 정수9223372036854775807
float32비트 부동 소수점19.99
double64비트 부동 소수점3.141592653589793
booleantrue/falsetrue
dateISO 8601 날짜"2024-12-26T10:00:00Z"
object중첩 객체 (인덱싱 on/off 가능){"key": "value"}
nested독립적으로 쿼리 가능한 객체 배열[{"name": "A"}, ...]
knn_vector벡터 검색용 임베딩 (Pro+)[0.1, 0.2, ...]
geo_point위치 {"lat", "lon"} — geo_distance 질의, where $near{"lat": 37.5, "lon": 127.0}
ipIPv4/IPv6 주소 — term 질의에 CIDR("10.0.0.0/8") 사용 가능"10.0.0.5"
date_range날짜 구간 {"gte", "lte"} — range 질의는 교차 여부로 매칭{"gte": "2026-01-01", "lte": "2026-01-31"}

언어(형태소 분석기) 설정

데이터베이스 생성 시 language를 주면 모든 text 필드의 기본 분석기가 됩니다(ko → nori). 필드별 language/analyzer가 있으면 그 값이 우선합니다. 기존 필드의 분석기는 바꿀 수 없고(재색인 필요) 이후 추가되는 필드부터 적용됩니다.

{
  "name": "articles",
  "language": "ko",
  "schema_fields": {
    "title":   { "type": "text" },
    "summary": { "type": "text", "language": "en" },
    "status":  { "type": "keyword" }
  }
}

동의어와 refresh 간격

`synonyms`는 Solr 형식의 검색 시점 규칙입니다 — "usa, united states"(동등) 또는 "phone => mobile"(단방향). 언어별(ko/en/ja/zh) 분석기를 통해 모든 text 필드에 적용되므로 문서를 다시 색인하지 않습니다. 관리형 클러스터는 인덱스를 닫을 수 없어 나중에 바꾸려면 POST /reindex(스키마 진화)를 거칩니다. `refresh_interval`(1s … 60s, 기본 1s)은 검색 반영 상한으로, 쓰기가 많은 DB에서 올리면 색인 처리량이 좋아지며 PATCH로 즉시 적용됩니다.

POST /v1/databases
{
  "name": "places",
  "language": "ko",
  "synonyms": ["휴대폰, 핸드폰, 스마트폰", "usa, united states", "phone => mobile"],
  "refresh_interval": "5s",
  "schema_fields": {
    "name": {"type": "text"},
    "loc":  {"type": "geo_point"},
    "ip":   {"type": "ip"},
    "open": {"type": "date_range"}
  }
}

PATCH /v1/databases/{database_uid}        {"refresh_interval": "30s"}
POST  /v1/databases/{database_uid}/reindex {"synonyms": ["usa, united states, u.s."]}
POST  /v1/databases/{database_uid}/search  {"where": {"loc": {"$near": {"lat": 37.5, "lon": 127.0, "distance": "5km"}}}}

벡터 필드 설정

벡터 검색을 위한 필드는 dimension과 space_type을 지정해야 합니다:

"embedding": {
  "type": "knn_vector",
  "dimension": 1536,
  "space_type": "cosinesimil"
}
2

문서 CRUD

자동 메타데이터 (_meta)

모든 문서에는 _meta 필드가 자동으로 추가됩니다:

  • _meta.created_at 문서 생성 시각 (ISO 8601)
  • _meta.updated_at 마지막 수정 시각 (ISO 8601)
  • _meta.created_by_api_key 생성에 사용된 API 키 UID

문서 생성

docs.databases.crud.createCode

생성 전용: 이미 존재하는 id로 POST하면 409(document_already_exists)를 반환하며 덮어쓰지 않습니다. 기존 문서를 바꾸려면 PUT/PATCH(필요 시 ?upsert=true)를 쓰세요. _bulk는 종전대로 같은 id를 덮어씁니다.

대량 문서 생성

curl -X POST https://api.core.today/v1/databases/{database_uid}/documents/_bulk \
  -H "X-API-Key: cdt_your_api_key" \
  -H "Content-Type: application/json" \
  -d '{
    "documents": [
      {"id": "product-001", "data": {"title": "Product 1", "price": 99.99}},
      {"id": "product-002", "data": {"title": "Product 2", "price": 149.99}},
      {"id": "product-003", "data": {"title": "Product 3", "price": 199.99}}
    ]
  }'

응답: 성공/실패 개수와 에러 상세 정보가 반환됩니다.

문서 조회

# 특정 문서 조회
curl https://api.core.today/v1/databases/{database_uid}/documents/{doc_id} \
  -H "X-API-Key: cdt_your_api_key"

# 특정 필드만 조회 (Field Projection)
curl "https://api.core.today/v1/databases/{database_uid}/documents/{doc_id}?_source=title,price" \
  -H "X-API-Key: cdt_your_api_key"

# 문서 목록 조회 (페이지네이션)
curl "https://api.core.today/v1/databases/{database_uid}/documents?size=20&from=0" \
  -H "X-API-Key: cdt_your_api_key"

# 문서 목록 - 특정 필드만 조회
curl "https://api.core.today/v1/databases/{database_uid}/documents?size=20&_source=title,price" \
  -H "X-API-Key: cdt_your_api_key"

_source: 쉼표로 구분된 필드 목록을 지정하면 해당 필드만 응답에 포함됩니다. 생략하면 모든 필드를 반환합니다.

문서 수정

docs.databases.crud.updateCode

업서트: PUT/PATCH는 문서가 없으면 404를 반환합니다. ?upsert=true를 붙이면 없을 때 생성하고 있을 때 수정합니다 — "먼저 PATCH, 404면 POST" 패턴을 대체하며, 생성/수정 여부는 응답의 result 필드로 구분합니다. _bulk_update도 ?upsert=true를 지원합니다.

일관성 모델

ID로 읽기·수정·삭제는 어떤 쓰기 직후에도 즉시 일관됩니다. 검색·목록·_count·집계는 쓰기 후 약 1초 이내에 반영됩니다(eventual). 모든 쓰기 엔드포인트는 ?refresh=false|true|wait_for 를 받으며 기본값은 false입니다. 쓰기 직후 반드시 검색해야 할 때만 refresh=true 를 쓰세요 — 해당 요청의 지연이 최대 약 1초 늘어납니다.

내구성과 복구

응답된 모든 쓰기는 먼저 S3에 저장되고(문서당 객체 하나, 조건부 PUT) 그 다음 색인됩니다. 검색 인덱스는 질의 레이어로, 언제든 S3에서 재구축할 수 있습니다(POST /reindex, 무중단). 일일 감사가 DB마다 S3와 인덱스의 문서 수를 대조해 어긋나면 운영팀에 알립니다. 삭제는 S3와 인덱스 모두에서 제거되며 휴지통이 없으니, 사본이 필요하면 먼저 POST /export 로 내보내세요.

문서 삭제

curl -X DELETE https://api.core.today/v1/databases/{database_uid}/documents/{doc_id} \
  -H "X-API-Key: cdt_your_api_key"

대량 삭제

curl -X POST https://api.core.today/v1/databases/{database_uid}/documents/_bulk_delete \
  -H "X-API-Key: cdt_your_api_key" \
  -H "Content-Type: application/json" \
  -d '{
    "ids": ["product-001", "product-002", "product-003"]
  }'

크레딧: 0.05 × 문서 수. 존재하지 않는 ID는 에러로 보고됩니다.

대량 수정

curl -X POST https://api.core.today/v1/databases/{database_uid}/documents/_bulk_update \
  -H "X-API-Key: cdt_your_api_key" \
  -H "Content-Type: application/json" \
  -d '{
    "documents": [
      {"id": "product-001", "data": {"price": 179.99, "in_stock": false}},
      {"id": "product-002", "data": {"price": 129.99}},
      {"id": "product-003", "data": {"category": "accessories"}}
    ]
  }'

크레딧: 0.1 × 문서 수. 부분 수정(PATCH)으로 동작하며 지정된 필드만 업데이트됩니다.

내보내기 / 가져오기 (API)

# Export everything to a gzip NDJSON file (async, free) — poll for download_url (valid 7 days)
curl -X POST https://api.core.today/v1/databases/{database_uid}/export -H "X-API-Key: cdt_your_api_key"
curl https://api.core.today/v1/databases/{database_uid}/export -H "X-API-Key: cdt_your_api_key"
# → {"export": {"status": "done", "docs": 1200, "download_url": "https://…", "expires_at": "…"}}

# Import from a URL (NDJSON lines {"id"?, "data"} or plain objects; JSON array; gzip ok; ≤50 MB)
curl -X POST https://api.core.today/v1/databases/{database_uid}/import \
  -H "X-API-Key: cdt_your_api_key" -H "Content-Type: application/json" \
  -d '{"url": "https://files.core.today/…/products.ndjson.gz", "mode": "upsert"}'
curl https://api.core.today/v1/databases/{database_uid}/import -H "X-API-Key: cdt_your_api_key"

동작·크레딧: 내보내기는 S3 원본 전체를 gzip NDJSON({id, data, _meta} 한 줄에 한 문서)으로 만들어 7일 유효한 서명 URL을 돌려주며 무료입니다(리셀러는 X-Customer-Id로 해당 회원분만). 가져오기는 URL을 서버가 받아(사설망 차단, 최대 50 MB) 파싱한 뒤 문서 수만큼 벌크 생성 요율(100건당 5cr)을 먼저 차감하고 백그라운드로 적재합니다. mode=upsert는 같은 id를 덮어쓰고, mode=create는 기존 id를 항목별 오류로 남깁니다. 진행 상황은 GET …/import 의 done/failed/errors(최대 100건)로 확인하세요. 한 DB에 동시에 한 작업만(409 job_already_running).

스키마 변경 · 재색인 (reindex)

# Rebuild with a new schema (types may change, fields may be dropped) and/or language
curl -X POST https://api.core.today/v1/databases/{database_uid}/reindex \
  -H "X-API-Key: cdt_your_api_key" -H "Content-Type: application/json" \
  -d '{"language": "ko", "schema_fields": {"title": {"type": "text"}, "price": {"type": "float"}}}'
# → 202 {"status": "started", "reindex": {"status": "running", "docs_done": 0, ...}}

# Poll until done
curl https://api.core.today/v1/databases/{database_uid}/reindex -H "X-API-Key: cdt_your_api_key"

동작: POST /fields는 필드 추가만 가능합니다. 타입 변경·필드 삭제·분석기(language) 변경은 S3 원본에서 새 인덱스를 만드는 재색인으로 처리합니다 — 비동기(202)이며 진행 중에도 조회·검색은 기존 인덱스로 계속 됩니다. 쓰기(생성·수정·삭제·벌크)는 완료까지 409 database_reindexing으로 거부되니 Retry-After 뒤 재시도하세요. 완료되면 레코드가 새 인덱스로 전환되고 옛 인덱스는 삭제됩니다. 실패하면 새 인덱스를 버리고 원래 상태로 복구되며 reindex.error에 원인이 남습니다. 무료.

낙관적 동시성 제어 (If-Match)

# Every read/write returns _meta.version (also as the ETag header on GET)
curl https://api.core.today/v1/databases/{database_uid}/documents/task-123 \
  -H "X-API-Key: cdt_your_api_key"
# → {"id": "task-123", "data": {...}, "_meta": {"version": "12.3", ...}}

# Write only if nobody changed it since — 412 precondition_failed otherwise
curl -X PATCH https://api.core.today/v1/databases/{database_uid}/documents/task-123 \
  -H "X-API-Key: cdt_your_api_key" -H "Content-Type: application/json" \
  -H 'If-Match: "12.3"' \
  -d '{"data": {"status": "done"}}'

동작: _meta.version은 저장되지 않는 응답 전용 토큰(<seq_no>.<primary_term>)이며 GET·검색 히트·쓰기 응답 모두에 실립니다. PUT/PATCH/DELETE에 If-Match로 되돌려 보내면 그 버전 이후 문서가 바뀌었을 때 412 precondition_failed로 거부되어(아무것도 쓰지 않음) 읽고-수정-쓰기 경합에서 마지막 쓰기가 앞선 변경을 덮어쓰는 것을 막습니다. 토큰 형식이 잘못되면 400 invalid_if_match(과금 전). upsert 생성과는 함께 쓸 수 없습니다.

변경 이벤트 웹훅

문서가 생성·수정·삭제되면 팀 웹훅으로 document.created / document.updated / document.deleted 이벤트가, 벌크·조건부·TTL 정리로 바뀌면 documents.bulk_created / bulk_updated / bulk_deleted(ids 최대 1,000개 + count, source 필드)가 전송됩니다. 콘솔 › Webhooks에서 구독하세요. 기존 웹훅과 같은 HMAC 서명·재시도·전송 이력이 적용되며, 폴링 대신 이 이벤트로 외부 시스템을 동기화할 수 있습니다.

{
  "event": "document.updated",
  "team_id": "…", "database_uid": "db_abc123",
  "doc_id": "task-123", "result": "updated",
  "source": "api",                 // api | by_query | ttl_sweep
  "timestamp": "2026-09-13T02:10:00Z"
}
// bulk: { "event": "documents.bulk_deleted", "count": 1500, "ids": [...1000], "ids_truncated": true }

문서 만료 (TTL)

# Expire in 1 hour (relative) — works on POST, PUT, PATCH and ?upsert=true
curl -X POST https://api.core.today/v1/databases/{database_uid}/documents \
  -H "X-API-Key: cdt_your_api_key" -H "Content-Type: application/json" \
  -d '{"id": "session-42", "data": {"state": "open"}, "ttl_seconds": 3600}'

# Absolute expiry (ISO 8601, UTC) / clear an expiry with null
curl -X PATCH https://api.core.today/v1/databases/{database_uid}/documents/session-42 \
  -H "X-API-Key: cdt_your_api_key" -H "Content-Type: application/json" \
  -d '{"data": {"state": "closed"}, "expires_at": "2026-12-31T00:00:00Z"}'

동작: expires_at(절대 시각) 또는 ttl_seconds(상대, 최소 60초)를 주면 _meta.expires_at에 저장되고, 매시간 도는 정리 작업이 만료된 문서를 삭제합니다(S3 원본까지 함께, 삭제 크레딧 없음). 만료 시각이 지나도 다음 정리 전까지는 조회·검색될 수 있으니 필요하면 _meta.expires_at으로 필터하세요. PATCH/PUT에 expires_at: null을 보내면 만료가 해제됩니다.

조건부 대량 삭제 / 수정 (_delete_by_query, _update_by_query)

# Delete every document matching a query (up to max_docs, default 1000)
curl -X POST https://api.core.today/v1/databases/{database_uid}/documents/_delete_by_query \
  -H "X-API-Key: cdt_your_api_key" \
  -H "Content-Type: application/json" \
  -d '{"query": {"range": {"created": {"lt": "2026-01-01"}}}, "max_docs": 500}'

# Merge fields into every matching document
curl -X POST https://api.core.today/v1/databases/{database_uid}/documents/_update_by_query \
  -H "X-API-Key: cdt_your_api_key" \
  -H "Content-Type: application/json" \
  -d '{"query": {"term": {"status": "queued"}}, "data": {"status": "cancelled"}}'

동작·크레딧: 쿼리로 대상 ID를 먼저 선택(검색 0.5cr)한 뒤 벌크 삭제(0.05×건)/벌크 수정(0.1×건)을 수행합니다 — S3 원본 동기화·리셀러 스코프·항목별 결과가 벌크 엔드포인트와 동일합니다. 한 번에 max_docs(기본 1000, 플랜 벌크 한도 이내)까지 처리하고 응답의 matched가 그보다 크면 반복 호출하세요. 크레딧이 부족하면 아무것도 삭제·수정하지 않고 402를 반환합니다.

문서 수 조회

# 전체 문서 수
curl https://api.core.today/v1/databases/{database_uid}/documents/_count \
  -H "X-API-Key: cdt_your_api_key"

# 조건부 문서 수 (query 파라미터로 JSON 전달)
curl "https://api.core.today/v1/databases/{database_uid}/documents/_count?q=%7B%22term%22%3A%7B%22category%22%3A%22electronics%22%7D%7D" \
  -H "X-API-Key: cdt_your_api_key"

무료: 크레딧이 차감되지 않습니다. 응답: {"count": 1234}

필드 제외 (_source_excludes)

응답에서 특정 필드를 제외할 수 있습니다. _source 와 함께 사용하면 includes/excludes로 변환됩니다.

docs.databases.crud.excludeCode
3

검색

OpenSearch Query DSL을 사용한 강력한 검색 기능을 제공합니다.

전문 검색 (Full-text)

curl -X POST https://api.core.today/v1/databases/{database_uid}/search \
  -H "X-API-Key: cdt_your_api_key" \
  -H "Content-Type: application/json" \
  -d '{
    "query": {
      "match": {
        "description": "wireless headphones"
      }
    },
    "size": 10,
    "from": 0,
    "_source": ["title", "price", "category"]
  }'

_source: 응답에 포함할 필드 목록입니다. 생략하면 모든 필드를 반환합니다.

단순 필터 (where)

대부분의 조회는 Query DSL이 필요 없습니다. `where`는 평범한 JSON 객체로, 서버가 필터(점수 없음)로 컴파일하며 `query`/`filter`와 함께 주면 AND로 결합됩니다. 검색·목록(`?where=<json>`)·벡터 검색·집계·by-query 엔드포인트 모두에서 동작합니다.

POST /v1/databases/{database_uid}/search
{
  "where": {
    "category": "electronics",
    "price": {"$gte": 100, "$lte": 300},
    "status": {"$in": ["active", "preorder"]},
    "title": {"$contains": "headphones"},
    "deleted_at": {"$exists": false},
    "$or": [{"brand": "sony"}, {"rating": {"$gte": 4.5}}]
  },
  "sort": [{"price": "asc"}],
  "size": 20
}

GET /v1/databases/{database_uid}/documents?where={"status":"active"}
POST /v1/databases/{database_uid}/documents/_delete_by_query  {"where": {"updated": {"$lt": "2026-01-01"}}}

연산자: 동등 `{"status": "active"}` · `$ne` · `$in`/`$nin` · `$gt`/`$gte`/`$lt`/`$lte` · `$exists` · `$prefix` · `$contains`(text 필드는 구절 일치, keyword는 부분 문자열) · `$match`(분석된 전문 검색) · `$and`/`$or`/`$not`. text 필드의 정확 비교는 `.keyword` 서브필드로 자동 변환됩니다. `null`은 "값 없음"입니다. 잘못된 필터는 과금 전에 400 `invalid_where`로 거부됩니다.

복합 검색 (Boolean Query)

{
  "query": {
    "bool": {
      "must": [
        {"match": {"description": "headphones"}}
      ],
      "filter": [
        {"term": {"category": "electronics"}},
        {"range": {"price": {"gte": 100, "lte": 300}}}
      ],
      "should": [
        {"term": {"in_stock": true}}
      ]
    }
  },
  "sort": [
    {"price": {"order": "asc"}}
  ],
  "highlight": {
    "fields": {"description": {}}
  }
}

Deep Pagination (search_after)

from + size 가 10,000을 초과하면 search_after를 사용해야 합니다. 이전 결과의 마지막 sort 값을 전달하여 다음 페이지를 조회합니다.

{
  "query": {"match_all": {}},
  "sort": [
    {"_meta.created_at": {"order": "desc"}},
    {"_id": {"order": "asc"}}
  ],
  "size": 100,
  "search_after": ["2025-01-01T00:00:00Z", "product-500"]
}

주의: search_after 사용 시 반드시 sort를 지정해야 하며, tie-breaker로 _id 를 포함하는 것을 권장합니다.

검색 응답 예시

{
  "total": 42,
  "hits": [
    {
      "id": "product-001",
      "score": 1.5,
      "data": {
        "title": "Wireless Headphones",
        "price": 199.99,
        ...
      },
      "highlight": {
        "description": ["High-quality <em>wireless</em> <em>headphones</em>..."]
      }
    }
  ]
}
4

벡터 검색 (k-NN)

Pro+

임베딩 벡터를 사용한 시맨틱 유사도 검색입니다. AI 모델로 생성한 텍스트/이미지 임베딩을 저장하고 검색할 수 있습니다.

벡터 필드가 있는 스키마

{
  "name": "articles",
  "schema_fields": {
    "title": {"type": "text"},
    "content": {"type": "text"},
    "embedding": {
      "type": "knn_vector",
      "dimension": 1536,
      "space_type": "cosinesimil"
    }
  }
}

벡터 검색 요청

curl -X POST https://api.core.today/v1/databases/{database_uid}/search/vector \
  -H "X-API-Key: cdt_your_api_key" \
  -H "Content-Type: application/json" \
  -d '{
    "field": "embedding",
    "vector": [0.1, 0.2, 0.3, ...],
    "k": 10,
    "filter": {
      "term": {"category": "technology"}
    },
    "_source": ["title", "content"]
  }'

자동 임베딩 (벡터를 직접 관리하지 않아도 됩니다)

knn_vector 필드에 `embed`를 선언하면 `source_field`를 담은 모든 쓰기에서 벡터가 자동 계산됩니다 — LLM API를 내 API 키로 호출하므로 직접 임베딩 호출과 같은 과금(text-embedding-3-small 1K 토큰당 약 0.04 크레딧)입니다. 검색은 `vector` 대신 `text`를 보내면 됩니다. 소스 필드를 건드리지 않는 patch는 임베딩 호출이 없고, 벡터를 직접 담아 보낸 문서는 그대로 저장됩니다.

// 1. schema: the vector is derived from "body"
"schema_fields": {
  "body": {"type": "text"},
  "vec":  {"type": "knn_vector", "dimension": 1536,
           "embed": {"source_field": "body", "model": "openai/text-embedding-3-small"}}
}

// 2. write documents with text only
POST /v1/databases/{database_uid}/documents
{"id": "faq-1", "data": {"body": "How do I reset my password?"}}

// 3. search by text
POST /v1/databases/{database_uid}/search/vector
{"field": "vec", "text": "forgot password", "k": 5}

`dimension`은 모델과 일치해야 합니다: openai/text-embedding-3-small → 1536, openai/text-embedding-3-large → 3072. 임베딩 호출이 실패하면 쓰기는 502 `embedding_failed`로 중단됩니다(아무것도 저장되지 않음). 콘솔 쓰기는 워크스페이스의 활성 API 키 중 하나를 사용합니다.

임베딩 생성 팁

위의 `embed`를 권장합니다. 벡터를 직접 계산한다면 OpenAI의 text-embedding-3-small (1536차원)이 기준 모델입니다 — LLM API로 임베딩을 생성해 knn_vector 필드에 저장하세요.

5

집계 (Aggregations)

데이터 분석을 위한 집계 기능을 제공합니다.

curl -X POST https://api.core.today/v1/databases/{database_uid}/aggregate \
  -H "X-API-Key: cdt_your_api_key" \
  -H "Content-Type: application/json" \
  -d '{
    "query": {"match_all": {}},
    "aggregations": {
      "by_category": {
        "terms": {"field": "category", "size": 10}
      },
      "avg_price": {
        "avg": {"field": "price"}
      },
      "price_ranges": {
        "range": {
          "field": "price",
          "ranges": [
            {"to": 100},
            {"from": 100, "to": 200},
            {"from": 200}
          ]
        }
      }
    }
  }'

집계 응답 예시

{
  "total": 1000,
  "aggregations": {
    "by_category": {
      "buckets": [
        {"key": "electronics", "doc_count": 350},
        {"key": "clothing", "doc_count": 280},
        ...
      ]
    },
    "avg_price": {"value": 149.99},
    "price_ranges": {
      "buckets": [
        {"key": "*-100.0", "doc_count": 200},
        {"key": "100.0-200.0", "doc_count": 500},
        {"key": "200.0-*", "doc_count": 300}
      ]
    }
  }
}
6

데이터베이스 관리

데이터베이스 목록

curl https://api.core.today/v1/databases \
  -H "X-API-Key: cdt_your_api_key"

이름/설명 수정

표시 이름(display_name)과 설명(description)만 수정할 수 있습니다. 스키마 필드는 이 API로 변경할 수 없으며, 필드 추가는 아래 필드 추가 API를 사용하세요.

curl -X PATCH https://api.core.today/v1/databases/{database_uid} \
  -H "X-API-Key: cdt_your_api_key" \
  -H "Content-Type: application/json" \
  -d '{
    "display_name": "New Display Name",
    "description": "Updated description"
  }'

필드 추가

기존 데이터베이스에 새 필드를 추가할 수 있습니다. 기존 필드의 타입은 변경할 수 없습니다.

curl -X POST https://api.core.today/v1/databases/{database_uid}/fields \
  -H "X-API-Key: cdt_your_api_key" \
  -H "Content-Type: application/json" \
  -d '{
    "fields": {
      "rating": {"type": "float"},
      "reviews_count": {"type": "integer"}
    }
  }'

통계 조회

curl https://api.core.today/v1/databases/{database_uid}/stats \
  -H "X-API-Key: cdt_your_api_key"

데이터베이스 삭제

주의: 데이터베이스 삭제는 되돌릴 수 없습니다. 모든 문서가 영구 삭제됩니다.

잘못 만든 스키마를 바로잡는 방법은 삭제 후 재생성뿐입니다. 필요한 문서는 삭제 전에 반드시 백업하세요. 콘솔의 Databases 페이지에서도 삭제할 수 있습니다.

curl -X DELETE https://api.core.today/v1/databases/{database_uid} \
  -H "X-API-Key: cdt_your_api_key"

필드값 자동완성 (Suggest)

keyword 타입 필드의 값을 자동완성으로 조회합니다.

curl "https://api.core.today/v1/databases/{database_uid}/suggest?field=category&prefix=ele&limit=10" \
  -H "X-API-Key: cdt_your_api_key"

# 응답: {"suggestions": ["electronics", "electric-vehicles", ...]}

무료: 크레딧이 차감되지 않습니다.

쿼리 저장

자주 사용하는 검색 쿼리를 저장하고 관리할 수 있습니다. 모두 무료입니다.

docs.databases.mgmt.queriesCode

SDK 예제 (@coredot/aiapi · aiapi)

JavaScript / TypeScript

import { AIAPI } from "@coredot/aiapi";

const client = new AIAPI({ apiKey: "cdt_your_api_key" });

// Create a database (Korean analyzer for text fields)
const db = await client.databases.create({
  name: "products",
  language: "ko",
  schema_fields: { title: { type: "text" }, price: { type: "float" }, status: { type: "keyword" } },
});

// Create-or-merge — no "PATCH, then POST on 404"
const doc = await client.databases.upsert(db.uid, "product-001", { title: "무선 헤드폰", price: 99.99 });
console.log(doc.result); // "created" | "updated"

// Search (text fields are sortable; writes are searchable within ~1s)
const results = await client.databases.search(db.uid, {
  query: { match: { title: "헤드폰" } },
  sort: [{ title: "asc" }],
  size: 10,
});
console.log(results.hits);

Python

from aiapi import AIAPI

client = AIAPI(api_key="cdt_your_api_key")

# Create a database (Korean analyzer for text fields)
db = client.databases.create(
    "products",
    {"title": {"type": "text"}, "price": {"type": "float"}, "status": {"type": "keyword"}},
    language="ko",
)

# Create-or-merge — no "PATCH, then POST on 404"
doc = client.databases.upsert(db.uid, "product-001", {"title": "무선 헤드폰", "price": 99.99})
print(doc.result)  # "created" | "updated"

# Search (text fields are sortable; writes are searchable within ~1s)
results = client.databases.search(db.uid, {"match": {"title": "헤드폰"}}, sort=[{"title": "asc"}], size=10)
print(results.hits)

에러 응답

요청 실패 시 다음과 같은 에러 응답이 반환됩니다. 크레딧이 차감된 작업이 실패하면 자동으로 환불됩니다.

402 — 크레딧 부족

{
  "detail": "Insufficient credits. Required: 0.50, Available: 0.12"
}

404 — 데이터베이스 또는 문서 없음

{
  "detail": "Database not found"
}
// 또는
{
  "detail": "Document not found"
}

400 — 유효성 검증 실패

{
  "detail": "Database with name 'products' already exists"
}
// 또는
{
  "detail": "Invalid query: Query type 'script' is not allowed"
}
// 또는
{
  "detail": "Pagination limit exceeded: from (9990) + size (20) = 10010 exceeds maximum of 10000. Use search_after for deep pagination."
}

403 — 플랜 제한

{
  "detail": "Vector search is not available in your plan"
}
// 또는
{
  "detail": "Maximum number of databases (1) reached for your plan"
}
R

리셀러: 고객별 데이터 격리

리셀러 모드가 활성화된 팀이 공개 문서 엔드포인트에 X-Customer-Id 헤더를 함께 보내면, 하나의 데이터베이스 안에서 모든 문서 읽기/쓰기/검색이 해당 최종 고객(end-customer)에게 자동으로 격리됩니다. 고객마다 별도의 DB를 만들 필요가 없습니다.

동작 방식

  • 쓰기: 생성/대량 생성/수정 시 문서에 해당 고객 태그가 자동으로 부착됩니다.
  • 읽기·검색: 조회/검색/카운트/벡터 검색이 해당 고객의 문서로 자동 필터링됩니다.
  • 교차 접근 차단: 다른 고객의 문서를 get/delete 하면 404 를 반환합니다(존재 여부도 노출되지 않음).
  • 헤더가 없으면: 리셀러 팀에서는 400 (customer_scope_required)으로 거부됩니다 — 격리가 조용히 풀리지 않습니다. 전체 데이터셋을 직접 관리하려면 X-Customer-Scope: all 헤더를 명시적으로 보내세요. 비리셀러 팀은 영향 없습니다.

적용 엔드포인트: /databases/{uid}/documents (및 /_bulk), /documents/{doc_id}, /documents/_count, /search, /search/vector.

격리 보장: 한 고객의 데이터는 다른 고객에게 절대 노출되지 않습니다. 리셀러 모드가 아닌 팀이 이 헤더를 보내면 무시됩니다.

예약 필드 접두사 _ — 이름 규칙과 다릅니다

문서 필드 이름 중 밑줄(_)로 시작하는 것은 시스템 예약이며, 문서 본문에서 자동으로 제거됩니다(고객 태그 등 내부 필드를 스푸핑할 수 없도록). 이는 앞서 설명한 데이터베이스 이름 접두사 규칙(aiapi_, db_system_, _ 시작하는 DB 이름을 금지)과는 다른 규칙입니다. 전자는 문서의 필드 키, 후자는 DB의 이름에 적용됩니다.

예시 — 같은 DB, 두 고객의 문서 집합이 서로 격리됨

docs.databases.reseller.exampleCode

Best Practices

  • 스키마 설계: 검색에 자주 사용되는 필드는 keyword 타입으로, 전문 검색이 필요한 필드는 text 타입으로 지정하세요.
  • 대량 작업: 여러 문서를 처리할 때는 /_bulk 엔드포인트를 사용하세요.
  • 페이지네이션: from + size 는 최대 10,000까지 가능합니다. 그 이상은 search_after sort를 함께 사용하세요.
  • 필터 vs 쿼리: 정확한 값 매칭은 filter에, 점수 계산이 필요한 검색은 query에 작성하세요.
  • 벡터 검색: 벡터 차원은 임베딩 모델에 맞게 설정하고, 필터와 함께 사용하면 더 효율적입니다.