Firebase에서 데이터를 구조화하는 가장 좋은 방법은 무엇인가요?


111

저는 firebase를 처음 사용하며 데이터를 구조화하는 가장 좋은 방법이 무엇인지 알고 싶습니다.

간단한 예가 있습니다.

내 프로젝트에 지원자와 지원서가 있습니다. 1 명의 지원자가 여러 개의 지원서를 가질 수 있습니다. 이 두 개체를 Firebase에 어떻게 연결할 수 있습니까? 관계형 데이터베이스처럼 작동합니까? 아니면 데이터 디자인 측면에서 접근 방식이 완전히 달라야합니까?

답변:


137

업데이트 : 이제 데이터 구조화에 대한 문서가 있습니다 . 또한 NoSQL 데이터 구조 에 대한이 훌륭한 게시물을 참조하십시오 .

RDBMS와 달리 계층 적 데이터의 주요 문제는 데이터를 중첩 할 수 있기 때문에 데이터를 중첩하려는 유혹이 있다는 것입니다. 일반적으로 조인 문 및 쿼리가 없음에도 불구하고 SQL에서와 마찬가지로 데이터를 어느 정도 정규화하려고합니다.

또한 읽기 효율성이 중요한 곳에서 비정규 화 를 원합니다 . 이는 모든 대규모 앱 (예 : Twitter 및 Facebook)에서 사용하는 기술이며 DRY 원칙에 위배되지만 일반적으로 확장 가능한 앱의 필수 기능입니다.

여기서 요점은 읽기를 쉽게 만들기 위해 열심히 쓰기를 원한다는 것입니다. 개별적으로 읽는 논리적 구성 요소를 별도로 보관합니다 (예 : 채팅방의 경우 메시지, 방에 대한 메타 정보 및 구성원 목록을 모두 같은 위치에 두지 마십시오. 나중에 그룹을 반복 할 수 있도록하려는 경우).

Firebase의 실시간 데이터와 SQL 환경의 주요 차이점은 데이터 쿼리입니다. 데이터의 실시간 특성으로 인해 "SELECT USERS WHERE X = Y"라고 말할 수있는 간단한 방법은 없습니다 (동기화 된 클라이언트를 확인하기 위해 더 간단한 내부 모델이 필요한 지속적으로 변경, 분할, 조정 등).

간단한 예는 아마도 당신을 올바른 마음 상태로 만들 것입니다.

/users/uid
/users/uid/email
/users/uid/messages
/users/uid/widgets

이제 계층 구조이므로 사용자의 이메일 주소를 반복하려면 다음과 같이합니다.

// I could also use on('child_added') here to great success
// but this is simpler for an example
firebaseRef.child('users').once('value')
.then(userPathSnapshot => {
   userPathSnapshot.forEach(
      userSnap => console.log('email', userSnap.val().email)
   );
})
.catch(e => console.error(e));

이 방법의 문제는 난 그냥 사용자의 모든 다운로드 할 수있는 클라이언트 강제 것입니다 messageswidgets도합니다. 수천 개에 해당하는 것이 하나도 없다면 별일 없을 것입니다. 그러나 각각 5k 이상의 메시지를 가진 10k 사용자에게는 큰 문제입니다.

따라서 이제 계층 적 실시간 구조를위한 최적의 전략이 더욱 분명해졌습니다.

/user_meta/uid/email
/messages/uid/...
/widgets/uid/...

이 환경에서 매우 유용한 추가 도구는 인덱스입니다. 특정 속성을 가진 사용자의 인덱스를 생성함으로써 간단히 인덱스를 반복하여 SQL 쿼리를 빠르게 시뮬레이션 할 수 있습니다.

/users_with_gmail_accounts/uid/email

이제 Gmail 사용자를위한 메시지를 받고 싶다면 다음과 같이 할 수 있습니다.

var ref = firebase.database().ref('users_with_gmail_accounts');
ref.once('value').then(idx_snap => {
   idx_snap.forEach(idx_entry => {
       let msg = idx_entry.name() + ' has a new message!';
       firebase.database().ref('messages').child(idx_entry.name())
          .on(
             'child_added', 
             ss => console.log(msg, ss.key);
          );
   });
})
.catch(e => console.error(e));

데이터 비정규 화에 대한 다른 SO 게시물에서 몇 가지 세부 정보를 제공 했으므로 이들도 확인하십시오 . Frank가 이미 Anant의 기사를 게시 한 것을 보았습니다. 그래서 여기서 반복하지는 않겠습니다.


이 통찰력 Kato에 감사드립니다!
호퍼

2
당분간. Firebase v2 릴리스의 뷰에는 해당 프로세스를 자동화하는 몇 가지 훌륭한 기능이 포함됩니다.
Kato

여기에서 오래된 댓글 스레드를 부활시키고 있지만 더 최신 솔루션을 찾기 위해 고군분투하고 있습니다. 이것이 여전히 최선의 접근 방식입니까? 즉, 모든 users_with_gmail_accounts를 얻은 다음 forEach를 실행합니까?
owiewio

48

중포 기지 아주 많이 하지 관계형 데이터베이스처럼. 무엇과 비교하고 싶다면 계층 적 데이터베이스와 비교하겠습니다.

Anant는 최근 Firebase 블로그에 데이터 비정규 화에 대한 훌륭한 게시물을 작성했습니다. https://www.firebase.com/blog/2013-04-12-denormalizing-is-normal.html

각 지원자의 자녀로서 각 지원서의 "ID"를 유지하는 것이 좋습니다.


감사합니다 Frank! 이것은 정말 도움이됩니다. 정확히 내가 찾던 것!
호퍼

4

귀하의 시나리오는 관계형 세계에서 일대 다처럼 보입니다. 귀하의 예에 따라 신청자는 많은 응용 프로그램을 가지고 있습니다. firebase nosql 방식으로 오면 아래와 같습니다. 성능 문제없이 확장되어야합니다. 그렇기 때문에 아래에 언급 된 비정규 화가 필요합니다.

applicants:{
applicant1:{
    .
    .
    applications:{
        application1:true,
        application3:true
    }
},
applicant2:{
    .
    .
    applications:{
        application2:true,
        application4:true
    }
}}

applications:{
application1:{
    .
    .
},
application2:{
    .
    .
},
application3:{
    .
    .
},
application4:{
    .
    .
}}

좋지만 후속 작업이 있습니다. Swift 또는 Firebase SDK를 사용하여 어디서든이 구조를 만들려면 어떻게해야합니까? 또한 Firebase 유효성 검사 규칙을 사용하여 애플리케이션 노드에 추가 된 새 데이터가 애플리케이션 목록에 실제로 존재하는지 어떻게 확인할 수 있습니까?
Tommie C. 2016

@prateep, 좋은 예입니다. 하지만 여기서 문제는 application1이 일부 지원자에 대해 자식 인 applications / application1 경로를 삭제할 때입니다. 내가 거기에없는 경로 신청자 / 신청 1에 접근하려고하면. 그래서 당신은 application1 : {신청자 : {applicant1 : true} ...}와 같은 두 곳 모두에서 색인을 업데이트해야합니다. 그래서 지금 신청자 1을 삭제할 때 저는 신청을 위해 하위 신청자를 확인하고 신청자의 하위 노드를 업데이트해야합니다. :)
Satish Sojitra
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.