// Copyright 2025-present the zvec project // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. #include #include #include #include "db/sqlengine/sqlengine_impl.h" #include "zvec/db/doc.h" #include "zvec/db/schema.h" #include "profiler.h" namespace zvec::sqlengine { class QueryInfoTest : public testing::Test { public: // Sets up the test fixture. static void SetUpTestSuite() { schema = std::make_shared(); auto ¶m = *schema; param.set_name("1collection"); auto column1 = std::make_shared(); auto vector_params = std::make_shared(MetricType::IP); column1->set_name("face_feature"); column1->set_index_params(vector_params); column1->set_dimension(4); column1->set_data_type(DataType::VECTOR_FP32); param.add_field(column1); auto column2 = std::make_shared(); column2->set_name("name"); column2->set_data_type(DataType::UINT32); param.add_field(column2); auto column3 = std::make_shared(); column3->set_name("category"); column3->set_data_type(DataType::STRING); param.add_field(column3); auto column4 = std::make_shared(); column4->set_name("face_feature"); column4->set_dimension(4); column4->set_data_type(DataType::VECTOR_FP32); param.add_field(column4); auto column5 = std::make_shared(); column5->set_name("1-dash_score_field"); column5->set_dimension(5); column5->set_data_type(DataType::STRING); param.add_field(column5); { auto column = std::make_shared(); column->set_name("name_array"); column->set_data_type(DataType::ARRAY_UINT32); param.add_field(column); } { auto column = std::make_shared(); column->set_name("category_array"); column->set_data_type(DataType::ARRAY_STRING); param.add_field(column); } } // Tears down the test fixture. static void TearDownTestSuite() {} protected: Profiler::Ptr profiler_{new Profiler}; inline static CollectionSchema::Ptr schema; }; TEST_F(QueryInfoTest, BasicQueryRequest) { SearchQuery query; query.output_fields_ = {"*"}; query.topk_ = 11; query.target_.set_vector("[0.1, 0.2, 0.3, 0.4]"); query.target_.field_name_ = "face_feature"; query.include_vector_ = false; query.target_.query_params_ = std::make_shared(IndexType::FLAT); query.target_.query_params_->set_radius(0.8F); auto engine = std::make_shared(std::make_shared()); auto ret = engine->build_query_info(schema, query, nullptr); ASSERT_TRUE(ret.has_value()) << ret.error().c_str(); QueryInfo::Ptr new_query_info = ret.value(); auto &query_fields = new_query_info->query_fields(); EXPECT_EQ(query_fields.size(), 5); EXPECT_EQ(query_fields[0]->field_name(), "name"); EXPECT_EQ(query_fields[1]->field_name(), "category"); EXPECT_EQ(query_fields[2]->field_name(), "1-dash_score_field"); EXPECT_EQ(query_fields[3]->field_name(), "name_array"); EXPECT_EQ(query_fields[4]->field_name(), "category_array"); EXPECT_EQ(new_query_info->query_topn(), 11); EXPECT_FALSE(new_query_info->filter_cond()); EXPECT_FALSE(new_query_info->invert_cond()); EXPECT_FALSE(new_query_info->post_filter_cond()); EXPECT_FALSE(new_query_info->post_invert_cond()); ASSERT_TRUE(new_query_info->vector_cond_info()); auto vector_cond = new_query_info->vector_cond_info(); EXPECT_EQ(1, vector_cond->batch()); EXPECT_EQ("face_feature", vector_cond->vector_field_name()); EXPECT_EQ(query.target_.get_vector_clause()->query_vector_, vector_cond->vector_term()); EXPECT_EQ(query.target_.get_vector_clause()->sparse_indices_, vector_cond->vector_sparse_indices()); EXPECT_EQ(query.target_.get_vector_clause()->sparse_values_, vector_cond->vector_sparse_values()); EXPECT_EQ(query.target_.query_params_, vector_cond->query_params()); } TEST_F(QueryInfoTest, QueryRequestWithFilter) { SearchQuery query; query.output_fields_ = {"*"}; query.topk_ = 11; query.target_.set_vector("[0.1, 0.2, 0.3, 0.4]"); query.target_.field_name_ = "face_feature"; query.include_vector_ = false; query.target_.query_params_ = std::make_shared(IndexType::FLAT); query.target_.query_params_->set_radius(0.8F); query.filter_ = "name<3 or name=4 or 1-dash_score_field='test'"; auto engine = std::make_shared(std::make_shared()); auto ret = engine->build_query_info(schema, query, nullptr); ASSERT_TRUE(ret.has_value()); QueryInfo::Ptr new_query_info = ret.value(); auto &query_fields = new_query_info->query_fields(); EXPECT_EQ(query_fields.size(), 5); EXPECT_EQ(query_fields[0]->field_name(), "name"); EXPECT_EQ(query_fields[1]->field_name(), "category"); EXPECT_EQ(query_fields[2]->field_name(), "1-dash_score_field"); EXPECT_EQ(query_fields[3]->field_name(), "name_array"); EXPECT_EQ(query_fields[4]->field_name(), "category_array"); EXPECT_EQ(new_query_info->query_topn(), 11); EXPECT_TRUE(new_query_info->filter_cond()); EXPECT_FALSE(new_query_info->invert_cond()); EXPECT_FALSE(new_query_info->post_filter_cond()); EXPECT_FALSE(new_query_info->post_invert_cond()); ASSERT_TRUE(new_query_info->vector_cond_info()); auto vector_cond = new_query_info->vector_cond_info(); EXPECT_EQ(1, vector_cond->batch()); EXPECT_EQ("face_feature", vector_cond->vector_field_name()); EXPECT_EQ(query.target_.get_vector_clause()->query_vector_, vector_cond->vector_term()); EXPECT_EQ(query.target_.get_vector_clause()->sparse_indices_, vector_cond->vector_sparse_indices()); EXPECT_EQ(query.target_.get_vector_clause()->sparse_values_, vector_cond->vector_sparse_values()); EXPECT_EQ(query.target_.query_params_, vector_cond->query_params()); EXPECT_TRUE(new_query_info->filter_cond()); // (nullptr) and (xxx) auto filter_cond = new_query_info->filter_cond(); EXPECT_EQ(filter_cond->op_name(), "and"); EXPECT_FALSE(filter_cond->left()); // ((name<3) or (name=4)) or (1-dash_score_field=test) auto right = std::dynamic_pointer_cast(filter_cond->right()); EXPECT_TRUE(right); EXPECT_EQ(right->op_name(), "or"); // 1-dash_score_field=test auto number_field_filter = std::dynamic_pointer_cast(right->right()); ASSERT_TRUE(number_field_filter); EXPECT_EQ(number_field_filter->op_name(), "="); auto left_key = std::dynamic_pointer_cast(number_field_filter->left()); EXPECT_EQ(left_key->op_name(), "ID"); EXPECT_EQ(left_key->value(), "1-dash_score_field"); auto right_const = std::dynamic_pointer_cast( number_field_filter->right()); ASSERT_TRUE(right_const); EXPECT_EQ(right_const->op_name(), "STRING_VALUE"); EXPECT_EQ(right_const->value(), "test"); // (name<3) or (name=4) auto left = std::dynamic_pointer_cast(right->left()); ASSERT_TRUE(left); EXPECT_EQ(left->op_name(), "or"); auto or1 = std::dynamic_pointer_cast(left->left()); EXPECT_EQ(or1->op_name(), "<"); auto id1 = std::dynamic_pointer_cast(or1->left()); ASSERT_TRUE(id1); EXPECT_EQ(id1->op_name(), "ID"); EXPECT_EQ(id1->value(), "name"); auto const1 = std::dynamic_pointer_cast(or1->right()); ASSERT_TRUE(const1); EXPECT_EQ(const1->op_name(), "INT_VALUE"); EXPECT_EQ(const1->value(), "3"); auto or2 = std::dynamic_pointer_cast(left->right()); EXPECT_EQ(or2->op_name(), "="); } TEST_F(QueryInfoTest, QueryRequestWithIncludeVector) { SearchQuery query; query.output_fields_ = {"*"}; query.topk_ = 11; query.target_.set_vector("[0.1, 0.2, 0.3, 0.4]"); query.target_.field_name_ = "face_feature"; query.include_vector_ = false; query.target_.query_params_ = std::make_shared(IndexType::FLAT); query.target_.query_params_->set_radius(0.8F); query.include_vector_ = true; auto engine = std::make_shared(std::make_shared()); auto ret = engine->build_query_info(schema, query, nullptr); ASSERT_TRUE(ret.has_value()); QueryInfo::Ptr new_query_info = ret.value(); auto &query_fields = new_query_info->query_fields(); EXPECT_EQ(query_fields.size(), 6); EXPECT_EQ(query_fields[0]->field_name(), "name"); EXPECT_EQ(query_fields[1]->field_name(), "category"); EXPECT_EQ(query_fields[2]->field_name(), "1-dash_score_field"); EXPECT_EQ(query_fields[3]->field_name(), "name_array"); EXPECT_EQ(query_fields[4]->field_name(), "category_array"); EXPECT_EQ(query_fields[5]->field_name(), "face_feature"); EXPECT_EQ(new_query_info->query_topn(), 11); EXPECT_FALSE(new_query_info->filter_cond()); EXPECT_FALSE(new_query_info->invert_cond()); EXPECT_FALSE(new_query_info->post_filter_cond()); EXPECT_FALSE(new_query_info->post_invert_cond()); ASSERT_TRUE(new_query_info->vector_cond_info()); auto vector_cond = new_query_info->vector_cond_info(); EXPECT_EQ(1, vector_cond->batch()); EXPECT_EQ("face_feature", vector_cond->vector_field_name()); EXPECT_EQ(query.target_.get_vector_clause()->query_vector_, vector_cond->vector_term()); EXPECT_EQ(query.target_.get_vector_clause()->sparse_indices_, vector_cond->vector_sparse_indices()); EXPECT_EQ(query.target_.get_vector_clause()->sparse_values_, vector_cond->vector_sparse_values()); EXPECT_EQ(query.target_.query_params_, vector_cond->query_params()); } TEST_F(QueryInfoTest, OR_ANCESTOR) { SearchQuery query; query.output_fields_ = {"*"}; query.topk_ = 11; query.target_.set_vector("[0.1, 0.2, 0.3, 0.4]"); query.target_.field_name_ = "face_feature"; query.include_vector_ = false; query.target_.query_params_ = std::make_shared(IndexType::FLAT); query.target_.query_params_->set_radius(0.8F); query.filter_ = "name=1 and (name=2 or name=3)"; auto engine = std::make_shared(std::make_shared()); auto ret = engine->build_query_info(schema, query, nullptr); ASSERT_TRUE(ret.has_value()); QueryInfo::Ptr new_query_info = ret.value(); } TEST_F(QueryInfoTest, QueryRequestWithInFilter) { SearchQuery query; query.output_fields_ = {"*"}; query.topk_ = 10; query.target_.set_vector("[0.1, 0.2, 0.3, 0.4]"); query.target_.field_name_ = "face_feature"; query.include_vector_ = false; query.target_.query_params_ = std::make_shared(IndexType::FLAT); query.target_.query_params_->set_radius(0.8F); query.filter_ = "name=3 or name in (1, 2, 3) or category not in (\"a\", \"b\", \"c\")"; auto engine = std::make_shared(std::make_shared()); auto ret = engine->build_query_info(schema, query, nullptr); ASSERT_TRUE(ret.has_value()); QueryInfo::Ptr new_query_info = ret.value(); auto &query_fields = new_query_info->query_fields(); EXPECT_EQ(query_fields.size(), 5); EXPECT_EQ(query_fields[0]->field_name(), "name"); EXPECT_EQ(query_fields[1]->field_name(), "category"); EXPECT_EQ(query_fields[2]->field_name(), "1-dash_score_field"); EXPECT_EQ(query_fields[3]->field_name(), "name_array"); EXPECT_EQ(query_fields[4]->field_name(), "category_array"); EXPECT_EQ(new_query_info->query_topn(), 10); EXPECT_FALSE(new_query_info->invert_cond()); EXPECT_FALSE(new_query_info->post_filter_cond()); EXPECT_FALSE(new_query_info->post_invert_cond()); ASSERT_TRUE(new_query_info->vector_cond_info()); auto vector_cond = new_query_info->vector_cond_info(); EXPECT_EQ(1, vector_cond->batch()); EXPECT_EQ("face_feature", vector_cond->vector_field_name()); std::vector data{1.1, 2.2, 3.3, 4.4}; EXPECT_EQ(query.target_.get_vector_clause()->query_vector_, vector_cond->vector_term()); EXPECT_TRUE(new_query_info->filter_cond()); // (nullptr) and (xxx) auto filter_cond = new_query_info->filter_cond(); EXPECT_EQ(filter_cond->op_name(), "and"); EXPECT_FALSE(filter_cond->left()); // ((name=3) or (name in (1, 2, 3))) or (category not in ("a", "b", "c")) auto right = std::dynamic_pointer_cast(filter_cond->right()); EXPECT_TRUE(right); EXPECT_EQ(right->op_name(), "or"); // category in ("a", "b", "c") auto category_filter = std::dynamic_pointer_cast(right->right()); ASSERT_TRUE(category_filter); EXPECT_EQ(category_filter->op_name(), " in "); auto left_key = std::dynamic_pointer_cast(category_filter->left()); EXPECT_EQ(left_key->op_name(), "ID"); EXPECT_EQ(left_key->value(), "category"); auto right_const = std::dynamic_pointer_cast(category_filter->right()); ASSERT_TRUE(right_const); EXPECT_EQ(right_const->op_name(), "LIST_VALUE"); EXPECT_EQ(right_const->text(), "NOT (a, b, c)"); // (name=3) or (name in (1, 2, 3)) auto left = std::dynamic_pointer_cast(right->left()); ASSERT_TRUE(left); EXPECT_EQ(left->op_name(), "or"); auto or1 = std::dynamic_pointer_cast(left->left()); EXPECT_EQ(or1->op_name(), "="); auto id1 = std::dynamic_pointer_cast(or1->left()); ASSERT_TRUE(id1); EXPECT_EQ(id1->op_name(), "ID"); EXPECT_EQ(id1->value(), "name"); auto const1 = std::dynamic_pointer_cast(or1->right()); ASSERT_TRUE(const1); EXPECT_EQ(const1->op_name(), "INT_VALUE"); EXPECT_EQ(const1->value(), "3"); auto or2 = std::dynamic_pointer_cast(left->right()); EXPECT_EQ(or2->op_name(), " in "); auto id2 = std::dynamic_pointer_cast(or2->left()); ASSERT_TRUE(id2); EXPECT_EQ(id2->op_name(), "ID"); EXPECT_EQ(id2->value(), "name"); auto const2 = std::dynamic_pointer_cast(or2->right()); ASSERT_TRUE(const2); EXPECT_EQ(const2->op_name(), "LIST_VALUE"); EXPECT_EQ(const2->text(), "(1, 2, 3)"); } TEST_F(QueryInfoTest, QueryRequestWithInFilterWrong) { SearchQuery query; query.output_fields_ = {"*"}; query.topk_ = 11; query.target_.set_vector("[0.1, 0.2, 0.3, 0.4]"); query.target_.field_name_ = "face_feature"; query.include_vector_ = false; query.target_.query_params_ = std::make_shared(IndexType::FLAT); query.target_.query_params_->set_radius(0.8F); auto engine = std::make_shared(std::make_shared()); auto ret = engine->build_query_info(schema, query, nullptr); ASSERT_TRUE(ret.has_value()); query.filter_ = ("name in ()"); ret = engine->build_query_info(schema, query, nullptr); ASSERT_FALSE(ret.has_value()); query.filter_ = ("name in (\"a\", 2, 3)"); ret = engine->build_query_info(schema, query, nullptr); ASSERT_FALSE(ret.has_value()); query.filter_ = ("name in (1.1, 2, 3)"); ret = engine->build_query_info(schema, query, nullptr); ASSERT_FALSE(ret.has_value()); query.filter_ = ("category in (1.1, \"b\")"); ret = engine->build_query_info(schema, query, nullptr); ASSERT_FALSE(ret.has_value()); } TEST_F(QueryInfoTest, QueryRequestWithInFilterNum1024) { SearchQuery query; query.output_fields_ = {"*"}; query.topk_ = 10; query.target_.set_vector("[0.1, 0.2, 0.3, 0.4]"); query.target_.field_name_ = "face_feature"; query.include_vector_ = false; query.target_.query_params_ = std::make_shared(IndexType::FLAT); query.target_.query_params_->set_radius(0.8F); std::string filter_str; for (int i = 0; i < 1024; i++) { if (i != 0) { filter_str += " or "; } filter_str += "name=" + std::to_string(i); } query.filter_ = filter_str; auto engine = std::make_shared(std::make_shared()); auto ret = engine->build_query_info(schema, query, nullptr); ASSERT_TRUE(ret.has_value()); QueryInfo::Ptr new_query_info = ret.value(); auto &query_fields = new_query_info->query_fields(); EXPECT_EQ(query_fields.size(), 5); EXPECT_EQ(query_fields[0]->field_name(), "name"); EXPECT_EQ(query_fields[1]->field_name(), "category"); EXPECT_EQ(query_fields[2]->field_name(), "1-dash_score_field"); EXPECT_EQ(query_fields[3]->field_name(), "name_array"); EXPECT_EQ(query_fields[4]->field_name(), "category_array"); EXPECT_EQ(new_query_info->query_topn(), 10); EXPECT_FALSE(new_query_info->invert_cond()); EXPECT_FALSE(new_query_info->post_filter_cond()); EXPECT_FALSE(new_query_info->post_invert_cond()); ASSERT_TRUE(new_query_info->vector_cond_info()); auto vector_cond = new_query_info->vector_cond_info(); EXPECT_EQ(1, vector_cond->batch()); EXPECT_EQ("face_feature", vector_cond->vector_field_name()); } TEST_F(QueryInfoTest, QueryRequestWithFilter_contain) { SearchQuery query; query.output_fields_ = {"*"}; query.topk_ = 10; query.target_.set_vector("[0.1, 0.2, 0.3, 0.4]"); query.target_.field_name_ = "face_feature"; query.include_vector_ = false; query.target_.query_params_ = std::make_shared(IndexType::FLAT); query.target_.query_params_->set_radius(0.8F); query.filter_ = R"( name_array contain_all (1, 2, 3) and )" R"( (name_array not contain_all (4, 5) or category_array contain_any ("a", "b")) )" R"( or category_array not contain_any ("c", "d", "e") )"; auto engine = std::make_shared(std::make_shared()); auto ret = engine->build_query_info(schema, query, nullptr); ASSERT_TRUE(ret.has_value()); QueryInfo::Ptr new_query_info = ret.value(); auto &query_fields = new_query_info->query_fields(); // pre-defined schema field EXPECT_EQ(query_fields.size(), 5); EXPECT_EQ(query_fields[0]->field_name(), "name"); EXPECT_EQ(query_fields[1]->field_name(), "category"); EXPECT_EQ(query_fields[2]->field_name(), "1-dash_score_field"); EXPECT_EQ(query_fields[3]->field_name(), "name_array"); EXPECT_EQ(query_fields[4]->field_name(), "category_array"); EXPECT_EQ(new_query_info->query_topn(), 10); EXPECT_FALSE(new_query_info->invert_cond()); EXPECT_FALSE(new_query_info->post_filter_cond()); EXPECT_FALSE(new_query_info->post_invert_cond()); ASSERT_TRUE(new_query_info->vector_cond_info()); auto vector_cond = new_query_info->vector_cond_info(); EXPECT_EQ(1, vector_cond->batch()); EXPECT_EQ("face_feature", vector_cond->vector_field_name()); EXPECT_TRUE(new_query_info->filter_cond()); /* _________________[and]__________________ / \ [nullptr(vector_cond)] [filter condition] */ // (nullptr) and (xxx) auto filter_cond = new_query_info->filter_cond(); EXPECT_EQ(filter_cond->op_name(), "and"); EXPECT_FALSE(filter_cond->left()); /* _______________[or]_______________ / \ _____________[and]_____________ [category_array not contain_any ("c", "d", "e")] / \ [name_array contain_all (1, 2, 3)] ___________[or]______________ / \ [name_array not contain_all (4, 5)] [category_array contain_any ("a", "b")] */ // name_array contain_all (1, 2, 3) and // (name_array not contain_all (4, 5) or category_array contain_any ("a", // "b")) or category_array not contain_any ("c", "d", "e") auto parent_node = std::dynamic_pointer_cast(filter_cond); auto cur_node = std::dynamic_pointer_cast(filter_cond->right()); EXPECT_TRUE(cur_node); EXPECT_EQ(cur_node->op_name(), "or"); // category_array not contain_any ("c", "d", "e") parent_node = std::dynamic_pointer_cast(cur_node); cur_node = std::dynamic_pointer_cast(cur_node->right()); EXPECT_TRUE(cur_node); EXPECT_EQ(cur_node->op_name(), " contain_any "); { auto left_key = std::dynamic_pointer_cast(cur_node->left()); EXPECT_EQ(left_key->op_name(), "ID"); EXPECT_EQ(left_key->value(), "category_array"); auto right_const = std::dynamic_pointer_cast(cur_node->right()); ASSERT_TRUE(right_const); EXPECT_EQ(right_const->op_name(), "LIST_VALUE"); EXPECT_EQ(right_const->text(), "NOT (c, d, e)"); } cur_node = parent_node; // name_array contain_all (1, 2, 3) and // (name_array not contain_all (4, 5) or category_array contain_any ("a", // "b")) parent_node = std::dynamic_pointer_cast(cur_node); cur_node = std::dynamic_pointer_cast(cur_node->left()); EXPECT_TRUE(cur_node); EXPECT_EQ(cur_node->op_name(), "and"); // the left side of 'and' // name_array contain_all (1, 2, 3) parent_node = std::dynamic_pointer_cast(cur_node); cur_node = std::dynamic_pointer_cast(cur_node->left()); EXPECT_TRUE(cur_node); EXPECT_EQ(cur_node->op_name(), " contain_all "); { auto left_key = std::dynamic_pointer_cast(cur_node->left()); EXPECT_EQ(left_key->op_name(), "ID"); EXPECT_EQ(left_key->value(), "name_array"); auto right_const = std::dynamic_pointer_cast(cur_node->right()); ASSERT_TRUE(right_const); EXPECT_EQ(right_const->op_name(), "LIST_VALUE"); EXPECT_EQ(right_const->text(), "(1, 2, 3)"); } cur_node = parent_node; // the right side of 'and' // (name_array not contain_all (4, 5) or category_array contain_any ("a", // "b")) parent_node = std::dynamic_pointer_cast(cur_node); cur_node = std::dynamic_pointer_cast(cur_node->right()); EXPECT_TRUE(cur_node); EXPECT_EQ(cur_node->op_name(), "or"); // name_array not contain_all (4, 5) parent_node = std::dynamic_pointer_cast(cur_node); cur_node = std::dynamic_pointer_cast(cur_node->left()); EXPECT_TRUE(cur_node); EXPECT_EQ(cur_node->op_name(), " contain_all "); { auto left_key = std::dynamic_pointer_cast(cur_node->left()); EXPECT_EQ(left_key->op_name(), "ID"); EXPECT_EQ(left_key->value(), "name_array"); auto right_const = std::dynamic_pointer_cast(cur_node->right()); ASSERT_TRUE(right_const); EXPECT_EQ(right_const->op_name(), "LIST_VALUE"); EXPECT_EQ(right_const->text(), "NOT (4, 5)"); } cur_node = parent_node; // category_array contain_any ("a", "b")) parent_node = std::dynamic_pointer_cast(cur_node); cur_node = std::dynamic_pointer_cast(cur_node->right()); EXPECT_TRUE(cur_node); EXPECT_EQ(cur_node->op_name(), " contain_any "); { auto left_key = std::dynamic_pointer_cast(cur_node->left()); EXPECT_EQ(left_key->op_name(), "ID"); EXPECT_EQ(left_key->value(), "category_array"); auto right_const = std::dynamic_pointer_cast(cur_node->right()); ASSERT_TRUE(right_const); EXPECT_EQ(right_const->op_name(), "LIST_VALUE"); EXPECT_EQ(right_const->text(), "(a, b)"); } cur_node = parent_node; } TEST_F(QueryInfoTest, SelectNonExistField) { SearchQuery query; query.output_fields_ = {"category_array", "not_exist_field"}; query.topk_ = 11; query.include_vector_ = false; auto engine = std::make_shared(std::make_shared()); auto ret = engine->build_query_info(schema, query, nullptr); ASSERT_FALSE(ret.has_value()); EXPECT_THAT(ret.error().message(), testing::HasSubstr("not defined in schema")); } TEST_F(QueryInfoTest, ContainAllExceedLimit) { SearchQuery query; query.topk_ = 200; query.filter_ = "name_array not contain_all ("; for (int i = 0; i <= 32; i++) { query.filter_ += std::to_string(i); if (i < 32) { query.filter_ += ", "; } } query.filter_ += ")"; auto engine = std::make_shared(std::make_shared()); auto ret = engine->build_query_info(schema, query, nullptr); ASSERT_FALSE(ret.has_value()); EXPECT_THAT(ret.error().message(), testing::HasSubstr( "Contain_* rel expr only support list size no more than 32")); } TEST_F(QueryInfoTest, ContainAnyExceedLimit) { SearchQuery query; query.topk_ = 200; query.filter_ = "name_array not contain_any ("; for (int i = 0; i <= 32; i++) { query.filter_ += std::to_string(i); if (i < 32) { query.filter_ += ", "; } } query.filter_ += ")"; auto engine = std::make_shared(std::make_shared()); auto ret = engine->build_query_info(schema, query, nullptr); ASSERT_FALSE(ret.has_value()); EXPECT_THAT(ret.error().message(), testing::HasSubstr( "Contain_* rel expr only support list size no more than 32")); } TEST_F(QueryInfoTest, ArrayLengthNonExistField) { SearchQuery query; query.topk_ = 200; query.filter_ = "array_length(not_exist_field) > 1"; auto engine = std::make_shared(std::make_shared()); auto ret = engine->build_query_info(schema, query, nullptr); ASSERT_FALSE(ret.has_value()); EXPECT_THAT(ret.error().message(), testing::HasSubstr("array_length argument not found in schema")); } TEST_F(QueryInfoTest, ArrayLengthOnNonArrayField) { SearchQuery query; query.topk_ = 200; query.filter_ = "array_length(name) > 1"; auto engine = std::make_shared(std::make_shared()); auto ret = engine->build_query_info(schema, query, nullptr); ASSERT_FALSE(ret.has_value()); EXPECT_THAT(ret.error().message(), testing::HasSubstr("array_length only support array")); } TEST_F(QueryInfoTest, ArrayLengthInvalidArgument) { SearchQuery query; query.topk_ = 200; query.filter_ = "array_length(name_array) > '1'"; auto engine = std::make_shared(std::make_shared()); auto ret = engine->build_query_info(schema, query, nullptr); ASSERT_FALSE(ret.has_value()); EXPECT_THAT( ret.error().message(), testing::HasSubstr("array_length right side only support integer")); } TEST_F(QueryInfoTest, ArrayLengthInvalidOp) { SearchQuery query; query.topk_ = 200; query.filter_ = "array_length(name_array) like '%'"; auto engine = std::make_shared(std::make_shared()); auto ret = engine->build_query_info(schema, query, nullptr); ASSERT_FALSE(ret.has_value()); EXPECT_THAT(ret.error().message(), testing::HasSubstr("syntax error")); } } // namespace zvec::sqlengine