-
Notifications
You must be signed in to change notification settings - Fork 560
/
FieldFilter.java
205 lines (180 loc) · 6 KB
/
FieldFilter.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
// Copyright 2018 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file 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.
package com.google.firebase.firestore.core;
import static com.google.firebase.firestore.util.Assert.hardAssert;
import com.google.firebase.firestore.model.Document;
import com.google.firebase.firestore.model.FieldPath;
import com.google.firebase.firestore.model.Values;
import com.google.firebase.firestore.util.Assert;
import com.google.firestore.v1.Value;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
/** Represents a filter to be applied to query. */
public class FieldFilter extends Filter {
public enum Operator {
LESS_THAN("<"),
LESS_THAN_OR_EQUAL("<="),
EQUAL("=="),
NOT_EQUAL("!="),
GREATER_THAN(">"),
GREATER_THAN_OR_EQUAL(">="),
ARRAY_CONTAINS("array_contains"),
ARRAY_CONTAINS_ANY("array_contains_any"),
IN("in"),
NOT_IN("not_in");
private final String text;
Operator(String text) {
this.text = text;
}
@Override
public String toString() {
return text;
}
}
private final Operator operator;
private final Value value;
private final FieldPath field;
/**
* Creates a new filter that compares fields and values. Only intended to be called from
* Filter.create().
*/
protected FieldFilter(FieldPath field, Operator operator, Value value) {
this.field = field;
this.operator = operator;
this.value = value;
}
public Operator getOperator() {
return operator;
}
public FieldPath getField() {
return field;
}
public Value getValue() {
return value;
}
/**
* Gets a Filter instance for the provided path, operator, and value.
*
* <p>Note that if the relation operator is EQUAL and the value is null or NaN, this will return
* the appropriate NullFilter or NaNFilter class instead of a FieldFilter.
*/
public static FieldFilter create(FieldPath path, Operator operator, Value value) {
if (path.isKeyField()) {
if (operator == Operator.IN) {
return new KeyFieldInFilter(path, value);
} else if (operator == Operator.NOT_IN) {
return new KeyFieldNotInFilter(path, value);
} else {
hardAssert(
operator != Operator.ARRAY_CONTAINS && operator != Operator.ARRAY_CONTAINS_ANY,
operator.toString() + "queries don't make sense on document keys");
return new KeyFieldFilter(path, operator, value);
}
} else if (operator == Operator.ARRAY_CONTAINS) {
return new ArrayContainsFilter(path, value);
} else if (operator == Operator.IN) {
return new InFilter(path, value);
} else if (operator == Operator.ARRAY_CONTAINS_ANY) {
return new ArrayContainsAnyFilter(path, value);
} else if (operator == Operator.NOT_IN) {
return new NotInFilter(path, value);
} else {
return new FieldFilter(path, operator, value);
}
}
@Override
public boolean matches(Document doc) {
Value other = doc.getField(field);
// Types do not have to match in NOT_EQUAL filters.
if (operator == Operator.NOT_EQUAL) {
return other != null && this.matchesComparison(Values.compare(other, value));
}
// Only compare types with matching backend order (such as double and int).
return other != null
&& Values.typeOrder(other) == Values.typeOrder(value)
&& this.matchesComparison(Values.compare(other, value));
}
protected boolean matchesComparison(int comp) {
switch (operator) {
case LESS_THAN:
return comp < 0;
case LESS_THAN_OR_EQUAL:
return comp <= 0;
case EQUAL:
return comp == 0;
case NOT_EQUAL:
return comp != 0;
case GREATER_THAN:
return comp > 0;
case GREATER_THAN_OR_EQUAL:
return comp >= 0;
default:
throw Assert.fail("Unknown FieldFilter operator: %s", operator);
}
}
public boolean isInequality() {
return Arrays.asList(
Operator.LESS_THAN,
Operator.LESS_THAN_OR_EQUAL,
Operator.GREATER_THAN,
Operator.GREATER_THAN_OR_EQUAL,
Operator.NOT_EQUAL,
Operator.NOT_IN)
.contains(operator);
}
@Override
public String getCanonicalId() {
// TODO: Technically, this won't be unique if two values have the same description,
// such as the int 3 and the string "3". So we should add the types in here somehow, too.
return getField().canonicalString() + getOperator().toString() + Values.canonicalId(getValue());
}
@Override
public List<FieldFilter> getFlattenedFilters() {
// This is already a field filter, so we return a list of size one.
return Collections.singletonList(this);
}
@Override
public List<Filter> getFilters() {
// This is the only filter within this object, so we return a list of size one.
return Collections.singletonList(this);
}
@Override
public FieldPath getFirstInequalityField() {
if (isInequality()) {
return getField();
}
return null;
}
@Override
public String toString() {
return getCanonicalId();
}
@Override
public boolean equals(Object o) {
if (o == null || !(o instanceof FieldFilter)) {
return false;
}
FieldFilter other = (FieldFilter) o;
return operator == other.operator && field.equals(other.field) && value.equals(other.value);
}
@Override
public int hashCode() {
int result = 37;
result = 31 * result + operator.hashCode();
result = 31 * result + field.hashCode();
result = 31 * result + value.hashCode();
return result;
}
}