feat(firestore): support limitToLast query filter (#3702)

Co-authored-by: Mike Diarmid <mike.diarmid@gmail.com>
This commit is contained in:
Russell Wheatley
2020-06-22 12:14:19 +01:00
committed by GitHub
parent ca6c88f46b
commit dc7f9213c0
8 changed files with 1805 additions and 1513 deletions

3132
docs/typedoc.json vendored

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

View File

@@ -118,6 +118,11 @@ public class ReactNativeFirebaseFirestoreQuery {
query = query.limit(limit);
}
if (options.hasKey("limitToLast")) {
int limitToLast = options.getInt("limitToLast");
query = query.limitToLast(limitToLast);
}
if (options.hasKey("startAt")) {
List<Object> fieldList = parseReadableArray(query.getFirestore(), options.getArray("startAt"));
query = query.startAt(Objects.requireNonNull(fieldList.toArray()));

View File

@@ -0,0 +1,106 @@
/*
* Copyright (c) 2016-present Invertase Limited & Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this library except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
const { wipe } = require('../helpers');
describe('firestore().collection().limitToLast()', () => {
before(() => wipe());
it('throws if limitToLast is invalid', () => {
try {
firebase
.firestore()
.collection('v6')
.limitToLast(-1);
return Promise.reject(new Error('Did not throw an Error.'));
} catch (error) {
error.message.should.containEql("'limitToLast' must be a positive integer value");
return Promise.resolve();
}
});
it('sets limitToLast on internals', async () => {
const colRef = firebase
.firestore()
.collection('v6')
.limitToLast(123);
should(colRef._modifiers.options.limitToLast).equal(123);
});
it('removes limit query if limitToLast is set afterwards', () => {
const colRef = firebase
.firestore()
.collection('v6')
.limit(2)
.limitToLast(123);
should(colRef._modifiers.options.limit).equal(undefined);
});
it('removes limitToLast query if limit is set afterwards', () => {
const colRef = firebase
.firestore()
.collection('v6')
.limitToLast(123)
.limit(2);
should(colRef._modifiers.options.limitToLast).equal(undefined);
});
it('limitToLast the number of documents', async () => {
const colRef = firebase.firestore().collection('v6');
// Add 3
await colRef.add({ count: 1 });
await colRef.add({ count: 2 });
await colRef.add({ count: 3 });
const docs = await firebase
.firestore()
.collection('v6')
.limitToLast(2)
.orderBy('count', 'desc')
.get();
const results = [];
docs.forEach(doc => {
results.push(doc.data());
});
should(results.length).equal(2);
should(results[0].count).equal(2);
should(results[1].count).equal(1);
});
it("throws error if no 'orderBy' is set on the query", () => {
try {
firebase
.firestore()
.collection('v6')
.limitToLast(3)
.get();
return Promise.reject(new Error('Did not throw an Error.'));
} catch (error) {
error.message.should.containEql(
'limitToLast() queries require specifying at least one firebase.firestore().collection().orderBy() clause',
);
return Promise.resolve();
}
});
});

View File

@@ -90,6 +90,10 @@
if (_options[@"limit"]) {
_query = [_query queryLimitedTo:[_options[@"limit"] intValue]];
}
if (_options[@"limitToLast"]) {
_query = [_query queryLimitedToLast:[_options[@"limitToLast"] intValue]];
}
if (_options[@"startAt"]) {
NSArray *fieldList = [RNFBFirestoreSerialize parseNSArray:_firestore array:_options[@"startAt"]];

View File

@@ -164,6 +164,8 @@ export default class FirestoreQuery {
);
}
this._modifiers.validatelimitToLast();
return this._firestore.native
.collectionGet(
this._collectionPath.relativeName,
@@ -220,12 +222,26 @@ export default class FirestoreQuery {
return new FirestoreQuery(this._firestore, this._collectionPath, modifiers);
}
limitToLast(limitToLast) {
if (this._modifiers.isValidLimitToLast(limitToLast)) {
throw new Error(
"firebase.firestore().collection().limitToLast(*) 'limitToLast' must be a positive integer value.",
);
}
const modifiers = this._modifiers._copy().limitToLast(limitToLast);
return new FirestoreQuery(this._firestore, this._collectionPath, modifiers);
}
onSnapshot(...args) {
let snapshotListenOptions;
let callback;
let onNext;
let onError;
this._modifiers.validatelimitToLast();
try {
const options = parseSnapshotArgs(args);
snapshotListenOptions = options.snapshotListenOptions;
@@ -405,8 +421,6 @@ export default class FirestoreQuery {
throw new Error(`firebase.firestore().collection().where() ${e.message}`);
}
modifiers.validateWhere();
return new FirestoreQuery(this._firestore, this._collectionPath, modifiers);
}
}

View File

@@ -45,6 +45,7 @@ const DIRECTIONS = {
export default class FirestoreQueryModifiers {
constructor() {
this._limit = undefined;
this._limitToLast = undefined;
this._filters = [];
this._orders = [];
this._type = 'collection';
@@ -58,6 +59,7 @@ export default class FirestoreQueryModifiers {
_copy() {
const newInstance = new FirestoreQueryModifiers();
newInstance._limit = this._limit;
newInstance._limitToLast = this._limitToLast;
newInstance._filters = [...this._filters];
newInstance._orders = [...this._orders];
newInstance._type = this._type;
@@ -82,6 +84,11 @@ export default class FirestoreQueryModifiers {
if (this._limit) {
options.limit = this._limit;
}
if (this._limitToLast) {
options.limitToLast = this._limitToLast;
}
if (this._startAt) {
options.startAt = this._startAt;
}
@@ -141,10 +148,35 @@ export default class FirestoreQueryModifiers {
}
limit(limit) {
this._limitToLast = undefined;
this._limit = limit;
return this;
}
/**
* limitToLast
*/
isValidLimitToLast(limit) {
return !isNumber(limit) || Math.floor(limit) !== limit || limit <= 0;
}
validatelimitToLast() {
if (this._limitToLast) {
if (!this._orders.length) {
throw new Error(
'firebase.firestore().collection().limitToLast() queries require specifying at least one firebase.firestore().collection().orderBy() clause',
);
}
}
}
limitToLast(limitToLast) {
this._limit = undefined;
this._limitToLast = limitToLast;
return this;
}
/**
* Filters
*/

View File

@@ -971,6 +971,25 @@ export namespace FirebaseFirestoreTypes {
* @param limit The maximum number of items to return.
*/
limit(limit: number): Query;
/**
* Creates and returns a new Query where the results are limited to the specified number of documents
* starting from the last document. The order is dependent on the second parameter for the `orderBy`
* method. If `desc` is used, the order is reversed. `orderBy` method call is required when calling `limitToLast`.
*
* #### Example
*
* ```js
* // Get the last 10 users in reverse order of age
* const querySnapshot = firebase.firestore()
* .collection('users')
* .orderBy('age', 'desc')
* .limitToLast(10)
* .get();
* ```
*
* @param limitToLast The maximum number of items to return.
*/
limitToLast(limitToLast: number): Query;
/**
* Attaches a listener for `QuerySnapshot` events.