Skip to content

Commit 8c6e53a

Browse files
authored
Merge pull request #555 from weaviate/feat/query-profile
2 parents d58cf69 + 76d76bc commit 8c6e53a

14 files changed

Lines changed: 11088 additions & 1354 deletions

File tree

.github/workflows/test.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,7 @@ jobs:
9191
strategy:
9292
fail-fast: false
9393
matrix:
94-
WEAVIATE_VERSION: ["1.32.24", "1.33.11", "1.34.7", "1.35.2", "1.36.1"]
94+
WEAVIATE_VERSION: ["1.32.24", "1.33.11", "1.34.7", "1.35.2", "1.36.9"]
9595
steps:
9696
- uses: actions/checkout@v4
9797

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ public enum Version {
4444
V133(1, 33, 11),
4545
V134(1, 34, 7),
4646
V135(1, 35, 2),
47-
V136(1, 36, 1);
47+
V136(1, 36, 9);
4848

4949
public final SemanticVersion semver;
5050

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

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,10 @@
3636
import io.weaviate.client6.v1.api.collections.query.Filter;
3737
import io.weaviate.client6.v1.api.collections.query.GroupBy;
3838
import io.weaviate.client6.v1.api.collections.query.Metadata;
39+
import io.weaviate.client6.v1.api.collections.query.QueryProfile;
40+
import io.weaviate.client6.v1.api.collections.query.QueryResponse;
3941
import io.weaviate.client6.v1.api.collections.query.QueryResponseGroup;
42+
import io.weaviate.client6.v1.api.collections.query.QueryResponseGrouped;
4043
import io.weaviate.client6.v1.api.collections.query.Rerank;
4144
import io.weaviate.client6.v1.api.collections.query.SortBy;
4245
import io.weaviate.client6.v1.api.collections.query.Target;
@@ -48,6 +51,7 @@
4851
import io.weaviate.containers.Img2VecNeural;
4952
import io.weaviate.containers.Model2Vec;
5053
import io.weaviate.containers.Weaviate;
54+
import io.weaviate.containers.Weaviate.Version;
5155

5256
public class SearchITest extends ConcurrentTest {
5357
private static final ContainerGroup compose = Container.compose(
@@ -839,4 +843,45 @@ public void testVectorizerModel2VecPropeties() throws IOException {
839843
Assertions.assertThat(v.getSingle("title_vec")).isEqualTo(v.getSingle("author_vec"));
840844
});
841845
}
846+
847+
@Test
848+
public void testQueryProfile() throws Exception {
849+
Version.V136.orSkip();
850+
851+
var things = client.collections.use(COLLECTION);
852+
var resp = things.query.nearVector(searchVector,
853+
opt -> opt.distance(10f)
854+
.returnMetadata(Metadata.QUERY_PROFILE));
855+
856+
Assertions.assertThat(resp)
857+
.extracting(QueryResponse::queryProfile).isNotNull()
858+
.extracting(QueryProfile::shards, InstanceOfAssertFactories.list(QueryProfile.ShardProfile.class))
859+
.hasSize(1)
860+
.allSatisfy(shardProfile -> Assertions.assertThat(shardProfile)
861+
.extracting(QueryProfile.ShardProfile::searches,
862+
InstanceOfAssertFactories.map(String.class, String.class))
863+
.isNotEmpty());
864+
}
865+
866+
@Test
867+
@Ignore("v1.36.9 does not support query profiles for grouped queries")
868+
public void testQueryProfile_groupBy() throws Exception {
869+
Version.V136.orSkip();
870+
871+
var things = client.collections.use(COLLECTION);
872+
var resp = things.query.nearVector(searchVector,
873+
opt -> opt.distance(10f)
874+
.returnMetadata(Metadata.QUERY_PROFILE),
875+
GroupBy.property("category", 2, 5));
876+
877+
Assertions.assertThat(resp)
878+
.extracting(QueryResponseGrouped::queryProfile).isNotNull()
879+
.extracting(QueryProfile::shards,
880+
InstanceOfAssertFactories.list(QueryProfile.ShardProfile.class))
881+
.hasSize(1)
882+
.allSatisfy(shardProfile -> Assertions.assertThat(shardProfile)
883+
.extracting(QueryProfile.ShardProfile::searches,
884+
InstanceOfAssertFactories.map(String.class, String.class))
885+
.isNotEmpty());
886+
}
842887
}

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

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,13 @@
1111
public interface Metadata {
1212
void appendTo(WeaviateProtoSearchGet.MetadataRequest.Builder metadata);
1313

14-
/** Include metadata in the metadata response. */
14+
/**
15+
* Include all metadata in the metadata response.
16+
*
17+
* <p>
18+
* Collecting {@link #QUERY_PROFILE} involves significant overhead on the
19+
* server side, so it is excluded from ALL and must be requested explicitly.
20+
*/
1521
public static final Metadata ALL = MetadataField.ALL;
1622
/** Include object creation time in the metadata response. */
1723
public static final Metadata CREATION_TIME_UNIX = MetadataField.CREATION_TIME_UNIX;
@@ -64,6 +70,13 @@ public interface Metadata {
6470
*/
6571
public static final Metadata EXPLAIN_SCORE = MetadataField.EXPLAIN_SCORE;
6672

73+
/**
74+
* Include a per-shard execution timing breakdowns for search queries.
75+
*
76+
* @see QueryProfile
77+
*/
78+
public static final Metadata QUERY_PROFILE = MetadataField.QUERY_PROFILE;
79+
6780
/**
6881
* MetadataField are collection properties that can be requested for any object.
6982
*/
@@ -76,13 +89,15 @@ enum MetadataField implements Metadata {
7689
DISTANCE,
7790
CERTAINTY,
7891
SCORE,
79-
EXPLAIN_SCORE;
92+
EXPLAIN_SCORE,
93+
QUERY_PROFILE;
8094

8195
public void appendTo(WeaviateProtoSearchGet.MetadataRequest.Builder metadata) {
8296
switch (this) {
8397
case ALL:
8498
for (final var f : MetadataField.values()) {
85-
if (f != ALL) {
99+
// QUERY_PROFILE is expensive, require an explicit opt-in.
100+
if (f != ALL && f != QUERY_PROFILE) {
86101
f.appendTo(metadata);
87102
}
88103
}
@@ -111,6 +126,8 @@ public void appendTo(WeaviateProtoSearchGet.MetadataRequest.Builder metadata) {
111126
case SCORE:
112127
metadata.setScore(true);
113128
break;
129+
case QUERY_PROFILE:
130+
metadata.setQueryProfile(true);
114131
}
115132
}
116133
}
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
package io.weaviate.client6.v1.api.collections.query;
2+
3+
import java.util.List;
4+
import java.util.Map;
5+
6+
/** Per-shard execution timing breakdowns for search queries. */
7+
public record QueryProfile(List<ShardProfile> shards) {
8+
/**
9+
* Search profiling samples keyed by shard name.
10+
* <p>
11+
* Vector searches:
12+
*
13+
* <ul>
14+
* <li>{@code vector_search_took}: time in the vector index
15+
* <li>{@code hnsw_flat_search}: "true" / "false", whether flat search was used
16+
* <li>{@code knn_search_layer_N_took}: per-layer HNSW traversal time
17+
* <li>{@code knn_search_rescore_took}: PQ/BQ rescore time
18+
* <li>{@code flat_search_iteration_took}, {@code flat_search_rescore_took}
19+
* </ul>
20+
*
21+
* <p>
22+
* Keyword (BM25) searches:
23+
*
24+
* <ul>
25+
* <li>{@code kwd_method}, {@code kwd_time}, {@code kwd_filter_size}
26+
* <li>{@code kwd_1_tok_time}: tokenization time
27+
* <li>{@code kwd_3_term_time}: term lookup time
28+
* <li>{@code kwd_4_wand_time / kwd_4_bmw_time}: scoring algorithm time
29+
* <li>{@code kwd_5_objects_time}: object fetch time
30+
* <li>{@code kwd_6_res_count}: result count
31+
* </ul>
32+
*
33+
* <p>
34+
* Filtered searches (any type):
35+
*
36+
* <ul>
37+
* <li>{@code filters_build_allow_list_took}: filter evaluation time
38+
* <li>{@code filters_ids_matched}: number of IDs matching the filter
39+
* <li>{@code sort_took}, objects_took
40+
* </ul>
41+
*/
42+
public static record ShardProfile(Map<String, Map<String, String>> searches) {
43+
}
44+
}

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package io.weaviate.client6.v1.api.collections.query;
22

33
import io.weaviate.client6.v1.api.collections.CollectionHandleDefaults;
4+
import io.weaviate.client6.v1.internal.Debug;
45
import io.weaviate.client6.v1.internal.grpc.Rpc;
56
import io.weaviate.client6.v1.internal.grpc.protocol.WeaviateGrpc.WeaviateBlockingStub;
67
import io.weaviate.client6.v1.internal.grpc.protocol.WeaviateGrpc.WeaviateFutureStub;

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

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,9 @@
33
import java.util.Arrays;
44
import java.util.HashMap;
55
import java.util.List;
6+
import java.util.Map;
67
import java.util.UUID;
8+
import java.util.stream.Collectors;
79
import java.util.stream.Stream;
810

911
import io.weaviate.client6.v1.api.collections.GeoCoordinates;
@@ -19,7 +21,8 @@
1921
import io.weaviate.client6.v1.internal.orm.PropertiesBuilder;
2022

2123
public record QueryResponse<PropertiesT>(
22-
List<WeaviateObject<PropertiesT>> objects) {
24+
List<WeaviateObject<PropertiesT>> objects,
25+
QueryProfile queryProfile) {
2326

2427
static <PropertiesT> QueryResponse<PropertiesT> unmarshal(WeaviateProtoSearchGet.SearchReply reply,
2528
CollectionDescriptor<PropertiesT> collection) {
@@ -29,7 +32,22 @@ static <PropertiesT> QueryResponse<PropertiesT> unmarshal(WeaviateProtoSearchGet
2932
.map(obj -> QueryResponse.unmarshalResultObject(
3033
obj.getProperties(), obj.getMetadata(), collection))
3134
.toList();
32-
return new QueryResponse<>(objects);
35+
return new QueryResponse<>(objects, unmarshalQueryProfile(reply.getQueryProfile()));
36+
}
37+
38+
static QueryProfile unmarshalQueryProfile(WeaviateProtoSearchGet.QueryProfile queryProfile) {
39+
return new QueryProfile(queryProfile
40+
.getShardsList()
41+
.stream()
42+
.map(shard -> new QueryProfile.ShardProfile(
43+
shard
44+
.getSearchesMap()
45+
.entrySet()
46+
.stream()
47+
.collect(Collectors.toMap(
48+
Map.Entry::getKey,
49+
entry -> entry.getValue().getDetailsMap()))))
50+
.toList());
3351
}
3452

3553
public static <PropertiesT> WeaviateObject<PropertiesT> unmarshalResultObject(

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

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,8 @@ public record QueryResponseGrouped<PropertiesT>(
1414
/** All objects retrieved in the query. */
1515
List<QueryObjectGrouped<PropertiesT>> objects,
1616
/** Grouped response objects. */
17-
Map<String, QueryResponseGroup<PropertiesT>> groups) {
17+
Map<String, QueryResponseGroup<PropertiesT>> groups,
18+
QueryProfile queryProfile) {
1819

1920
static <PropertiesT> QueryResponseGrouped<PropertiesT> unmarshal(
2021
WeaviateProtoSearchGet.SearchReply reply,
@@ -47,6 +48,8 @@ static <PropertiesT> QueryResponseGrouped<PropertiesT> unmarshal(
4748
// about the server's response.
4849
.collect(Collectors.toMap(QueryResponseGroup::name, Function.identity()));
4950

50-
return new QueryResponseGrouped<PropertiesT>(allObjects, groups);
51+
return new QueryResponseGrouped<PropertiesT>(
52+
allObjects, groups,
53+
QueryResponse.unmarshalQueryProfile(reply.getQueryProfile()));
5154
}
5255
}

0 commit comments

Comments
 (0)