Skip to content

Commit a2607bf

Browse files
committed
Add encode/decode test for composite filters.
1 parent 23cf19b commit a2607bf

File tree

3 files changed

+130
-1
lines changed

3 files changed

+130
-1
lines changed

firebase-firestore/src/main/java/com/google/firebase/firestore/core/CompositeFilter.java

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,9 @@ public boolean isDisjunction() {
8080
public boolean isFlatConjunction() {
8181
if (operator != Operator.AND) return false;
8282
for (Filter filter : filters) {
83-
if (filter instanceof CompositeFilter) return false;
83+
if (filter instanceof CompositeFilter) {
84+
return false;
85+
}
8486
}
8587
return true;
8688
}
@@ -143,4 +145,26 @@ public String getCanonicalId() {
143145
public String toString() {
144146
return getCanonicalId();
145147
}
148+
149+
@Override
150+
public boolean equals(Object o) {
151+
if (o == null || !(o instanceof CompositeFilter)) {
152+
return false;
153+
}
154+
CompositeFilter other = (CompositeFilter) o;
155+
// Note: This comparison requires order of filters in the list to be the same, and it does not
156+
// remove duplicate subfilters from each composite filter. It is therefore way less expensive.
157+
// TODO(orquery): Consider removing duplicates and ignoring order of filters in the list.
158+
return operator == other.operator && filters.equals(other.filters);
159+
}
160+
161+
@Override
162+
public int hashCode() {
163+
int result = 37;
164+
result += 31 * operator.hashCode();
165+
for (Filter filter : filters) {
166+
result += 31 * filter.hashCode();
167+
}
168+
return result;
169+
}
146170
}

firebase-firestore/src/test/java/com/google/firebase/firestore/remote/RemoteSerializerTest.java

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
package com.google.firebase.firestore.remote;
1616

1717
import static com.google.firebase.firestore.model.Values.refValue;
18+
import static com.google.firebase.firestore.testutil.TestUtil.andFilter;
1819
import static com.google.firebase.firestore.testutil.TestUtil.bound;
1920
import static com.google.firebase.firestore.testutil.TestUtil.deleteMutation;
2021
import static com.google.firebase.firestore.testutil.TestUtil.deletedDoc;
@@ -24,6 +25,7 @@
2425
import static com.google.firebase.firestore.testutil.TestUtil.key;
2526
import static com.google.firebase.firestore.testutil.TestUtil.map;
2627
import static com.google.firebase.firestore.testutil.TestUtil.mergeMutation;
28+
import static com.google.firebase.firestore.testutil.TestUtil.orFilter;
2729
import static com.google.firebase.firestore.testutil.TestUtil.orderBy;
2830
import static com.google.firebase.firestore.testutil.TestUtil.patchMutation;
2931
import static com.google.firebase.firestore.testutil.TestUtil.query;
@@ -680,6 +682,85 @@ public void testEncodesMultipleFiltersOnDeeperCollections() {
680682
serializer.decodeQueryTarget(serializer.encodeQueryTarget(q.toTarget())), q.toTarget());
681683
}
682684

