JSON 경로
JSON 경로 (JSON Path)
RedisJSON은 JSON 문서의 특정 요소에 접근하기 위해 JSONPath 문법을 사용해요. 이 페이지에서는 JSONPath 지원 범위, 문법, 평가 의미(semantics), 그리고 필터 표현식과 함수들을 정리해 드릴게요. 코드를 실행하며 경로를 익히는 방식으로 따라와 주세요.
JSONPath 지원 (JSONPath support)
RedisJSON은 JSON 문서의 요소를 접근하기 위한 경로 언어로 JSONPath를 지원해요. JSON.GET, JSON.SET, JSON.DEL 같은 명령에서 경로를 지정해 문서의 일부만 읽거나 쓸 수 있죠.
JSONPath 문법 (JSONPath syntax)
JSONPath는 문서의 루트를 나타내는 $로 시작해요. 몇 가지 핵심 문법을 보여드릴게요.
| 문법 | 의미 |
|---|---|
$ |
루트 요소 |
$.key |
객체의 key 필드 |
$["key"] |
대괄호 표기법으로 key 필드 |
$[0] |
배열의 첫 요소 |
$[*] |
배열의 모든 요소, 또는 객체의 모든 값 |
$..key |
재귀적 하강(recursive descent)으로 모든 key |
$[?(@.price > 10)] |
필터 표현식(조건을 만족하는 요소) |
$[0,1] 또는 $[0:2] |
여러 인덱스 / 슬라이스 |
평가 의미 (Evaluation semantics)
경로가 반환하는 결과는 요청한 요소들이에요. 경로가 존재하지 않는 요소를 가리키면 빈 결과가 반환되고, 명령에 따라 오류가 나거나 그냥 비어 있을 수 있어요.
부정 연산자: Negation !
필터 표현식에서 느낌표 !는 부정(negation)을 나타내요. 예를 들어 !(@.active == true)처럼 "조건이 거짓인 요소"를 선택할 수 있어요.
배열/객체 리터럴 비교 (Comparing array and object literals)
필터 표현식에서 배열이나 객체 리터럴을 비교할 수 있어요. 예를 들어 @.tags == ["a", "b"]처럼 배열 전체를 비교하거나, @.config == {"x": 1}처럼 객체를 비교할 수 있어요.
산술 연산자 (Arithmetic operators)
필터와 경로에서 +, -, *, /, % 같은 산술 연산자를 사용할 수 있어요. 예를 들어 @.price * @.quantity > 100 같은 조건을 만들 수 있죠.
멤버십 연산자: in과 nin
in: 요소가 값 목록에 속하면 참nin: 요소가 값 목록에 속하지 않으면 참
예: @.status in ["active", "pending"]
집합 관계 연산자: subsetof, anyof, noneof
subsetof: 요소가 다른 집합의 부분집합이면 참anyof: 요소와 다른 집합 사이에 공통 요소가 하나라도 있으면 참noneof: 공통 요소가 하나도 없으면 참
크기/빈 연산자: size (sizeof)와 empty
size(sizeof): 배열의 크기를 반환해요.empty: 배열/객체가 비어 있으면 참.
예: @.items size > 0 또는 @.items empty
키 가져오기 연산자: Get-keys ~
~ 연산자는 객체의 키(필드 이름) 목록을 반환해요. 예: $.config~는 config 객체의 키 목록을 돌려줍니다.
함수 (Functions)
Advanced JSONPath에서 지원하는 함수들은 다음과 같아요.
length()— 배열이나 문자열의 길이count()— 경로 결과의 개수value()— 단일 값으로 축소(이산)한 결과 반환keys()— 객체의 키 목록match()/search()— 문자열 패턴 매칭concat()— 문자열 혹은 배열 병합abs(),ceiling(),floor()— 수치 연산first(),last(),index()— 위치 기반 선택min(),max(),avg(),sum(),stddev()— 집계 함수append()— 배열에 요소 추가
프로젝션 표현식 (Projection expressions)
프로젝션(projection)은 경로가 여러 요소를 가리킬 때 그 결과를 배열로 "펼치는" 동작 의미를 말해요. 예를 들어 $.items[*].name은 각 아이템의 name을 모아 배열로 반환해요.
접근 예제 (Access examples)
다음 문서를 기준으로 해볼게요.
{
"store": {
"bicycle": { "color": "red", "price": 19.95 },
"book": [
{ "author": "Smith", "price": 8.99 },
{ "author": "Jones", "price": 12.99 }
]
}
}
$.store.bicycle.color→"red"$.store.book[*].author→["Smith", "Jones"]$..price→ 모든price값의 배열[19.95, 8.99, 12.99]$..book[0]→{"author": "Smith", "price": 8.99}
필터 예제 (Filter examples)
$.store.book[?(@.price < 10)].author→ 가격이 10 미만인 책의 저자$.store.book[?(@.price >= 8.99)].author→ 가격이 8.99 이상인 책의 저자$..book[?(@.price <= $.store.bicycle.price)].title→ 자전거 가격 이하인 책의 제목
갱신 예제 (Update examples)
JSON.SET과 함께 경로를 쓰면 문서의 일부만 수정할 수 있어요.
> JSON.SET store:1 $.store.bicycle.color "blue"
"OK"
> JSON.GET store:1 $.store.bicycle.color
"\"blue\""
레거시 경로 문법 (Legacy path syntax)
RedisJSON은 기본적으로(기본 경로 모드에서) 새로운 JSONPath 문법을 사용해요. 하지만 일부 구버전 호환을 위해 레거시 경로 문법(Legacy path syntax)도 지원해요.
JSON 키 이름과 경로 호환성 (JSON key names and path compatibility)
레거시 경로에서 JSON 키 이름과의 호환성에 주의가 필요해요. 예를 들어 키 이름에 특수 문자가 있거나, 경로 구분자(.)와 충돌하는 문자가 포함된 키는 따옴표(")로 감싸서 접근해야 해요. $.store["bicycle.color"]처럼 이미 존재하는 키가 아니라면, 점(.)은 경로 구분자로 해석된다는 점을 기억해야 합니다.
경로 평가의 시간 복잡도 (Time complexity of path evaluation)
JSONPath 평가의 시간 복잡도는 경로의 형태에 따라 달라져요.
- 단순 접근: O(M + N) (M은 문서 내 요소 수, N은 요청된 경로의 요소 수)
- 필터 표현식: 조건을 평가하기 위해 관련 요소를 순회해야 하므로 문서 크기에 비례할 수 있어요.
- 재귀 하강(
..): 문서의 모든 요소를 탐색하므로 문서 전체 크기에 비례해요.
세부 복잡도는 명령 참조에서 확인하실 수 있어요.
더 알아보기 (Learn more)
- RedisJSON 데이터 타입 — JSON 데이터 타입 전체 문서
- JSON 명령어 참조 (JSON.SET, JSON.GET, JSON.DEL, JSON.ARRAPPEND 등)
- JSON 성능 — JSON 처리 성능 고려사항
- Developer notes — RedisJSON 개발/빌드/테스트 노트