Skip to content

Commit 29747e2

Browse files
Query: add findFirstId and findUniqueId (#149)
1 parent 48331e7 commit 29747e2

File tree

3 files changed

+75
-8
lines changed

3 files changed

+75
-8
lines changed

objectbox-java/src/main/java/io/objectbox/exception/NonUniqueResultException.java

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,11 @@
1616

1717
package io.objectbox.exception;
1818

19-
/** Throw if {@link io.objectbox.query.Query#findUnique()} returns more than one result. */
19+
/**
20+
* Thrown if {@link io.objectbox.query.Query#findUnique() Query.findUnique()} or
21+
* {@link io.objectbox.query.Query#findUniqueId() Query.findUniqueId()} is called,
22+
* but the query matches more than one object.
23+
*/
2024
public class NonUniqueResultException extends DbException {
2125
public NonUniqueResultException(String message) {
2226
super(message);

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

Lines changed: 33 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,10 @@ public class Query<T> implements Closeable {
6161

6262
native List<T> nativeFind(long handle, long cursorHandle, long offset, long limit) throws Exception;
6363

64+
native long nativeFindFirstId(long handle, long cursorHandle);
65+
66+
native long nativeFindUniqueId(long handle, long cursorHandle);
67+
6468
native long[] nativeFindIds(long handle, long cursorHandle, long offset, long limit);
6569

6670
native long nativeCount(long handle, long cursorHandle);
@@ -191,9 +195,9 @@ private void ensureNoComparator() {
191195
}
192196

193197
/**
194-
* Find the unique Object matching the query.
195-
*
196-
* @throws io.objectbox.exception.NonUniqueResultException if result was not unique
198+
* If there is a single matching object, returns it. If there is more than one matching object,
199+
* throws {@link io.objectbox.exception.NonUniqueResultException NonUniqueResultException}.
200+
* If there are no matches returns null.
197201
*/
198202
@Nullable
199203
public T findUnique() {
@@ -244,6 +248,32 @@ public List<T> find(final long offset, final long limit) {
244248
});
245249
}
246250

251+
/**
252+
* Returns the ID of the first matching object. If there are no results returns 0.
253+
* <p>
254+
* Like {@link #findFirst()}, but more efficient as no object is created.
255+
* <p>
256+
* Ignores any {@link QueryBuilder#filter(QueryFilter) query filter}.
257+
*/
258+
public long findFirstId() {
259+
checkOpen();
260+
return box.internalCallWithReaderHandle(cursorHandle -> nativeFindFirstId(handle, cursorHandle));
261+
}
262+
263+
/**
264+
* If there is a single matching object, returns its ID. If there is more than one matching object,
265+
* throws {@link io.objectbox.exception.NonUniqueResultException NonUniqueResultException}.
266+
* If there are no matches returns 0.
267+
* <p>
268+
* Like {@link #findUnique()}, but more efficient as no object is created.
269+
* <p>
270+
* Ignores any {@link QueryBuilder#filter(QueryFilter) query filter}.
271+
*/
272+
public long findUniqueId() {
273+
checkOpen();
274+
return box.internalCallWithReaderHandle(cursorHandle -> nativeFindUniqueId(handle, cursorHandle));
275+
}
276+
247277
/**
248278
* Very efficient way to get just the IDs without creating any objects. IDs can later be used to lookup objects
249279
* (lookups by ID are also very efficient in ObjectBox).

tests/objectbox-java-test/src/test/java/io/objectbox/query/QueryTest.java

Lines changed: 37 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -104,14 +104,16 @@ public void useAfterQueryClose_fails() {
104104
assertThrowsQueryIsClosed(query::count);
105105
assertThrowsQueryIsClosed(query::describe);
106106
assertThrowsQueryIsClosed(query::describeParameters);
107+
assertThrowsQueryIsClosed(query::findFirst);
108+
assertThrowsQueryIsClosed(query::findUnique);
107109
assertThrowsQueryIsClosed(query::find);
108110
assertThrowsQueryIsClosed(() -> query.find(0, 1));
109-
assertThrowsQueryIsClosed(query::findFirst);
111+
assertThrowsQueryIsClosed(query::findFirstId);
112+
assertThrowsQueryIsClosed(query::findUniqueId);
110113
assertThrowsQueryIsClosed(query::findIds);
111114
assertThrowsQueryIsClosed(() -> query.findIds(0, 1));
112115
assertThrowsQueryIsClosed(query::findLazy);
113116
assertThrowsQueryIsClosed(query::findLazyCached);
114-
assertThrowsQueryIsClosed(query::findUnique);
115117
assertThrowsQueryIsClosed(query::remove);
116118

117119
// For setParameter(s) the native method is not actually called, so fine to use incorrect alias and property.
@@ -162,14 +164,16 @@ public void useAfterStoreClose_failsIfUsingStore() {
162164

163165
// All methods accessing the store throw.
164166
assertThrowsStoreIsClosed(query::count);
167+
assertThrowsStoreIsClosed(query::findFirst);
168+
assertThrowsStoreIsClosed(query::findUnique);
165169
assertThrowsStoreIsClosed(query::find);
166170
assertThrowsStoreIsClosed(() -> query.find(0, 1));
167-
assertThrowsStoreIsClosed(query::findFirst);
171+
assertThrowsStoreIsClosed(query::findFirstId);
172+
assertThrowsStoreIsClosed(query::findUniqueId);
168173
assertThrowsStoreIsClosed(query::findIds);
169174
assertThrowsStoreIsClosed(() -> query.findIds(0, 1));
170175
assertThrowsStoreIsClosed(query::findLazy);
171176
assertThrowsStoreIsClosed(query::findLazyCached);
172-
assertThrowsStoreIsClosed(query::findUnique);
173177
assertThrowsStoreIsClosed(query::remove);
174178
assertThrowsStoreIsClosed(() -> query.subscribe().observer(data -> {
175179
}));
@@ -915,6 +919,35 @@ public void testRemove() {
915919
assertEquals(4, box.count());
916920
}
917921

922+
@Test
923+
public void findFirstId() {
924+
putTestEntitiesScalars();
925+
try (Query<TestEntity> query = box.query(simpleInt.greater(2006)).build()) {
926+
assertEquals(8, query.findFirstId());
927+
}
928+
// No result.
929+
try (Query<TestEntity> query = box.query(simpleInt.equal(-1)).build()) {
930+
assertEquals(0, query.findFirstId());
931+
}
932+
}
933+
934+
@Test
935+
public void findUniqueId() {
936+
putTestEntitiesScalars();
937+
try (Query<TestEntity> query = box.query(simpleInt.equal(2006)).build()) {
938+
assertEquals(7, query.findUniqueId());
939+
}
940+
// No result.
941+
try (Query<TestEntity> query = box.query(simpleInt.equal(-1)).build()) {
942+
assertEquals(0, query.findUniqueId());
943+
}
944+
// More than one result.
945+
try (Query<TestEntity> query = box.query(simpleInt.greater(2006)).build()) {
946+
NonUniqueResultException e = assertThrows(NonUniqueResultException.class, query::findUniqueId);
947+
assertEquals("Query does not have a unique result (more than one result): 3", e.getMessage());
948+
}
949+
}
950+
918951
@Test
919952
public void testFindIds() {
920953
putTestEntitiesScalars();

0 commit comments

Comments
 (0)