1

I'm just starting to experiment with Firebase. It's a real head bender when you're used to relational databases!

I'm trying to design an app that will allow users to search for meals by barcode or name and retrieve the number of calories. Additionally, I need to be able to store the meals eaten by a user, and finally retrieve the food eaten by a user each day, week or month.

I was thinking each meal would have a unique ID (e.g. M1234 for Pizza), then I'd have 2 lookup sections - one by barcode and one by name, so that should hopefully cover the search functionality.

Each user would have the meals eaten stored in the eaten 'table' (what is the correct term for 'table' in a Firebase database?) by date, just referencing the meal by ID.

This is how I've designed the database.

  {
// Here are the users.
    "users": {
      "mchen": {
        "name": "Mary Chen",
        "email": "mary@chen.com",

        }
      },
      ...
    },
// Here are the meals eaten by date.
    "eaten": {
      "mchen": {
         // index Mary's meals in her profile /eaten/mchen/meals/20161217 should return 'M1234' (pizza) and 'M8765' (chips)
        "meals": {
          "20161217": {
             "M1234": true,
             "M8765": true
          },
          "20161218": {
             "M2222": true,
             "M8765": true
          }
      },
      ...
    },
// Here are the meals with calorie information.
    "meals": {
      "M1234": {
        "name": "Pizza"
        "calories": 400
      },
      "M2222": {
        "name": "Curry"
        "calories": 250
      },
      "M8765": {
        "name": "Chips"
        "calories": 100
      },
    },
// Here is the barcode lookup
    "barcode-lookup": {
      "12345678": {
        "id": "M1234"
      },
      "87654321": {
        "id": "M2222"
      },
      "11223344": {
        "id": "M8765"
      }
    },
// Here is the name lookup
    "name-lookup": {
      "Chips": {
        "id": "M8765"
      },
      "Pizza": {
        "id": "M1234"
      },
      "Curry": {
        "id": "M2222"
      }
    }

  }

Does it seem reasonable or are there any obvious flaws?

halfer
  • 19,824
  • 17
  • 99
  • 186
Damian
  • 1,652
  • 4
  • 26
  • 44
  • This may help: [NoSQL Data Modeling Techniques](https://highlyscalable.wordpress.com/2012/03/01/nosql-data-modeling-techniques/) – Kato Dec 21 '16 at 19:57

2 Answers2

1

The structure looks fine (though I would let firebase generate the ids). The only thing that won't work like what you're expecting is searching. Based on your data if I searched for pizza you couldn't write a query that would return the Pizza entry. My suggestion would be to either use Algolia (or something similar) for searching or to roll another key with your name lowerCased to make it possible for a query to work. The only issue with running your own is you won't be able to search for things like izz and have Pizza turn up. See my answer Firebase - How can I filter similarly to equalTo() but instead check if it contains the value? for how to do a search.

Community
  • 1
  • 1
Mathew Berg
  • 28,625
  • 11
  • 69
  • 90
  • Based on the structure, I don't think the OP will be searching for Pizza. He will be searching for M1234, which would work just fine. – Jay Dec 21 '16 at 19:31
  • Thanks Mathew. I'll definitely let Firebase generate the ids for me, and thanks for the search info - it might be enough for me to search for meals that begin with the search string so ending with '\uf8ff' may well work in my case! – Damian Dec 22 '16 at 08:50
1

You will want to leverage .childByAutoId() and let Firebase create the parent key names. It's best practice to disassociate your child data from the parent node and allowing Firebase to create 'random' key's for the parents will make that work.

Along with that, it's customary to create a /users node and the parent nodes for each user would be the uid which was created by Firebase when the user was first created.

In your original structure, there's a barcode and name lookup which I have integrated into the following structure to reduce complexity.

users
  uid_0
    name: "Mary Chen",
    email: "mary@chen.com"
  uid_1
    name: "Larry David"
    email: "ldavid@david.com"

and then the dining

dining
  -Yuiia09skjspo
    dining_timestamp: "20161207113010"
    Y79joa90ksss: true
    Yjs9990kokod: true
    user: uid_0
    uid_timestamp: "uid_0_ 20161207113010"
  -Yi9sjmsospkos
    dining_timestamp: "20161207173000"
    Y79joa90ksss: true
    Yjs9990kokod: true
    user: uid_1
    uid_timestamp: "uid_1_ 20161207173000"

and the meals the user can choose from

meal
  -Y79joa90ksss
    name: "Pizza"
    calories: "400"
    barcode: "008481816164"
  -Yjs9990kokod
    name: "Burger"
    calories: "520"
    barcode: "991994411815"

As you can see, the dining node contains a dining event for each user (so all of the dining events are in one node)

This enables you to query for all kinds of things:

All dining for all users by date or range of dates.
All dining that contain a certain meal
All meals by a user
->The cool one<- all dining for a specific user within a date range.

The one omission is a search for dining that contains two meals, however, the solution to that is also in this answer.

All in all, your structure is sound - just needs a little tweaking.

Jay
  • 34,438
  • 18
  • 52
  • 81
  • Thanks for the in depth answer Jay. Using your structure, would I be able to quickly retrieve the calories a meal contains if I only know the barcode? Sorry if that's a dumb question - I'm going to be using AngularJS to write this app but haven't really got much of a grasp on what can actually be done yet. I thought it best to try design the database first. – Damian Dec 22 '16 at 08:51
  • @Damian I think you are going the right direction! You can query the meal node for any name, calories or barcode, so yes. Also, you could for example query the meal node for any meal that is between 300 and 500 calories as well using a .startingAt and .endingAt query. Also if you want to know the total calories in a single dining experience, you would read that dining node, which would retrieve the timestamp, uid and which meals (by reference) were included. You would then query for each meal by their reference to get the calories for each. – Jay Dec 22 '16 at 12:15
  • Technically, barcodes are supposed to be unique so they could be used as a key for the meals in the meal node. However, it's still best practice to disassociate the parent name from the child data. – Jay Dec 22 '16 at 12:18
  • Thanks again Jay. I'll put some code together based on your design. Damian – Damian Dec 22 '16 at 14:43
  • @Jay hey do you mind to take a look at this problem as well? https://stackoverflow.com/questions/44962262/firebase-database-query-from-multiple-childs – QWERTY Jul 07 '17 at 07:07