685+
@Test
686+
public void testEncodesCompositeFiltersOnDeeperCollections() {
687+
// (prop < 42) || (author == "dimond" && tags array-contains "pending")
688+
Query q =
689+
Query.atPath(ResourcePath.fromString("rooms/1/messages/10/attachments"))
690+
.filter(
691+
orFilter(
692+
filter("prop", "<", 42),
693+
andFilter(
694+
filter("author", "==", "dimond"),
695+
filter("tags", "array-contains", "pending"))));
696+
Target actual = serializer.encodeTarget(wrapTargetData(q));
697+
698+
StructuredQuery.Builder structuredQueryBuilder =
699+
StructuredQuery.newBuilder()
700+
.addFrom(CollectionSelector.newBuilder().setCollectionId("attachments"))
701+
.setWhere(
702+
Filter.newBuilder()
703+
.setCompositeFilter(
704+
StructuredQuery.CompositeFilter.newBuilder()
705+
// TODO(orquery): Replace with Operator.OR once it's available.
706+
.setOp(CompositeFilter.Operator.OPERATOR_UNSPECIFIED)
707+
.addFilters(
708+
Filter.newBuilder()
709+
.setFieldFilter(
710+
StructuredQuery.FieldFilter.newBuilder()
711+
.setField(
712+
FieldReference.newBuilder().setFieldPath("prop"))
713+
.setOp(Operator.LESS_THAN)
714+
.setValue(Value.newBuilder().setIntegerValue(42))))
715+
.addFilters(
716+
Filter.newBuilder()
717+
.setCompositeFilter(
718+
StructuredQuery.CompositeFilter.newBuilder()
719+
.setOp(CompositeFilter.Operator.AND)
720+
.addFilters(
721+
Filter.newBuilder()
722+
.setFieldFilter(
723+
StructuredQuery.FieldFilter.newBuilder()
724+
.setField(
725+
FieldReference.newBuilder()
726+
.setFieldPath("author"))
727+
.setOp(Operator.EQUAL)
728+
.setValue(
729+
Value.newBuilder()
730+
.setStringValue("dimond"))))
731+
.addFilters(
732+
Filter.newBuilder()
733+
.setFieldFilter(
734+
StructuredQuery.FieldFilter.newBuilder()
735+
.setField(
736+
FieldReference.newBuilder()
737+
.setFieldPath("tags"))
738+
.setOp(Operator.ARRAY_CONTAINS)
739+
.setValue(
740+
Value.newBuilder()
741+
.setStringValue(
742+
"pending"))))))))
743+
.addOrderBy(
744+
Order.newBuilder()
745+
.setField(FieldReference.newBuilder().setFieldPath("prop"))
746+
.setDirection(Direction.ASCENDING))
747+
.addOrderBy(defaultKeyOrder());
748+
QueryTarget.Builder queryBuilder =
749+
QueryTarget.newBuilder()
750+
.setParent("projects/p/databases/d/documents/rooms/1/messages/10")
751+
.setStructuredQuery(structuredQueryBuilder);
752+
Target expected =
753+
Target.newBuilder()
754+
.setQuery(queryBuilder)
755+
.setTargetId(1)
756+
.setResumeToken(ByteString.EMPTY)
757+
.build();
758+
759+
assertEquals(expected, actual);
760+
assertEquals(
761+
serializer.decodeQueryTarget(serializer.encodeQueryTarget(q.toTarget())), q.toTarget());
762+
}
763+
683764
@Test
684765
public void testInSerialization() {
685766
FieldFilter inputFilter = filter("field", "in", asList(42));

firebase-firestore/src/testUtil/java/com/google/firebase/firestore/testutil/TestUtil.java

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,8 +41,10 @@
4141
import com.google.firebase.firestore.UserDataReader;
4242
import com.google.firebase.firestore.UserDataWriter;
4343
import com.google.firebase.firestore.core.Bound;
44+
import com.google.firebase.firestore.core.CompositeFilter;
4445
import com.google.firebase.firestore.core.FieldFilter;
4546
import com.google.firebase.firestore.core.FieldFilter.Operator;
47+
import com.google.firebase.firestore.core.Filter;
4648
import com.google.firebase.firestore.core.OrderBy;
4749
import com.google.firebase.firestore.core.OrderBy.Direction;
4850
import com.google.firebase.firestore.core.Query;
@@ -77,6 +79,7 @@
7779
import com.google.firebase.firestore.remote.WatchChange;
7880
import com.google.firebase.firestore.remote.WatchChange.DocumentChange;
7981
import com.google.firebase.firestore.remote.WatchChangeAggregator;
82+
import com.google.firestore.v1.StructuredQuery;
8083
import com.google.firestore.v1.Value;
8184
import com.google.protobuf.ByteString;
8285
import java.io.IOException;
@@ -255,6 +258,27 @@ public static FieldFilter filter(String key, String operator, Object value) {
255258
return FieldFilter.create(field(key), operatorFromString(operator), wrap(value));
256259
}
257260

261+
public static CompositeFilter andFilter(List<Filter> filters) {
262+
return new CompositeFilter(filters, StructuredQuery.CompositeFilter.Operator.AND);
263+
}
264+
265+
public static CompositeFilter andFilter(Filter... filters) {
266+
return new CompositeFilter(
267+
Arrays.asList(filters), StructuredQuery.CompositeFilter.Operator.AND);
268+
}
269+
270+
public static CompositeFilter orFilter(Filter... filters) {
271+
// TODO(orquery): Replace this with Operator.OR once it is available.
272+
return new CompositeFilter(
273+
Arrays.asList(filters), StructuredQuery.CompositeFilter.Operator.OPERATOR_UNSPECIFIED);
274+
}
275+
276+
public static CompositeFilter orFilter(List<Filter> filters) {
277+
// TODO(orquery): Replace this with Operator.OR once it is available.
278+
return new CompositeFilter(
279+
filters, StructuredQuery.CompositeFilter.Operator.OPERATOR_UNSPECIFIED);
280+
}
281+
258282
public static Operator operatorFromString(String s) {
259283
if (s.equals("<")) {
260284
return Operator.LESS_THAN;

0 commit comments

Comments
 (0)