Skip to content

Commit 171287c

Browse files
authored
Merge pull request #592 from weaviate/feat/boost
feat(query): add Boost parameter to all query types
2 parents f5f5762 + 4c89835 commit 171287c

10 files changed

Lines changed: 6834 additions & 281 deletions

File tree

.github/workflows/test.yaml

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,15 @@ jobs:
9292
fail-fast: false
9393
matrix:
9494
WEAVIATE_VERSION:
95-
["1.32.24", "1.33.11", "1.34.7", "1.35.2", "1.36.9", "1.37.7"]
95+
[
96+
"1.32.24",
97+
"1.33.11",
98+
"1.34.7",
99+
"1.35.2",
100+
"1.36.9",
101+
"1.37.7",
102+
"1.38.2",
103+
]
96104
steps:
97105
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
98106

src/it/java/io/weaviate/ConcurrentTest.java

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,21 @@ public static void requireAtLeast(Weaviate.Version required) {
126126
.isGreaterThanOrEqualTo(required.semver);
127127
}
128128

129+
/**
130+
* Skip the test if the version that the {@link Weaviate}
131+
* container is running is newer than the required one.
132+
*
133+
* <br>
134+
* This is useful for guarding tests from server versions
135+
* which the client does not fully support yet.
136+
*/
137+
public static void requireAtMost(Weaviate.Version required) {
138+
var actual = SemanticVersion.of(Weaviate.VERSION);
139+
Assumptions.assumeThat(actual)
140+
.as("requires at most %s, but running %s", required.semver, actual)
141+
.isLessThanOrEqualTo(required.semver);
142+
}
143+
129144
@FunctionalInterface
130145
public interface ThrowingRunnable {
131146
void run() throws Exception;

src/it/java/io/weaviate/containers/Weaviate.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,8 @@ public enum Version {
4545
V134(1, 34, 7),
4646
V135(1, 35, 2),
4747
V136(1, 36, 9),
48-
V137(1, 37, 7);
48+
V137(1, 37, 7),
49+
V138(1, 38, 2);
4950

5051
public final SemanticVersion semver;
5152

src/it/java/io/weaviate/integration/RbacITest.java

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66

77
import org.assertj.core.api.Assertions;
88
import org.assertj.core.api.InstanceOfAssertFactories;
9+
import org.junit.BeforeClass;
910
import org.junit.Test;
1011

1112
import io.weaviate.ConcurrentTest;
@@ -14,10 +15,10 @@
1415
import io.weaviate.client6.v1.api.rbac.AliasesPermission;
1516
import io.weaviate.client6.v1.api.rbac.BackupsPermission;
1617
import io.weaviate.client6.v1.api.rbac.ClusterPermission;
17-
import io.weaviate.client6.v1.api.rbac.McpPermission;
1818
import io.weaviate.client6.v1.api.rbac.CollectionsPermission;
1919
import io.weaviate.client6.v1.api.rbac.DataPermission;
2020
import io.weaviate.client6.v1.api.rbac.GroupsPermission;
21+
import io.weaviate.client6.v1.api.rbac.McpPermission;
2122
import io.weaviate.client6.v1.api.rbac.NodesPermission;
2223
import io.weaviate.client6.v1.api.rbac.Permission;
2324
import io.weaviate.client6.v1.api.rbac.ReplicatePermission;
@@ -56,6 +57,12 @@ public class RbacITest extends ConcurrentTest {
5657
private static final WeaviateClient client = container
5758
.getClient(fn -> fn.authentication(Authentication.apiKey(API_KEY)));
5859

60+
@BeforeClass
61+
public static void __() {
62+
// TODO(dyma): remove once namespace permissions are supported (v1.38 feature)
63+
ConcurrentTest.requireAtMost(Weaviate.Version.V137);
64+
}
65+
5966
@Test
6067
public void test_roles_Lifecycle() throws IOException {
6168
// Arrange

src/it/java/io/weaviate/integration/SearchITest.java

Lines changed: 70 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,11 @@
1717
import org.junit.Ignore;
1818
import org.junit.Test;
1919
import org.junit.rules.TestRule;
20+
import org.junit.runner.RunWith;
21+
22+
import com.jparams.junit4.JParamsTestRunner;
23+
import com.jparams.junit4.data.DataMethod;
24+
import com.jparams.junit4.description.Name;
2025

2126
import io.weaviate.ConcurrentTest;
2227
import io.weaviate.client6.v1.api.WeaviateApiException;
@@ -32,6 +37,7 @@
3237
import io.weaviate.client6.v1.api.collections.generate.GenerativeObject;
3338
import io.weaviate.client6.v1.api.collections.generate.TaskOutput;
3439
import io.weaviate.client6.v1.api.collections.generative.DummyGenerative;
40+
import io.weaviate.client6.v1.api.collections.query.Boost;
3541
import io.weaviate.client6.v1.api.collections.query.Diversity;
3642
import io.weaviate.client6.v1.api.collections.query.FetchObjectById;
3743
import io.weaviate.client6.v1.api.collections.query.Filter;
@@ -54,6 +60,7 @@
5460
import io.weaviate.containers.Weaviate;
5561
import io.weaviate.containers.Weaviate.Version;
5662

63+
@RunWith(JParamsTestRunner.class)
5764
public class SearchITest extends ConcurrentTest {
5865
private static final ContainerGroup compose = Container.compose(
5966
Weaviate.custom()
@@ -132,7 +139,10 @@ private static Map<String, float[]> populateTest(int n) throws IOException {
132139
for (int i = 0; i < n; i++) {
133140
var vector = randomVector(10, -.01f, .001f);
134141
var object = things.data.insert(
135-
Map.of("category", CATEGORIES.get(i % CATEGORIES.size())),
142+
Map.of(
143+
"category", CATEGORIES.get(i % CATEGORIES.size()),
144+
"created_at", OffsetDateTime.now(),
145+
"position", i),
136146
metadata -> metadata
137147
.uuid(randomUUID())
138148
.vectors(Vectors.of(VECTOR_INDEX, vector)));
@@ -150,7 +160,10 @@ private static Map<String, float[]> populateTest(int n) throws IOException {
150160
*/
151161
private static void createTestCollection() throws IOException {
152162
client.collections.create(COLLECTION, cfg -> cfg
153-
.properties(Property.text("category"))
163+
.properties(
164+
Property.text("category"),
165+
Property.date("created_at"),
166+
Property.integer("position"))
154167
.vectorConfig(VectorConfig.selfProvided(VECTOR_INDEX)));
155168
}
156169

@@ -886,6 +899,61 @@ public void testQueryProfile_groupBy() throws Exception {
886899
.isNotEmpty());
887900
}
888901

902+
public static Object[][] boostCases() {
903+
return new Object[][] {
904+
{ "filter", Boost.filter(Filter.property("category").eq("red")), },
905+
{ "timeDecay", Boost.timeDecay("created_at", "365d",
906+
time -> time
907+
.origin("2024-01-01T00:00:00Z")
908+
.curve(Boost.Curve.EXPONENTIAL)
909+
.decay(.3f)
910+
.weight(1f)),
911+
},
912+
{
913+
"numericDecay", Boost.numericDecay("position", 1f, 3f,
914+
num -> num
915+
.curve(Boost.Curve.LINEAR)
916+
.decay(0.2f)
917+
.weight(1f)),
918+
},
919+
{
920+
"numericProperty", Boost.numericProperty("position",
921+
prop -> prop
922+
.modifier(Boost.Modifier.LOG1P)
923+
.weight(1f)),
924+
},
925+
};
926+
}
927+
928+
@Test
929+
@DataMethod(source = SearchITest.class, method = "boostCases")
930+
@Name("{0}")
931+
public void testBoost(String __, Object boost) throws Exception {
932+
Version.V138.orSkip();
933+
934+
// Boosting reranks query results. To verify the boost parameter
935+
// made it to request, check that boosted results arrive in the
936+
// different order from those in a plain vector search.
937+
//
938+
// Because boosting is a common parameter for all query types,
939+
// testing one (e.g. nearVector) is sufficient.
940+
941+
// Arrange
942+
var things = client.collections.use(COLLECTION);
943+
var baseline = things.query.nearVector(searchVector,
944+
opt -> opt.limit(5))
945+
.objects().stream().map(WeaviateObject::uuid).toList();
946+
947+
// Act
948+
var got = things.query.nearVector(searchVector,
949+
opt -> opt.limit(5).boost((Boost) boost))
950+
.objects().stream().map(WeaviateObject::uuid).toList();
951+
952+
// Assert
953+
Assertions.assertThat(got).hasSameSizeAs(baseline);
954+
Assertions.assertThat(got).doesNotContainSequence(baseline);
955+
}
956+
889957
@Test
890958
public void testDiversity() throws Exception {
891959
Version.V137.orSkip();

src/main/java/io/weaviate/client6/v1/api/collections/query/BaseQueryOptions.java

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ public record BaseQueryOptions(
2020
String after,
2121
ConsistencyLevel consistencyLevel,
2222
Filter filters,
23+
Boost boost,
2324
GenerativeSearch generativeSearch,
2425
List<String> returnProperties,
2526
List<QueryReference> returnReferences,
@@ -38,6 +39,7 @@ private <T extends Object> BaseQueryOptions(Builder<? extends Builder<?, T>, T>
3839
builder.after,
3940
builder.consistencyLevel,
4041
builder.filter,
42+
builder.boost,
4143
builder.generativeSearch,
4244
builder.returnProperties,
4345
builder.returnReferences,
@@ -54,6 +56,7 @@ public static abstract class Builder<SelfT extends Builder<SelfT, T>, T extends
5456
private String after;
5557
private ConsistencyLevel consistencyLevel;
5658
private Filter filter;
59+
private Boost boost;
5760
private GenerativeSearch generativeSearch;
5861
private List<String> returnProperties = new ArrayList<>();
5962
private List<QueryReference> returnReferences = new ArrayList<>();
@@ -143,6 +146,12 @@ public final SelfT filters(Filter... filters) {
143146
return (SelfT) this;
144147
}
145148

149+
/** Boost search results. */
150+
public final SelfT boost(Boost boost) {
151+
this.boost = boost;
152+
return (SelfT) this;
153+
}
154+
146155
/** Select properties to include in the query result. */
147156
public final SelfT returnProperties(String... properties) {
148157
return returnProperties(Arrays.asList(properties));
@@ -230,6 +239,10 @@ final void appendTo(WeaviateProtoSearchGet.SearchRequest.Builder req) {
230239
req.setFilters(filter);
231240
}
232241

242+
if (boost != null) {
243+
req.setBoost(boost.toProto());
244+
}
245+
233246
if (generativeSearch != null) {
234247
var generative = WeaviateProtoGenerative.GenerativeSearch.newBuilder();
235248
generativeSearch.appendTo(generative);

0 commit comments

Comments
 (0)