Querying and Filtering
Table of contents
- Example data
- Simple Queries
- Get First result
- Query operators
- List membership
- in and array-contains-any
- Compound queries
- Sub collection
- Query limitations
FireO provides powerful query functionality for specifying which documents you want to retrieve from a collection. These queries can also be used with either get()
or fetch()
Example data
To get started, write some data about cities so we can look at different ways to read it back:
const {Model, Field} = require("fireo");
class City extends Model{
short_name = Field.ID();
name = Field.Text();
state = Field.Text();
country = Field.Text();
capital = Field.Boolean();
population = Field.Number();
regions = Field.List();
}
const c1 = City.fromObject({
short_name: 'SF', name: 'San Francisco', state: 'CA', country: 'USA',
capital: false, population: 860000, regions: ['west_coast', 'norcal']
});
await c1.save();
const c2 = City.fromObject({
short_name: 'LA', name: 'Los Angeles', state: 'CA', country: 'USA',
capital: false, population: 3900000, regions: ['west_coast', 'socal']
});
await c2.save();
const c3 = City.fromObject({
short_name: 'DC', name: 'Washington D.C.', state: 'CA', country: 'USA',
capital: true, population: 680000, regions: ['east_coast']
});
await c3.save();
const c4 = City.fromObject({
short_name: 'TOK', name: 'Tokyo', country: 'Japan',
capital: true, population: 9000000, regions: ['kanto', 'honshu']
});
await c4.save();
const c5 = City.fromObject({
short_name: 'BJ', name: 'Beijing', country: 'China',
capital: true, population: 21500000, regions: ['hebei']
});
Simple Queries
The following query returns all cities with state CA
await City.collection.where('state', '==', 'CA').fetch();
The following query returns all the capital cities
await City.collection.where('capital', '==', true).fetch();
Get First result
After creating a query object, use the get()
function to retrieve the first matching result. fetch()
return all mating result.
The following query returns first matching city with state CA
const city = await City.collection.where('state', '==', 'CA').get();
Query operators
The where()
method takes three parameters: a field to filter on, a comparison operation, and a value. The comparison can be <
, <=
, ==
, >
, >=
, array-contains
, in
and array-contains-any
.
Some example filters:
await City.collection.where('state', '==', 'CA');
await City.collection.where('population', '<', 1000000);
await City.collection.where('name', '>=', 'San Francisco');
List membership
You can use the array-contains
operator to filter based on list values. For example:
City.collection.where('regions', 'array_contains', 'west_coast');
This query returns every city document where the regions field is an array that contains west_coast. If the array has multiple instances of the value you query on, the document is included in the results only once.
in and array-contains-any
Use the in
operator to combine up to 10 equality (==
) clauses on the same field with a logical OR
. An in
query returns documents where the given field matches any of the comparison values. For example:
City.collection.where('country', 'in', ['USA', 'Japan']);
This query returns every city
document where the country
field is set to USA
or Japan
. From the example data, this includes the SF
, LA
, DC
, and TOK
documents.
Similarly, use the array-contains-any
operator to combine up to 10 array-contains
clauses on the same field with a logical OR
. An array-contains-any
query returns documents where the given field is an array that contains one or more of the comparison values:
City.collection.where(
'regions', 'array_contains_any', ['west_coast', 'east_coast']
)
This query returns every city document where the region
field is an array that contains west_coast
or east_coast
. From the example data, this includes the SF
, LA
, and DC
documents.
Results from array-contains-any
are de-duped. Even if a document’s array field matches more than one of the comparison values, the result set includes that document only once.
array-contains-any
always filters by the array data type. For example, the query above would not return a city document where instead of an array, the region
field is the string west_coast
.
You can use an array value as a comparison value for in
, but unlike array_contains_any
, the clause matches for an exact match of array length, order, and values. For example:
City.where(
'regions', 'in', [['west_coast'], ['east_coast']]
)
This query returns every city document where the region
field is an array that contains exactly one element of either west_coast
or east_coast
. From the example data, only the DC
document qualifies with its region
field of ["east_coast"]
. The SF
document, however, does not match because its region
field is ["west_coast", "norcal"]
.
Limitations
Note the following limitations for in
and array-contains-any
:
in
andarray-contains-any
support up to 10 comparison values.- You can use only one
in
orarray-contains-any
clause per query. You can’t use bothin
andarray-contains-any
in the same query. - You can combine
array-contains
within
but not witharray-contains-any
.
Compound queries
You can also chain multiple filter()
methods to create more specific queries (logical AND
). However, to combine the equality operator (==
) with a range or array-contains clause (<
, <=
, >
, >=
, or array-contains
), make sure to create a composite index.
const sydney_query = City.collection.where('state', '==', 'CO').where('name', '==', 'Denver');
const large_us_cities_query = City.collection.where('state', '==', 'CA').where('population', '>', 1000000);
You can only perform range comparisons (<
, <=
, >
, >=
) on a single field, and you can include at most one array-contains
clause in a compound query:
Valid: Range filters on only one field
City.collection.where('state', '>=', 'CA').where('state', '<=', 'IN');
Invalid: Range filters on different fields
City.collection.where('state', '>=', 'CA').where('population', '>=', 1000000)
Sub collection
Sub collection queries work in same fashion but you need to pass parent_key
to search in specific collection.
Sample Data
const {Model, Field} = require("fireo");
class Post extends Model{
title = Field.Text();
content = Field.Text();
}
class Review extends Model{
name = Field.Text();
stars = Field.Number();
}
const p = Post.fromObject({title: "First Post", content: "Some Content"});
await p.save();
const r1 = Review.init({parent: p.key});
r1.name = 'Azeem';
r1.stars = 5;
await r1.save();
const r2 = Review.init({parent: p.key});
r2.name = 'Arfan';
r2.stars = 3;
await r2.save();
Example Usage
The following query returns all reviews which is posted by Azeem
await Review.collection.parent(post_key).where('name', '==', 'Azeem').fetch();
Query limitations
Cloud Firestore does not support the following types of queries:
- Queries with range filters on different fields, as described in the previous section.
- Queries with a
!=
clause. In this case, you should split the query into a greater-than query and a less-than query. For example, although the query clausewhere("age", "!=", "30")
is not supported, you can get the same result set by combining two queries, one with the clausewhere("age", "<", "30")
and one with the clausewhere("age", ">", 30)
. - Cloud Firestore provides limited support for logical
OR
queries. Thein
andarray-contains-any
operators support a logicalOR
of up to 10 equality (==
) orarray-contains
conditions on a single field. For other cases, create a separate query for eachOR
condition and merge the query results in your app.