Skip to content

Commit f4228fd

Browse files
HNSW index: add find with score query methods
1 parent 1bbd111 commit f4228fd

File tree

3 files changed

+166
-1
lines changed

3 files changed

+166
-1
lines changed
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
/*
2+
* Copyright 2024 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.query;
18+
19+
/**
20+
* Wraps the ID of a matching object and a score when using {@link Query#findIdsWithScores}.
21+
*/
22+
public class IdWithScore {
23+
24+
private final long id;
25+
private final double score;
26+
27+
// Note: this constructor is called by JNI, check before modifying/removing it.
28+
public IdWithScore(long id, double score) {
29+
this.id = id;
30+
this.score = score;
31+
}
32+
33+
/**
34+
* The object ID.
35+
*/
36+
public long getId() {
37+
return id;
38+
}
39+
40+
/**
41+
* The query score for the {@link #getId() id}.
42+
* <p>
43+
* The query score indicates some quality measurement. E.g. for vector nearest neighbor searches, the score is the
44+
* distance to the given vector.
45+
*/
46+
public double getScore() {
47+
return score;
48+
}
49+
}
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
/*
2+
* Copyright 2024 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.query;
18+
19+
/**
20+
* Wraps a matching object and a score when using {@link Query#findWithScores}.
21+
*/
22+
public class ObjectWithScore<T> {
23+
24+
private final T object;
25+
private final double score;
26+
27+
// Note: this constructor is called by JNI, check before modifying/removing it.
28+
public ObjectWithScore(T object, double score) {
29+
this.object = object;
30+
this.score = score;
31+
}
32+
33+
/**
34+
* The object.
35+
*/
36+
public T getObject() {
37+
return object;
38+
}
39+
40+
/**
41+
* The query score for the {@link #getObject() object}.
42+
* <p>
43+
* The query score indicates some quality measurement. E.g. for vector nearest neighbor searches, the score is the
44+
* distance to the given vector.
45+
*/
46+
public double getScore() {
47+
return score;
48+
}
49+
}

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

Lines changed: 68 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2017-2020 ObjectBox Ltd. All rights reserved.
2+
* Copyright 2017-2024 ObjectBox Ltd. All rights reserved.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -31,6 +31,7 @@
3131
import io.objectbox.BoxStore;
3232
import io.objectbox.InternalAccess;
3333
import io.objectbox.Property;
34+
import io.objectbox.annotation.HnswIndex;
3435
import io.objectbox.exception.NonUniqueResultException;
3536
import io.objectbox.reactive.DataObserver;
3637
import io.objectbox.reactive.DataSubscriptionList;
@@ -71,6 +72,10 @@ public class Query<T> implements Closeable {
7172

7273
native long[] nativeFindIds(long handle, long cursorHandle, long offset, long limit);
7374

75+
native List<ObjectWithScore<T>> nativeFindWithScores(long handle, long cursorHandle, long offset, long limit);
76+
77+
native List<IdWithScore> nativeFindIdsWithScores(long handle, long cursorHandle, long offset, long limit);
78+
7479
native long nativeCount(long handle, long cursorHandle);
7580

7681
native long nativeRemove(long handle, long cursorHandle);
@@ -380,6 +385,68 @@ public LazyList<T> findLazyCached() {
380385
return new LazyList<>(box, findIds(), true);
381386
}
382387

388+
/**
389+
* Like {@link #findIdsWithScores()}, but can skip and limit results.
390+
* <p>
391+
* Use to get a slice of the whole result, e.g. for "result paging".
392+
*
393+
* @param offset If greater than 0, skips this many results.
394+
* @param limit If greater than 0, returns at most this many results.
395+
*/
396+
@Nonnull
397+
public List<IdWithScore> findIdsWithScores(final long offset, final long limit) {
398+
checkOpen();
399+
return box.internalCallWithReaderHandle(cursorHandle -> nativeFindIdsWithScores(handle, cursorHandle, offset, limit));
400+
}
401+
402+
/**
403+
* Finds IDs of objects matching the query associated to their query score (e.g. distance in NN search).
404+
* <p>
405+
* This only works on objects with a property with an {@link HnswIndex}.
406+
*
407+
* @return A list of {@link IdWithScore} that wraps IDs of matching objects and their score, sorted by score in
408+
* ascending order.
409+
*/
410+
@Nonnull
411+
public List<IdWithScore> findIdsWithScores() {
412+
return findIdsWithScores(0, 0);
413+
}
414+
415+
/**
416+
* Like {@link #findWithScores()}, but can skip and limit results.
417+
* <p>
418+
* Use to get a slice of the whole result, e.g. for "result paging".
419+
*
420+
* @param offset If greater than 0, skips this many results.
421+
* @param limit If greater than 0, returns at most this many results.
422+
*/
423+
@Nonnull
424+
public List<ObjectWithScore<T>> findWithScores(final long offset, final long limit) {
425+
ensureNoFilterNoComparator();
426+
return callInReadTx(() -> {
427+
List<ObjectWithScore<T>> results = nativeFindWithScores(handle, cursorHandle(), offset, limit);
428+
if (eagerRelations != null) {
429+
for (int i = 0; i < results.size(); i++) {
430+
resolveEagerRelationForNonNullEagerRelations(results.get(i).getObject(), i);
431+
}
432+
}
433+
return results;
434+
});
435+
}
436+
437+
/**
438+
* Finds objects matching the query associated to their query score (e.g. distance in NN search).
439+
* <p>
440+
* This only works on objects with a property with an {@link HnswIndex}.
441+
*
442+
* @return A list of {@link ObjectWithScore} that wraps matching objects and their score, sorted by score in
443+
* ascending order.
444+
*/
445+
@Nonnull
446+
public List<ObjectWithScore<T>> findWithScores() {
447+
return findWithScores(0, 0);
448+
}
449+
383450
/**
384451
* Creates a {@link PropertyQuery} for the given property.
385452
* <p>

0 commit comments

Comments
 (0)