Hi, Firebase developers!
In what might be my shortest (but most exciting) blog post this year, we wanted to let you know that Cloud Firestore now has support for not-equal queries. This means you can now query, for example, all documents in a "Projects" collection where the project's status field is not equal to the value "completed"
status
"completed"
On a similar note, Cloud Firestore also supports not-in queries, where you can query for documents where fields are not in a list of values. So you can, for example, find all documents in a "Projects" collection where the status isn't equal to "completed" or "dropped" with a single query.
not-in
"dropped"
Note that neither of these calls will allow you to fetch documents where this field doesn't exist. If a field is completely missing from a document, it will not be returned in your query results.
Notice that project 4593 does not get included in the results, because it has no owner field
When it comes to combining these not-equal operators with others in the same query, they have many of the same restrictions as other inequality operators (<, >=, etc.). You can't use a != operator against two different fields, for instance. Similarly, you can't use a != query on one field and then sort by a second field. And combining a != query in one field with a == query in another field requires your creating a composite index. Make sure to check out the official documentation for all the details.
<
>=
!=
==
This functionality is currently supported by the iOS, Android and Web client libraries, as well as the Node.js and Java server-side SDKs. Support for C++ and other server libraries is coming soon.
We hope this new addition makes it a little easier to develop Cloud Firestore-powered applications, and as always, if you have questions, please feel free to reach out on StackOverflow.
Happy coding!
Hello, Cloud Firestore developers! We wanted to let you know about some useful new querying features we've added to Cloud Firestore this week. Starting with… in queries!
in
With the in query, you can query a specific field for multiple values (up to 10) in a single query. You do this by passing a list containing all the values you want to search for, and Cloud Firestore will match any document whose field equals one of those values.
in queries are a good way to run simple OR queries in Cloud Firestore. For instance, if the database for your E-commerce app had a customer_orders collection, and you wanted to find which orders had a "Ready to ship", "Out for delivery" or "Completed" status, this is now something you can do with a single query, like so:
customer_orders
We've launched another feature similar to the in query, the array-contains-any query. This feature allows you to perform array-contains queries against multiple values at the same time.
array-contains-any
array-contains
For example, if your app had a products collection, and those documents contained an array of categories that every item belongs in, you could now look for items that were in the "Appliances" or "Electronics" category, by passing these values into a single array-contains-any query.
Note that the baby monitor document will only be returned once in your query, even though it matches with multiple categories.
These queries are also supported in the Firebase console, which gives you the ability to try them out on your dataset before you start modifying your client code.
This also seems like a good time to remind you that you can apply filters directly in the Firebase console. Neat, huh?
Security rule behavior for these queries is pretty straightforward. Cloud Firestore will look at each potential value passed in for your in or array-contains-any operation and make sure your query would be allowed for that value. If any value is not allowed, the entire query fails.
For example, if your project was set up with these security rules…
match /projects/{project} { allow read: if resource.data.status != "secret"; ... }
This request would work…
db.collection("projects").where("status", "in", ["public", "unlisted"]);
...but this entire request would fail, because it's possible that our query will return documents that are forbidden in our security rules.
db.collection("projects").where("status", "in", ["public", "unlisted", "secret"]);
Not sure why we couldn't just send you back the allowed documents? Make sure to review the 'Rules are not filters' section of this video.
While we're excited to have you unlock the potential of in queries and array-contains-any queries, you should know about a few important limitations:
I think there's a lot of exciting things you can do now with in queries, and we're looking forward to hearing what new functionality you've added to your apps. So make sure you've upgraded your client libraries to the latest versions to take advantage of the new features, check out the documentation, and happy databasing!
Hey, there Firebase developers. Did you hear the exciting news? Last month at Google I/O, we announced support for collection group queries in Cloud Firestore! Let's dig into this new feature a little more, and see if we answer some of your burning questions…
Q: So, what are collection group queries and why should I care?
In Cloud Firestore, your data is divided up into documents and collections. Documents often point to subcollections that contain other documents, like in this example, where each restaurant document contains a subcollection with all the reviews of that restaurant.
In the past, you could query for documents within a single collection. But querying for documents across multiple collections wasn't possible. So, for instance, I could search for all reviews for Tony's Tacos, sorted by score, because those are in a single subcollection.
But if I wanted to find reviews for all restaurants where I was the author, that wasn't possible before because that query would span multiple reviews collections.
reviews
But with collection group queries, you're now able to query for documents across a collection group; that is, several collections that all have the same name. So I can now search for all the reviews I've written, even if they're in different collections.
Q: Great! So how do I use them?
The most important step in using a collection group query is enabling the index that allows you to run a query in the first place. Continuing our example, if we want to find all reviews that a particular person has written, we would tell Cloud Firestore, "Go index every author field in every single reviews collection as if it were one giant collection."
author
You can do this manually by going to the Firebase Console, selecting the "Index" tab for Cloud Firestore, going to the "Single Field" section, clicking the "Add exemption" button, specifying you want to create an exemption for the "reviews" collection with the "author" field and a "collection group" scope, and then enabling ascending and/or descending indexes.
But that's a lot of steps, and I tend to be pretty lazy. So, instead, I like enabling collection group indexes the same way I enable composite indexes. First, I'll write the code for the collection group query I want to use and attempt to run it. For example, here's some sample code I might write to search for all reviews where I'm the author.
var myUserId = firebase.auth().currentUser.uid; var myReviews = firebase.firestore().collectionGroup('reviews') .where('author', '==', myUserId); myReviews.get().then(function (querySnapshot) { // Do something with these reviews! })
Notice that I'm specifying a collectionGroup() for my query instead of a collection or document.
collectionGroup()
When I run this code, the client SDK will give me an error message, because the collection group index hasn't been created yet. But along with this error message is a URL I can follow to fix it.
Following that URL will take me directly to the console, with my collection group index ready to be created.
Once that index has been created, I can go ahead and re-run my query, and it will find all reviews where I'm the author.
If I wanted to search by another field (like rating), I would need to create a separate index with the rating field path instead of the author field.
rating
Q: Any gotchas I need to watch out for?
Why, yes! There are three things you should watch out for.
First, remember that collection group queries search across all collections with the same name (e.g., `reviews`), no matter where they appear in my database. If, for instance, I decided to expand into the food delivery service and let users write reviews for my couriers, then suddenly my collection group query would return reviews both for restaurants and for couriers in the same query.
This is (probably) not what I want, so the best thing to do would be to make sure that collections have different names if they contain different objects. For example, I would probably want to rename my courier review collections something like courier_reviews.
courier_reviews.
If it's too late to do that, the second best thing would be to add something like an isCourier Boolean field to each document and then limit your queries based on that.
isCourier
Second, you need to set up special security rules to support queries. You might think in my example that if I had a security rule like this:
I would be able to run this collection group query. After all, all of my review documents would fall under this rule, right? So why does this fail?
Well if you've seen our video on Cloud Firestore security rules, you would know that when it comes to querying multiple documents, Cloud Firestore needs to prove that a query would be allowed by the security rules without actually examining the underlying data in your database.
And the issue with my collection group query is that there's no guarantee it will only return documents in the restaurants → reviews collection. Remember, I could just as easily have a couriers → reviews collection, or a restaurant → dishes → reviews collection. Cloud Firestore has no way of knowing unless it examines the results of the data set.
So the better way to do this is to declare that any path that ends with "reviews" can be readable based on whatever security rules I want to implement. Something like this:
Note that this solution requires using version 2 of the security rules, which changes the way recursive wildcards work.
Third, keep in mind that these collection group indexes are counted against the 200 index exemptions limit per database. So before you start creating collection group indexes willy-nilly, take a moment and ask yourself what queries you really want to run, and just create indexes for those. You can always add more later.
Q: Can I do collection group queries for multiple fields?
Yes. If you're doing equality searches across multiple fields, just make sure you have an index created for each field with a collection group scope.
If you're combining an equality clause with a greater-than-or-less-than clause, you'll need to create a composite index with a collection group scope. Again, I find it's best to just try to run the query in the code and follow the link to generate the index. For instance, trying to run a collection group query for all reviews that I wrote with a rating of 4 or higher gave me a URL that opened this dialog box.
Q: It still seems like I could do all of this in a top-level collection. How are collection group queries better?
So this question is based on the idea that one alternative to creating collection group queries is to not store data hierarchically at all, and just store documents in a separate top level collection.
For instance, I could simply keep my restaurants and my reviews as two different top-level collections, instead of storing them hierarchically.
With this setup, I can still search for all reviews belonging to a particular restaurant…
As well as all reviews belonging to a particular author…
And you'll notice that with the separate top level collection, I no longer need to use one of my 200 custom indexes to create this query.
So, why go with the subcollection setup? Are collection group queries needed at all? Well, one big advantage to putting documents into subcollections is that if I expect that I'll want to order restaurant reviews by rating, or publish date, or most upvotes, I can do that within a reviews subcollection without needing a composite index. In the larger top level collection, I'd need to create a separate composite index for each one of those, and I also have a limit of 200 composite indexes.
Also, from a security rules standpoint, it's fairly common to restrict child documents based on some data that exists in their parent, and that's significantly easier to do when you have data set up in subcollections.
So when should you store things in a separate top level collection vs. using subcollections? If you think you have a situation where you're mostly going to be querying documents based on a common "parent" and only occasionally want to perform queries across all collections, go with a subcollection setup and enable collection group queries when appropriate. On the other hand, if it seems like no matter how you divide up your documents, the majority of your queries are going to require a collection group query, maybe keep them as a top level collection.
But if that's too hard to figure out, I would say that you should pick the solution that makes sense to you intuitively when you first think about your data. That tends to be the correct answer most of the time.
Hope that helps you get more comfortable with collection group queries! As always, if you have questions, feel free to check out our documentation, or post questions on Stack Overflow.