Skip to content

Commit

Permalink
Support optional valueType parameter when filtering Firestore colle…
Browse files Browse the repository at this point in the history
…ctions. Resolves #496.
  • Loading branch information
dpa99c committed Sep 15, 2020
1 parent 09e5f87 commit 03d0561
Show file tree
Hide file tree
Showing 4 changed files with 149 additions and 42 deletions.
42 changes: 34 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3157,9 +3157,30 @@ Fetches all the documents in the specific collection.

**Parameters**:
- {string} collection - name of top-level collection to fetch.
- {array} filters - a list of filters to sort/filter the documents returned from your collection.
- {array} filters (optional) - a list of filters to sort/filter the documents returned from your collection.
- Supports `where`, `orderBy`, `startAt`, `endAt` and `limit` filters.
- See the [Firestore documentation](https://firebase.google.com/docs/firestore/query-data/queries) for more details.
- See the [Firestore documentation](https://firebase.google.com/docs/firestore/query-data/queries) for more details.
- Each filter is defined as an array of filter components:
- `where`: [`where`, `fieldName`, `operator`, `value`, `valueType`]
- `fieldName` - name of field to match
- `operator` - operator to apply to match
- supported operators: `==`, `<`, `>`, `<=`, `>=`, `array-contains`
- `value` - field value to match
- `valueType` (optional) - type of variable to fetch value as
- supported types: `string`, `boolean`, `integer`, `double`, `long`
- if not specified, defaults to `string`
- `startAt`: [`startAt`, `value`, `valueType`]
- `value` - field value to start at
- `valueType` (optional) - type of variable to fetch value as (as above)
- `endAt`: [`endAt`, `value`, `valueType`]
- `value` - field value to end at
- `valueType` (optional) - type of variable to fetch value as (as above)
- `orderBy`: [`orderBy`, `fieldName`, `sortDirection`]
- `fieldName` - name of field to order by
- `sortDirection` - direction to order in: `asc` or `desc`
- `limit`: [`limit`, `value`]
- `value` - `integer` defining maximum number of results to return.

- {function} success - callback function to call on successfully deleting the document.
Will be passed an {object} containing all the documents in the collection, indexed by document ID.
If a Firebase collection with that name does not exist or it contains no documents, the object will be empty.
Expand All @@ -3168,9 +3189,15 @@ If a Firebase collection with that name does not exist or it contains no documen
```javascript
var collection = "my_collection";
var filters = [
['where', 'field', '==', 'value'],
['orderBy', 'field', 'desc']
['where', 'my_string', '==', 'foo'],
['where', 'my_integer', '>=', 0, 'integer'],
['where', 'my_boolean', '==', true, 'boolean'],
['orderBy', 'an_integer', 'desc'],
['startAt', 'an_integer', 10, 'integer'],
['endAt', 'an_integer', 100, 'integer'],
['limit', 100000]
];

FirebasePlugin.fetchFirestoreCollection(collection, filters, function(documents){
console.log("Successfully fetched collection: "+JSON.stringify(documents));
}, function(error){
Expand Down Expand Up @@ -3316,10 +3343,9 @@ See the [Firestore documentation](https://firebase.google.com/docs/firestore/que
Will be passed an {object} representing the `id` or `change` event.
- {function} error - callback function which will be passed a {string} error message as an argument.
- {string} collection - name of top-level collection to listen to the document in.
- {array} filters - a list of filters to sort/filter the documents returned from your collection.
- Supports `where`, `orderBy`, `startAt`, `endAt` and `limit` filters.
- See the [Firestore documentation](https://firebase.google.com/docs/firestore/query-data/queries) for more details.
- {boolean} includeMetadata - whether to listen for changes to document metadata.
- {array} filters (optional) - a list of filters to sort/filter the documents returned from your collection.
- See [fetchFirestoreCollection](#fetchfirestorecollection)
- {boolean} includeMetadata (optional) - whether to listen for changes to document metadata.
- Defaults to `false`.
- See [Events for metadata changes](https://firebase.google.com/docs/firestore/query-data/listen#events-metadata-changes) for more info.

Expand Down
77 changes: 55 additions & 22 deletions src/android/FirebasePlugin.java
Original file line number Diff line number Diff line change
Expand Up @@ -2272,7 +2272,9 @@ public void run() {
JSONArray filters = args.getJSONArray(1);
Query query = firestore.collection(collection);

applyFiltersToFirestoreCollectionQuery(filters, query);
if(filters != null){
query = applyFiltersToFirestoreCollectionQuery(filters, query);
}

query.get()
.addOnCompleteListener(new OnCompleteListener<QuerySnapshot>() {
Expand Down Expand Up @@ -2314,7 +2316,7 @@ public void run() {
Query query = firestore.collection(collection);

if(filters != null){
applyFiltersToFirestoreCollectionQuery(filters, query);
query = applyFiltersToFirestoreCollectionQuery(filters, query);
}

ListenerRegistration registration = query
Expand Down Expand Up @@ -2380,28 +2382,31 @@ public void onEvent(@Nullable QuerySnapshot snapshots,
});
}

private void applyFiltersToFirestoreCollectionQuery(JSONArray filters, Query query) throws JSONException{
private Query applyFiltersToFirestoreCollectionQuery(JSONArray filters, Query query) throws JSONException{
for(int i = 0; i < filters.length(); i++) {
JSONArray filter = filters.getJSONArray(i);
switch(filter.getString(0)) {
case "where":
if (Objects.equals(filter.getString(2), new String("=="))) {
query = query.whereEqualTo(filter.getString(1), filter.getString(3));
}
if (Objects.equals(filter.getString(2), new String("<"))) {
query = query.whereLessThan(filter.getString(1), filter.getString(3));
}
if (Objects.equals(filter.getString(2), new String(">"))) {
query = query.whereGreaterThan(filter.getString(1), filter.getString(3));
}
if (Objects.equals(filter.getString(2), new String("<="))) {
query = query.whereLessThanOrEqualTo(filter.getString(1), filter.getString(3));
}
if (Objects.equals(filter.getString(2), new String(">="))) {
query = query.whereGreaterThanOrEqualTo(filter.getString(1), filter.getString(3));
}
if (Objects.equals(filter.getString(2), new String("array-contains"))) {
query = query.whereArrayContains(filter.getString(1), filter.getString(3));
String fieldName = filter.getString(1);
String operator = filter.getString(2);
switch (operator){
case "<":
query = query.whereLessThan(fieldName, getFilterValueAsType(filter, 3, 4));
break;
case ">":
query = query.whereGreaterThan(fieldName, getFilterValueAsType(filter, 3, 4));
break;
case "<=":
query = query.whereLessThanOrEqualTo(fieldName, getFilterValueAsType(filter, 3, 4));
break;
case ">=":
query = query.whereGreaterThanOrEqualTo(fieldName, getFilterValueAsType(filter, 3, 4));
break;
case "array-contains":
query = query.whereArrayContains(fieldName, getFilterValueAsType(filter, 3, 4));
break;
default:
query = query.whereEqualTo(fieldName, getFilterValueAsType(filter, 3, 4));
}
break;
case "orderBy":
Expand All @@ -2412,16 +2417,44 @@ private void applyFiltersToFirestoreCollectionQuery(JSONArray filters, Query que
query = query.orderBy(filter.getString(1), direction);
break;
case "startAt":
query = query.startAt(filter.getString(1));
query = query.startAt(getFilterValueAsType(filter, 1, 2));
break;
case "endAt":
query = query.endAt(filter.getString(1));
query = query.endAt(getFilterValueAsType(filter, 1, 2));
break;
case "limit":
query = query.limit(filter.getLong(1));
break;
}
}
return query;
}

private Object getFilterValueAsType(JSONArray filter, int valueIndex, int typeIndex) throws JSONException{
Object typedValue;
String type = "string";
if(!filter.isNull(typeIndex)){
type = filter.getString(typeIndex);
}

switch (type){
case "boolean":
typedValue = filter.getBoolean(valueIndex);
break;
case "integer":
typedValue = filter.getInt(valueIndex);
break;
case "double":
typedValue = filter.getDouble(valueIndex);
break;
case "long":
typedValue = filter.getLong(valueIndex);
break;
default:
typedValue = filter.getString(valueIndex);
}

return typedValue;
}


Expand Down
70 changes: 59 additions & 11 deletions src/ios/FirebasePlugin.m
Original file line number Diff line number Diff line change
Expand Up @@ -1643,7 +1643,7 @@ - (void)fetchFirestoreCollection:(CDVInvokedUrlCommand*)command {

FIRQuery* query = [firestore collectionWithPath:collection];
if(filters != nil){
[self applyFiltersToFirestoreCollectionQuery:filters query:query];
query = [self applyFiltersToFirestoreCollectionQuery:filters query:query];
}

[query getDocumentsWithCompletion:^(FIRQuerySnapshot * _Nullable snapshot, NSError * _Nullable error) {
Expand Down Expand Up @@ -1675,7 +1675,7 @@ - (void)listenToFirestoreCollection:(CDVInvokedUrlCommand*)command {

FIRQuery* query = [firestore collectionWithPath:collection];
if(filters != nil){
[self applyFiltersToFirestoreCollectionQuery:filters query:query];
query = [self applyFiltersToFirestoreCollectionQuery:filters query:query];
}

id<FIRListenerRegistration> listener = [query
Expand Down Expand Up @@ -1733,27 +1733,27 @@ - (void)listenToFirestoreCollection:(CDVInvokedUrlCommand*)command {
}];
}

- (void) applyFiltersToFirestoreCollectionQuery:(NSArray*)filters query:(FIRQuery*)query {
- (FIRQuery*) applyFiltersToFirestoreCollectionQuery:(NSArray*)filters query:(FIRQuery*)query {
for (int i = 0; i < [filters count]; i++) {
NSArray* filter = [filters objectAtIndex:i];
if ([[filter objectAtIndex:0] isEqualToString:@"where"]) {
if ([[filter objectAtIndex:2] isEqualToString:@"=="]) {
query = [query queryWhereField:[filter objectAtIndex:1] isEqualTo:[filter objectAtIndex:3]];
query = [query queryWhereField:[filter objectAtIndex:1] isEqualTo: [self getFilterValueAsType:filter valueIndex:3 typeIndex:4]];
}
if ([[filter objectAtIndex:2] isEqualToString:@"<"]) {
query = [query queryWhereField:[filter objectAtIndex:1] isLessThan:[filter objectAtIndex:3]];
query = [query queryWhereField:[filter objectAtIndex:1] isLessThan:[self getFilterValueAsType:filter valueIndex:3 typeIndex:4]];
}
if ([[filter objectAtIndex:2] isEqualToString:@">"]) {
query = [query queryWhereField:[filter objectAtIndex:1] isGreaterThan:[filter objectAtIndex:3]];
query = [query queryWhereField:[filter objectAtIndex:1] isGreaterThan:[self getFilterValueAsType:filter valueIndex:3 typeIndex:4]];
}
if ([[filter objectAtIndex:2] isEqualToString:@"<="]) {
query = [query queryWhereField:[filter objectAtIndex:1] isLessThanOrEqualTo:[filter objectAtIndex:3]];
query = [query queryWhereField:[filter objectAtIndex:1] isLessThanOrEqualTo:[self getFilterValueAsType:filter valueIndex:3 typeIndex:4]];
}
if ([[filter objectAtIndex:2] isEqualToString:@">="]) {
query = [query queryWhereField:[filter objectAtIndex:1] isGreaterThanOrEqualTo:[filter objectAtIndex:3]];
query = [query queryWhereField:[filter objectAtIndex:1] isGreaterThanOrEqualTo:[self getFilterValueAsType:filter valueIndex:3 typeIndex:4]];
}
if ([[filter objectAtIndex:2] isEqualToString:@"array-contains"]) {
query = [query queryWhereField:[filter objectAtIndex:1] arrayContains:[filter objectAtIndex:3]];
query = [query queryWhereField:[filter objectAtIndex:1] arrayContains:[self getFilterValueAsType:filter valueIndex:3 typeIndex:4]];
}
continue;
}
Expand All @@ -1762,18 +1762,66 @@ - (void) applyFiltersToFirestoreCollectionQuery:(NSArray*)filters query:(FIRQuer
continue;
}
if ([[filter objectAtIndex:0] isEqualToString:@"startAt"]) {
query = [query queryStartingAtValues:[filter objectAtIndex:1]];
query = [query queryStartingAtValues:[self getFilterValueAsType:filter valueIndex:1 typeIndex:2]];
continue;
}
if ([[filter objectAtIndex:0] isEqualToString:@"endAt"]) {
query = [query queryEndingAtValues:[filter objectAtIndex:1]];
query = [query queryEndingAtValues:[self getFilterValueAsType:filter valueIndex:1 typeIndex:2]];
continue;
}
if ([[filter objectAtIndex:0] isEqualToString:@"limit"]) {
query = [query queryLimitedTo:[(NSNumber *)[filter objectAtIndex:1] integerValue]];
continue;
}
}
return query;
}

- (id) getFilterValueAsType: (NSArray*)filter valueIndex:(int)valueIndex typeIndex:(int)typeIndex{
id typedValue = [filter objectAtIndex:valueIndex];

NSString* type = @"string";
if([filter objectAtIndex:typeIndex] != nil){
type = [filter objectAtIndex:typeIndex];
}

if([type isEqual:@"boolean"]){
if([typedValue isKindOfClass:[NSNumber class]]){
typedValue = [NSNumber numberWithBool:typedValue];
}else if([typedValue isKindOfClass:[NSString class]]){
bool boolValue = [typedValue boolValue];
typedValue = [NSNumber numberWithBool:boolValue];
}
} else if([type isEqual:@"integer"] || [type isEqual:@"long"]){
if([typedValue isKindOfClass:[NSString class]]){
NSInteger intValue = [typedValue integerValue];
typedValue = [NSNumber numberWithInteger:intValue];
}
} else if([type isEqual:@"double"]){
if([typedValue isKindOfClass:[NSString class]]){
double doubleValue = [typedValue doubleValue];
typedValue = [NSNumber numberWithDouble:doubleValue];
}
} else{ //string
if([typedValue isKindOfClass:[NSNumber class]]){
if([self isBoolNumber:typedValue]){
bool boolValue = [typedValue boolValue];
typedValue = boolValue ? @"true" : @"false";
}else{
typedValue = [typedValue stringValue];
}
}
}

return typedValue;
}

// https://stackoverflow.com/a/30223989/777265
- (BOOL) isBoolNumber:(NSNumber *)num
{
CFTypeID boolID = CFBooleanGetTypeID(); // the type ID of CFBoolean
CFTypeID numID = CFGetTypeID((__bridge CFTypeRef)(num)); // the type ID of num
return numID == boolID;
}

- (NSNumber*) saveFirestoreListener: (id<FIRListenerRegistration>) firestoreListener {
Expand Down
2 changes: 1 addition & 1 deletion www/firebase.js
Original file line number Diff line number Diff line change
Expand Up @@ -400,7 +400,7 @@ exports.fetchDocumentInFirestoreCollection = function (documentId, collection, s

exports.fetchFirestoreCollection = function (collection, filters, success, error) {
if(typeof collection !== 'string') return error("'collection' must be a string specifying the Firestore collection name");
if(filters && (typeof filters !== 'object' || typeof filters.length === 'undefined')) return error("'filters' must be a array specifying a list of filters to apply to documents in the Firestore collection");
if(filters && (typeof filters !== 'object' || typeof filters.length === 'undefined' || (filters.length && typeof filters[0] !== 'object'))) return error("'filters' must be a array specifying a list of filters (as arrays) to apply to documents in the Firestore collection");

exec(success, error, "FirebasePlugin", "fetchFirestoreCollection", [collection, filters || []]);
};
Expand Down

0 comments on commit 03d0561

Please sign in to comment.