1
votes

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?

2

2 Answers

1
votes

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.

0
votes

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!