diff --git a/README.md b/README.md index ac2316ff0..f458921a2 100644 --- a/README.md +++ b/README.md @@ -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. @@ -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){ @@ -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. diff --git a/src/android/FirebasePlugin.java b/src/android/FirebasePlugin.java index 1a41e3853..c4bf4a82d 100755 --- a/src/android/FirebasePlugin.java +++ b/src/android/FirebasePlugin.java @@ -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() { @@ -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 @@ -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": @@ -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; } diff --git a/src/ios/FirebasePlugin.m b/src/ios/FirebasePlugin.m index b5ded5b80..b79f1f70a 100644 --- a/src/ios/FirebasePlugin.m +++ b/src/ios/FirebasePlugin.m @@ -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) { @@ -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 listener = [query @@ -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; } @@ -1762,11 +1762,11 @@ - (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"]) { @@ -1774,6 +1774,54 @@ - (void) applyFiltersToFirestoreCollectionQuery:(NSArray*)filters query:(FIRQuer 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) firestoreListener { diff --git a/www/firebase.js b/www/firebase.js index d0d0db031..3f46c5d75 100644 --- a/www/firebase.js +++ b/www/firebase.js @@ -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 || []]); };