2016-06-04 12 views
3

을 참조하는 내가 그룹 메시징 애플리케이션을 구축하기 위해 노력하고있어 가정 수 있습니다, 그래서 너무처럼 내 데이터베이스 구조 설계 :이 큰 규모 것 중포 기지의 문서에 따르면중포 기지 데이터베이스 : 동적 값

users: { 
    uid1: { //A user id using push() 
     username: "user1" 
     email: "[email protected]" 
     timestampJoined: 18594659346 
     groups: { 
      gid1: true, 
      gid3: true 
     } 
    } 
    uid2: { 
     username: "user2" 
     email: "[email protected]" 
     timestampJoined: 34598263402 
     groups: { 
      gid1: true, 
      gid5: true 
     } 
    } 
    .... 
} 

groups: { 
    gid1: { //A group id using push() 
     name: "group1" 
     users: { 
      uid1: true, 
      uid2: true 
     } 
    } 
    gid2: { 
     name: "group2" 
     users: { 
      uid5: true, 
      uid7: true, 
      uid80: true 
     } 
    } 
    ... 
} 

messages: { 
    gid1: { 
     mid1: { //A message id using push() 
      sender: uid1 
      message: "hello" 
      timestamp: 12839617675 
     } 
     mid2: { 
      sender: uid2 
      message: "welcome" 
      timestamp: 39653027465 
     } 
     ... 
    } 
    ... 
} 

합니다.

이제 내 응용 프로그램 내부에서 모든 메시지에 보낸 사람의 사용자 이름을 표시하려고한다고 가정합니다.

모든 단일 메시지의 사용자 이름을 쿼리하는 것은 분명히 나쁘다. 그래서 내가 발견 한 해결책 중 하나는 모든 메시지에서 사용자 이름을 복제하는 것이 었습니다.

메시지 노드는 지금과 같이 보일 것이다 :

messages: { 
    gid1: { 
     mid1: { //A message id using push() 
      sender: uid1 
      username: "user1" 
      message: "hello" 
      timestamp: 12839617675 
     } 
     mid2: { 
      sender: uid2 
      username: "user2" 
      message: "welcome" 
      timestamp: 39653027465 
     } 
     ... 
    } 
    ... 
} 

지금 나는 그의 이름을 변경하려면 사용자에 대한 옵션을 추가 할 수 있습니다.

사용자가 사용자 이름을 변경하기로 결정한 경우 사용자 노드와 그가 보낸 모든 단일 메시지에서 사용자를 업데이트해야합니다.

"모든 메시지에 대한 청취자"접근법을 사용하면 단일 위치에서 이름을 변경해야하므로 사용자 이름을 변경하는 것이 쉽습니다.

이제 그는 보낸 모든 그룹의 모든 메시지에서 이름을 업데이트해야합니다.

전체 메시지 노드에 사용자 ID를 쿼리하는 것은 좋지 않은 디자인이므로 사용자가 보낸 모든 메시지의 위치를 ​​저장하는 다른 노드를 만드는 것이 좋습니다.

그것은 다음과 같이 보일 것입니다 :

userMessages: { 
    uid1: { 
     gid1: { 
      mid1: true 
     } 
     gid3: { 
      mid6: true, 
      mid12: true 
     } 
     ... 
    } 
    uid2: { 
     gid1: { 
      mid2: true 
     } 
     gid5: { 
      mid13: true, 
      mid25: true 
     } 
     ... 
    } 
    ... 
} 

을 그래서 지금은 신속하게 특정 사용자에 대한 모든 메시지의 위치를 ​​가져, 단일 updateChildren와 사용자 이름() 호출을 업데이트 할 수 있습니다.

이 방법이 가장 좋은 방법입니까? 동적 인 값 (사용자 이름)을 참조하기 때문에 정말로 많은 데이터 (수백만 개의 메시지)를 복제해야합니까?

동적 데이터를 처리 할 때 더 좋은 방법이 있습니까?

+1

많은 좋은 답변이 이미 있습니다. 나는 당신이 그 경로를 선택하는 경우를 대비하여 삼진/비 표준화 된 데이터를 작성하는 방법에 대해 잠시 전에 쓴 대답에 연결합니다 : http://stackoverflow.com/questions/30693785/how-to-write-denormalized -data-in-firebase –

답변

3

이것은 일반적으로 부모 노드 이름 (키)이 포함하거나 나타내는 값에서 분리해야하는 이유의 완벽한 예입니다.

그래서 큰 그림을 생각하면 도움이 될 수 있으며 사용자 환경을 고려하면 답을 얻을 수 있습니다.

이제 내 응용 프로그램 내에서 모든 메시지에 보낸 사람의 사용자 이름을 표시하려고한다고 가정합니다.

하지만 정말로하고 싶습니까? 사용자가 실제로 10,000 개의 메시지 목록을 스크롤하려고합니까? 아마도 그렇지 않습니다. 대부분의 경우, 앱은 메시지의 하위 집합을 표시하고 심지어는 한 번에 10 개 또는 12 개의 메시지를 표시합니다.

messages 
    msg_1 
    sender: uid_1 
    message: "hello" 
    timestamp: 12839617675 
    observers: 
     uid_0: true 
     uid_1: true 
     uid_2: true 

의 각 사용자 로그 및 응용 프로그램이 메시지 노드를 관찰 쿼리를 수행

