Skip to content

Commit

Permalink
Merge branch '113-string-condition-case-updates' into dev
Browse files Browse the repository at this point in the history
  • Loading branch information
greenrobot-team committed Oct 18, 2021
2 parents cdc4174 + 6b67922 commit b5f73aa
Show file tree
Hide file tree
Showing 7 changed files with 36 additions and 132 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -325,7 +325,8 @@ void applyCondition(QueryBuilder<T> 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);
Expand Down
119 changes: 12 additions & 107 deletions objectbox-java/src/main/java/io/objectbox/query/QueryBuilder.java
Original file line number Diff line number Diff line change
Expand Up @@ -38,14 +38,14 @@
*
* <pre>
* userBox.query()
* .equal(User_.firstName, "Joe")
* .equal(User_.firstName, "Joe", StringOrder.CASE_SENSITIVE)
* .order(User_.lastName)
* .build()
* .find()
* </pre>
*
* <p>
* 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.
* <p>
* Use {@link #build()} to create a {@link Query} object, which is used to actually get the results.
Expand All @@ -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".
* <p>
* 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
}
Expand Down Expand Up @@ -449,7 +450,7 @@ public QueryBuilder<T> 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.
* <p>
Expand Down Expand Up @@ -733,30 +734,6 @@ public QueryBuilder<T> between(Property<T> property, Date value1, Date value2) {

/**
* Creates an "equal ('=')" condition for this property.
* <p>
* Ignores case when matching results, e.g. {@code equal(prop, "example")} matches both "Example" and "example".
* <p>
* Use {@link #equal(Property, String, StringOrder) equal(prop, value, StringOrder.CASE_SENSITIVE)} to only match
* if case is equal.
* <p>
* 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<T> equal(Property<T> property, String value) {
verifyHandle();
checkCombineCondition(nativeEqual(handle, property.getId(), value, false));
return this;
}

/**
* Creates an "equal ('=')" condition for this property.
* <p>
* 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".
* <p>
* 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<T> equal(Property<T> property, String value, StringOrder order) {
verifyHandle();
Expand All @@ -766,30 +743,6 @@ public QueryBuilder<T> equal(Property<T> property, String value, StringOrder ord

/**
* Creates a "not equal ('&lt;&gt;')" condition for this property.
* <p>
* Ignores case when matching results, e.g. {@code notEqual(prop, "example")} excludes both "Example" and "example".
* <p>
* Use {@link #notEqual(Property, String, StringOrder) notEqual(prop, value, StringOrder.CASE_SENSITIVE)} to only exclude
* if case is equal.
* <p>
* 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<T> notEqual(Property<T> property, String value) {
verifyHandle();
checkCombineCondition(nativeNotEqual(handle, property.getId(), value, false));
return this;
}

/**
* Creates a "not equal ('&lt;&gt;')" condition for this property.
* <p>
* 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".
* <p>
* 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<T> notEqual(Property<T> property, String value, StringOrder order) {
verifyHandle();
Expand All @@ -798,56 +751,32 @@ public QueryBuilder<T> notEqual(Property<T> 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.
* <p>
* Note: for a String array property, use {@link #containsElement} instead.
*/
public QueryBuilder<T> contains(Property<T> property, String value) {
public QueryBuilder<T> contains(Property<T> 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<T> containsElement(Property<T> property, String value) {
public QueryBuilder<T> containsElement(Property<T> 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<T> startsWith(Property<T> 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<T> endsWith(Property<T> property, String value) {
verifyHandle();
checkCombineCondition(nativeEndsWith(handle, property.getId(), value, false));
return this;
}

public QueryBuilder<T> contains(Property<T> property, String value, StringOrder order) {
void containsNoTypeCheck(Property<T> property, String value, StringOrder order) {
verifyHandle();
checkCombineCondition(nativeContains(handle, property.getId(), value, order == StringOrder.CASE_SENSITIVE));
return this;
}

public QueryBuilder<T> startsWith(Property<T> property, String value, StringOrder order) {
Expand All @@ -862,14 +791,6 @@ public QueryBuilder<T> endsWith(Property<T> 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<T> less(Property<T> property, String value) {
return less(property, value, StringOrder.CASE_INSENSITIVE);
}

public QueryBuilder<T> less(Property<T> property, String value, StringOrder order) {
verifyHandle();
checkCombineCondition(nativeLess(handle, property.getId(), value, order == StringOrder.CASE_SENSITIVE, false));
Expand All @@ -882,14 +803,6 @@ public QueryBuilder<T> lessOrEqual(Property<T> 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<T> greater(Property<T> property, String value) {
return greater(property, value, StringOrder.CASE_INSENSITIVE);
}

public QueryBuilder<T> greater(Property<T> property, String value, StringOrder order) {
verifyHandle();
checkCombineCondition(nativeGreater(handle, property.getId(), value, order == StringOrder.CASE_SENSITIVE, false));
Expand All @@ -902,14 +815,6 @@ public QueryBuilder<T> greaterOrEqual(Property<T> 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<T> in(Property<T> property, String[] values) {
return in(property, values, StringOrder.CASE_INSENSITIVE);
}

public QueryBuilder<T> in(Property<T> property, String[] values, StringOrder order) {
verifyHandle();
checkCombineCondition(nativeIn(handle, property.getId(), values, order == StringOrder.CASE_SENSITIVE));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,10 +28,6 @@ inline fun <reified T> QueryBuilder<T>.inValues(property: Property<T>, values: L
inline fun <reified T> QueryBuilder<T>.inValues(property: Property<T>, values: IntArray): QueryBuilder<T> =
`in`(property, values)

/** An alias for the "in" method, which is a reserved keyword in Kotlin. */
inline fun <reified T> QueryBuilder<T>.inValues(property: Property<T>, values: Array<String>): QueryBuilder<T> =
`in`(property, values)

/** An alias for the "in" method, which is a reserved keyword in Kotlin. */
inline fun <reified T> QueryBuilder<T>.inValues(
property: Property<T>, values: Array<String>,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ public void testFindStrings() {
putTestEntity("BAR", 100);
putTestEntitiesStrings();
putTestEntity("banana", 101);
Query<TestEntity> query = box.query().startsWith(simpleString, "b").build();
Query<TestEntity> query = box.query().startsWith(simpleString, "b", StringOrder.CASE_INSENSITIVE).build();

String[] result = query.property(simpleString).findStrings();
assertEquals(5, result.length);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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();
}
Expand Down Expand Up @@ -272,11 +272,11 @@ public void testOffsetLimit() {
public void testString() {
List<TestEntity> 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
Expand All @@ -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<TestEntity> results = box.query().containsElement(simpleStringArray, "banana").build().find();
List<TestEntity> results = box.query().containsElement(simpleStringArray, "banana", StringOrder.CASE_INSENSITIVE).build().find();
assertEquals(1, results.size());
assertEquals("banana", results.get(0).getSimpleStringArray()[0]);
}
Expand All @@ -299,7 +299,7 @@ public void testStringArray() {
public void testStringLess() {
putTestEntitiesStrings();
putTestEntity("BaNaNa Split", 100);
Query<TestEntity> query = box.query().less(simpleString, "banana juice").order(simpleString).build();
Query<TestEntity> query = box.query().less(simpleString, "banana juice", StringOrder.CASE_INSENSITIVE).order(simpleString).build();
List<TestEntity> entities = query.find();
assertEquals(2, entities.size());
assertEquals("apple", entities.get(0).getSimpleString());
Expand Down Expand Up @@ -355,7 +355,7 @@ public void string_lessOrEqual_works() {
public void testStringGreater() {
putTestEntitiesStrings();
putTestEntity("FOO", 100);
Query<TestEntity> query = box.query().greater(simpleString, "banana juice").order(simpleString).build();
Query<TestEntity> query = box.query().greater(simpleString, "banana juice", StringOrder.CASE_INSENSITIVE).order(simpleString).build();
List<TestEntity> entities = query.find();
assertEquals(4, entities.size());
assertEquals("banana milk shake", entities.get(0).getSimpleString());
Expand Down Expand Up @@ -410,7 +410,7 @@ public void testStringIn() {
putTestEntitiesStrings();
putTestEntity("BAR", 100);
String[] values = {"bar", "foo bar"};
Query<TestEntity> query = box.query().in(simpleString, values).order(simpleString, OrderFlags.CASE_SENSITIVE)
Query<TestEntity> query = box.query().in(simpleString, values, StringOrder.CASE_INSENSITIVE).order(simpleString, OrderFlags.CASE_SENSITIVE)
.build();
List<TestEntity> entities = query.find();
assertEquals(3, entities.size());
Expand Down Expand Up @@ -609,15 +609,15 @@ public void testBigResultList() {
}
box.put(entities);
int count = entities.size();
List<TestEntity> entitiesQueried = box.query().equal(simpleString, sameValueForAll).build().find();
List<TestEntity> entitiesQueried = box.query().equal(simpleString, sameValueForAll, StringOrder.CASE_INSENSITIVE).build().find();
assertEquals(count, entitiesQueried.size());
}

@Test
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());
}

Expand Down Expand Up @@ -800,7 +800,7 @@ public void testSetParameter2Floats() {
@Test
public void testSetParameterString() {
putTestEntitiesStrings();
Query<TestEntity> query = box.query().equal(simpleString, "banana").parameterAlias("foo").build();
Query<TestEntity> 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());
Expand Down Expand Up @@ -836,7 +836,7 @@ public void parameterAlias_combinedConditions() {
public void testForEach() {
List<TestEntity> 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());

Expand All @@ -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();
Expand Down Expand Up @@ -947,7 +947,7 @@ public void testDescribe() {

// Some conditions.
Query<TestEntity> query = box.query()
.equal(TestEntity_.simpleString, "Hello")
.equal(TestEntity_.simpleString, "Hello", StringOrder.CASE_INSENSITIVE)
.or().greater(TestEntity_.simpleInt, 42)
.build();
String describeActual = query.describe();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@

package io.objectbox.query;

import io.objectbox.query.QueryBuilder.StringOrder;
import org.junit.Test;

import java.util.List;
Expand All @@ -41,7 +42,7 @@ public void newQueryApi() {

// current query API
Query<TestEntity> 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})
Expand Down
Loading

0 comments on commit b5f73aa

Please sign in to comment.