2

I've made a cloud function for removing the expired alert messages and my data is structured like below:

Alerts
|-{country1}
  |-{c1_state1}
    |-{key}
      |-msg: "bla bla"
      |-end: 1601251200000
  |-{c1_state2}
    |-{key}
      |-msg: "bla bla"
      |-end: 1601251200000

|-{country2}
  |-{c2_state1}
    |-{key}
      |-msg: "bla bla"
      |-end: 1601251200000
  |-{c2_state2}
    |-{key}
      |-msg: "bla bla"
      |-end: 1601251200000

Looking at the log messages, I noticed that there are lots of warnings for each of the query in the for loop (states variable).

[2020-09-29T02:04:28.585Z] @firebase/database: FIREBASE WARNING: Using an unspecified index. Your data will be downloaded and filtered on the client. Consider adding ".indexOn": "end" at /Alerts/BR/RR to your security rules for better performance.

I've searched a lot about setting rules in firebase database, but I couldn't put the rules to work. In my database, I am looping in countries and states and that's why I used wildcards ($coutry and $state)

{
  /* Visit https://firebase.google.com/docs/database/security to learn more about security rules. */
  "rules": {
    ".read": "auth != null",
    ".write": "auth != null",
    "Alerts": {
      "$country": {
        "Sstate": {
          ".indexOn": ["end"]
        }
      }
    }
  }
}

My function works and the data get deleted as expected, but the warnings keep coming.

exports.closeAnnouncementsBRTZ3 = functions.pubsub
.schedule('10 0 * * *')
.timeZone('America/Sao_Paulo') // Users can choose timezone - default is America/Los_Angeles
.onRun((context) => {

  const expireTime = 1601251200000;

  const ref = admin.database().ref().child('Alerts').child('BR');
  
  const states = ['AC', 'AM', 'MS', 'MT', 'RO', 'RR'];
  
  return Promise.all(states.map(async (state) => {
    return await ref.child(state).orderByChild('end').endAt(expireTime).once('value', (dataSnapshot) => {
      console.log('await dataSnapshot: ', state);
      if (dataSnapshot.val() !== null) {
        dataSnapshot.forEach(childSnapshot => {
          console.log('child to be removed: ', childSnapshot.key);
          childSnapshot.ref.remove();
        });
      }
    });
  }));
  
});

So, how to set the rules properly in a way that improves the performance of my queries and without warnings?

Frank van Puffelen
  • 565,676
  • 79
  • 828
  • 807
Aliton Oliveira
  • 1,224
  • 1
  • 15
  • 26

2 Answers2

1

Indeed, your queries will work and you might not even sense the performance issue, in case your application doesn't have much data right now, however, indeed, using wildcards might affect in your performance and an index might help you, as it won't depends on it being filtered and executed client-side.

However, as clarified in the comment of this question here, provided by Firebase Engineer, it's not possible to set up a wildcard index, unfortunately. As indicated in this case here, changing the structure of your database would help you solve this issue, as you model it in a way that you can use indexes easily and improve your peformance.

To summarize, you would need to change your database structure for it to have improved performance, as indexes don't work with wildcards as you are doing.

gso_gabriel
  • 4,199
  • 1
  • 10
  • 22
  • Thank you for your answer! Is it possible to add rules one by one? I am looping in `country` and `states` and I see no problem in adding **".indexOn": "end"** at each one of the nodes. For me, it is easier than changing the business logic of my app. – Aliton Oliveira Sep 29 '20 at 19:24
  • Hi @AlitonOliveira you won't be able to do that, unfortunately. The `indexOn` is used in the last part of the rules, where then, you will be indexing the exactly data you want. And I agree with you, while, indeed, using it would be easier then changing your logic, unfortunately, it also doesn't work with wildcards, so you won't be able to work with index this way. In case you want, you can raise a Feature Request for Google to check about implementing this [here](https://issuetracker.google.com/issues/new?component=530136&template=0). – gso_gabriel Sep 30 '20 at 06:02
1

I've solved it by adding ".indexOn": "end" at each one of the nodes, even though there are some nodes that do not exist yet.

{
  /* Visit https://firebase.google.com/docs/database/security to learn more about security rules. */
  "rules": {
    ".read": "auth != null",
    ".write": "auth != null",
    "Alerts": {
      "BR": {
        "AC": {
          ".indexOn": ["end"]
        },
        "AM": {
          ".indexOn": ["end"]
        },
        "MS": {
          ".indexOn": ["end"]
        },
        "MT": {
          ".indexOn": ["end"]
        },
        "RO": {
          ".indexOn": ["end"]
        },
        "RR": {
          ".indexOn": ["end"]
        }
      }
    }
  }
}

It's not a convenient way of solving it, because we have to set the rule for every possible node in our project. But once it's done... it's done!

Aliton Oliveira
  • 1,224
  • 1
  • 15
  • 26