@@ -26,11 +26,20 @@ using namespace NYdb::NTable;
2626
2727Y_UNIT_TEST_SUITE (KqpPrefixedVectorIndexes) {
2828
29- std::vector<i64 > DoPositiveQueryVectorIndex (TSession& session, const TString& query) {
29+ std::vector<i64 > DoPositiveQueryVectorIndex (TSession& session, const TString& query, bool covered = false ) {
3030 {
3131 auto result = session.ExplainDataQuery (query).ExtractValueSync ();
3232 UNIT_ASSERT_C (result.IsSuccess (),
3333 " Failed to explain: `" << query << " ` with " << result.GetIssues ().ToString ());
34+
35+ if (covered) {
36+ // Check that the query doesn't use main table
37+ NJson::TJsonValue plan;
38+ NJson::ReadJsonTree (result.GetPlan (), &plan, true );
39+ UNIT_ASSERT (ValidatePlanNodeIds (plan));
40+ auto mainTableAccess = CountPlanNodesByKv (plan, " Table" , " TestTable" );
41+ UNIT_ASSERT_VALUES_EQUAL (mainTableAccess, 0 );
42+ }
3443 }
3544 {
3645 auto result = session.ExecuteDataQuery (query,
@@ -53,7 +62,7 @@ Y_UNIT_TEST_SUITE(KqpPrefixedVectorIndexes) {
5362 }
5463 }
5564
56- void DoPositiveQueriesVectorIndex (TSession& session, const TString& mainQuery, const TString& indexQuery) {
65+ void DoPositiveQueriesVectorIndex (TSession& session, const TString& mainQuery, const TString& indexQuery, bool covered = false ) {
5766 auto toStr = [](const auto & rs) -> TString {
5867 TStringBuilder b;
5968 for (const auto & r : rs) {
@@ -66,7 +75,7 @@ Y_UNIT_TEST_SUITE(KqpPrefixedVectorIndexes) {
6675 UNIT_ASSERT_EQUAL_C (mainResults.size (), 3 , toStr (mainResults));
6776 UNIT_ASSERT_C (std::unique (mainResults.begin (), mainResults.end ()) == mainResults.end (), toStr (mainResults));
6877
69- auto indexResults = DoPositiveQueryVectorIndex (session, indexQuery);
78+ auto indexResults = DoPositiveQueryVectorIndex (session, indexQuery, covered );
7079 absl::c_sort (indexResults);
7180 UNIT_ASSERT_EQUAL_C (indexResults.size (), 3 , toStr (indexResults));
7281 UNIT_ASSERT_C (std::unique (indexResults.begin (), indexResults.end ()) == indexResults.end (), toStr (indexResults));
@@ -79,13 +88,17 @@ Y_UNIT_TEST_SUITE(KqpPrefixedVectorIndexes) {
7988 std::string_view function,
8089 std::string_view direction,
8190 std::string_view left,
82- std::string_view right) {
91+ std::string_view right,
92+ bool covered = false ) {
8393 constexpr std::string_view init =
8494 " $target = \"\x67\x68\x03\" ;\n "
8595 " $user = \" user_b\" ;" ;
8696 std::string metric = std::format (" Knn::{}({}, {})" , function, left, right);
8797 // no metric in result
8898 {
99+ // TODO(vitaliff): Exclude index-covered WHERE fields from KqpReadTableRanges.
100+ // Currently even if we SELECT only pk, emb, data WHERE user=xxx we also get `user`
101+ // in SELECT columns and thus it's required to add it to covered columns.
89102 const TString plainQuery (Q1_ (std::format (R"( {}
90103 SELECT * FROM `/Root/TestTable`
91104 WHERE user = $user
@@ -100,7 +113,7 @@ Y_UNIT_TEST_SUITE(KqpPrefixedVectorIndexes) {
100113 ORDER BY {} {}
101114 LIMIT 3;
102115 )" , init, metric, direction)));
103- DoPositiveQueriesVectorIndex (session, plainQuery, indexQuery);
116+ DoPositiveQueriesVectorIndex (session, plainQuery, indexQuery, covered );
104117 }
105118 // metric in result
106119 {
@@ -117,7 +130,7 @@ Y_UNIT_TEST_SUITE(KqpPrefixedVectorIndexes) {
117130 ORDER BY {} {}
118131 LIMIT 3;
119132 )" , init, metric, metric, direction)));
120- DoPositiveQueriesVectorIndex (session, plainQuery, indexQuery);
133+ DoPositiveQueriesVectorIndex (session, plainQuery, indexQuery, covered );
121134 }
122135 // metric as result
123136 // TODO(mbkkt) fix this behavior too
@@ -136,27 +149,28 @@ Y_UNIT_TEST_SUITE(KqpPrefixedVectorIndexes) {
136149 ORDER BY m {}
137150 LIMIT 3;
138151 )" , init, metric, direction)));
139- DoPositiveQueriesVectorIndex (session, plainQuery, indexQuery);
152+ DoPositiveQueriesVectorIndex (session, plainQuery, indexQuery, covered );
140153 }
141154 }
142155
143156 void DoPositiveQueriesPrefixedVectorIndexOrderBy (
144157 TSession& session,
145158 std::string_view function,
146- std::string_view direction) {
159+ std::string_view direction,
160+ bool covered = false ) {
147161 // target is left, member is right
148- DoPositiveQueriesPrefixedVectorIndexOrderBy (session, function, direction, " $target" , " emb" );
162+ DoPositiveQueriesPrefixedVectorIndexOrderBy (session, function, direction, " $target" , " emb" , covered );
149163 // target is right, member is left
150- DoPositiveQueriesPrefixedVectorIndexOrderBy (session, function, direction, " emb" , " $target" );
164+ DoPositiveQueriesPrefixedVectorIndexOrderBy (session, function, direction, " emb" , " $target" , covered );
151165 }
152166
153- void DoPositiveQueriesPrefixedVectorIndexOrderByCosine (TSession& session) {
167+ void DoPositiveQueriesPrefixedVectorIndexOrderByCosine (TSession& session, bool covered = false ) {
154168 // distance, default direction
155- DoPositiveQueriesPrefixedVectorIndexOrderBy (session, " CosineDistance" , " " );
169+ DoPositiveQueriesPrefixedVectorIndexOrderBy (session, " CosineDistance" , " " , covered );
156170 // distance, asc direction
157- DoPositiveQueriesPrefixedVectorIndexOrderBy (session, " CosineDistance" , " ASC" );
171+ DoPositiveQueriesPrefixedVectorIndexOrderBy (session, " CosineDistance" , " ASC" , covered );
158172 // similarity, desc direction
159- DoPositiveQueriesPrefixedVectorIndexOrderBy (session, " CosineSimilarity" , " DESC" );
173+ DoPositiveQueriesPrefixedVectorIndexOrderBy (session, " CosineSimilarity" , " DESC" , covered );
160174 }
161175
162176 TSession DoCreateTableForPrefixedVectorIndex (TTableClient& db, bool nullable) {
@@ -406,6 +420,7 @@ Y_UNIT_TEST_SUITE(KqpPrefixedVectorIndexes) {
406420 .ExtractValueSync ();
407421
408422 UNIT_ASSERT_C (result.IsSuccess (), result.GetIssues ().ToString ());
423+ // FIXME: result does not return failure/issues when index is created but fails to be filled with data
409424 }
410425 {
411426 auto result = session.DescribeTable (" /Root/TestTable" ).ExtractValueSync ();
@@ -425,6 +440,54 @@ Y_UNIT_TEST_SUITE(KqpPrefixedVectorIndexes) {
425440 DoPositiveQueriesPrefixedVectorIndexOrderByCosine (session);
426441 }
427442
443+ Y_UNIT_TEST_TWIN (PrefixedVectorIndexOrderByCosineDistanceWithCover, Nullable) {
444+ NKikimrConfig::TFeatureFlags featureFlags;
445+ featureFlags.SetEnableVectorIndex (true );
446+ auto setting = NKikimrKqp::TKqpSetting ();
447+ auto serverSettings = TKikimrSettings ()
448+ .SetFeatureFlags (featureFlags)
449+ .SetKqpSettings ({setting});
450+
451+ TKikimrRunner kikimr (serverSettings);
452+ kikimr.GetTestServer ().GetRuntime ()->SetLogPriority (NKikimrServices::BUILD_INDEX, NActors::NLog::PRI_TRACE);
453+ kikimr.GetTestServer ().GetRuntime ()->SetLogPriority (NKikimrServices::FLAT_TX_SCHEMESHARD, NActors::NLog::PRI_TRACE);
454+
455+ auto db = kikimr.GetTableClient ();
456+ auto session = DoCreateTableForPrefixedVectorIndex (db, Nullable);
457+ {
458+ const TString createIndex (Q_ (R"(
459+ ALTER TABLE `/Root/TestTable`
460+ ADD INDEX index
461+ GLOBAL USING vector_kmeans_tree
462+ ON (user, emb) COVER (user, emb, data)
463+ WITH (distance=cosine, vector_type="uint8", vector_dimension=2, levels=2, clusters=2);
464+ )" ));
465+
466+ auto result = session.ExecuteSchemeQuery (createIndex)
467+ .ExtractValueSync ();
468+
469+ UNIT_ASSERT_C (result.IsSuccess (), result.GetIssues ().ToString ());
470+ }
471+ {
472+ auto result = session.DescribeTable (" /Root/TestTable" ).ExtractValueSync ();
473+ UNIT_ASSERT_VALUES_EQUAL (result.GetStatus (), NYdb::EStatus::SUCCESS);
474+ const auto & indexes = result.GetTableDescription ().GetIndexDescriptions ();
475+ UNIT_ASSERT_EQUAL (indexes.size (), 1 );
476+ UNIT_ASSERT_EQUAL (indexes[0 ].GetIndexName (), " index" );
477+ std::vector<std::string> indexKeyColumns{" user" , " emb" };
478+ UNIT_ASSERT_EQUAL (indexes[0 ].GetIndexColumns (), indexKeyColumns);
479+ std::vector<std::string> indexDataColumns{" user" , " emb" , " data" };
480+ UNIT_ASSERT_EQUAL (indexes[0 ].GetDataColumns (), indexDataColumns);
481+ const auto & settings = std::get<TKMeansTreeSettings>(indexes[0 ].GetIndexSettings ());
482+ UNIT_ASSERT_EQUAL (settings.Settings .Metric , NYdb::NTable::TVectorIndexSettings::EMetric::CosineDistance);
483+ UNIT_ASSERT_EQUAL (settings.Settings .VectorType , NYdb::NTable::TVectorIndexSettings::EVectorType::Uint8);
484+ UNIT_ASSERT_EQUAL (settings.Settings .VectorDimension , 2 );
485+ UNIT_ASSERT_EQUAL (settings.Levels , 2 );
486+ UNIT_ASSERT_EQUAL (settings.Clusters , 2 );
487+ }
488+ DoPositiveQueriesPrefixedVectorIndexOrderByCosine (session, true /* covered*/ );
489+ }
490+
428491}
429492
430493}
0 commit comments