How to query Cloud Firestore for non-existing keys of documents

Let's say I have a data model with some optional properties. This could be for example a user object with a "firstname", a "lastname" and an optional "website" property.

In Cloud Firestore only user documents with a known website would have the "website" property set, for all other user documents this property would not exist.

My questions is now how to query for all user documents without a "website" property.

Thanks.


Documents can contain properties with a null value data type (see data types documentation). This will then allow you to construct a query to limit results where the website property is null.

This is not quite the same as a missing property, but if you use custom objects to write data to Firestore, empty properties will automatically be saved as null rather than not at all. You can also manually/programmatically write a null value to the database.

In Android, I tested this using the following:

FirebaseFirestore.getInstance().collection("test").whereEqualTo("website", null).get();

Where my database structure looked like:

example firestore structure

This returned only the document inuwlZOvZNTHuBakS6GV, because document 9Hf7uwORiiToOKz6zcsX contains a string value in the website property.

I believe you usually develop in Swift, where unfortunately custom objects aren't supported, but you can use NSNull() to write a null value to Firestore. For example (I'm not proficient in Swift, so feel free to correct any issues):

// Writing data
let docData: [String: Any] = [
    "firstname": "Example",
    "lastname": "User",
    "website": NSNull()
]
db.collection("data").document("one").setData(docData) { err in
    if let err = err {
        print("Error writing document: \(err)")
    } else {
        print("Document successfully written!")
    }
}

// Querying for null values
let query = db.collection("test").whereField("website", isEqualTo: NSNull())

The documentation doesn't mention a method to query for values that don't exist, so this seems like the next best approach. If anyone can improve or suggest alternatives, please do.