1

New to noSQL and particularly Firebase. I have a list of articles being retrieved from Firebase with this basic structure:

{
  "Items" : [ {
    "ArticleID" : 0,
    "Title" : "Article1",
    "Teaser" : "Lorem Ipsum Dolor",
    "PublishDate" : "2017-05-23",
    "ArticleText" : "Some article text here...",
    "Category" : "Sports"
  },
  {
    "ArticleID" : 1,
    "Title" : "Article2",
    "Teaser" : "Lorem Ipsum Dolor",
    "PublishDate" : "2017-05-22",
    "ArticleText" : "Some article text here...",
    "Category" : "World"
  },
  {
    "ArticleID" : 2,
    "Title" : "Article3",
    "Teaser" : "Lorem Ipsum Dolor",
    "PublishDate" : "2017-05-23",
    "ArticleText" : "Some article text here...",
    "Category" : "US News"
  }
  ],
  "UserActions" : [
    {
      "UID" : "3049873049870987x87098",
      "ArticleID" : 0,
      "Read" : true,
      "Liked" : true,
      "Saved" : false
    },
    {
      "UID" : "3049873049870987x87098",
      "ArticleID" : 2,
      "Read" : true,
      "Liked" : false,
      "Saved" : false
    }
  ]
}

As a registered user is viewing/browsing the articles, we need to know if they've already read the article, liked it, or saved it for later reading so we can show the appropriate icon next to the article.

I'm not sure if I'm structuring this correctly. Something tells me I'm diving into old relational db habits here.

Hareesh
  • 6,770
  • 4
  • 33
  • 60
Indy-Jones
  • 668
  • 1
  • 7
  • 19

1 Answers1

6

Modeling for NoSQL database depends, more than anything else, on the use-cases in your app. For some predictable use-cases, I see one thing you're doing well and one thing that can be improved.

Keep your data structure flat

You didn't fall for the common trap of wanting to nest the user actions under the articles themselves. I'm not sure if this is your SQL background kicking in, but it's the right solution. In most apps you won't need to read the user actions at the same time as you read a list of articles. Keeping them separate makes that possible. It also allows you to keep separate security rules for articles and user actions.

But...

Create a data structure that scales

In your current solution you've modeled the user actions as a flat list. One of the most common use-cases for this list is likely to get the actions for a specific user. That now requires you to query the list of all user actions for a (likely quite small) subset of the actions. Over time this may become a scalability limit.

It is better to structure the data for how you want to read it. If you want to frequently get the actions for a user, store the actions per user:

"UserActions": {
  "3049873049870987x87098": {
    "ArticleID" : 0,
    "Read" : true,
    "Liked" : true,
    "Saved" : false
  },
  {
    "ArticleID" : 2,
    "Read" : true,
    "Liked" : false,
    "Saved" : false
  }
}

Now reading the list of actions for a specific user is as simple as reading (not querying) the data under /users/<UID>.

If you also want the use-case to show the users that have liked a specific article, you'll also want to keep an inverted version of the structure above, something like ArticleActions. This duplication of data may feel unnatural at first, but is key to having a database that scales with the number of users/articles/actions.

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

Frank van Puffelen
  • 565,676
  • 79
  • 828
  • 807
  • What a great answer. Thank you! Just curious, in an Angular situation, I'm grabbing the list of the articles (let's say the latest 100). In the ngFor I'm obviously looping through each one. Do I need to do some type of "join" to this other list so that I can then use something like
    Mark as Read
    – Indy-Jones Jul 31 '17 at 18:17
  • Angular3/4 joins with AngularFire2: https://stackoverflow.com/questions/38421551/joining-flattened-data – Frank van Puffelen Jul 31 '17 at 18:40