비즈니스 애플리케이션에서 데이터는 대량의 동시 저지연 쿼리를 위해 모델링되는 경우가 많습니다. 그러나 추세를 살펴봄으로써 인사이트를 얻고자 한다면 완전히 다른 데이터 모델을 원하게 됩니다. 이 문제를 해결하기 위한 기존의 방법은 데이터를 다른 곳으로 이동, 변환, 로드하는 것이었지만, 여기에는 용납할 수 없는 지연 시간, 여러 데이터 소스, 많은 비용 등 여러 가지 문제가 수반됩니다.
카우치베이스 고객은 분석 서비스를 통해 현재 운영 중인 데이터에 대한 실시간 분석 및 트렌드 보고를 손쉽게 처리할 수 있다는 사실을 잘 알고 있습니다. 최근 기업 파트너 로열티 프로그램과 관련된 고급 고객 활동을 파악하고자 하는 고객과 함께 작업할 때 그 예가 떠올랐습니다. 기본 문서 모델은 보고가 아닌 대화형 애플리케이션을 염두에 두고 설계된 것이 분명했습니다. (고통스러운 경험을 통해 아시다시피 이는 전혀 드문 일이 아닙니다.) 문제와 그 해결 방법을 간단히 살펴보겠습니다.
문서 예시
당사의 경우(온라인 예약 애플리케이션을 지원하는) 문서 모델은 네 개의 섹션으로 구성되어 있습니다. 첫 번째 섹션에는 기본 문서 및 앱 식별자가 포함됩니다. 두 번째 섹션은 여행에 대한 예약 정보를 설명합니다. 세 번째 섹션에는 예약과 관련된 하나 이상의 여정에 대한 세부 정보와 한 명 이상의 승객에 대한 승객 요구 사항이 포함됩니다. 마지막 섹션에서는 각 승객이 소속된 기업 로열티 프로그램에 대해 설명합니다.
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 |
{ "_유형": "예약", "_header": { "created": 1562888960, "source": "app", "버전": "v1.1" }, "예약": { "status": "예약됨", "예약 유형": "agency", "세부 정보": { "에이전트": "FBL33", "contact": "Arlene", "seats": 2, "여행": { "착수": 1562958000, "장비": "123X", "line": "SRF", "fromStation": { "code": "LAX", "시설 유형": 1 }, "toStation": { "code": "SOL", "시설 유형": 2 }, "bookingAgency": "PC", "agencyType": "3" } } }, "여정": [ { "daysOnboard": 1, "승객": [ { "passengerNumber": 1, "specialAccomodations": false }, { "passengerNumber": 2, "specialAccomodations": false } ], "itineraryType": "business" } ], "passengerDetails": [ { "loyaltyId": "aaaabbbbccccdddd", "passId": 1, "프로그램 유형": { "기업파트너": true, "partnerId": 1 } }, { "loyaltyId": "이이이이이이이이이이이이이이이이이이이이이이이이이이이이이이이이이이이이이이이", "passId": 2, "프로그램 유형": { "기업파트너": false } } ] } |
쿼리 요소
분석을 완료하기 위해 고객은 다음 필드를 가져오거나 필터링해야 했습니다:
- 상태, 장비, 승선(사람이 읽을 수 있는 형식으로 변환), 노선, _유형, 탑승일수, 승객번호, 로열티아이디, 파트너아이디
물론 문제는 이러한 필드가 문서 모델 내에서 완전히 다른 계층적 수준에 존재한다는 것입니다. 일부는 스칼라 값으로, 간단한 쿼리로 쉽게 액세스할 수 있습니다:
- 상태, 장비, 승선, 라인, _유형
다른 하나는 배열 내의 요소(여행 일정으로 구성됨)로, 중첩을 해제해야 합니다:
- 일수온보드
이 동일한 배열 안에는 두 번째 배열(승객 세부 정보로 구성됨)이 있으며, 이 배열의 요소를 조인 키로 사용해야 합니다:
- passengerNumber
이 조인 키는 비즈니스 애플리케이션의 이유로 두 번째 배열 내에 중첩되지 않는 세 번째 배열 내에서 요소에 액세스하는 데 사용됩니다:
- 로열티아이디, 파트너아이디
이러한 다양한 수준은 서로 다른 액세스 경로와 동일하므로 분석에 약간의 복잡성을 더합니다. 다행히도 분석용 N1QL은 우리에게 필요한 구문 도구를 제공합니다. 다음은 쿼리를 작성하는 데 사용할 수 있는 프로세스에 대한 단계별 설명입니다.
1단계 - 하나의 스칼라 요소의 간단한 선택
이 단계는 SQL 경험이 있는 사람이라면 누구나 쉽게 이해할 수 있을 것입니다. 선택 문을 사용하여 라인 버킷에서 스칼라 값을 검색합니다. 예약 섹션의 일부로 상태 필드를 한정하고 반환할 레코드 수를 제한합니다.
|
1 2 3 |
선택 예약.상태 에서 라인 limit 1; |
쿼리 결과:
|
1 2 3 4 5 |
[ { "status": "예약됨" } ] |
2단계 - 첫 번째 배열에서 중첩 해제 및 요소 추가하기
다음으로 문서의 여정 섹션에서 데이터를 추가합니다. 그러나 이러한 요소는 배열 내에 포함되어 있으므로 먼저 배열의 중첩을 해제해야 합니다.
|
1 2 3 4 5 |
선택 l.예약.상태, i.일수온보드 에서 라인 l unnest l.여정 i limit 1; |
쿼리 결과:
|
1 2 3 4 5 6 |
[ { "status": "예약됨", "daysOnboard": 1 } ] |
3단계 - 두 번째(첫 번째 배열 내) 배열에서 중첩 해제 및 요소 추가하기
이제 임베디드 승객 배열에서 요소를 추가합니다. (배열에서 실제로 둘 이상의 요소에 액세스하고 있는지 확인하기 위해 제한을 늘렸습니다.) 참고하세요.
|
1 2 3 4 5 6 7 |
선택 l.예약.상태, i.일수온보드, p.passengerNumber 에서 라인 l unnest l.예약.여정 i unnest i.승객 p limit 2; |
쿼리 결과:
|
1 2 3 4 5 6 7 8 9 10 11 12 |
[ { "status": "예약됨", "daysOnboard": 1, "passengerNumber": 1 }, { "status": "예약됨", "daysOnboard": 1, "passengerNumber": 2 } ] |
4단계 - 조인을 통해 액세스 가능한 세 번째 배열에서 중첩 해제 및 요소 추가
세 번째 배열(passengerDetails)의 요소는 중첩을 해제하고 위의 승객 배열의 요소에 연결해야 합니다. 이 작업은 where 절을 통해 수행합니다.
|
1 2 3 4 5 6 7 8 9 10 |
선택 l.예약.상태, i.일수온보드, p.passengerNumber, pd.loyaltyId 에서 라인 l unnest l.여정 i unnest i.승객 p unnest l.승객 세부 정보 pd 어디 p.passengerNumber = pd.passId limit 2; |
쿼리 결과:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
[ { "status": "예약됨", "daysOnboard": 1, "passengerNumber": 1, "loyaltyId": "aaaabbbbccccdddd" }, { "status": "예약됨", "daysOnboard": 1, "passengerNumber": 2, "loyaltyId": "이이이이이이이이이이이이이이이이이이이이이이이이이이이이이이이이이이이이이이이" } ] |
5단계 - 나머지 쿼리 요소 추가
쿼리를 완료하려면 다른 필드가 필요합니다. 특히 where 절에 추가된 _type 필드에 주목하세요. 프로덕션 시스템에서는 버킷에 여러 유형의 문서가 포함될 가능성이 높습니다. 쿼리 결과는 아래 예에서와 같이 쿼리 자체에서 필터링되거나 애널리틱스 데이터 세트 생성의 일부로 필터링될 수 있습니다.
|
1 2 3 4 5 6 7 8 9 10 11 12 |
선택 l.예약.상태, l.예약.세부 정보.여행.장비, l.예약.세부 정보.여행.라인, i.일수온보드, p.passengerNumber, pd.loyaltyId, pd.프로그램 유형.partnerId, millis_to_str(l.예약.세부 정보.여행.출항*1000) 출항 에서 라인 l unnest l.여정 i unnest i.승객 p unnest l.승객 세부 정보 pd 어디 p.passengerNumber = pd.passId 그리고 l._유형 = "예약" 그리고 str_to_millis("2019-07-12T19:00:00Z") = l.예약.세부 정보.여행.출항*1000; |
쿼리 결과:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
[ { "착수": "2019-07-12T19:00:00Z", "status": "예약됨", "장비": "123X", "line": "SRF", "daysOnboard": 1, "passengerNumber": 1, "loyaltyId": "aaaabbbbccccdddd", "partnerId": 1 }, { "착수": "2019-07-12T19:00:00Z", "status": "예약됨", "장비": "123X", "line": "SRF", "daysOnboard": 1, "passengerNumber": 2, "loyaltyId": "이이이이이이이이이이이이이이이이이이이이이이이이이이이이이이이이이이이이이이이" } ] |
추가 읽기
Unnest는 데이터에 포함된 여러 요소를 마스터할 수 있는 수단을 제공하는 분석용 N1QL의 강력한 기능입니다. 구문에 대한 자세한 내용은 여기에서 확인할 수 있습니다: https://docs.couchbase.com/server/6.0/analytics/3_query.html#Unnest_clauses
애널리틱스용 N1QL에 대한 전체 가이드(제가 이 가이드의 사인본을 소장하고 있습니다)는 여기에서 확인할 수 있습니다: https://www.amazon.com/SQL-Users-Tutorial-Don-Chamberlin/dp/0692184503/
직접 사용해 보세요
바로 가기 https://docs.couchbase.com/server/6.0/analytics/quick-start.html#Using_docker 를 클릭하고 Docker 기반 튜토리얼로 바로 시작하세요. 또는 원하는 경우 이 페이지에서 Couchbase Server 6 Enterprise를 다운로드하세요: https://www.couchbase.com/downloads