From 6b679223df3f84939d931bf6fb5266dd483826fe Mon Sep 17 00:00:00 2001 From: greenrobot Team <13865709+greenrobot-team@users.noreply.github.com> Date: Mon, 28 Jun 2021 12:44:51 +0200 Subject: [PATCH] String order: require on string conditions in QueryBuilder. - Do not check type if contains is used internally. --- .../query/PropertyQueryConditionImpl.java | 3 +- .../java/io/objectbox/query/QueryBuilder.java | 119 ++---------------- .../io/objectbox/kotlin/QueryBuilder.kt | 4 - .../io/objectbox/query/PropertyQueryTest.java | 2 +- .../java/io/objectbox/query/QueryTest.java | 34 ++--- .../java/io/objectbox/query/QueryTest2.java | 3 +- .../java/io/objectbox/query/QueryTestK.kt | 3 +- 7 files changed, 36 insertions(+), 132 deletions(-) diff --git a/objectbox-java/src/main/java/io/objectbox/query/PropertyQueryConditionImpl.java b/objectbox-java/src/main/java/io/objectbox/query/PropertyQueryConditionImpl.java index 2baa2b68..b699018f 100644 --- a/objectbox-java/src/main/java/io/objectbox/query/PropertyQueryConditionImpl.java +++ b/objectbox-java/src/main/java/io/objectbox/query/PropertyQueryConditionImpl.java @@ -325,7 +325,8 @@ void applyCondition(QueryBuilder builder) { builder.lessOrEqual(property, value, order); break; case CONTAINS: - builder.contains(property, value, order); + // Note: contains used for String[] as well, so do not enforce String type here. + builder.containsNoTypeCheck(property, value, order); break; case STARTS_WITH: builder.startsWith(property, value, order); diff --git a/objectbox-java/src/main/java/io/objectbox/query/QueryBuilder.java b/objectbox-java/src/main/java/io/objectbox/query/QueryBuilder.java index 78cd0a51..c8ffbc60 100644 --- a/objectbox-java/src/main/java/io/objectbox/query/QueryBuilder.java +++ b/objectbox-java/src/main/java/io/objectbox/query/QueryBuilder.java @@ -38,14 +38,14 @@ * *
  * userBox.query()
- *     .equal(User_.firstName, "Joe")
+ *     .equal(User_.firstName, "Joe", StringOrder.CASE_SENSITIVE)
  *     .order(User_.lastName)
  *     .build()
  *     .find()
  * 
* *

- * To add a condition use the appropriate method, for example {@link #equal(Property, String)} or + * To add a condition use the appropriate method, for example {@link #equal(Property, String, StringOrder)} or * {@link #isNull(Property)}. To order results use {@link #order(Property)} and its related methods. *

* Use {@link #build()} to create a {@link Query} object, which is used to actually get the results. @@ -70,10 +70,11 @@ public enum StringOrder { CASE_INSENSITIVE, /** - * Checks case of ASCII characters when macthing results, + * Checks case of ASCII characters when matching results, * e.g. the condition "= example" only matches "example", but not "Example". *

- * Use this if the property has an index. + * Use this if the property has an {@link io.objectbox.annotation.Index @Index} + * to dramatically increase the speed of looking-up results. */ CASE_SENSITIVE } @@ -449,7 +450,7 @@ public QueryBuilder eager(int limit, RelationInfo relationInfo, @Nullable Rel /** * Sets a filter that executes on primary query results (returned from the db core) on a Java level. - * For efficiency reasons, you should always prefer primary criteria like {@link #equal(Property, String)} if + * For efficiency reasons, you should always prefer primary criteria like {@link #equal(Property, long)} if * possible. * A filter requires to instantiate full Java objects beforehand, which is less efficient. *

@@ -733,30 +734,6 @@ public QueryBuilder between(Property property, Date value1, Date value2) { /** * Creates an "equal ('=')" condition for this property. - *

- * Ignores case when matching results, e.g. {@code equal(prop, "example")} matches both "Example" and "example". - *

- * Use {@link #equal(Property, String, StringOrder) equal(prop, value, StringOrder.CASE_SENSITIVE)} to only match - * if case is equal. - *

- * Note: Use a case sensitive condition to utilize an {@link io.objectbox.annotation.Index @Index} - * on {@code property}, dramatically speeding up look-up of results. - */ - public QueryBuilder equal(Property property, String value) { - verifyHandle(); - checkCombineCondition(nativeEqual(handle, property.getId(), value, false)); - return this; - } - - /** - * Creates an "equal ('=')" condition for this property. - *

- * Set {@code order} to {@link StringOrder#CASE_SENSITIVE StringOrder.CASE_SENSITIVE} to only match - * if case is equal. E.g. {@code equal(prop, "example", StringOrder.CASE_SENSITIVE)} only matches "example", - * but not "Example". - *

- * Note: Use a case sensitive condition to utilize an {@link io.objectbox.annotation.Index @Index} - * on {@code property}, dramatically speeding up look-up of results. */ public QueryBuilder equal(Property property, String value, StringOrder order) { verifyHandle(); @@ -766,30 +743,6 @@ public QueryBuilder equal(Property property, String value, StringOrder ord /** * Creates a "not equal ('<>')" condition for this property. - *

- * Ignores case when matching results, e.g. {@code notEqual(prop, "example")} excludes both "Example" and "example". - *

- * Use {@link #notEqual(Property, String, StringOrder) notEqual(prop, value, StringOrder.CASE_SENSITIVE)} to only exclude - * if case is equal. - *

- * Note: Use a case sensitive condition to utilize an {@link io.objectbox.annotation.Index @Index} - * on {@code property}, dramatically speeding up look-up of results. - */ - public QueryBuilder notEqual(Property property, String value) { - verifyHandle(); - checkCombineCondition(nativeNotEqual(handle, property.getId(), value, false)); - return this; - } - - /** - * Creates a "not equal ('<>')" condition for this property. - *

- * Set {@code order} to {@link StringOrder#CASE_SENSITIVE StringOrder.CASE_SENSITIVE} to only exclude - * if case is equal. E.g. {@code notEqual(prop, "example", StringOrder.CASE_SENSITIVE)} only excludes "example", - * but not "Example". - *

- * Note: Use a case sensitive condition to utilize an {@link io.objectbox.annotation.Index @Index} - * on {@code property}, dramatically speeding up look-up of results. */ public QueryBuilder notEqual(Property property, String value, StringOrder order) { verifyHandle(); @@ -798,56 +751,32 @@ public QueryBuilder notEqual(Property property, String value, StringOrder } /** - * Ignores case when matching results. Use the overload and pass - * {@link StringOrder#CASE_SENSITIVE StringOrder.CASE_SENSITIVE} to specify that case should not be ignored. + * Creates an contains condition. *

* Note: for a String array property, use {@link #containsElement} instead. */ - public QueryBuilder contains(Property property, String value) { + public QueryBuilder contains(Property property, String value, StringOrder order) { if (String[].class == property.type) { throw new UnsupportedOperationException("For String[] only containsElement() is supported at this time."); } - verifyHandle(); - checkCombineCondition(nativeContains(handle, property.getId(), value, false)); + containsNoTypeCheck(property, value, order); return this; } /** * For a String array property, matches if at least one element equals the given value. */ - public QueryBuilder containsElement(Property property, String value) { + public QueryBuilder containsElement(Property property, String value, StringOrder order) { if (String[].class != property.type) { throw new IllegalArgumentException("containsElement is only supported for String[] properties."); } - verifyHandle(); - checkCombineCondition(nativeContains(handle, property.getId(), value, false)); + containsNoTypeCheck(property, value, order); return this; } - /** - * Ignores case when matching results. Use the overload and pass - * {@link StringOrder#CASE_SENSITIVE StringOrder.CASE_SENSITIVE} to specify that case should not be ignored. - */ - public QueryBuilder startsWith(Property property, String value) { - verifyHandle(); - checkCombineCondition(nativeStartsWith(handle, property.getId(), value, false)); - return this; - } - - /** - * Ignores case when matching results. Use the overload and pass - * {@link StringOrder#CASE_SENSITIVE StringOrder.CASE_SENSITIVE} to specify that case should not be ignored. - */ - public QueryBuilder endsWith(Property property, String value) { - verifyHandle(); - checkCombineCondition(nativeEndsWith(handle, property.getId(), value, false)); - return this; - } - - public QueryBuilder contains(Property property, String value, StringOrder order) { + void containsNoTypeCheck(Property property, String value, StringOrder order) { verifyHandle(); checkCombineCondition(nativeContains(handle, property.getId(), value, order == StringOrder.CASE_SENSITIVE)); - return this; } public QueryBuilder startsWith(Property property, String value, StringOrder order) { @@ -862,14 +791,6 @@ public QueryBuilder endsWith(Property property, String value, StringOrder return this; } - /** - * Ignores case when matching results. Use the overload and pass - * {@link StringOrder#CASE_SENSITIVE StringOrder.CASE_SENSITIVE} to specify that case should not be ignored. - */ - public QueryBuilder less(Property property, String value) { - return less(property, value, StringOrder.CASE_INSENSITIVE); - } - public QueryBuilder less(Property property, String value, StringOrder order) { verifyHandle(); checkCombineCondition(nativeLess(handle, property.getId(), value, order == StringOrder.CASE_SENSITIVE, false)); @@ -882,14 +803,6 @@ public QueryBuilder lessOrEqual(Property property, String value, StringOrd return this; } - /** - * Ignores case when matching results. Use the overload and pass - * {@link StringOrder#CASE_SENSITIVE StringOrder.CASE_SENSITIVE} to specify that case should not be ignored. - */ - public QueryBuilder greater(Property property, String value) { - return greater(property, value, StringOrder.CASE_INSENSITIVE); - } - public QueryBuilder greater(Property property, String value, StringOrder order) { verifyHandle(); checkCombineCondition(nativeGreater(handle, property.getId(), value, order == StringOrder.CASE_SENSITIVE, false)); @@ -902,14 +815,6 @@ public QueryBuilder greaterOrEqual(Property property, String value, String return this; } - /** - * Ignores case when matching results. Use the overload and pass - * {@link StringOrder#CASE_SENSITIVE StringOrder.CASE_SENSITIVE} to specify that case should not be ignored. - */ - public QueryBuilder in(Property property, String[] values) { - return in(property, values, StringOrder.CASE_INSENSITIVE); - } - public QueryBuilder in(Property property, String[] values, StringOrder order) { verifyHandle(); checkCombineCondition(nativeIn(handle, property.getId(), values, order == StringOrder.CASE_SENSITIVE)); diff --git a/objectbox-kotlin/src/main/kotlin/io/objectbox/kotlin/QueryBuilder.kt b/objectbox-kotlin/src/main/kotlin/io/objectbox/kotlin/QueryBuilder.kt index 933a0d72..b9162281 100644 --- a/objectbox-kotlin/src/main/kotlin/io/objectbox/kotlin/QueryBuilder.kt +++ b/objectbox-kotlin/src/main/kotlin/io/objectbox/kotlin/QueryBuilder.kt @@ -28,10 +28,6 @@ inline fun QueryBuilder.inValues(property: Property, values: L inline fun QueryBuilder.inValues(property: Property, values: IntArray): QueryBuilder = `in`(property, values) -/** An alias for the "in" method, which is a reserved keyword in Kotlin. */ -inline fun QueryBuilder.inValues(property: Property, values: Array): QueryBuilder = - `in`(property, values) - /** An alias for the "in" method, which is a reserved keyword in Kotlin. */ inline fun QueryBuilder.inValues( property: Property, values: Array, diff --git a/tests/objectbox-java-test/src/test/java/io/objectbox/query/PropertyQueryTest.java b/tests/objectbox-java-test/src/test/java/io/objectbox/query/PropertyQueryTest.java index 23baf22d..9887a2f8 100644 --- a/tests/objectbox-java-test/src/test/java/io/objectbox/query/PropertyQueryTest.java +++ b/tests/objectbox-java-test/src/test/java/io/objectbox/query/PropertyQueryTest.java @@ -82,7 +82,7 @@ public void testFindStrings() { putTestEntity("BAR", 100); putTestEntitiesStrings(); putTestEntity("banana", 101); - Query query = box.query().startsWith(simpleString, "b").build(); + Query query = box.query().startsWith(simpleString, "b", StringOrder.CASE_INSENSITIVE).build(); String[] result = query.property(simpleString).findStrings(); assertEquals(5, result.length); diff --git a/tests/objectbox-java-test/src/test/java/io/objectbox/query/QueryTest.java b/tests/objectbox-java-test/src/test/java/io/objectbox/query/QueryTest.java index 23e9e141..0826e11d 100644 --- a/tests/objectbox-java-test/src/test/java/io/objectbox/query/QueryTest.java +++ b/tests/objectbox-java-test/src/test/java/io/objectbox/query/QueryTest.java @@ -78,8 +78,8 @@ public void testBuildTwice() { queryBuilder.between(TestEntity_.simpleInt, 42, 43); queryBuilder.in(TestEntity_.simpleInt, new int[]{42}); queryBuilder.notIn(TestEntity_.simpleInt, new int[]{42}); - queryBuilder.contains(TestEntity_.simpleString, "42"); - queryBuilder.startsWith(TestEntity_.simpleString, "42"); + queryBuilder.contains(TestEntity_.simpleString, "42", StringOrder.CASE_INSENSITIVE); + queryBuilder.startsWith(TestEntity_.simpleString, "42", StringOrder.CASE_SENSITIVE); queryBuilder.order(TestEntity_.simpleInt); queryBuilder.build().find(); } @@ -272,11 +272,11 @@ public void testOffsetLimit() { public void testString() { List entities = putTestEntitiesStrings(); int count = entities.size(); - assertEquals(1, getUniqueNotNull(box.query().equal(simpleString, "banana").build()).getId()); - assertEquals(count - 1, box.query().notEqual(simpleString, "banana").build().count()); - assertEquals(4, getUniqueNotNull(box.query().startsWith(simpleString, "ba").endsWith(simpleString, "shake").build()) + assertEquals(1, getUniqueNotNull(box.query().equal(simpleString, "banana", StringOrder.CASE_INSENSITIVE).build()).getId()); + assertEquals(count - 1, box.query().notEqual(simpleString, "banana", StringOrder.CASE_INSENSITIVE).build().count()); + assertEquals(4, getUniqueNotNull(box.query().startsWith(simpleString, "ba", StringOrder.CASE_INSENSITIVE).endsWith(simpleString, "shake", StringOrder.CASE_INSENSITIVE).build()) .getId()); - assertEquals(2, box.query().contains(simpleString, "nana").build().count()); + assertEquals(2, box.query().contains(simpleString, "nana", StringOrder.CASE_INSENSITIVE).build().count()); } @Test @@ -285,12 +285,12 @@ public void testStringArray() { // Using contains should not work on String array. Exception exception = assertThrows(UnsupportedOperationException.class, - () -> box.query().contains(simpleStringArray, "banana")); + () -> box.query().contains(simpleStringArray, "banana", StringOrder.CASE_INSENSITIVE)); assertEquals("For String[] only containsElement() is supported at this time.", exception.getMessage()); // containsElement(prop, value) matches if value is equal to one of the array items. // Verify by not matching entity where 'banana' is only a substring of an array item ('banana milk shake'). - List results = box.query().containsElement(simpleStringArray, "banana").build().find(); + List results = box.query().containsElement(simpleStringArray, "banana", StringOrder.CASE_INSENSITIVE).build().find(); assertEquals(1, results.size()); assertEquals("banana", results.get(0).getSimpleStringArray()[0]); } @@ -299,7 +299,7 @@ public void testStringArray() { public void testStringLess() { putTestEntitiesStrings(); putTestEntity("BaNaNa Split", 100); - Query query = box.query().less(simpleString, "banana juice").order(simpleString).build(); + Query query = box.query().less(simpleString, "banana juice", StringOrder.CASE_INSENSITIVE).order(simpleString).build(); List entities = query.find(); assertEquals(2, entities.size()); assertEquals("apple", entities.get(0).getSimpleString()); @@ -355,7 +355,7 @@ public void string_lessOrEqual_works() { public void testStringGreater() { putTestEntitiesStrings(); putTestEntity("FOO", 100); - Query query = box.query().greater(simpleString, "banana juice").order(simpleString).build(); + Query query = box.query().greater(simpleString, "banana juice", StringOrder.CASE_INSENSITIVE).order(simpleString).build(); List entities = query.find(); assertEquals(4, entities.size()); assertEquals("banana milk shake", entities.get(0).getSimpleString()); @@ -410,7 +410,7 @@ public void testStringIn() { putTestEntitiesStrings(); putTestEntity("BAR", 100); String[] values = {"bar", "foo bar"}; - Query query = box.query().in(simpleString, values).order(simpleString, OrderFlags.CASE_SENSITIVE) + Query query = box.query().in(simpleString, values, StringOrder.CASE_INSENSITIVE).order(simpleString, OrderFlags.CASE_SENSITIVE) .build(); List entities = query.find(); assertEquals(3, entities.size()); @@ -609,7 +609,7 @@ public void testBigResultList() { } box.put(entities); int count = entities.size(); - List entitiesQueried = box.query().equal(simpleString, sameValueForAll).build().find(); + List entitiesQueried = box.query().equal(simpleString, sameValueForAll, StringOrder.CASE_INSENSITIVE).build().find(); assertEquals(count, entitiesQueried.size()); } @@ -617,7 +617,7 @@ public void testBigResultList() { public void testEqualStringOrder() { putTestEntitiesStrings(); putTestEntity("BAR", 100); - assertEquals(2, box.query().equal(simpleString, "bar").build().count()); + assertEquals(2, box.query().equal(simpleString, "bar", StringOrder.CASE_INSENSITIVE).build().count()); assertEquals(1, box.query().equal(simpleString, "bar", StringOrder.CASE_SENSITIVE).build().count()); } @@ -800,7 +800,7 @@ public void testSetParameter2Floats() { @Test public void testSetParameterString() { putTestEntitiesStrings(); - Query query = box.query().equal(simpleString, "banana").parameterAlias("foo").build(); + Query query = box.query().equal(simpleString, "banana", StringOrder.CASE_INSENSITIVE).parameterAlias("foo").build(); assertEquals(1, getUniqueNotNull(query).getId()); query.setParameter(simpleString, "bar"); assertEquals(3, getUniqueNotNull(query).getId()); @@ -836,7 +836,7 @@ public void parameterAlias_combinedConditions() { public void testForEach() { List testEntities = putTestEntitiesStrings(); final StringBuilder stringBuilder = new StringBuilder(); - box.query().startsWith(simpleString, "banana").build() + box.query().startsWith(simpleString, "banana", StringOrder.CASE_INSENSITIVE).build() .forEach(data -> stringBuilder.append(data.getSimpleString()).append('#')); assertEquals("banana#banana milk shake#", stringBuilder.toString()); @@ -849,7 +849,7 @@ public void testForEach() { public void testForEachBreak() { putTestEntitiesStrings(); final StringBuilder stringBuilder = new StringBuilder(); - box.query().startsWith(simpleString, "banana").build() + box.query().startsWith(simpleString, "banana", StringOrder.CASE_INSENSITIVE).build() .forEach(data -> { stringBuilder.append(data.getSimpleString()); throw new BreakForEach(); @@ -947,7 +947,7 @@ public void testDescribe() { // Some conditions. Query query = box.query() - .equal(TestEntity_.simpleString, "Hello") + .equal(TestEntity_.simpleString, "Hello", StringOrder.CASE_INSENSITIVE) .or().greater(TestEntity_.simpleInt, 42) .build(); String describeActual = query.describe(); diff --git a/tests/objectbox-java-test/src/test/java/io/objectbox/query/QueryTest2.java b/tests/objectbox-java-test/src/test/java/io/objectbox/query/QueryTest2.java index b3a92d35..90cec623 100644 --- a/tests/objectbox-java-test/src/test/java/io/objectbox/query/QueryTest2.java +++ b/tests/objectbox-java-test/src/test/java/io/objectbox/query/QueryTest2.java @@ -16,6 +16,7 @@ package io.objectbox.query; +import io.objectbox.query.QueryBuilder.StringOrder; import org.junit.Test; import java.util.List; @@ -41,7 +42,7 @@ public void newQueryApi() { // current query API Query query = box.query() - .equal(TestEntity_.simpleString, "Fry") + .equal(TestEntity_.simpleString, "Fry", StringOrder.CASE_INSENSITIVE) .less(TestEntity_.simpleInt, 12) .or() .in(TestEntity_.simpleLong, new long[]{1012}) diff --git a/tests/objectbox-java-test/src/test/java/io/objectbox/query/QueryTestK.kt b/tests/objectbox-java-test/src/test/java/io/objectbox/query/QueryTestK.kt index ddd1f36b..6e60b4cb 100644 --- a/tests/objectbox-java-test/src/test/java/io/objectbox/query/QueryTestK.kt +++ b/tests/objectbox-java-test/src/test/java/io/objectbox/query/QueryTestK.kt @@ -2,6 +2,7 @@ package io.objectbox.query import io.objectbox.TestEntity_ import io.objectbox.kotlin.* +import io.objectbox.query.QueryBuilder.StringOrder import org.junit.Assert.assertEquals import org.junit.Test @@ -37,7 +38,7 @@ class QueryTestK : AbstractQueryTest() { less(TestEntity_.simpleInt, 12) or() inValues(TestEntity_.simpleLong, longArrayOf(1012)) - equal(TestEntity_.simpleString, "Fry") + equal(TestEntity_.simpleString, "Fry", StringOrder.CASE_INSENSITIVE) order(TestEntity_.simpleInt) } val results = query.find()