필터 연산자
필터 연산자는 메타데이터(metadata) 조건(predicate)으로 버킷(bucket) 검색 결과를 좁히는 데 사용됩니다. v2 버킷 검색 요청 본문(request body)에서는 filter 객체 안에 필터를 전달합니다. 일반 스칼라 값은 정확한 일치(exact match)를 위한 단축 표현이며, 더 풍부한 조건을 표현하려면 값을 연산자 객체로 감싸면 됩니다.
참고: 메타데이터 값은 문자열로 저장되고 비교됩니다. 불리언 값은
"true"/"false"가 되며, 숫자는 비교 전 문자열로 강제 변환됩니다.
API 버전
섹션 제목: “API 버전”| 버전 | 상태 | 비고 |
|---|---|---|
v2 | 현재 | POST /v2/buckets/\{bucket_id\}/search. 모든 신규 통합에 사용하세요. |
v1 | 사용 중단 | POST /v1/buckets/\{bucket_id\}/search는 기존 클라이언트를 위해 유지되며, v2 후속 버전을 가리키는 Deprecation, Warning, Link 헤더를 반환합니다. |
연산자
섹션 제목: “연산자”| 연산자 | 타입 | 예시 | 설명 |
|---|---|---|---|
eq | any | {"eq": "urgent"} | 정확한 일치. 최상위 eq는 엔진 고속 경로로 전달됩니다. |
ne | any | {"ne": "draft"} | 같지 않음. 사후 필터(post-filter)로 적용됩니다. |
like | 문자열 | {"like": "%legal%"} | SQL LIKE: %는 임의의 문자열, _는 한 문자, \% / \_는 리터럴입니다. 대소문자를 구분하지 않습니다. |
prefix | 문자열 | {"prefix": "2024-"} | 시작 문자열. 대소문자를 구분하지 않습니다. |
in | 배열 | {"in": ["a", "b"]} | 멤버십(membership) 테스트. 최대 100개 항목까지 가능합니다. |
gt / gte | 숫자 / ISO 날짜 | {"gte": 0.8} | 초과(또는 이상). |
lt / lte | 숫자 / ISO 날짜 | {"lt": 100} | 미만(또는 이하). |
exists | 불리언 | {"exists": true} | 필드 존재 여부: true는 존재하고 비어 있지 않음을 의미하며, false는 누락되었거나 비어 있음을 의미합니다. |
연산자 객체는 정확히 하나의 키를 포함해야 합니다. 단일 필터 키는 한 번에 하나의 연산자만 사용할 수 있습니다.
키 간 OR($or)
섹션 제목: “키 간 OR($or)”$or는 서로 다른 메타데이터 키 간의 논리합(disjunction)을 표현하는 데 사용됩니다. $or는 하위 필터 딕셔너리(sub-filter dictionary)의 리스트를 받으며, 최대 3단계까지 중첩할 수 있습니다.
{ "filter": { "doc_type": "policy", "$or": [ {"severity": "high"}, {"priority": {"in": ["P0", "P1"]}} ] }}최상위 키는 여전히 AND 조건입니다. 위 예시는 doc_type = "policy" AND (severity = "high" OR priority ∈ {"P0", "P1"})인 문서를 찾습니다. 각 분기(arm)는 완전한 하위 필터이며 어떤 연산자든 사용할 수 있습니다. 단일 $or 리스트는 최대 16개의 분기를 지원합니다.
의미 체계
섹션 제목: “의미 체계”filter의 최상위 키는 논리곱(AND) 조건입니다.- 동일 키 OR은
in연산자를 사용하고, 키 간 OR은$or를 사용합니다. - 정확한 일치 키는 후보를 가지치기(pruning)하기 위해 벡터 엔진(vector engine)으로 푸시됩니다. 연산자 키는 반환된 후보(candidate)에 대해 서버 측에서 평가되므로 엔진 고속 경로는 그대로 유지됩니다.
안전 제한
섹션 제목: “안전 제한”| 제한 | 값 |
|---|---|
최대 필터 JSON 크기(내부 filter 쿼리 매개변수) | 8 KB |
| 최대 필터 중첩 깊이 | 16 |
like / prefix 패턴 길이 | 256자 |
like 와일드카드 개수(% + _) | 16 |
in 리스트 길이 | 100개 항목 |
$or 중첩 깊이 | 3단계 |
$or 리스트당 분기 | 16 |
패턴은 모든 정규 표현식 메타문자를 이스케이프한 앵커된 대소문자 구분 없는 정규 표현식으로 변환되므로, SQL 인젝션 공격 표면은 없습니다.
POST /v2/buckets/{bucket_id}/search
섹션 제목: “POST /v2/buckets/{bucket_id}/search”텍스트 쿼리와 메타데이터 필터로 버킷을 검색합니다.
경로 매개변수
섹션 제목: “경로 매개변수”| 이름 | 타입 | 설명 |
|---|---|---|
bucket_id | 문자열 | 검색할 버킷의 UUID 또는 슬러그(slug). |
요청 본문
섹션 제목: “요청 본문”| 필드 | 타입 | 필수 | 기본값 | 설명 |
|---|---|---|---|---|
query | 문자열 | 예 | — | 자연어 쿼리. 최대 8,192자. |
top_k | 정수 | 아니오 | 10 | 반환할 최대 결과 개수. |
filter | 객체 | 아니오 | null | 메타데이터 조건. 일반 값은 정확한 일치이며, 연산자 객체는 위의 연산자를 사용합니다. |
mode | 문자열 | 아니오 | "hybrid" | "vector" 또는 "hybrid". |
rerank | 불리언 | 아니오 | false | 후보를 재정렬할지 여부. |
min_score | 숫자 | 아니오 | null | 최소 점수 임계값(0.0–1.0). |
요청 예시
섹션 제목: “요청 예시”curl -X POST ${API_BASE_URL:-https://api.schift.io}/v2/buckets/product-docs/search \ -H "Authorization: Bearer $SCHIFT_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "query": "fire safety inspection cycle", "top_k": 10, "filter": { "tag": "urgent", "source_url": {"like": "%fire-safety%"}, "filename": {"prefix": "2024-"}, "doc_type": {"in": ["policy", "spec"]}, "score": {"gte": 0.8}, "stage": {"ne": "draft"}, "author": {"exists": true}, "$or": [ {"severity": "high"}, {"priority": {"in": ["P0", "P1"]}} ] } }'| 필드 | 타입 | 설명 |
|---|---|---|
bucket_id | 문자열 | 검색된 버킷. |
query | 문자열 | 실행된 쿼리. |
search_id | 문자열 | null | 서버가 생성한 검색 식별자. |
results | 배열 | 관련성 순으로 정렬된 일치 청크. |
results[].id | 문자열 | 청크 식별자. |
results[].score | 숫자 | 최종 관련성 점수. |
results[].text | 문자열 | 청크 텍스트. |
results[].metadata | 객체 | 청크 메타데이터. |
results[].citation | 문자열 | null | 요청 시 서식이 지정된 인용. |
degraded | 불리언 | 저하 모드(degraded mode)로 생성된 응답인지 여부. |
warnings | 배열 | 검색 경고가 있는 경우 해당 항목. |
응답 예시
섹션 제목: “응답 예시”{ "bucket_id": "product-docs", "query": "fire safety inspection cycle", "search_id": "srch_01j8x9q2mvk8r", "results": [ { "id": "chunk_01j8x9q2mvn9q", "score": 0.91, "text": "Annual fire safety inspections must follow the 2024 inspection cycle.", "metadata": { "doc_type": "policy", "filename": "2024-fire-safety.pdf", "tag": "urgent", "author": "safety-team" }, "citation": "[1] 2024-fire-safety.pdf, p. 4" } ], "degraded": false, "warnings": []}오류 예시
섹션 제목: “오류 예시”// 400 Bad Request — 잘못된 연산자 조합{ "detail": "FilterOperator must have exactly one of eq/ne/like/prefix/in/gt/gte/lt/lte/exists"}// 400 Bad Request — 지원하지 않는 필터 키{ "detail": "Invalid filter: unsupported metadata key 'internal_tag'"}// 413 Payload Too Large — 필터 크기 제한 초과{ "detail": "filter parameter exceeds 8KB"}// 413 Payload Too Large — 필터 중첩이 너무 깊음{ "detail": "filter JSON exceeds nesting depth"}// 402 Payment Required — 검색 할당량 소진{ "allowed": false, "reason": "quota_exceeded"}// 403 Forbidden — 검색 할당량 사용 불가{ "detail": "Search quota unavailable. Upgrade your plan."}// 404 Not Found{ "detail": "Bucket 'product-docs' not found"}