JSON 경로

JSON 경로 (JSON Path)

RedisJSON은 JSON 문서의 특정 요소에 접근하기 위해 JSONPath 문법을 사용해요. 이 페이지에서는 JSONPath 지원 범위, 문법, 평가 의미(semantics), 그리고 필터 표현식과 함수들을 정리해 드릴게요. 코드를 실행하며 경로를 익히는 방식으로 따라와 주세요.

출처: Redis 공식 문서 — Path

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 같은 조건을 만들 수 있죠.

멤버십 연산자: innin

  • 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)