39
39
import com .google .firebase .firestore .model .Document ;
40
40
import com .google .firebase .firestore .model .DocumentKey ;
41
41
import com .google .firebase .firestore .model .ResourcePath ;
42
+ import com .google .firebase .firestore .model .value .ArrayValue ;
42
43
import com .google .firebase .firestore .model .value .FieldValue ;
43
44
import com .google .firebase .firestore .model .value .ReferenceValue ;
44
45
import com .google .firebase .firestore .model .value .ServerTimestampValue ;
@@ -84,76 +85,6 @@ public FirebaseFirestore getFirestore() {
84
85
return firestore ;
85
86
}
86
87
87
- private void validateOrderByFieldMatchesInequality (
88
- com .google .firebase .firestore .model .FieldPath orderBy ,
89
- com .google .firebase .firestore .model .FieldPath inequality ) {
90
- if (!orderBy .equals (inequality )) {
91
- String inequalityString = inequality .canonicalString ();
92
- throw new IllegalArgumentException (
93
- String .format (
94
- "Invalid query. You have an inequality where filter (whereLessThan(), "
95
- + "whereGreaterThan(), etc.) on field '%s' and so you must also have '%s' as "
96
- + "your first orderBy() field, but your first orderBy() is currently on field "
97
- + "'%s' instead." ,
98
- inequalityString , inequalityString , orderBy .canonicalString ()));
99
- }
100
- }
101
-
102
- private void validateNewFilter (Filter filter ) {
103
- if (filter instanceof RelationFilter ) {
104
- Operator filterOp = ((RelationFilter ) filter ).getOperator ();
105
- List <Operator > arrayOps = Arrays .asList (Operator .ARRAY_CONTAINS , Operator .ARRAY_CONTAINS_ANY );
106
- List <Operator > disjunctiveOps = Arrays .asList (Operator .ARRAY_CONTAINS_ANY , Operator .IN );
107
- boolean isArrayOp = arrayOps .contains (filterOp );
108
- boolean isDisjunctiveOp = disjunctiveOps .contains (filterOp );
109
-
110
- RelationFilter relationFilter = (RelationFilter ) filter ;
111
- if (relationFilter .isInequality ()) {
112
- com .google .firebase .firestore .model .FieldPath existingInequality = query .inequalityField ();
113
- com .google .firebase .firestore .model .FieldPath newInequality = filter .getField ();
114
-
115
- if (existingInequality != null && !existingInequality .equals (newInequality )) {
116
- throw new IllegalArgumentException (
117
- String .format (
118
- "All where filters other than whereEqualTo() must be on the same field. But you "
119
- + "have filters on '%s' and '%s'" ,
120
- existingInequality .canonicalString (), newInequality .canonicalString ()));
121
- }
122
- com .google .firebase .firestore .model .FieldPath firstOrderByField =
123
- query .getFirstOrderByField ();
124
- if (firstOrderByField != null ) {
125
- validateOrderByFieldMatchesInequality (firstOrderByField , newInequality );
126
- }
127
- } else if (isDisjunctiveOp || isArrayOp ) {
128
- // You can have at most 1 disjunctive filter and 1 array filter. Check if the new filter
129
- // conflicts with an existing one.
130
- Operator conflictingOp = null ;
131
- if (isDisjunctiveOp ) {
132
- conflictingOp = this .query .findOperatorFilter (disjunctiveOps );
133
- }
134
- if (conflictingOp == null && isArrayOp ) {
135
- conflictingOp = this .query .findOperatorFilter (arrayOps );
136
- }
137
- if (conflictingOp != null ) {
138
- // We special case when it's a duplicate op to give a slightly clearer error message.
139
- if (conflictingOp == filterOp ) {
140
- throw new IllegalArgumentException (
141
- "Invalid Query. You cannot use more than one '"
142
- + filterOp .toString ()
143
- + "' filter." );
144
- } else {
145
- throw new IllegalArgumentException (
146
- "Invalid Query. You cannot use '"
147
- + filterOp .toString ()
148
- + "' filters with '"
149
- + conflictingOp .toString ()
150
- + "' filters." );
151
- }
152
- }
153
- }
154
- }
155
- }
156
-
157
88
/**
158
89
* Creates and returns a new Query with the additional filter that documents must contain the
159
90
* specified field and the value should be equal to the specified value.
@@ -421,66 +352,19 @@ private Query whereHelper(@NonNull FieldPath fieldPath, Operator op, Object valu
421
352
"Invalid query. You can't perform '"
422
353
+ op .toString ()
423
354
+ "' queries on FieldPath.documentId()." );
424
- }
425
- if (value instanceof String ) {
426
- String documentKey = (String ) value ;
427
- if (documentKey .isEmpty ()) {
428
- throw new IllegalArgumentException (
429
- "Invalid query. When querying with FieldPath.documentId() you must provide a valid "
430
- + "document ID, but it was an empty string." );
355
+ } else if (op == Operator .IN ) {
356
+ validateDisjunctiveFilterElements (value , op );
357
+ List <FieldValue > referenceList = new ArrayList <>();
358
+ for (Object arrayValue : (List ) value ) {
359
+ referenceList .add (parseDocumentIdValue (arrayValue ));
431
360
}
432
- if (!query .isCollectionGroupQuery () && documentKey .contains ("/" )) {
433
- throw new IllegalArgumentException (
434
- "Invalid query. When querying a collection by FieldPath.documentId() you must "
435
- + "provide a plain document ID, but '"
436
- + documentKey
437
- + "' contains a '/' character." );
438
- }
439
- ResourcePath path = query .getPath ().append (ResourcePath .fromString (documentKey ));
440
- if (!DocumentKey .isDocumentKey (path )) {
441
- throw new IllegalArgumentException (
442
- "Invalid query. When querying a collection group by FieldPath.documentId(), the "
443
- + "value provided must result in a valid document path, but '"
444
- + path
445
- + "' is not because it has an odd number of segments ("
446
- + path .length ()
447
- + ")." );
448
- }
449
- fieldValue =
450
- ReferenceValue .valueOf (this .getFirestore ().getDatabaseId (), DocumentKey .fromPath (path ));
451
- } else if (value instanceof DocumentReference ) {
452
- DocumentReference ref = (DocumentReference ) value ;
453
- fieldValue = ReferenceValue .valueOf (this .getFirestore ().getDatabaseId (), ref .getKey ());
361
+ fieldValue = ArrayValue .fromList (referenceList );
454
362
} else {
455
- throw new IllegalArgumentException (
456
- "Invalid query. When querying with FieldPath.documentId() you must provide a valid "
457
- + "String or DocumentReference, but it was of type: "
458
- + Util .typeName (value ));
363
+ fieldValue = parseDocumentIdValue (value );
459
364
}
460
365
} else {
461
366
if (op == Operator .IN || op == Operator .ARRAY_CONTAINS_ANY ) {
462
- if (!(value instanceof List ) || ((List ) value ).size () == 0 ) {
463
- throw new IllegalArgumentException (
464
- "Invalid Query. A non-empty array is required for '" + op .toString () + "' filters." );
465
- }
466
- if (((List ) value ).size () > 10 ) {
467
- throw new IllegalArgumentException (
468
- "Invalid Query. '"
469
- + op .toString ()
470
- + "' filters support a maximum of 10 elements in the value array." );
471
- }
472
- if (((List ) value ).contains (null )) {
473
- throw new IllegalArgumentException (
474
- "Invalid Query. '"
475
- + op .toString ()
476
- + "' filters cannot contain 'null' in the value array." );
477
- }
478
- if (((List ) value ).contains (Double .NaN ) || ((List ) value ).contains (Float .NaN )) {
479
- throw new IllegalArgumentException (
480
- "Invalid Query. '"
481
- + op .toString ()
482
- + "' filters cannot contain 'NaN' in the value array." );
483
- }
367
+ validateDisjunctiveFilterElements (value , op );
484
368
}
485
369
fieldValue = firestore .getDataConverter ().parseQueryValue (value );
486
370
}
@@ -497,6 +381,144 @@ private void validateOrderByField(com.google.firebase.firestore.model.FieldPath
497
381
}
498
382
}
499
383
384
+ /**
385
+ * Parses the given documentIdValue into a ReferenceValue, throwing appropriate errors if the
386
+ * value is anything other than a DocumentReference or String, or if the string is malformed.
387
+ */
388
+ private ReferenceValue parseDocumentIdValue (Object documentIdValue ) {
389
+ if (documentIdValue instanceof String ) {
390
+ String documentId = (String ) documentIdValue ;
391
+ if (documentId .isEmpty ()) {
392
+ throw new IllegalArgumentException (
393
+ "Invalid query. When querying with FieldPath.documentId() you must provide a valid "
394
+ + "document ID, but it was an empty string." );
395
+ }
396
+ if (!query .isCollectionGroupQuery () && documentId .contains ("/" )) {
397
+ throw new IllegalArgumentException (
398
+ "Invalid query. When querying a collection by FieldPath.documentId() you must "
399
+ + "provide a plain document ID, but '"
400
+ + documentId
401
+ + "' contains a '/' character." );
402
+ }
403
+ ResourcePath path = query .getPath ().append (ResourcePath .fromString (documentId ));
404
+ if (!DocumentKey .isDocumentKey (path )) {
405
+ throw new IllegalArgumentException (
406
+ "Invalid query. When querying a collection group by FieldPath.documentId(), the "
407
+ + "value provided must result in a valid document path, but '"
408
+ + path
409
+ + "' is not because it has an odd number of segments ("
410
+ + path .length ()
411
+ + ")." );
412
+ }
413
+ return ReferenceValue .valueOf (
414
+ this .getFirestore ().getDatabaseId (), DocumentKey .fromPath (path ));
415
+ } else if (documentIdValue instanceof DocumentReference ) {
416
+ DocumentReference ref = (DocumentReference ) documentIdValue ;
417
+ return ReferenceValue .valueOf (this .getFirestore ().getDatabaseId (), ref .getKey ());
418
+ } else {
419
+ throw new IllegalArgumentException (
420
+ "Invalid query. When querying with FieldPath.documentId() you must provide a valid "
421
+ + "String or DocumentReference, but it was of type: "
422
+ + Util .typeName (documentIdValue ));
423
+ }
424
+ }
425
+
426
+ /** Validates that the value passed into a disjunctive filter satisfies all array requirements. */
427
+ private void validateDisjunctiveFilterElements (Object value , Operator op ) {
428
+ if (!(value instanceof List ) || ((List ) value ).size () == 0 ) {
429
+ throw new IllegalArgumentException (
430
+ "Invalid Query. A non-empty array is required for '" + op .toString () + "' filters." );
431
+ }
432
+ if (((List ) value ).size () > 10 ) {
433
+ throw new IllegalArgumentException (
434
+ "Invalid Query. '"
435
+ + op .toString ()
436
+ + "' filters support a maximum of 10 elements in the value array." );
437
+ }
438
+ if (((List ) value ).contains (null )) {
439
+ throw new IllegalArgumentException (
440
+ "Invalid Query. '"
441
+ + op .toString ()
442
+ + "' filters cannot contain 'null' in the value array." );
443
+ }
444
+ if (((List ) value ).contains (Double .NaN ) || ((List ) value ).contains (Float .NaN )) {
445
+ throw new IllegalArgumentException (
446
+ "Invalid Query. '"
447
+ + op .toString ()
448
+ + "' filters cannot contain 'NaN' in the value array." );
449
+ }
450
+ }
451
+
452
+ private void validateOrderByFieldMatchesInequality (
453
+ com .google .firebase .firestore .model .FieldPath orderBy ,
454
+ com .google .firebase .firestore .model .FieldPath inequality ) {
455
+ if (!orderBy .equals (inequality )) {
456
+ String inequalityString = inequality .canonicalString ();
457
+ throw new IllegalArgumentException (
458
+ String .format (
459
+ "Invalid query. You have an inequality where filter (whereLessThan(), "
460
+ + "whereGreaterThan(), etc.) on field '%s' and so you must also have '%s' as "
461
+ + "your first orderBy() field, but your first orderBy() is currently on field "
462
+ + "'%s' instead." ,
463
+ inequalityString , inequalityString , orderBy .canonicalString ()));
464
+ }
465
+ }
466
+
467
+ private void validateNewFilter (Filter filter ) {
468
+ if (filter instanceof RelationFilter ) {
469
+ Operator filterOp = ((RelationFilter ) filter ).getOperator ();
470
+ List <Operator > arrayOps = Arrays .asList (Operator .ARRAY_CONTAINS , Operator .ARRAY_CONTAINS_ANY );
471
+ List <Operator > disjunctiveOps = Arrays .asList (Operator .ARRAY_CONTAINS_ANY , Operator .IN );
472
+ boolean isArrayOp = arrayOps .contains (filterOp );
473
+ boolean isDisjunctiveOp = disjunctiveOps .contains (filterOp );
474
+
475
+ RelationFilter relationFilter = (RelationFilter ) filter ;
476
+ if (relationFilter .isInequality ()) {
477
+ com .google .firebase .firestore .model .FieldPath existingInequality = query .inequalityField ();
478
+ com .google .firebase .firestore .model .FieldPath newInequality = filter .getField ();
479
+
480
+ if (existingInequality != null && !existingInequality .equals (newInequality )) {
481
+ throw new IllegalArgumentException (
482
+ String .format (
483
+ "All where filters other than whereEqualTo() must be on the same field. But you "
484
+ + "have filters on '%s' and '%s'" ,
485
+ existingInequality .canonicalString (), newInequality .canonicalString ()));
486
+ }
487
+ com .google .firebase .firestore .model .FieldPath firstOrderByField =
488
+ query .getFirstOrderByField ();
489
+ if (firstOrderByField != null ) {
490
+ validateOrderByFieldMatchesInequality (firstOrderByField , newInequality );
491
+ }
492
+ } else if (isDisjunctiveOp || isArrayOp ) {
493
+ // You can have at most 1 disjunctive filter and 1 array filter. Check if the new filter
494
+ // conflicts with an existing one.
495
+ Operator conflictingOp = null ;
496
+ if (isDisjunctiveOp ) {
497
+ conflictingOp = this .query .findOperatorFilter (disjunctiveOps );
498
+ }
499
+ if (conflictingOp == null && isArrayOp ) {
500
+ conflictingOp = this .query .findOperatorFilter (arrayOps );
501
+ }
502
+ if (conflictingOp != null ) {
503
+ // We special case when it's a duplicate op to give a slightly clearer error message.
504
+ if (conflictingOp == filterOp ) {
505
+ throw new IllegalArgumentException (
506
+ "Invalid Query. You cannot use more than one '"
507
+ + filterOp .toString ()
508
+ + "' filter." );
509
+ } else {
510
+ throw new IllegalArgumentException (
511
+ "Invalid Query. You cannot use '"
512
+ + filterOp .toString ()
513
+ + "' filters with '"
514
+ + conflictingOp .toString ()
515
+ + "' filters." );
516
+ }
517
+ }
518
+ }
519
+ }
520
+ }
521
+
500
522
/**
501
523
* Creates and returns a new Query that's additionally sorted by the specified field.
502
524
*
0 commit comments