Skip to content

Commit 00272bf

Browse files
feat(datastore) add support for notContains query operator (#1145)
1 parent fd36189 commit 00272bf

File tree

10 files changed

+163
-0
lines changed

10 files changed

+163
-0
lines changed

aws-api-appsync/src/main/java/com/amplifyframework/core/model/query/predicate/GsonPredicateAdapters.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,8 @@ public QueryOperator<?> deserialize(JsonElement json, Type type, JsonDeserializa
6464
switch (QueryOperator.Type.valueOf(operatorType)) {
6565
case CONTAINS:
6666
return context.deserialize(json, ContainsQueryOperator.class);
67+
case NOT_CONTAINS:
68+
return context.deserialize(json, NotContainsQueryOperator.class);
6769
case GREATER_OR_EQUAL:
6870
return context.deserialize(json, GreaterOrEqualQueryOperator.class);
6971
case LESS_OR_EQUAL:
@@ -93,6 +95,8 @@ public QueryOperator<?> deserialize(JsonElement json, Type type, JsonDeserializa
9395
public JsonElement serialize(QueryOperator<?> operator, Type type, JsonSerializationContext context) {
9496
if (operator instanceof ContainsQueryOperator) {
9597
return context.serialize(operator, ContainsQueryOperator.class);
98+
} else if (operator instanceof NotContainsQueryOperator) {
99+
return context.serialize(operator, NotContainsQueryOperator.class);
96100
} else if (operator instanceof GreaterOrEqualQueryOperator) {
97101
return context.serialize(operator, GreaterOrEqualQueryOperator.class);
98102
} else if (operator instanceof LessOrEqualQueryOperator) {

aws-api/src/main/java/com/amplifyframework/api/aws/AppSyncGraphQLRequestFactory.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@
3737
import com.amplifyframework.core.model.query.predicate.GreaterThanQueryOperator;
3838
import com.amplifyframework.core.model.query.predicate.LessOrEqualQueryOperator;
3939
import com.amplifyframework.core.model.query.predicate.LessThanQueryOperator;
40+
import com.amplifyframework.core.model.query.predicate.NotContainsQueryOperator;
4041
import com.amplifyframework.core.model.query.predicate.NotEqualQueryOperator;
4142
import com.amplifyframework.core.model.query.predicate.QueryOperator;
4243
import com.amplifyframework.core.model.query.predicate.QueryPredicate;
@@ -336,6 +337,8 @@ private static Object appSyncOpValue(QueryOperator<?> qOp) {
336337
return ((GreaterThanQueryOperator<?>) qOp).value();
337338
case CONTAINS:
338339
return ((ContainsQueryOperator) qOp).value();
340+
case NOT_CONTAINS:
341+
return ((NotContainsQueryOperator) qOp).value();
339342
case BETWEEN:
340343
BetweenQueryOperator<?> betweenOp = (BetweenQueryOperator<?>) qOp;
341344
return Arrays.asList(betweenOp.start(), betweenOp.end());

aws-datastore/src/main/java/com/amplifyframework/datastore/appsync/AppSyncRequestFactory.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@
3838
import com.amplifyframework.core.model.query.predicate.GreaterThanQueryOperator;
3939
import com.amplifyframework.core.model.query.predicate.LessOrEqualQueryOperator;
4040
import com.amplifyframework.core.model.query.predicate.LessThanQueryOperator;
41+
import com.amplifyframework.core.model.query.predicate.NotContainsQueryOperator;
4142
import com.amplifyframework.core.model.query.predicate.NotEqualQueryOperator;
4243
import com.amplifyframework.core.model.query.predicate.QueryOperator;
4344
import com.amplifyframework.core.model.query.predicate.QueryPredicate;
@@ -248,6 +249,8 @@ private static Object appSyncOpValue(QueryOperator<?> qOp) throws DataStoreExcep
248249
return ((GreaterThanQueryOperator<?>) qOp).value();
249250
case CONTAINS:
250251
return ((ContainsQueryOperator) qOp).value();
252+
case NOT_CONTAINS:
253+
return ((NotContainsQueryOperator) qOp).value();
251254
case BETWEEN:
252255
BetweenQueryOperator<?> betweenOp = (BetweenQueryOperator<?>) qOp;
253256
return Arrays.asList(betweenOp.start(), betweenOp.end());

aws-datastore/src/main/java/com/amplifyframework/datastore/storage/sqlite/adapter/SQLPredicate.java

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
import com.amplifyframework.core.model.query.predicate.GreaterThanQueryOperator;
2424
import com.amplifyframework.core.model.query.predicate.LessOrEqualQueryOperator;
2525
import com.amplifyframework.core.model.query.predicate.LessThanQueryOperator;
26+
import com.amplifyframework.core.model.query.predicate.NotContainsQueryOperator;
2627
import com.amplifyframework.core.model.query.predicate.NotEqualQueryOperator;
2728
import com.amplifyframework.core.model.query.predicate.QueryOperator;
2829
import com.amplifyframework.core.model.query.predicate.QueryPredicate;
@@ -163,6 +164,19 @@ private StringBuilder parsePredicateOperation(QueryPredicateOperation<?> operati
163164
.append(SqlKeyword.fromQueryOperator(QueryOperator.Type.GREATER_THAN))
164165
.append(SqlKeyword.DELIMITER)
165166
.append("0");
167+
168+
case NOT_CONTAINS:
169+
NotContainsQueryOperator notContainsOp = (NotContainsQueryOperator) op;
170+
addBinding(notContainsOp.value());
171+
return builder.append("instr(")
172+
.append(column)
173+
.append(",")
174+
.append("?")
175+
.append(")")
176+
.append(SqlKeyword.DELIMITER)
177+
.append(SqlKeyword.fromQueryOperator(QueryOperator.Type.EQUAL))
178+
.append(SqlKeyword.DELIMITER)
179+
.append("0");
166180
case BEGINS_WITH:
167181
BeginsWithQueryOperator beginsWithOp = (BeginsWithQueryOperator) op;
168182
addBinding(beginsWithOp.value() + "%");

aws-datastore/src/test/java/com/amplifyframework/datastore/storage/sqlite/SQLPredicateTest.java

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,9 +81,37 @@ public void testContainsForStringList() throws DataStoreException {
8181
validateSQLExpressionForContains(sqlPredicate, "tags");
8282
}
8383

84+
/**
85+
* Test notContains in the context of a String field.
86+
* @throws DataStoreException Not thrown.
87+
*/
88+
@Test
89+
public void testNotContainsForStringField() throws DataStoreException {
90+
QueryPredicate predicate = Where.matches(Blog.NAME.notContains("something")).getQueryPredicate();
91+
SQLPredicate sqlPredicate = new SQLPredicate(predicate);
92+
validateSQLExpressionForNotContains(sqlPredicate, "name");
93+
}
94+
95+
/**
96+
* Test notContains in the context of a list.
97+
* @throws DataStoreException Not thrown.
98+
*/
99+
@Test
100+
public void testNotContainsForStringList() throws DataStoreException {
101+
QueryPredicateOperation<String> predicate = Blog.TAGS.notContains("something");
102+
SQLPredicate sqlPredicate = new SQLPredicate(predicate);
103+
validateSQLExpressionForNotContains(sqlPredicate, "tags");
104+
}
105+
84106
private void validateSQLExpressionForContains(SQLPredicate sqlPredicate, String fieldName) {
85107
assertEquals(1, sqlPredicate.getBindings().size());
86108
assertEquals("something", sqlPredicate.getBindings().get(0));
87109
assertEquals("instr(" + fieldName + ",?) > 0", sqlPredicate.toString());
88110
}
111+
112+
private void validateSQLExpressionForNotContains(SQLPredicate sqlPredicate, String fieldName) {
113+
assertEquals(1, sqlPredicate.getBindings().size());
114+
assertEquals("something", sqlPredicate.getBindings().get(0));
115+
assertEquals("instr(" + fieldName + ",?) = 0", sqlPredicate.toString());
116+
}
89117
}
Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
/*
2+
* Copyright 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License").
5+
* You may not use this file except in compliance with the License.
6+
* A copy of the License is located at
7+
*
8+
* http://aws.amazon.com/apache2.0
9+
*
10+
* or in the "license" file accompanying this file. This file is distributed
11+
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
12+
* express or implied. See the License for the specific language governing
13+
* permissions and limitations under the License.
14+
*/
15+
16+
package com.amplifyframework.core.model.query.predicate;
17+
18+
import androidx.core.util.ObjectsCompat;
19+
20+
/**
21+
* Represents a notContains condition with a target value for comparison.
22+
*/
23+
public final class NotContainsQueryOperator extends QueryOperator<String> {
24+
private final String value;
25+
26+
/**
27+
* Constructs a notContains condition.
28+
* @param value the value to be used in the comparison
29+
*/
30+
NotContainsQueryOperator(String value) {
31+
super(Type.NOT_CONTAINS);
32+
this.value = value;
33+
}
34+
35+
/**
36+
* Returns the value to be used in the comparison.
37+
* @return the value to be used in the comparison
38+
*/
39+
public Object value() {
40+
return value;
41+
}
42+
43+
/**
44+
* Returns true if the provided field value does not contain
45+
* the value associated with this operator.
46+
* @param field the field value to operate on
47+
* @return evaluated result of the operator
48+
*/
49+
@Override
50+
public boolean evaluate(String field) {
51+
return !field.contains(value);
52+
}
53+
54+
@Override
55+
public boolean equals(Object obj) {
56+
if (this == obj) {
57+
return true;
58+
} else if (obj == null || getClass() != obj.getClass()) {
59+
return false;
60+
} else {
61+
NotContainsQueryOperator op = (NotContainsQueryOperator) obj;
62+
63+
return ObjectsCompat.equals(type(), op.type()) &&
64+
ObjectsCompat.equals(value(), op.value());
65+
}
66+
}
67+
68+
@Override
69+
public int hashCode() {
70+
return ObjectsCompat.hash(
71+
type(),
72+
value()
73+
);
74+
}
75+
76+
@Override
77+
public String toString() {
78+
return "NotContainsQueryOperator { " +
79+
"type: " + type() +
80+
", value: " + value() +
81+
" }";
82+
}
83+
}

core/src/main/java/com/amplifyframework/core/model/query/predicate/QueryField.java

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,15 @@ public QueryPredicateOperation<String> contains(String value) {
143143
return new QueryPredicateOperation<>(modelName, fieldName, new ContainsQueryOperator(value));
144144
}
145145

146+
/**
147+
* Generates a new notContains comparison object to compare this field to the specified value.
148+
* @param value the value to be compared
149+
* @return an operation object representing the contains condition
150+
*/
151+
public QueryPredicateOperation<String> notContains(String value) {
152+
return new QueryPredicateOperation<>(modelName, fieldName, new NotContainsQueryOperator(value));
153+
}
154+
146155
/**
147156
* Generates a new sort object specifying a field that should be sorted in ascending order for a query.
148157
*

core/src/main/java/com/amplifyframework/core/model/query/predicate/QueryOperator.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,10 @@ public enum Type {
7171
* Contains some value comparison.
7272
*/
7373
CONTAINS,
74+
/**
75+
* Does not contain some value comparison.
76+
*/
77+
NOT_CONTAINS,
7478
/**
7579
* Between two values comparison.
7680
*/

core/src/test/java/com/amplifyframework/core/model/query/predicate/OperatorTest.java

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -203,5 +203,18 @@ public void testContainsOperator() {
203203
assertFalse(operator.evaluate("World"));
204204
assertFalse(operator.evaluate(""));
205205
}
206+
207+
/**
208+
* Test the accuracy of notContains operator evaluation.
209+
*/
210+
@Test
211+
public void testNotContainsOperator() {
212+
final NotContainsQueryOperator operator = new NotContainsQueryOperator("e");
213+
214+
assertFalse(operator.evaluate("Hello"));
215+
assertFalse(operator.evaluate("e"));
216+
assertTrue(operator.evaluate("World"));
217+
assertTrue(operator.evaluate(""));
218+
}
206219
}
207220

core/src/test/java/com/amplifyframework/core/model/query/predicate/PredicateTest.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,8 @@ public void testPredicateOperationEvaluation() {
7878
assertTrue(Person.FIRST_NAME.eq("Jane").evaluate(jane));
7979
assertTrue(Person.FIRST_NAME.beginsWith("J").evaluate(jane));
8080
assertTrue(Person.FIRST_NAME.contains("Jan").evaluate(jane));
81+
assertTrue(Person.FIRST_NAME.notContains("D").evaluate(jane));
82+
assertFalse(Person.FIRST_NAME.notContains("Jan").evaluate(jane));
8183
assertFalse(Person.LAST_NAME.eq("Jane").evaluate(jane));
8284
}
8385

0 commit comments

Comments
 (0)