4

I have following character collection structure in my database (firestore)

/characters/{uid}
 - username: string
 - clan: string
 - mana: number
 - health: number
 etc...

I am trying to figure out a security rule for /characters/{uid} with following logic

service cloud.firestore {
  match /databases/{database}/documents {

    // Characters
    match /characters/{characterID} {
      allow create: if isValidUsername();
    }
  }
}

here function isValidUsername checks for various things like length, special characters etc... but one thing I can't figure out is how to check following inside of the function

Make sure that request.resource.data.username is unique i.e. not present inside any other document of /characters collection.

Ilja
  • 44,142
  • 92
  • 275
  • 498

1 Answers1

8

TL;DR: Enforcing uniqueness is only possible by creating an extra collection.

In your current structure, to know if a username is unique, you will need to read each document. This is incredibly inefficient, and on top of that it isn't possible in security rules, since they can only read a few documents per rule.

The trick is to create an extra collection usernames, where you also have a document for each user, but now the key/ID of each document is the username. With such a collection, you can check for the existence of a certain document, which is a primitive operation in the security rules.

Also see:

Frank van Puffelen
  • 565,676
  • 79
  • 828
  • 807
  • In terms of efficiency / pricing is this approach (security rules) better than querying characters where username == username, while in the app before writing new users to characters collection? – Ilja Aug 25 '18 at 14:14
  • 1
    The client would have to query all documents, which constitutes many charged reads, unless each client already has all user profiles in its cache. The approach with the extra collection will perform more (roughly 2x) document writes, but requires exactly one read to check for uniqueness after that. The latter should usually be cheaper. But each use-case is different, so some quick back-of-napkin calculations are definitely recommended. – Frank van Puffelen Aug 25 '18 at 14:35