0

I'm relatively new to Firebase Database, having used MySQL up until now. I know that there's no 'associated' data in Firebase as such, but I think I'm still trying to create it, because that's how my mind works! So, it may be that the problem I'm having is because my data is formatted badly - I'd appreciate any pointers.

In my app (using Android Studio), each user can have a number of Boxes. Each Box uses a single colour Palette (it can be the default one, or a user-defined one). A Palette consists of a number of Colours.

Currently, my data is like this:

Boxes
   BoxKey1
      name: Test Box
      paletteKey: paletteKey1
      belongsTo: userKey1
   BoxKey2
      ... etc ...
Colours
   ColourKey1
       name: Red
       hexCode: ff0000
   ColourKey2
       name: Blue
       ... etc ...
Palettes
   PaletteKey1
       name: default
       colours
           ColourKey1: true
           ColourKey7: true
           ... etc ...
   PaletteKey2
       ... etc ...
Users
   UserKey1
      name: Joe Bloggs
      boxes
          BoxKey1: true
          BoxKey5: true
   ... etc ...

So, I can retrieve a list of the User's Boxes easily enough, and list all the names. If the User clicks on a name, then the Box with that name is retrieved and displayed. I also need to display the Palette used (the name and the Colours it contains).

In the Activity, I retrieve the Box as follows:

    mBox.setKey(boxKey);
    mBox.initialiseBox(new ValueEventListener() {
        @Override
        public void onDataChange(DataSnapshot dataSnapshot) {
            mBox = dataSnapshot.getValue(Box.class);
            boxName.setText(mBox.getName()); // Show Box name
// AT THIS POINT I HAVE THE BOX DETAILS, BUT I NEED THE PALETTE DETAILS TOO
        }
    });

In the Box class, initialiseBox looks like this:

public void initialiseBox(ValueEventListener listener) {
    if(this.key == null) return;
    DatabaseReference mBoxReference = FirebaseDatabase.getInstance().getReference()
            .child("boxes").child(this.key);
    mBoxReference.addListenerForSingleValueEvent(listener);
}

That's working fine, but at this point I've only retrieved the Palette key from the database along with the other Box data. How do I then get the actual Palette, with all its Colours, so I can show those as well?

I've been trying to do a kind of 'nested listener' like this in the Main Activity:

    mBox.initialiseBox(new ValueEventListener() {

        @Override
        public void onDataChange(DataSnapshot dataSnapshot) {

            // Same as before
            mBox = dataSnapshot.getValue(Box.class);
            boxName.setText(mBox.getName()); // Show Box name

            // Now add a new listener for the Palette
            mPalette.setKey(mBox.getPaletteKey());
            mPalette.initialisePalette(new ValueEventListener() {
                 @Override
                 public void onDataChange(DataSnapshot dataSnapshot) {
                      mPalette = dataSnapshot.getValue(Palette.class);
                      paletteName.setText(mPalette.getName());

                 }
            });
        }
    });

but it seems very unwieldy, and I can't quite get it to work (the Palette isn't getting populated, so at the paletteName.setText bit I'm getting an error).

Is this the correct approach to be taking? If not, what should I be doing? And if it's the right idea, can anyone see where I'm going wrong?

halfer
  • 19,824
  • 17
  • 99
  • 186
Sharon
  • 3,471
  • 13
  • 60
  • 93

1 Answers1

1

Related data in Firebase (and many other NoSQL databases) are almost as common as in SQL databases. The main differences are:

  1. In Firebase the relation is not managed for you by the DBMS. This means that you need to write your own code (and possibly server-side security rules) to ensure ensure the relationship stay in-tact.
  2. You often will duplicate some of the data in Firebase, which reduces the need for lookups. But this comes at the cost of needing to duplicate data during a write, and you'll need to consider strategies for keeping the duplicated data up to date.

What you're doing is called a client-side join and is indeed one valid way to get data from different top-level nodes into your app.

Whether it is the best approach to do this with a nested listener depends on the data and use-case. Things to consider here:

  1. If the target list is short, you might simply want to preload the entire list and remove the need for nested joins.
  2. What do you need from the joined item? If it's just a single property (e.g. the name of the palette), consider if it's worth it to duplicate that property under the source to remove the need for a nested join.
  3. Nesting the code is going to rapidly become unreadable. Pull out the listener into a utility-class, and invoke it with a simpler interface. i.e. you'll probably want to centralize the error handling, which you can perfectly do in the helper class. This leaves you with a single callback methods, which can be lambdafied in Java 8 - making it much easier to read.
  4. I often recommend that you model the screens of your app in Firebase. So if your app has a list of user names, model a list of user names in your database. Right now you also nest the box IDs under that list, which means you'll end up loading the box IDs for all users, just to show their names.

I highly recommend reading NoSQL data modeling and watching Firebase for SQL developers.

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