Queries are created by building on the firebase.database.Reference
.
db.list('/items', ref => ref.orderByChild('size').equalTo('large'))
Method | Purpose |
---|---|
orderByChild |
Specify a child to order by. |
orderByKey |
Boolean to order by Firebase Database keys. |
orderByValue |
Specify a value to order by. |
orderByPriority |
Boolean to order by Firebase Database priority. |
equalTo 2 |
Limit list to items that contain certain value. |
limitToFirst |
Sets the maximum number of items to return from the beginning of the ordered list of results. |
limitToLast |
Sets the maximum number of items to return from the end of the ordered list of results. |
startAt 2 |
Return items greater than or equal to the specified key or value, depending on the order-by method chosen. |
endAt 2 |
Return items less than or equal to the specified key or value, depending on the order-by method chosen. |
1 This is the old way of doing things and is no longer recommended for use. Anything you can achieve with orderByPriority
you should be doing with orderByChild
.
2 The Firebase SDK supports an optional key
parameter for startAt
, endAt
, and equalTo
when ordering by child, value, or priority. You can specify the key
parameter using an object literal that contains the value
and the key
. For example: startAt: { value: 'some-value', key: 'some-key' }
.
To learn more about how sorting and ordering data works in Firebase, check out the Firebase documentation on working with lists of data.
Queries can only be ordered by one method. This means you can only specify
orderByChild
, orderByKey
, orderByPriority
, or orderByValue
.
// WARNING: Do not copy and paste. This will not work!
ref.orderByChild('size').equalTo('large').orderByKey(true)
You can only use limitToFirst
or limitToLast
, but not both in combination.
// WARNING: Do not copy and paste. This will not work!
ref.limitToFirst(10).limitToLast(100)
To enable dynamic queries one should lean on RxJS Operators like switchMap
.
An RxJS Subject is imported below. A Subject is like an Observable, but can multicast to many Observers. Subjects are like EventEmitters: they maintain a registry of many listeners. See, What is a Subject for more information.
When we call switchMap
on the Subject, we can map each value to a new Observable; in this case a database query.
const size$ = new Subject<string>();
const queryObservable = size$.pipe(
switchMap(size =>
db.list('/items', ref => ref.orderByChild('size').equalTo(size)).valueChanges()
)
);
// subscribe to changes
queryObservable.subscribe(queriedItems => {
console.log(queriedItems);
});
// trigger the query
size$.next('large');
// re-trigger the query!!!
size$.next('small');
Example app:
See this example in action on StackBlitz.
import { Component } from '@angular/core';
import { AngularFireDatabase, AngularFireAction } from '@angular/fire/database';
import { Observable, Subscription, BehaviorSubject } from 'rxjs';
import { switchMap } 'rxjs/operators';
@Component({
selector: 'app-root',
template: `
<h1>Firebase widgets!</h1>
<div *ngIf="items$ | async; let items; else loading">
<ul>
<li *ngFor="let item of items">
{{ item.payload.val().text }}
<code>{{ item.payload.key }}</code>
</li>
</ul>
<div *ngIf="items.length === 0">No results, try clearing filters</div>
</div>
<ng-template #loading>Loading…</ng-template>
<div>
<h4>Filter by size</h4>
<button (click)="filterBy('small')">Small</button>
<button (click)="filterBy('medium')">Medium</button>
<button (click)="filterBy('large')">Large</button>
<button (click)="filterBy('x-large')">Extra Large</button>
<button (click)="filterBy(null)" *ngIf="this.size$.getValue()">
<em>clear filter</em>
</button>
</div>
`,
})
export class AppComponent {
items$: Observable<AngularFireAction<firebase.database.DataSnapshot>[]>;
size$: BehaviorSubject<string|null>;
constructor(db: AngularFireDatabase) {
this.size$ = new BehaviorSubject(null);
this.items$ = this.size$.pipe(
switchMap(size =>
db.list('/items', ref =>
size ? ref.orderByChild('size').equalTo(size) : ref
).snapshotChanges()
)
);
}
filterBy(size: string|null) {
this.size$.next(size);
}
}
To run the above example as is, you need to have sample data in you firebase database with the following structure:
{
"items": {
"a" : {
"size" : "small",
"text" : "small thing"
},
"b" : {
"size" : "medium",
"text" : "medium sample"
},
"c" : {
"size" : "large",
"text" : "large widget"
}
}
}