Skip to content

Commit a6910e8

Browse files
committed
Merge branch '36-property-query-updates' into dev
2 parents 4214b56 + 6d48419 commit a6910e8

File tree

7 files changed

+440
-89
lines changed

7 files changed

+440
-89
lines changed
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
/*
2+
* Copyright 2019 ObjectBox Ltd. 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+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package io.objectbox.exception;
18+
19+
/**
20+
* Thrown if a property query aggregate function can not compute a result due to a number type overflowing.
21+
*/
22+
public class NumericOverflowException extends DbException {
23+
24+
public NumericOverflowException(String message) {
25+
super(message);
26+
}
27+
}

objectbox-java/src/main/java/io/objectbox/query/PropertyQuery.java

Lines changed: 75 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,8 @@ native String nativeFindString(long handle, long cursorHandle, int propertyId, b
9494

9595
native double nativeAvg(long handle, long cursorHandle, int propertyId);
9696

97+
native long nativeAvgLong(long handle, long cursorHandle, int propertyId);
98+
9799
native long nativeCount(long handle, long cursorHandle, int propertyId, boolean distinct);
98100

99101
/** Clears all values (e.g. distinct and null value). */
@@ -179,7 +181,7 @@ public PropertyQuery nullValue(Object nullValue) {
179181
* <p>
180182
* Note: results are not guaranteed to be in any particular order.
181183
* <p>
182-
* See also: {@link #distinct}, {@link #distinct(QueryBuilder.StringOrder)}
184+
* See also: {@link #distinct()}, {@link #distinct(QueryBuilder.StringOrder)}
183185
*
184186
* @return Found strings
185187
*/
@@ -202,7 +204,7 @@ public String[] call() {
202204
* <p>
203205
* Note: results are not guaranteed to be in any particular order.
204206
* <p>
205-
* See also: {@link #distinct}
207+
* See also: {@link #distinct()}
206208
*
207209
* @return Found longs
208210
*/
@@ -223,7 +225,7 @@ public long[] call() {
223225
* <p>
224226
* Note: results are not guaranteed to be in any particular order.
225227
* <p>
226-
* See also: {@link #distinct}
228+
* See also: {@link #distinct()}
227229
*/
228230
public int[] findInts() {
229231
return (int[]) query.callInReadTx(new Callable<int[]>() {
@@ -242,7 +244,7 @@ public int[] call() {
242244
* <p>
243245
* Note: results are not guaranteed to be in any particular order.
244246
* <p>
245-
* See also: {@link #distinct}
247+
* See also: {@link #distinct()}
246248
*/
247249
public short[] findShorts() {
248250
return (short[]) query.callInReadTx(new Callable<short[]>() {
@@ -261,7 +263,7 @@ public short[] call() {
261263
* <p>
262264
* Note: results are not guaranteed to be in any particular order.
263265
* <p>
264-
* See also: {@link #distinct}
266+
* See also: {@link #distinct()}
265267
*/
266268
public char[] findChars() {
267269
return (char[]) query.callInReadTx(new Callable<char[]>() {
@@ -297,7 +299,7 @@ public byte[] call() {
297299
* <p>
298300
* Note: results are not guaranteed to be in any particular order.
299301
* <p>
300-
* See also: {@link #distinct}
302+
* See also: {@link #distinct()}
301303
*/
302304
public float[] findFloats() {
303305
return (float[]) query.callInReadTx(new Callable<float[]>() {
@@ -316,7 +318,7 @@ public float[] call() {
316318
* <p>
317319
* Note: results are not guaranteed to be in any particular order.
318320
* <p>
319-
* See also: {@link #distinct}
321+
* See also: {@link #distinct()}
320322
*/
321323
public double[] findDoubles() {
322324
return (double[]) query.callInReadTx(new Callable<double[]>() {
@@ -381,8 +383,17 @@ public Double findDouble() {
381383
return (Double) findNumber();
382384
}
383385

384-
385-
/** Sums up all values for the given property over all Objects matching the query. */
386+
/**
387+
* Sums up all values for the given property over all Objects matching the query.
388+
*
389+
* Note: this method is not recommended for properties of type long unless you know the contents of the DB not to
390+
* overflow. Use {@link #sumDouble()} instead if you cannot guarantee the sum to be in the long value range.
391+
*
392+
* @return 0 in case no elements matched the query
393+
* @throws io.objectbox.exception.NumericOverflowException if the sum exceeds the numbers {@link Long} can
394+
* represent.
395+
* This is different from Java arithmetic where it would "wrap around" (e.g. max. value + 1 = min. value).
396+
*/
386397
public long sum() {
387398
return (Long) query.callInReadTx(new Callable<Long>() {
388399
@Override
@@ -392,7 +403,13 @@ public Long call() {
392403
});
393404
}
394405

395-
/** Sums up all values for the given property over all Objects matching the query. */
406+
/**
407+
* Sums up all values for the given property over all Objects matching the query.
408+
*
409+
* Note: for integer types int and smaller, {@link #sum()} is usually preferred for sums.
410+
*
411+
* @return 0 in case no elements matched the query
412+
*/
396413
public double sumDouble() {
397414
return (Double) query.callInReadTx(new Callable<Double>() {
398415
@Override
@@ -402,7 +419,11 @@ public Double call() {
402419
});
403420
}
404421

405-
/** Finds the maximum value for the given property over all Objects matching the query. */
422+
/**
423+
* Finds the maximum value for the given property over all Objects matching the query.
424+
*
425+
* @return Long.MIN_VALUE in case no elements matched the query
426+
*/
406427
public long max() {
407428
return (Long) query.callInReadTx(new Callable<Long>() {
408429
@Override
@@ -412,7 +433,11 @@ public Long call() {
412433
});
413434
}
414435

415-
/** Finds the maximum value for the given property over all Objects matching the query. */
436+
/**
437+
* Finds the maximum value for the given property over all Objects matching the query.
438+
*
439+
* @return NaN in case no elements matched the query
440+
*/
416441
public double maxDouble() {
417442
return (Double) query.callInReadTx(new Callable<Double>() {
418443
@Override
@@ -422,7 +447,11 @@ public Double call() {
422447
});
423448
}
424449

425-
/** Finds the minimum value for the given property over all Objects matching the query. */
450+
/**
451+
* Finds the minimum value for the given property over all Objects matching the query.
452+
*
453+
* @return Long.MAX_VALUE in case no elements matched the query
454+
*/
426455
public long min() {
427456
return (Long) query.callInReadTx(new Callable<Long>() {
428457
@Override
@@ -432,7 +461,11 @@ public Long call() {
432461
});
433462
}
434463

435-
/** Finds the minimum value for the given property over all Objects matching the query. */
464+
/**
465+
* Finds the minimum value for the given property over all objects matching the query.
466+
*
467+
* @return NaN in case no elements matched the query
468+
*/
436469
public double minDouble() {
437470
return (Double) query.callInReadTx(new Callable<Double>() {
438471
@Override
@@ -442,7 +475,13 @@ public Double call() {
442475
});
443476
}
444477

445-
/** Calculates the average of all values for the given property over all Objects matching the query. */
478+
/**
479+
* Calculates the average of all values for the given number property over all Objects matching the query.
480+
* <p>
481+
* For integer properties you can also use {@link #avgLong()}.
482+
*
483+
* @return NaN in case no elements matched the query
484+
*/
446485
public double avg() {
447486
return (Double) query.callInReadTx(new Callable<Double>() {
448487
@Override
@@ -452,6 +491,27 @@ public Double call() {
452491
});
453492
}
454493

494+
/**
495+
* Calculates the average of all values for the given integer property over all Objects matching the query.
496+
* <p>
497+
* For floating-point properties use {@link #avg()}.
498+
*
499+
* @return 0 in case no elements matched the query
500+
*/
501+
public long avgLong() {
502+
return (Long) query.callInReadTx(new Callable<Long>() {
503+
@Override
504+
public Long call() {
505+
return nativeAvgLong(queryHandle, query.cursorHandle(), propertyId);
506+
}
507+
});
508+
}
509+
510+
/**
511+
* The count of non-null values.
512+
* <p>
513+
* See also: {@link #distinct()}
514+
*/
455515
public long count() {
456516
return (Long) query.callInReadTx(new Callable<Long>() {
457517
@Override

tests/objectbox-java-test/src/main/java/io/objectbox/TestEntity.java

Lines changed: 38 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,10 @@
1616

1717
package io.objectbox;
1818

19+
/** In "real" entity would be annotated with @Entity. */
1920
public class TestEntity {
2021

22+
/** In "real" entity would be annotated with @Id. */
2123
private long id;
2224
private boolean simpleBoolean;
2325
private byte simpleByte;
@@ -30,6 +32,12 @@ public class TestEntity {
3032
private String simpleString;
3133
/** Not-null value. */
3234
private byte[] simpleByteArray;
35+
/** In "real" entity would be annotated with @Unsigned. */
36+
private short simpleShortU;
37+
/** In "real" entity would be annotated with @Unsigned. */
38+
private int simpleIntU;
39+
/** In "real" entity would be annotated with @Unsigned. */
40+
private long simpleLongU;
3341

3442
transient boolean noArgsConstructorCalled;
3543

@@ -41,7 +49,7 @@ public TestEntity(long id) {
4149
this.id = id;
4250
}
4351

44-
public TestEntity(long id, boolean simpleBoolean, byte simpleByte, short simpleShort, int simpleInt, long simpleLong, float simpleFloat, double simpleDouble, String simpleString, byte[] simpleByteArray) {
52+
public TestEntity(long id, boolean simpleBoolean, byte simpleByte, short simpleShort, int simpleInt, long simpleLong, float simpleFloat, double simpleDouble, String simpleString, byte[] simpleByteArray, short simpleShortU, int simpleIntU, long simpleLongU) {
4553
this.id = id;
4654
this.simpleBoolean = simpleBoolean;
4755
this.simpleByte = simpleByte;
@@ -52,6 +60,9 @@ public TestEntity(long id, boolean simpleBoolean, byte simpleByte, short simpleS
5260
this.simpleDouble = simpleDouble;
5361
this.simpleString = simpleString;
5462
this.simpleByteArray = simpleByteArray;
63+
this.simpleShortU = simpleShortU;
64+
this.simpleIntU = simpleIntU;
65+
this.simpleLongU = simpleLongU;
5566
}
5667

5768
public long getId() {
@@ -138,4 +149,30 @@ public void setSimpleByteArray(byte[] simpleByteArray) {
138149
this.simpleByteArray = simpleByteArray;
139150
}
140151

152+
public short getSimpleShortU() {
153+
return simpleShortU;
154+
}
155+
156+
public TestEntity setSimpleShortU(short simpleShortU) {
157+
this.simpleShortU = simpleShortU;
158+
return this;
159+
}
160+
161+
public int getSimpleIntU() {
162+
return simpleIntU;
163+
}
164+
165+
public TestEntity setSimpleIntU(int simpleIntU) {
166+
this.simpleIntU = simpleIntU;
167+
return this;
168+
}
169+
170+
public long getSimpleLongU() {
171+
return simpleLongU;
172+
}
173+
174+
public TestEntity setSimpleLongU(long simpleLongU) {
175+
this.simpleLongU = simpleLongU;
176+
return this;
177+
}
141178
}

tests/objectbox-java-test/src/main/java/io/objectbox/TestEntityCursor.java

Lines changed: 28 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -16,14 +16,15 @@
1616

1717
package io.objectbox;
1818

19-
2019
import io.objectbox.annotation.apihint.Internal;
2120
import io.objectbox.internal.CursorFactory;
2221

23-
// THIS CODE IS based on GENERATED code BY ObjectBox
22+
// NOTE: Copied from a plugin project (& removed some unused Properties).
23+
// THIS CODE IS GENERATED BY ObjectBox, DO NOT EDIT.
2424

2525
/**
26-
* Cursor for DB entity "TestEntity".
26+
* ObjectBox generated Cursor implementation for "TestEntity".
27+
* Note that this is a low-level class: usually you should stick to the Box class.
2728
*/
2829
public final class TestEntityCursor extends Cursor<TestEntity> {
2930

@@ -32,14 +33,15 @@ public final class TestEntityCursor extends Cursor<TestEntity> {
3233

3334
@Internal
3435
static final class Factory implements CursorFactory<TestEntity> {
35-
public Cursor<TestEntity> createCursor(Transaction tx, long cursorHandle, BoxStore boxStoreForEntities) {
36+
@Override
37+
public Cursor<TestEntity> createCursor(io.objectbox.Transaction tx, long cursorHandle, BoxStore boxStoreForEntities) {
3638
return new TestEntityCursor(tx, cursorHandle, boxStoreForEntities);
3739
}
3840
}
3941

4042
private static final TestEntity_.TestEntityIdGetter ID_GETTER = TestEntity_.__ID_GETTER;
4143

42-
// Property IDs get verified in Cursor base class
44+
4345
private final static int __ID_simpleBoolean = TestEntity_.simpleBoolean.id;
4446
private final static int __ID_simpleByte = TestEntity_.simpleByte.id;
4547
private final static int __ID_simpleShort = TestEntity_.simpleShort.id;
@@ -49,8 +51,11 @@ public Cursor<TestEntity> createCursor(Transaction tx, long cursorHandle, BoxSto
4951
private final static int __ID_simpleDouble = TestEntity_.simpleDouble.id;
5052
private final static int __ID_simpleString = TestEntity_.simpleString.id;
5153
private final static int __ID_simpleByteArray = TestEntity_.simpleByteArray.id;
54+
private final static int __ID_simpleShortU = TestEntity_.simpleShortU.id;
55+
private final static int __ID_simpleIntU = TestEntity_.simpleIntU.id;
56+
private final static int __ID_simpleLongU = TestEntity_.simpleLongU.id;
5257

53-
public TestEntityCursor(Transaction tx, long cursor, BoxStore boxStore) {
58+
public TestEntityCursor(io.objectbox.Transaction tx, long cursor, BoxStore boxStore) {
5459
super(tx, cursor, TestEntity_.__INSTANCE, boxStore);
5560
}
5661

@@ -66,14 +71,25 @@ public final long getId(TestEntity entity) {
6671
*/
6772
@Override
6873
public final long put(TestEntity entity) {
69-
long __assignedId = collect313311(cursor, entity.getId(), PUT_FLAG_FIRST | PUT_FLAG_COMPLETE,
70-
__ID_simpleString, entity.getSimpleString(), 0, null,
71-
0, null, __ID_simpleByteArray, entity.getSimpleByteArray(),
72-
0, 0, __ID_simpleLong, entity.getSimpleLong(),
73-
INT_NULL_HACK ? 0 : __ID_simpleInt, entity.getSimpleInt(), __ID_simpleShort, entity.getSimpleShort(),
74-
__ID_simpleByte, entity.getSimpleByte(), __ID_simpleBoolean, entity.getSimpleBoolean() ? 1 : 0,
74+
String simpleString = entity.getSimpleString();
75+
int __id8 = simpleString != null ? __ID_simpleString : 0;
76+
byte[] simpleByteArray = entity.getSimpleByteArray();
77+
int __id9 = simpleByteArray != null ? __ID_simpleByteArray : 0;
78+
79+
collect313311(cursor, 0, PUT_FLAG_FIRST,
80+
__id8, simpleString, 0, null,
81+
0, null, __id9, simpleByteArray,
82+
__ID_simpleLong, entity.getSimpleLong(), __ID_simpleLongU, entity.getSimpleLongU(),
83+
INT_NULL_HACK ? 0 : __ID_simpleInt, entity.getSimpleInt(), __ID_simpleIntU, entity.getSimpleIntU(),
84+
__ID_simpleShort, entity.getSimpleShort(), __ID_simpleShortU, entity.getSimpleShortU(),
7585
__ID_simpleFloat, entity.getSimpleFloat(), __ID_simpleDouble, entity.getSimpleDouble());
86+
87+
long __assignedId = collect004000(cursor, entity.getId(), PUT_FLAG_COMPLETE,
88+
__ID_simpleByte, entity.getSimpleByte(), __ID_simpleBoolean, entity.getSimpleBoolean() ? 1 : 0,
89+
0, 0, 0, 0);
90+
7691
entity.setId(__assignedId);
92+
7793
return __assignedId;
7894
}
7995

0 commit comments

Comments
 (0)