-1

In my Android project I need Firebase to load/retrieve my list of 'children' (user with name) when retrieving an object (group) that is in a Many-to-Many relationship, like the structure below:

App {
   "users" : {
        "user123jsbgkjwroi" : {
            "name" : "Mr Guy", 
            "groups" : {
                "group567-alkfhhiuall_dajk" : true,
                ...
            }
        }
    }
    "groups" : {
        "group567-alkfhhiuall_dajk" : {
            "name" : "My Group 1",
            "groupShortCode" : "567",
            "groupPassPhrase" : "ABC",
            "members" : {
                 "user123jsbgkjwroi" : {
                     "A": 2,
                     "B": 1,
                     "C": 33
                 },
                 ...    
            }
        }
    }
} 

I've likely made my life harder than necessary because I'm new to NoSQL, my group object uses the user as a map key, against another object which contains the list (A, B, C). Something like the following:

class Group {
    private String name;
    private Map<User, GroupItems> members;
}

When a new User joins the group, by entering the groupShortCode and groupPassPhrase I need to load the whole Group object, with the other Users (members) of the group.

But I do not want to do this recursively (I would rather fetch as few documents as possible - I should not load the User's other groups). Here's what I've tried:

dbReference.child("groups").orderByChild("groupShortCode").equalTo(...)
           .addListenerForSingleValueEvent(new ValueEventListener() {

    @Override
    public void onDataChange(@NonNull DataSnapshot dataSnapshot) {
        List<User> users = new ArrayList<>();

        //TODO: Here I need load each User, but it means tons of calls   
        Iterable<DataSnapshot> profileKeys = dataSnapshot.child("members").getChildren();
        for (DataSnapshot keySnapshot : profileKeys) {
            dbReference.child("users").child(keySnapshot.getKey())
              .addListenerForSingleValueEvent( ... );
        }         

        //this constructor is because of the Object-to-Object Map so dataSnapshot.getValue() fails
        Group group = new Group(dataSnapshot, users);
        ...
    }

    @Override
    public void onCancelled(@NonNull DatabaseError databaseError) {
        ...
    }

});

How do I do this properly, efficiently!?

Nick Cardoso
  • 20,807
  • 14
  • 73
  • 124
  • 1
    It is more common to have four top-level lists for this scenario: `groups`, `users`, `groupUsers`, and `userGroups`. This allows you to load precisely what you need, at the cost at some extra code. See https://stackoverflow.com/questions/41527058/many-to-many-relationship-in-firebase. Aside from that: what's the problem with the code you shared? – Frank van Puffelen Sep 15 '18 at 15:49
  • It just seemed to me like I must be doing something wrong. For my user to log in and then join a group with 8 users I'm going to make 10 or 11 individual requests. Seems like a lot of room for failure – Nick Cardoso Sep 15 '18 at 16:10
  • What type of failure are you thinking of? It's quite some code, so definitely some room for mistakes there. But that's a one-time thing, where you should easily catch the errors during development. After that, all requests go over a single connection, so it's quite unlikely that one succeeds and other fails. – Frank van Puffelen Sep 15 '18 at 16:20
  • For example some 'User' call fails, or never returns at all (if that is possible - you've indicated not). Also because it's all asynchronous I have to wait and know when everything is loaded, before I construct my Group Object – Nick Cardoso Sep 17 '18 at 07:19
  • All calls go over the same connection, so chances of one failing due to connection problems are no bigger with multiple calls than they'd be with a singe call. See http://stackoverflow.com/questions/35931526/speed-up-fetching-posts-for-my-social-network-app-by-using-query-instead-of-obse/35932786#35932786 – Frank van Puffelen Sep 17 '18 at 14:28
  • Thanks for the feedback. Do you feel like adding it as an answer? – Nick Cardoso Sep 17 '18 at 16:49

1 Answers1

1

It is more common to have four top-level lists for this scenario: groups, users, groupUsers, and userGroups. This allows you to load precisely what you need, at the cost at some extra code. See Many to Many relationship in Firebase.

If you're worried about problems, it may help to know that all requests go over a single connection, so it's quite unlikely that one succeeds and other fails. Chances of one failing due to connection problems are no bigger with multiple calls than they'd be with a singe call. See Speed up fetching posts for my social network app by using query instead of observing a single event repeatedly

Frank van Puffelen
  • 565,676
  • 79
  • 828
  • 807