2016-11-17 22 views
19

Cấu trúc của bảng là:truy vấn căn cứ hỏa lực nếu con của con có chứa một giá trị

  • chat
  • -> randomId
  • -> -> tham gia
  • -> - -> -> 0: 'name1'
  • -> -> -> 1: 'name2'
  • -> -> chatItems

etc

Điều tôi đang cố gắng là truy vấn bảng trò chuyện để tìm tất cả các cuộc trò chuyện chứa người tham gia bằng chuỗi tên người dùng được chuyển vào.

Dưới đây là những gì tôi có cho đến nay:

subscribeChats(username: string) { 
    return this.af.database.list('chats', { 
     query: { 
      orderByChild: 'participants', 
      equalTo: username, // How to check if participants contain username 
     } 
    }); 
} 

Trả lời

32

Một vài vấn đề ở đây:

  • bạn đang lưu trữ một thiết như một mảng
  • bạn chỉ có thể chỉ mục trên cố định đường dẫn

Đặt vs mảng

Trò chuyện có thể có nhiều người tham gia, do đó bạn đã lập mô hình này làm mảng. Nhưng điều này thực sự không phải là cấu trúc dữ liệu lý tưởng. Có khả năng mỗi người tham gia chỉ có thể tham gia trò chuyện một lần. Nhưng bằng cách sử dụng một mảng, tôi có thể có:

participants: ["puf", "puf"] 

Đó rõ ràng không phải là những gì bạn nghĩ, nhưng cấu trúc dữ liệu cho phép. Bạn có thể cố gắng bảo mật mã này và các quy tắc bảo mật, nhưng sẽ dễ dàng hơn nếu bạn bắt đầu với cấu trúc dữ liệu phù hợp hoàn toàn với mô hình của bạn.

Quy tắc chung của tôi: nếu bạn thấy mình viết array.contains(), bạn nên sử dụng tập hợp.

Tập hợp là một cấu trúc mà mỗi trẻ có thể có mặt nhiều nhất một lần, vì vậy nó tự nhiên bảo vệ chống lại các bản sao. Trong căn cứ hỏa lực bạn muốn mô hình một tập như:

participants: { 
    "puf": true 
} 

Các true đây thực sự chỉ là một giá trị giả: điều quan trọng là chúng tôi đã chuyển tên để chìa khóa. Bây giờ nếu tôi muốn thử tham gia trò chuyện này một lần nữa, nó sẽ là một noop:

participants: { 
    "puf": true 
} 

Và khi bạn muốn tham gia:

participants: { 
    "john": true, 
    "puf": true 
} 

Đây là đại diện trực tiếp hầu hết yêu cầu của bạn: a bộ sưu tập chỉ có thể chứa mỗi người tham gia một lần.

Bạn chỉ có thể chỉ số cố định đường dẫn

Với cấu trúc trên, bạn thể truy vấn để chat mà bạn đang nhập bằng:

ref.child("chats").orderByChild("participants/john").equalTo(true) 

Vấn đề là rằng đây đòi hỏi hơn bạn xác định một chỉ số trên `người tham gia/john":

{ 
    "rules": { 
    "chats": { 
     "$chatid": { 
     "participants": { 
      ".indexOn": ["john", "puf"] 
     } 
     } 
    } 
    } 
} 

Điều này sẽ hoạt động và hoạt động tuyệt vời. Nhưng giờ mỗi lần ai đó mới tham gia ứng dụng trò chuyện, bạn ' sẽ cần thêm một chỉ mục khác. Đó rõ ràng không phải là một mô hình có thể mở rộng. Chúng tôi sẽ cần thay đổi cấu trúc dữ liệu của mình để cho phép truy vấn bạn muốn.

Invert chỉ số - kéo loại lên, làm phẳng các cây

quy tắc thứ hai của ngón tay cái: mô hình dữ liệu của bạn để phản ánh những gì bạn thể hiện trong ứng dụng của bạn.

Vì bạn đang tìm kiếm để hiển thị một danh sách các phòng chat cho người dùng, lưu trữ các phòng chat cho mỗi người dùng:

userChatrooms: { 
    john: { 
    chatRoom1: true, 
    chatRoom2: true 
    }, 
    puf: { 
    chatRoom1: true, 
    chatRoom3: true 
    } 
} 

Bây giờ bạn chỉ có thể xác định danh sách các phòng chat với:

ref.child("userChatrooms").child("john") 

Và sau đó lặp lại các phím để có được mỗi phòng.

Bạn sẽ thích có hai danh sách có liên quan trong ứng dụng của bạn:

  • danh sách các phòng chat cho một người dùng cụ
  • danh sách các thành viên tham gia một phòng chat cụ thể

Trong trường hợp đó bạn cũng sẽ có cả hai danh sách trong cơ sở dữ liệu.

chatroomUsers 
    chatroom1 
    user1: true 
    user2: true 
    chatroom2 
    user1: true 
    user3: true 
userChatrooms 
    user1: 
    chatroom1: true 
    chatroom2: true 
    user2: 
    chatroom1: true 
    user2: 
    chatroom2: true 

Tôi đã kéo cả hai danh sách lên cấp cao nhất của cây, vì Firebase đề xuất chống lại dữ liệu lồng nhau.

Có cả hai danh sách là hoàn toàn bình thường trong các giải pháp NoSQL. Trong ví dụ trên, chúng tôi sẽ tham chiếu đến userChatrooms làm chỉ số ngược của chatroomsUsers.

+0

Đây là một lời giải thích tuyệt vời. Tôi có dữ liệu của tôi được lưu trữ như thế này, cho phép tôi lấy một phòng chat từ danh sách của người dùng hoặc lấy người dùng từ các thành viên của phòng trò chuyện. Nhưng những gì nó không cho phép là để quan sát '.childChanged' cho các phòng chat mà tôi là một thành viên. Nếu tôi cố gắng thiết lập một người quan sát cho các cuộc trò chuyện trong đó 'members/myId = true', thì tôi sẽ nhận được lỗi chỉ mục không xác định. Làm thế nào bạn sẽ đi về điều này? Tôi có câu hỏi được đăng tại đây: https://stackoverflow.com/questions/47769044/swift-firebase-using-an-unspecified-index –

+0

@ frank-van-puffelen tham chiếu đoạn mã cuối cùng của bạn, giả sử tôi là "user1 "và tôi lấy tất cả các chìa khóa của chatroom tôi là một thành viên của. Sau đó, đối với mỗi khóa phòng trò chuyện, tôi tìm đối tượng phòng chat đầy đủ và hiển thị danh sách các phòng trò chuyện trong danh sách. Đó là tốt, tuy nhiên, những gì nếu tôi cũng cần thiết để truy cập các đối tượng người dùng đầy đủ là một phần của một phòng chat. Nó có vẻ rất lạ mà tôi sẽ phải làm khác cho vòng lặp (2 cấp lồng nhau cho vòng lặp!) Lấy cho mỗi người dùng duy nhất trong mỗi phòng chat duy nhất để có được dữ liệu đó. Có cách nào tốt hơn? – damirstuhec

Các vấn đề liên quan