users 
    uid_0 
    name: Charles 
    uid_1 
    name: Larry 
    uid_2: 
    name: Debbie 

과 메시지 테이블 :

는 사용자 테이블을 가정 : 여기

은 몇 가지 생각입니다 앱 디스플레이는 메시지의 메시지 텍스트는 물론 해당 메시지 ('그룹')도 관찰하는 각 사용자 이름을 표시합니다.

게시 한 사용자의 사용자 이름을 표시하는데도 사용할 수 있습니다.

해결 방법 1 : 앱이 시작되면 사용자 노드의 모든 사용자를로드하여 uid_를 키로 사용하여 사전에 저장합니다. 메시지 노드가 관찰 될 때

는 각 메시지가 장착되어 있고, 그래서 그냥 이름 선택 키로 users_dict에 저장되어있는 다른 사용자의 UID의 (또는 포스터)가됩니다

let name = users_dict["uid_2"] 

을 해결 방법 2 :

사용자 노드에 저장되는 데이터가 많고 (천천히) 사용자가 천명이라고 가정 해보십시오. 그 모든 데이터를로드 아무 소용이 없을 때 당신이에 관심이있는 모든 이름은 그래서 당신의 수 중 하나

A)를 사용하여 솔루션 # 1 단지 UID 및 이름이나

이외의 다른 모든 데이터를 무시

b) firebase에 별도의 'names'노드를 생성하여 사용자 이름 만 저장하면 사용자 노드에 저장할 필요가 없습니다.

names: 
    uid_0: Charles 
    uid_1: Larry 
    uid_2: Debbie 

당신도 몇 천 사용자와, 볼 수 있듯이, 그에서로드 할 데이터의 작은 비트입니다. 그리고 ... 여기 좋은 점은 당신이 이름 노드에 리스너를 추가하는 경우, 경우 사용자가 자신의 이름을 변경하면 앱에 알림이 전송되고 이에 따라 UI가 업데이트 될 수 있습니다.

해결책 3 :

필요에 따라 이름을로드하십시오. 기술적으로는 할 수 있지만 권장하지는 않습니다.

사용자가 속한 모든 메시지 노드를 관찰하십시오. 이러한 노드는 읽히고 읽을 때 uid의 사전을 작성하면 이름이 필요합니다. 그런 다음 uid를 기반으로 각 사용자 이름에 대한 쿼리를 수행합니다.이 작업은 가능하지만 Firebase의 비동기 특성을 고려하여 이름을로드 할 시간을 허용해야합니다. 마찬가지로 메시지를로드 한 다음 해당 메시지의 사용자 이름을 경로 : users/uid_x/user_name. 다시 말하면 비동기 타이밍 문제가 발생하지만 비동기 호출 또는 루프 내에서 비동기 호출을 중첩하는 것이므로 피할 것입니다.

사용자가 경험하고 Firebase 구조를 가능한 한 평평하게 유지하는 모든 솔루션의 중요한 점.

예를 들어 실제로 10,000 개의 메시지를로드하려는 경우 메시지 텍스트 나 제목을 다른 노드로 분리하여 초기 UI 목록에 해당 노드 만로드하는 것이 좋습니다. 사용자가 메시지를 드릴 다운하면 나머지 데이터가로드됩니다.

1

에 따를 절차 :

  • 완료 UID

  • 에 따라 응용 프로그램의 다시 시작할 때마다

  • 로컬 캐시들이 캐시에서

  • 쇼의 이름에서 이름을 가져

참고 : 사용자 이름을 가져 오는 방법은 구현 방법에 따라 다릅니다.

1

는 당신은

mid1: { //A message id using push() 
    sender: uid1 
    message: "hello" 
    timestamp: 12839617675 
    } 

사용자 이름은 사용자로부터 읽을 수있는이 구조를 필요로 직접 "사용자/UID1/사용자 이름"각 아이를 읽은 후 단일 값 이벤트 리스너를 사용하여. 중포 기지는 SQL처럼 복잡한 쿼리를 만들 수 없기 때문에, 연속 통화와

사용하도록되어 그리고 당신이 할 수 효율을 유지하기 위해 :

1) 캐시 핸들러로 사용 기준 사전 만들기 당신은 모든 메시지를 읽은 후 각 키의 값이 있으면하는 당신은 확인 :

[uid1:"John",uid2:"Peter",....etc...] 

그리고

키를 사용하면 단일 값 리스너/사용자/$의 UID/사용자 이름을 가리키는 추가 존재하지 않는 경우 그 콜백에서 "캐시에 추가"를 처리합니다.

2) limitTo startAt 및 endAt 쿼리를 사용하여 수신자를 페이지 매김하고 사용자가 볼 수없는 데이터를 가져 오는 것을 방지하십시오.

* 모든 사용자가 변경 될 때마다 모든 메시지와 모든 노드를 실제로 업데이트 할 필요가 없습니다. 모든 사용자가 20 개의 메시지를 가지고있는 100 명의 사용자가있는 채팅 그룹을 상상해보십시오. 하나의 updateChildren() 호출로 2000 개의 업데이트가 가능합니다. 확장 성이없고 사용자가 다시 볼 수없는 데이터를 업데이트하기 때문에 매우 비효율적입니다. 실생활 시나리오 (예 : 2000 채팅 메시지의 첫 번째 메시지)에서