firebasefirebase-realtime-databasefirebase-tools

Avoiding duplicate data in a Realtime Database


In a realtime database (on Firebase), I have the following rules for the collection called Resource:

{
  "rules": {
    ....
      "Resource": {
        ".read": true,
          ".indexOn": ["FieldOne","FieldTwo"]
      },
    ....
  }
}

Things work the way I wish. I would like nevertheless to make some improvements.

Here is my issue. At this point I can add a document to the collection from the CLI using a command like this one:

firebase database:push /..path../Resource --data '{"FieldOne":"field-1-value","FieldTwo":"field-2-value"}'

I can of course also add documents in different ways. But my problem is that I can repeat the same command as above and add again the same document in the Resource collection.

I want to know how to change my rules to avoid the possibility of adding duplicate data.

Precisely, this is what I would like, after executing the command above, I should still be able to insert these documents in the collection:

{"FieldOne":"field-3-value","FieldTwo":"field-4-value"}
{"FieldOne":"field-1-value","FieldTwo":"field-5-value"}
{"FieldOne":"field-7-value","FieldTwo":"field-2-value"}

But, I should not be able to insert this one:

{"FieldOne":"field-1-value","FieldTwo":"field-2-value"}

I could of course write code to control what is coming in, in the case of cloud functions or in the case of data being typed in through a web interface. But this is not the point of the present question. I just want to know if there is a smart way of setting up the rules to prevent the possibility of unwanted data from start.


Solution

  • Firebase Realtime Database has no built-in support to prevent duplicate values.

    On the other hand, the keys in a node are guaranteed to always be unique. This means that you'll typically want to store the values that you want to be unique in the keys in the database/JSON, rather than in values.


    Let's first look at what currently happens

    When you call push/POST, Firebase generates unique keys

    When you call push in Firebase (or POST on its REST API), the database or SDK generates a unique key for you. So in your example with the three child nodes (as Doug commented, Firebase Realtime Database has no concept of documents or collections, and I'll use the correct terminology here to prevent confusion) the JSON will actually be something like this:

    "Resource": {
      "-O12y93419y123": {"FieldOne":"field-3-value","FieldTwo":"field-4-value"},
      "-O12y93556as94": {"FieldOne":"field-1-value","FieldTwo":"field-5-value"},
      "-O12y93645ruiq": {"FieldOne":"field-7-value","FieldTwo":"field-2-value"}
    }
    

    This keys starting with a - are the push keys that Firebase generates. So if you call push/POST again (with different or the same) values, it generates another unique key.

    Generate a key based on the values that must be unique

    To prevent duplicates, you must make sure that the key is a presentation of the values - so that if you pass the same values a second time, you'll get the same key.

    One way to do that is to simple concatenate the values and use that as the key. So that would turn the above JSON into:

    "Resource": {
      "field-3-value_field-4-value": {"FieldOne":"field-3-value","FieldTwo":"field-4-value"},
      "field-1-value_field-5-value": {"FieldOne":"field-1-value","FieldTwo":"field-5-value"},
      "field-7-value_field-2-value": {"FieldOne":"field-7-value","FieldTwo":"field-2-value"}
    }
    

    In this structure, if you ever pass the same values into the database, it'll have the same key. And as said at the start: the keys in a node are guaranteed to always be unique. So, depending on the operation you use, the second write of the same values/key will either be rejected or it'll overwrite the existing values (with the same values, so it's a no-op).

    Using set/PUT rather than push/POST

    To write the above structure where you specify your own key, you need to use the set/PUT operation instead of using push/POST. So your example with the Firebase CLI would become:

    firebase database:set /..path../Resource/field-1-value_field-2-value --data '{"FieldOne":"field-1-value","FieldTwo":"field-2-value"}'
    

    In here you can see that we now:


    This topic has been covered before, so I also recommend checking out: