diff --git a/tests/python_client/milvus_client/test_milvus_client_collection.py b/tests/python_client/milvus_client/test_milvus_client_collection.py index 5d052c51a9..e70a6a2638 100644 --- a/tests/python_client/milvus_client/test_milvus_client_collection.py +++ b/tests/python_client/milvus_client/test_milvus_client_collection.py @@ -780,40 +780,6 @@ class TestMilvusClientCollectionValid(TestMilvusClientV2Base): if self.has_collection(client, collection_name)[0]: self.drop_collection(client, collection_name) - @pytest.mark.tags(CaseLabel.L1) - def test_milvus_client_collection_all_datatype_fields(self): - """ - target: Test create collection with all supported dataType fields - method: Create collection with schema containing all supported dataTypes - expected: Collection created successfully with all field types - """ - client = self._client() - collection_name = cf.gen_collection_name_by_testcase_name() - schema = self.create_schema(client, enable_dynamic_field=False)[0] - schema.add_field(ct.default_int64_field_name, DataType.INT64, is_primary=True) - schema.add_field(default_vector_field_name, DataType.FLOAT_VECTOR, dim=default_dim) - # Add all supported scalar data types (excluding vectors and unsupported types) - supported_types = [] - for k, v in DataType.__members__.items(): - if (v and v != DataType.UNKNOWN and v != DataType.STRING - and v != DataType.VARCHAR and v != DataType.FLOAT_VECTOR - and v != DataType.BINARY_VECTOR and v != DataType.ARRAY - and v != DataType.FLOAT16_VECTOR and v != DataType.BFLOAT16_VECTOR - and v != DataType.INT8_VECTOR): - supported_types.append((k.lower(), v)) - for field_name, data_type in supported_types: - if data_type != DataType.INT64: # Skip INT64 as it's already added as primary key - schema.add_field(field_name, data_type) - - self.create_collection(client, collection_name, schema=schema) - expected_field_count = len([name for name in supported_types]) + 2 - self.describe_collection(client, collection_name, - check_task=CheckTasks.check_describe_collection_property, - check_items={"collection_name": collection_name, - "enable_dynamic_field": False, - "fields_num": expected_field_count}) - self.drop_collection(client, collection_name) - @pytest.mark.tags(CaseLabel.L2) def test_milvus_client_collection_self_creation_multiple_vectors(self): """ @@ -1882,37 +1848,6 @@ class TestMilvusClientReleaseCollectionValid(TestMilvusClientV2Base): self.drop_collection(client, collection_name) -class TestMilvusClientReleaseAdvanced(TestMilvusClientV2Base): - """ - ****************************************************************** - The following cases are used to test release during search operations - ****************************************************************** - """ - - @pytest.mark.tags(CaseLabel.L0) - def test_milvus_client_release_collection_during_searching(self): - """ - target: test release collection during searching - method: insert entities into collection, flush and load collection, release collection during searching - expected: raise exception - """ - client = self._client() - collection_name = cf.gen_collection_name_by_testcase_name() - self.create_collection(client, collection_name, default_dim) - self.load_collection(client, collection_name) - load_state = self.get_load_state(client, collection_name)[0] - assert load_state["state"] == LoadState.Loaded, f"Expected Loaded, but got {load_state['state']}" - vectors_to_search = np.random.default_rng(seed=19530).random((1, default_dim)) - self.search(client, collection_name, vectors_to_search, limit=default_limit, _async=True) - self.release_collection(client, collection_name) - load_state = self.get_load_state(client, collection_name)[0] - assert load_state["state"] == LoadState.NotLoad, f"Expected NotLoad after release, but got {load_state['state']}" - error = {ct.err_code: 65535, ct.err_msg: "collection not loaded"} - self.search(client, collection_name, vectors_to_search, limit=default_limit, - check_task=CheckTasks.err_res, check_items=error) - self.drop_collection(client, collection_name) - - class TestMilvusClientLoadCollectionInvalid(TestMilvusClientV2Base): """ Test case of search interface """ """ @@ -2712,6 +2647,41 @@ class TestMilvusClientLoadCollectionValid(TestMilvusClientV2Base): self.drop_collection(client, collection_name) +class TestMilvusClientLoadPartition(TestMilvusClientV2Base): + """ Test case of load partition interface """ + + @pytest.mark.tags(CaseLabel.L2) + def test_milvus_client_load_collection_after_load_loaded_partition(self): + """ + target: test load partition after load partition + method: 1. create collection and two partitions + 4. release collection and load one partition twice + 5. query on the non-loaded partition (should fail) + 6. load the whole collection (should succeed) + expected: No exception on repeated partition load, error on querying non-loaded partition, success on collection load + """ + client = self._client() + collection_name = cf.gen_collection_name_by_testcase_name() + partition_name_1 = cf.gen_unique_str("partition1") + partition_name_2 = cf.gen_unique_str("partition2") + # 1. create collection + self.create_collection(client, collection_name, default_dim) + self.release_collection(client, collection_name) + # 2. create partitions + self.create_partition(client, collection_name, partition_name_1) + self.create_partition(client, collection_name, partition_name_2) + # 5. load partition1 twice + self.load_partitions(client, collection_name, [partition_name_1]) + self.load_partitions(client, collection_name, [partition_name_1]) + # 6. query on the non-loaded partition2 (should fail) + error = {ct.err_code: 65538, ct.err_msg: 'partition not loaded'} + self.query(client, collection_name, filter=default_search_exp, partition_names=[partition_name_2], + check_task=CheckTasks.err_res, check_items=error) + # 7. load the whole collection (should succeed) + self.load_collection(client, collection_name) + self.drop_collection(client, collection_name) + + class TestMilvusClientDescribeCollectionInvalid(TestMilvusClientV2Base): """ Test case of search interface """ """ @@ -3879,3 +3849,826 @@ class TestMilvusClientCollectionMultiCollections(TestMilvusClientV2Base): self.drop_collection(client, collection_name) +class TestMilvusClientCollectionString(TestMilvusClientV2Base): + """ + ****************************************************************** + # The following cases are used to test about string fields + ****************************************************************** + """ + + @pytest.mark.tags(CaseLabel.L1) + def test_milvus_client_collection_string_field_is_primary(self): + """ + target: test create collection with string field as primary key + method: create collection with id_type="string" using fast creation method + expected: Create collection successfully + """ + client = self._client() + collection_name = cf.gen_collection_name_by_testcase_name() + # Use fast creation method with string primary key + self.create_collection(client, collection_name, default_dim, id_type="string", max_length=100) + # Verify collection properties + self.describe_collection(client, collection_name, + check_task=CheckTasks.check_describe_collection_property, + check_items={"collection_name": collection_name, + "id_name": "id"}) + self.drop_collection(client, collection_name) + + @pytest.mark.tags(CaseLabel.L1) + def test_milvus_client_collection_string_field_primary_auto_id(self): + """ + target: test create collection with string primary field and auto_id=True + method: create collection with string field, the string field primary and auto id are true + expected: Create collection successfully + """ + client = self._client() + collection_name = cf.gen_collection_name_by_testcase_name() + # Create schema with VARCHAR primary key and auto_id=True + schema = self.create_schema(client, enable_dynamic_field=False)[0] + schema.add_field("string_pk", DataType.VARCHAR, max_length=100, is_primary=True, auto_id=True) + schema.add_field("vector", DataType.FLOAT_VECTOR, dim=default_dim) + # Create collection + self.create_collection(client, collection_name, schema=schema) + # Verify collection properties + self.describe_collection(client, collection_name, + check_task=CheckTasks.check_describe_collection_property, + check_items={"collection_name": collection_name, + "id_name": "string_pk", + "auto_id": True, + "enable_dynamic_field": False}) + self.drop_collection(client, collection_name) + + @pytest.mark.tags(CaseLabel.L1) + def test_milvus_client_collection_only_string_field(self): + """ + target: test create collection with only string field (no vector field) + method: create collection with only string field + expected: Raise exception + """ + client = self._client() + collection_name = cf.gen_collection_name_by_testcase_name() + # Create schema with only string field + schema = self.create_schema(client, enable_dynamic_field=False)[0] + schema.add_field("string_pk", DataType.VARCHAR, max_length=100, is_primary=True, auto_id=False) + # Try to create collection + error = {ct.err_code: 1100, ct.err_msg: "schema does not contain vector field: invalid parameter"} + self.create_collection(client, collection_name, schema=schema, + check_task=CheckTasks.err_res, check_items=error) + + @pytest.mark.tags(CaseLabel.L1) + def test_milvus_client_collection_string_field_over_max_length(self): + """ + target: test create collection with string field exceeding max length + method: 1. create collection with string field + 2. String field max_length exceeds maximum (65535) + expected: Raise exception + """ + client = self._client() + collection_name = cf.gen_collection_name_by_testcase_name() + # Create schema with string field exceeding max length + schema = self.create_schema(client, enable_dynamic_field=False)[0] + schema.add_field("int64_pk", DataType.INT64, is_primary=True, auto_id=False) + schema.add_field("vector", DataType.FLOAT_VECTOR, dim=default_dim) + # Try to add string field with max_length > 65535 + max_length = 65535 + 1 + schema.add_field("string_field", DataType.VARCHAR, max_length=max_length) + error = {ct.err_code: 1100, ct.err_msg: f"the maximum length specified for the field(string_field) should be in (0, 65535], " + f"but got {max_length} instead: invalid parameter"} + self.create_collection(client, collection_name, schema=schema, + check_task=CheckTasks.err_res, check_items=error) + + @pytest.mark.tags(CaseLabel.L1) + def test_milvus_client_collection_invalid_string_field_dtype(self): + """ + target: test create collection with invalid string field datatype + method: create collection with string field using DataType.STRING (deprecated) + expected: Raise exception + """ + client = self._client() + collection_name = cf.gen_collection_name_by_testcase_name() + # Create schema + schema = self.create_schema(client, enable_dynamic_field=False)[0] + schema.add_field("int64_pk", DataType.INT64, is_primary=True, auto_id=False) + schema.add_field("vector", DataType.FLOAT_VECTOR, dim=default_dim) + # Try to add field with deprecated DataType.STRING + error = {ct.err_code: 1100, ct.err_msg: "string data type not supported yet, please use VarChar type instead"} + schema.add_field("string_field", DataType.STRING) + self.create_collection(client, collection_name, schema=schema, + check_task=CheckTasks.err_res, check_items=error) + + +class TestMilvusClientCollectionJSON(TestMilvusClientV2Base): + """ + ****************************************************************** + # The following cases are used to test about JSON fields + ****************************************************************** + """ + + @pytest.mark.tags(CaseLabel.L1) + @pytest.mark.parametrize("auto_id", [True, False]) + def test_milvus_client_collection_json_field_as_primary_key(self, auto_id): + """ + target: test create collection with JSON field as primary key + method: 1. create collection with one JSON field, and vector field + 2. set json field is_primary=true + 3. set auto_id as true + expected: Raise exception + """ + client = self._client() + collection_name = cf.gen_collection_name_by_testcase_name() + # Test 1: create json field as primary key through field + schema1 = self.create_schema(client, enable_dynamic_field=False)[0] + schema1.add_field("json_field", DataType.JSON, is_primary=True, auto_id=auto_id) + schema1.add_field("vector_field", DataType.FLOAT_VECTOR, dim=default_dim) + error = {ct.err_code: 1100, ct.err_msg: "Primary key type must be DataType.INT64 or DataType.VARCHAR"} + self.create_collection(client, collection_name, schema=schema1, + check_task=CheckTasks.err_res, check_items=error) + # Test 2: create json field as primary key through schema + schema2 = self.create_schema(client, enable_dynamic_field=False, primary_field="json_field", auto_id=auto_id)[0] + schema2.add_field("json_field", DataType.JSON) + schema2.add_field("vector_field", DataType.FLOAT_VECTOR, dim=default_dim) + self.create_collection(client, collection_name, schema=schema2, primary_field="json_field", + check_task=CheckTasks.err_res, check_items=error) + + +class TestMilvusClientCollectionARRAY(TestMilvusClientV2Base): + """ + ****************************************************************** + # The following cases are used to test about ARRAY fields + ****************************************************************** + """ + + @pytest.mark.tags(CaseLabel.L2) + def test_milvus_client_collection_array_field_element_type_not_exist(self): + """ + target: test create collection with ARRAY field without element type + method: create collection with one array field without element type + expected: Raise exception + """ + client = self._client() + collection_name = cf.gen_collection_name_by_testcase_name() + schema = self.create_schema(client, enable_dynamic_field=False)[0] + schema.add_field("int64_pk", DataType.INT64, is_primary=True, auto_id=False) + schema.add_field("vector_field", DataType.FLOAT_VECTOR, dim=default_dim) + schema.add_field("array_field", DataType.ARRAY, element_type=None) + # Try to add array field without element_type + error = {ct.err_code: 1100, ct.err_msg: "element data type None is not valid"} + self.create_collection(client, collection_name, schema=schema, + check_task=CheckTasks.err_res, check_items=error) + + @pytest.mark.tags(CaseLabel.L2) + @pytest.mark.parametrize("element_type", [1001, 'a', [], (), {1}, DataType.BINARY_VECTOR, + DataType.FLOAT_VECTOR, DataType.JSON, DataType.ARRAY]) + def test_milvus_client_collection_array_field_element_type_invalid(self, element_type): + """ + target: Create a field with invalid element_type + method: Create a field with invalid element_type + 1. Type not in DataType: 1, 'a', ... + 2. Type in DataType: binary_vector, float_vector, json_field, array_field + expected: Raise exception + """ + client = self._client() + collection_name = cf.gen_collection_name_by_testcase_name() + schema = self.create_schema(client, enable_dynamic_field=False)[0] + schema.add_field("int64_pk", DataType.INT64, is_primary=True, auto_id=False) + schema.add_field("vector_field", DataType.FLOAT_VECTOR, dim=default_dim) + # Determine expected error based on element_type + error = {ct.err_code: 1100, ct.err_msg: f"element type {element_type} is not supported"} + if element_type in ['a', {1}]: + error = {ct.err_code: 1100, ct.err_msg: "Unexpected error"} + elif element_type == []: + error = {ct.err_code: 1100, ct.err_msg: "'list' object cannot be interpreted as an integer"} + elif element_type == (): + error = {ct.err_code: 1100, ct.err_msg: "'tuple' object cannot be interpreted as an integer"} + elif element_type in [DataType.BINARY_VECTOR, DataType.FLOAT_VECTOR, DataType.JSON, DataType.ARRAY]: + data_type = element_type.name + if element_type == DataType.BINARY_VECTOR: + data_type = "BinaryVector" + elif element_type == DataType.FLOAT_VECTOR: + data_type = "FloatVector" + elif element_type == DataType.ARRAY: + data_type = "Array" + error = {ct.err_code: 1100, ct.err_msg: f"element type {data_type} is not supported"} + # Try to add array field with invalid element_type + schema.add_field("array_field", DataType.ARRAY, element_type=element_type) + self.create_collection(client, collection_name, schema=schema, + check_task=CheckTasks.err_res, check_items=error) + + @pytest.mark.tags(CaseLabel.L2) + @pytest.mark.parametrize("max_capacity", [None, [], 'a', (), -1, 4097]) + def test_milvus_client_collection_array_field_invalid_capacity(self, max_capacity): + """ + target: Create a field with invalid max_capacity + method: Create a field with invalid max_capacity + 1. Type invalid: [], 'a', (), None + 2. Value invalid: <0, >max_capacity(4096) + expected: Raise exception + """ + client = self._client() + collection_name = cf.gen_collection_name_by_testcase_name() + + schema = self.create_schema(client, enable_dynamic_field=False)[0] + schema.add_field("int64_pk", DataType.INT64, is_primary=True, auto_id=False) + schema.add_field("vector_field", DataType.FLOAT_VECTOR, dim=default_dim) + # Determine expected error based on max_capacity type and value + if max_capacity in [[], 'a', (), None]: + error = {ct.err_code: 1100, ct.err_msg: "the value for max_capacity of field array_field must be an integer"} + else: + error = {ct.err_code: 1100, ct.err_msg: "the maximum capacity specified for a Array should be in (0, 4096]"} + # Try to add array field with invalid max_capacity + schema.add_field("array_field", DataType.ARRAY, element_type=DataType.INT64, max_capacity=max_capacity) + self.create_collection(client, collection_name, schema=schema, + check_task=CheckTasks.err_res, check_items=error) + + @pytest.mark.tags(CaseLabel.L2) + def test_milvus_client_collection_string_array_without_max_length(self): + """ + target: Create string array without giving max length + method: Create string array without giving max length + expected: Raise exception + """ + client = self._client() + collection_name = cf.gen_collection_name_by_testcase_name() + schema = self.create_schema(client, enable_dynamic_field=False)[0] + schema.add_field("int64_pk", DataType.INT64, is_primary=True, auto_id=False) + schema.add_field("vector_field", DataType.FLOAT_VECTOR, dim=default_dim) + # Try to add string array field without max_length - should fail at add_field stage + error = {ct.err_code: 1100, ct.err_msg: "type param(max_length) should be specified for the field(array_field)"} + schema.add_field("array_field", DataType.ARRAY, element_type=DataType.VARCHAR) + self.create_collection(client, collection_name, schema=schema, + check_task=CheckTasks.err_res, check_items=error) + + @pytest.mark.tags(CaseLabel.L2) + @pytest.mark.parametrize("max_length", [-1, 65536]) + def test_milvus_client_collection_string_array_max_length_invalid(self, max_length): + """ + target: Create string array with invalid max length + method: Create string array with invalid max length + Value invalid: <0, >max_length(65535) + expected: Raise exception + """ + client = self._client() + collection_name = cf.gen_collection_name_by_testcase_name() + schema = self.create_schema(client, enable_dynamic_field=False)[0] + schema.add_field("int64_pk", DataType.INT64, is_primary=True, auto_id=False) + schema.add_field("vector_field", DataType.FLOAT_VECTOR, dim=default_dim) + # Try to add string array field with invalid max_length + schema.add_field("array_field", DataType.ARRAY, element_type=DataType.VARCHAR, max_length=max_length) + error = {ct.err_code: 1100, ct.err_msg: f"the maximum length specified for the field(array_field) should be in (0, 65535], " + f"but got {max_length} instead: invalid parameter"} + self.create_collection(client, collection_name, schema=schema, + check_task=CheckTasks.err_res, check_items=error) + + + @pytest.mark.tags(CaseLabel.L2) + def test_milvus_client_collection_array_field_all_datatype(self): + """ + target: test create collection with ARRAY field all data type + method: 1. Create field respectively: int8, int16, int32, int64, varchar, bool, float, double + 2. Insert data respectively: int8, int16, int32, int64, varchar, bool, float, double + expected: Create collection successfully and insert data successfully + """ + client = self._client() + collection_name = cf.gen_collection_name_by_testcase_name() + # Create schema with all supported array data types + nb = default_nb + schema = self.create_schema(client, enable_dynamic_field=False)[0] + schema.add_field("int64", DataType.INT64, is_primary=True, auto_id=False) + schema.add_field("vector", DataType.FLOAT_VECTOR, dim=default_dim) + schema.add_field("int8_array", DataType.ARRAY, element_type=DataType.INT8, max_capacity=nb) + schema.add_field("int16_array", DataType.ARRAY, element_type=DataType.INT16, max_capacity=nb) + schema.add_field("int32_array", DataType.ARRAY, element_type=DataType.INT32, max_capacity=nb) + schema.add_field("int64_array", DataType.ARRAY, element_type=DataType.INT64, max_capacity=nb) + schema.add_field("bool_array", DataType.ARRAY, element_type=DataType.BOOL, max_capacity=nb) + schema.add_field("float_array", DataType.ARRAY, element_type=DataType.FLOAT, max_capacity=nb) + schema.add_field("double_array", DataType.ARRAY, element_type=DataType.DOUBLE, max_capacity=nb) + schema.add_field("string_array", DataType.ARRAY, element_type=DataType.VARCHAR, max_capacity=nb, max_length=100) + # Create collection + self.create_collection(client, collection_name, schema=schema) + # Verify collection properties and all fields + expected_fields = [ + {"field_id": 100, "name": "int64", "description": "", "type": DataType.INT64, "params": {}, "element_type": 0, "is_primary": True}, + {"field_id": 101, "name": "vector", "description": "", "type": DataType.FLOAT_VECTOR, "params": {"dim": default_dim}, "element_type": 0}, + {"field_id": 102, "name": "int8_array", "description": "", "type": DataType.ARRAY, "params": {"max_capacity": nb}, "element_type": DataType.INT8}, + {"field_id": 103, "name": "int16_array", "description": "", "type": DataType.ARRAY, "params": {"max_capacity": nb}, "element_type": DataType.INT16}, + {"field_id": 104, "name": "int32_array", "description": "", "type": DataType.ARRAY, "params": {"max_capacity": nb}, "element_type": DataType.INT32}, + {"field_id": 105, "name": "int64_array", "description": "", "type": DataType.ARRAY, "params": {"max_capacity": nb}, "element_type": DataType.INT64}, + {"field_id": 106, "name": "bool_array", "description": "", "type": DataType.ARRAY, "params": {"max_capacity": nb}, "element_type": DataType.BOOL}, + {"field_id": 107, "name": "float_array", "description": "", "type": DataType.ARRAY, "params": {"max_capacity": nb}, "element_type": DataType.FLOAT}, + {"field_id": 108, "name": "double_array", "description": "", "type": DataType.ARRAY, "params": {"max_capacity": nb}, "element_type": DataType.DOUBLE}, + {"field_id": 109, "name": "string_array", "description": "", "type": DataType.ARRAY, "params": {"max_length": 100, "max_capacity": nb}, "element_type": DataType.VARCHAR} + ] + self.describe_collection(client, collection_name, + check_task=CheckTasks.check_describe_collection_property, + check_items={"collection_name": collection_name, + "id_name": "int64", + "enable_dynamic_field": False, + "fields": expected_fields}) + # Generate and insert test data manually + insert_nb = 10 + pk_values = [i for i in range(insert_nb)] + float_vec = cf.gen_vectors(insert_nb, default_dim) + int8_values = [[numpy.int8(j) for j in range(insert_nb)] for i in range(insert_nb)] + int16_values = [[numpy.int16(j) for j in range(insert_nb)] for i in range(insert_nb)] + int32_values = [[numpy.int32(j) for j in range(insert_nb)] for i in range(insert_nb)] + int64_values = [[numpy.int64(j) for j in range(insert_nb)] for i in range(insert_nb)] + bool_values = [[numpy.bool_(j) for j in range(insert_nb)] for i in range(insert_nb)] + float_values = [[numpy.float32(j) for j in range(insert_nb)] for i in range(insert_nb)] + double_values = [[numpy.double(j) for j in range(insert_nb)] for i in range(insert_nb)] + string_values = [[str(j) for j in range(insert_nb)] for i in range(insert_nb)] + # Prepare data as list format + data = [] + for i in range(insert_nb): + row = { + "int64": pk_values[i], + "vector": float_vec[i], + "int8_array": int8_values[i], + "int16_array": int16_values[i], + "int32_array": int32_values[i], + "int64_array": int64_values[i], + "bool_array": bool_values[i], + "float_array": float_values[i], + "double_array": double_values[i], + "string_array": string_values[i] + } + data.append(row) + self.insert(client, collection_name, data) + self.flush(client, collection_name) + stats = self.get_collection_stats(client, collection_name)[0] + assert stats['row_count'] == insert_nb + self.drop_collection(client, collection_name) + + +class TestMilvusClientCollectionMultipleVectorValid(TestMilvusClientV2Base): + """ + ****************************************************************** + # Test case for collection with multiple vector fields - Valid cases + ****************************************************************** + """ + + @pytest.mark.tags(CaseLabel.L1) + @pytest.mark.parametrize("primary_key_type", ["int64", "varchar"]) + @pytest.mark.parametrize("auto_id", [True, False]) + @pytest.mark.parametrize("shards_num", [1, 3]) + def test_milvus_client_collection_multiple_vectors_all_supported_field_type(self, primary_key_type, auto_id, shards_num): + """ + target: test create collection with multiple vector fields and all supported field types + method: create collection with multiple vector fields and all supported field types + expected: collection created successfully with all field types + """ + client = self._client() + collection_name = cf.gen_collection_name_by_testcase_name() + # Create schema with multiple vector fields and all supported scalar types + schema = self.create_schema(client, enable_dynamic_field=False, auto_id=auto_id)[0] + # Add primary key field + if primary_key_type == "int64": + schema.add_field("id", DataType.INT64, is_primary=True, auto_id=auto_id) + else: + schema.add_field("id", DataType.VARCHAR, max_length=100, is_primary=True, auto_id=auto_id) + # Add multiple vector fields + schema.add_field(default_vector_field_name, DataType.FLOAT_VECTOR, dim=default_dim) + schema.add_field(ct.default_binary_vec_field_name, DataType.BINARY_VECTOR, dim=default_dim) + schema.add_field(ct.default_float16_vec_field_name, DataType.FLOAT16_VECTOR, dim=default_dim) + schema.add_field(ct.default_bfloat16_vec_field_name, DataType.BFLOAT16_VECTOR, dim=default_dim) + # Add all supported scalar data types from DataType.__members__ + supported_types = [] + for k, v in DataType.__members__.items(): + if (v and v != DataType.UNKNOWN and v != DataType.STRING + and v != DataType.VARCHAR and v != DataType.FLOAT_VECTOR + and v != DataType.BINARY_VECTOR and v != DataType.ARRAY + and v != DataType.FLOAT16_VECTOR and v != DataType.BFLOAT16_VECTOR + and v != DataType.INT8_VECTOR and v != DataType.SPARSE_FLOAT_VECTOR): + supported_types.append((k.lower(), v)) + for field_name, data_type in supported_types: + # Skip INT64 and VARCHAR as they're already added as primary key + if data_type != DataType.INT64 and data_type != DataType.VARCHAR: + schema.add_field(field_name, data_type) + # Add ARRAY field separately with required parameters + schema.add_field("array_field", DataType.ARRAY, element_type=DataType.INT64, max_capacity=10) + # Create collection + self.create_collection(client, collection_name, schema=schema, shards_num=shards_num) + # Verify collection properties + expected_field_count = len([name for name in supported_types]) + 5 + self.describe_collection(client, collection_name, + check_task=CheckTasks.check_describe_collection_property, + check_items={"collection_name": collection_name, + "enable_dynamic_field": False, + "auto_id": auto_id, + "num_shards": shards_num, + "fields_num": expected_field_count}) + # Create same collection again + self.create_collection(client, collection_name, schema=schema, shards_num=shards_num) + self.drop_collection(client, collection_name) + + @pytest.mark.tags(CaseLabel.L2) + @pytest.mark.parametrize("primary_key_type", ["int64", "varchar"]) + @pytest.mark.parametrize("auto_id", [True, False]) + @pytest.mark.parametrize("enable_dynamic_field", [True, False]) + def test_milvus_client_collection_multiple_vectors_different_dim(self, primary_key_type, auto_id, enable_dynamic_field): + """ + target: test create collection with multiple vector fields having different dimensions + method: create collection with multiple vector fields with different dims + expected: collection created successfully with different vector dimensions + """ + client = self._client() + collection_name = cf.gen_collection_name_by_testcase_name() + # Create schema with different vector dimensions + schema = self.create_schema(client, enable_dynamic_field=enable_dynamic_field, auto_id=auto_id)[0] + # Add primary key field + if primary_key_type == "int64": + schema.add_field("id", DataType.INT64, is_primary=True, auto_id=auto_id) + else: + schema.add_field("id", DataType.VARCHAR, max_length=100, is_primary=True, auto_id=auto_id) + # Add vector fields with different dimensions + schema.add_field("float_vec_max_dim", DataType.FLOAT_VECTOR, dim=ct.max_dim) + schema.add_field("float_vec_min_dim", DataType.FLOAT_VECTOR, dim=ct.min_dim) + schema.add_field("float_vec_default_dim", DataType.FLOAT_VECTOR, dim=default_dim) + # Create collection + self.create_collection(client, collection_name, schema=schema) + # Verify collection properties + expected_dims = [ct.max_dim, ct.min_dim, default_dim] + expected_vector_names = ["float_vec_max_dim", "float_vec_min_dim", "float_vec_default_dim"] + self.describe_collection(client, collection_name, + check_task=CheckTasks.check_describe_collection_property, + check_items={"collection_name": collection_name, + "auto_id": auto_id, + "enable_dynamic_field": enable_dynamic_field, + "dim": expected_dims, + "vector_name": expected_vector_names}) + self.drop_collection(client, collection_name) + + @pytest.mark.tags(CaseLabel.L2) + @pytest.mark.parametrize("primary_key_type", ["int64", "varchar"]) + def test_milvus_client_collection_multiple_vectors_maximum_dim(self, primary_key_type): + """ + target: test create collection with multiple vector fields at maximum dimension + method: create collection with multiple vector fields all using max dimension + expected: collection created successfully with maximum dimensions + """ + client = self._client() + collection_name = cf.gen_collection_name_by_testcase_name() + # Create schema with maximum dimension vectors + schema = self.create_schema(client, enable_dynamic_field=False)[0] + # Add primary key field + if primary_key_type == "int64": + schema.add_field("id", DataType.INT64, is_primary=True) + else: + schema.add_field("id", DataType.VARCHAR, max_length=100, is_primary=True) + # Add multiple vector fields with maximum dimension (up to max_vector_field_num) + vector_field_names = [] + for i in range(ct.max_vector_field_num): + vector_field_name = f"float_vec_{i+1}" + vector_field_names.append(vector_field_name) + schema.add_field(vector_field_name, DataType.FLOAT_VECTOR, dim=ct.max_dim) + # Create collection + self.create_collection(client, collection_name, schema=schema) + # Verify collection properties + expected_dims = [ct.max_dim] * ct.max_vector_field_num + expected_vector_names = vector_field_names + self.describe_collection(client, collection_name, + check_task=CheckTasks.check_describe_collection_property, + check_items={"collection_name": collection_name, + "enable_dynamic_field": False, + "dim": expected_dims, + "vector_name": expected_vector_names}) + self.drop_collection(client, collection_name) + + @pytest.mark.tags(CaseLabel.L2) + @pytest.mark.parametrize("primary_key_type", ["int64", "varchar"]) + @pytest.mark.parametrize("auto_id", [True, False]) + @pytest.mark.parametrize("partition_key_type", ["int64", "varchar"]) + def test_milvus_client_collection_multiple_vectors_partition_key(self, primary_key_type, auto_id, partition_key_type): + """ + target: test create collection with multiple vector fields and partition key + method: create collection with multiple vector fields and partition key + expected: collection created successfully with partition key and multiple partitions + """ + client = self._client() + collection_name = cf.gen_collection_name_by_testcase_name() + # Create schema with multiple vector fields and partition key + schema = self.create_schema(client, enable_dynamic_field=False, auto_id=auto_id)[0] + # Add primary key field + if primary_key_type == "int64": + schema.add_field("id", DataType.INT64, is_primary=True, auto_id=auto_id) + else: + schema.add_field("id", DataType.VARCHAR, max_length=100, is_primary=True, auto_id=auto_id) + schema.add_field(default_vector_field_name, DataType.FLOAT_VECTOR, dim=default_dim) + schema.add_field("vector_2", DataType.FLOAT_VECTOR, dim=default_dim) + # Add scalar fields + schema.add_field("int8_field", DataType.INT8) + schema.add_field("int16_field", DataType.INT16) + schema.add_field("int32_field", DataType.INT32) + schema.add_field("float_field", DataType.FLOAT) + schema.add_field("double_field", DataType.DOUBLE) + schema.add_field("json_field", DataType.JSON) + schema.add_field("bool_field", DataType.BOOL) + schema.add_field("array_field", DataType.ARRAY, element_type=DataType.INT64, max_capacity=10) + schema.add_field("binary_vec_field", DataType.BINARY_VECTOR, dim=default_dim) + # Add partition key field + if partition_key_type == "int64": + schema.add_field("partition_key_int", DataType.INT64, is_partition_key=True) + else: + schema.add_field("partition_key_varchar", DataType.VARCHAR, max_length=100, is_partition_key=True) + # Create collection + self.create_collection(client, collection_name, schema=schema) + # Verify collection properties + self.describe_collection(client, collection_name, + check_task=CheckTasks.check_describe_collection_property, + check_items={"collection_name": collection_name, + "auto_id": auto_id, + "enable_dynamic_field": False, + "num_partitions": ct.default_partition_num}) + self.drop_collection(client, collection_name) + + +class TestMilvusClientCollectionMultipleVectorInvalid(TestMilvusClientV2Base): + """ + ****************************************************************** + # Test case for collection with multiple vector fields - Invalid cases + ****************************************************************** + """ + + @pytest.fixture(scope="function", params=ct.invalid_dims) + def get_invalid_dim(self, request): + yield request.param + + @pytest.mark.tags(CaseLabel.L1) + @pytest.mark.parametrize("primary_key_type", ["int64", "varchar"]) + def test_milvus_client_collection_multiple_vectors_same_vector_field_name(self, primary_key_type): + """ + target: test create collection with multiple vector fields having duplicate names + method: create collection with multiple vector fields using same field name + expected: raise exception for duplicated field name + """ + client = self._client() + collection_name = cf.gen_collection_name_by_testcase_name() + # Create schema with duplicate vector field names + schema = self.create_schema(client, enable_dynamic_field=False)[0] + # Add primary key field + if primary_key_type == "int64": + schema.add_field("id", DataType.INT64, is_primary=True) + else: + schema.add_field("id", DataType.VARCHAR, max_length=100, is_primary=True) + # Add multiple vector fields with same name + schema.add_field("vector_field", DataType.FLOAT_VECTOR, dim=default_dim) + schema.add_field("vector_field", DataType.FLOAT_VECTOR, dim=default_dim) + # Add other fields + schema.add_field("int8_field", DataType.INT8) + schema.add_field("int16_field", DataType.INT16) + schema.add_field("int32_field", DataType.INT32) + schema.add_field("float_field", DataType.FLOAT) + schema.add_field("double_field", DataType.DOUBLE) + schema.add_field("varchar_field", DataType.VARCHAR, max_length=100) + schema.add_field("json_field", DataType.JSON) + schema.add_field("bool_field", DataType.BOOL) + schema.add_field("array_field", DataType.ARRAY, element_type=DataType.INT64, max_capacity=10) + schema.add_field("binary_vec_field", DataType.BINARY_VECTOR, dim=default_dim) + # Try to create collection with duplicate field names + error = {ct.err_code: 1100, ct.err_msg: "duplicated field name"} + self.create_collection(client, collection_name, schema=schema, + check_task=CheckTasks.err_res, check_items=error) + + @pytest.mark.tags(CaseLabel.L1) + @pytest.mark.parametrize("invalid_vector_name", ["12-s", "12 s", "(mn)", "中文", "%$#", "a".join("a" for i in range(256))]) + def test_milvus_client_collection_multiple_vectors_invalid_all_vector_field_name(self, invalid_vector_name): + """ + target: test create collection with multiple vector fields where all have invalid names + method: create collection with multiple vector fields, all with invalid names + expected: raise exception for invalid field name + """ + client = self._client() + collection_name = cf.gen_collection_name_by_testcase_name() + # Create schema with all invalid vector field names + schema = self.create_schema(client, enable_dynamic_field=False)[0] + schema.add_field("id", DataType.INT64, is_primary=True) + # Add vector fields - all with invalid names + schema.add_field(invalid_vector_name, DataType.FLOAT_VECTOR, dim=default_dim) + schema.add_field(invalid_vector_name + " ", DataType.FLOAT_VECTOR, dim=default_dim) + # Try to create collection with invalid field names + error = {ct.err_code: 1100, ct.err_msg: f"Invalid field name: {invalid_vector_name}"} + self.create_collection(client, collection_name, schema=schema, + check_task=CheckTasks.err_res, check_items=error) + + @pytest.mark.tags(CaseLabel.L1) + @pytest.mark.skip("issue #37543") + def test_milvus_client_collection_multiple_vectors_invalid_dim(self, get_invalid_dim): + """ + target: test create collection with multiple vector fields where one has invalid dimension + method: create collection with multiple vector fields, one with invalid dimension + expected: raise exception for invalid dimension + """ + client = self._client() + collection_name = cf.gen_collection_name_by_testcase_name() + # Create schema with one invalid vector dimension + schema = self.create_schema(client, enable_dynamic_field=False)[0] + schema.add_field("id", DataType.INT64, is_primary=True) + # Add vector fields - one with invalid dimension, one with valid dimension + schema.add_field("vector_field_1", DataType.FLOAT_VECTOR, dim=get_invalid_dim) + schema.add_field("vector_field_2", DataType.FLOAT_VECTOR, dim=default_dim) + # Try to create collection with invalid dimension + error = {ct.err_code: 65535, ct.err_msg: "invalid dimension"} + self.create_collection(client, collection_name, schema=schema, + check_task=CheckTasks.err_res, check_items=error) + + +class TestMilvusClientCollectionMmap(TestMilvusClientV2Base): + """ + ****************************************************************** + # Test case for collection mmap functionality + ****************************************************************** + """ + + @pytest.mark.tags(CaseLabel.L1) + def test_milvus_client_describe_collection_mmap(self): + """ + target: enable or disable mmap in the collection + method: enable or disable mmap in the collection + expected: description information contains mmap + """ + client = self._client() + collection_name = cf.gen_collection_name_by_testcase_name() + self.create_collection(client, collection_name, default_dim) + self.release_collection(client, collection_name) + # Test enable mmap + self.alter_collection_properties(client, collection_name, properties={"mmap.enabled": True}) + describe_res = self.describe_collection(client, collection_name)[0] + properties = describe_res.get("properties") + assert "mmap.enabled" in properties.keys() + assert properties["mmap.enabled"] == 'True' + # Test disable mmap + self.alter_collection_properties(client, collection_name, properties={"mmap.enabled": False}) + describe_res = self.describe_collection(client, collection_name)[0] + properties = describe_res.get("properties") + assert properties["mmap.enabled"] == 'False' + # Test enable mmap again + self.alter_collection_properties(client, collection_name, properties={"mmap.enabled": True}) + describe_res = self.describe_collection(client, collection_name)[0] + properties = describe_res.get("properties") + assert properties["mmap.enabled"] == 'True' + self.drop_collection(client, collection_name) + + @pytest.mark.tags(CaseLabel.L1) + def test_milvus_client_load_mmap_collection(self): + """ + target: after loading, enable mmap for the collection + method: 1. data preparation and create index + 2. load collection + 3. enable mmap on collection + expected: raise exception + """ + client = self._client() + collection_name = cf.gen_collection_name_by_testcase_name() + # Create collection with data and index + self.create_collection(client, collection_name, default_dim) + self.release_collection(client, collection_name) + self.drop_index(client, collection_name, "vector") + # Get collection schema to generate compatible data + collection_info = self.describe_collection(client, collection_name)[0] + data = cf.gen_row_data_by_schema(nb=ct.default_nb, schema=collection_info) + self.insert(client, collection_name, data) + self.flush(client, collection_name) + index_params = self.prepare_index_params(client)[0] + index_params.add_index(field_name="vector", index_type="HNSW", metric_type="L2") + self.create_index(client, collection_name, index_params) + + self.release_collection(client, collection_name) + # Set mmap enabled before loading + self.alter_collection_properties(client, collection_name, properties={"mmap.enabled": True}) + describe_res = self.describe_collection(client, collection_name)[0] + properties = describe_res.get("properties") + assert properties["mmap.enabled"] == 'True' + # Load collection + self.load_collection(client, collection_name) + # Try to alter mmap after loading - should raise exception + error = {ct.err_code: 999, ct.err_msg: "can not alter mmap properties if collection loaded"} + self.alter_collection_properties(client, collection_name, properties={"mmap.enabled": True}, + check_task=CheckTasks.err_res, check_items=error) + + self.drop_collection(client, collection_name) + + @pytest.mark.tags(CaseLabel.L2) + def test_milvus_client_drop_mmap_collection(self): + """ + target: set mmap on collection and then drop it + method: 1. set mmap on collection + 2. drop collection + 3. recreate collection with same name + expected: new collection doesn't inherit mmap settings + """ + client = self._client() + collection_name = "coll_mmap_test" + # Create collection and set mmap + self.create_collection(client, collection_name, default_dim) + self.release_collection(client, collection_name) + self.alter_collection_properties(client, collection_name, properties={"mmap.enabled": True}) + describe_res = self.describe_collection(client, collection_name)[0] + properties = describe_res.get("properties") + assert properties["mmap.enabled"] == 'True' + # Drop collection + self.drop_collection(client, collection_name) + # Recreate collection with same name + self.create_collection(client, collection_name, default_dim) + describe_res = self.describe_collection(client, collection_name)[0] + properties = describe_res.get("properties") + assert "mmap.enabled" not in properties.keys() + self.drop_collection(client, collection_name) + + @pytest.mark.tags(CaseLabel.L2) + def test_milvus_client_multiple_collections_enable_mmap(self): + """ + target: enabling mmap for multiple collections in a single instance + method: enabling mmap for multiple collections in a single instance + expected: the collection description message for mmap is normal + """ + client = self._client() + collection_name_1 = cf.gen_collection_name_by_testcase_name() + "_1" + collection_name_2 = cf.gen_collection_name_by_testcase_name() + "_2" + collection_name_3 = cf.gen_collection_name_by_testcase_name() + "_3" + # Create multiple collections + self.create_collection(client, collection_name_1, default_dim) + self.create_collection(client, collection_name_2, default_dim) + self.create_collection(client, collection_name_3, default_dim) + # Release collections before setting mmap + self.release_collection(client, collection_name_1) + self.release_collection(client, collection_name_2) + self.release_collection(client, collection_name_3) + # Enable mmap for first two collections + self.alter_collection_properties(client, collection_name_1, properties={"mmap.enabled": True}) + self.alter_collection_properties(client, collection_name_2, properties={"mmap.enabled": True}) + # Verify mmap settings + describe_res_1 = self.describe_collection(client, collection_name_1)[0] + describe_res_2 = self.describe_collection(client, collection_name_2)[0] + properties_1 = describe_res_1.get("properties") + properties_2 = describe_res_2.get("properties") + assert properties_1["mmap.enabled"] == 'True' + assert properties_2["mmap.enabled"] == 'True' + # Enable mmap for third collection + self.alter_collection_properties(client, collection_name_3, properties={"mmap.enabled": True}) + describe_res_3 = self.describe_collection(client, collection_name_3)[0] + properties_3 = describe_res_3.get("properties") + assert properties_3["mmap.enabled"] == 'True' + # Clean up + self.drop_collection(client, collection_name_1) + self.drop_collection(client, collection_name_2) + self.drop_collection(client, collection_name_3) + + @pytest.mark.tags(CaseLabel.L2) + def test_milvus_client_flush_collection_mmap(self): + """ + target: after flush, collection enables mmap + method: after flush, collection enables mmap + expected: the collection description message for mmap is normal + """ + client = self._client() + collection_name = cf.gen_collection_name_by_testcase_name() + # Create collection and insert data + self.create_collection(client, collection_name, default_dim) + # Get collection schema to generate compatible data + collection_info = self.describe_collection(client, collection_name)[0] + data = cf.gen_row_data_by_schema(nb=ct.default_nb, schema=collection_info) + self.insert(client, collection_name, data) + # Create index + self.release_collection(client, collection_name) + self.drop_index(client, collection_name, "vector") + index_params = self.prepare_index_params(client)[0] + index_params.add_index(field_name="vector", index_type="FLAT", metric_type="L2") + self.create_index(client, collection_name, index_params) + # Set index mmap to False + self.alter_index_properties(client, collection_name, "vector", properties={"mmap.enabled": False}) + # Flush data + self.flush(client, collection_name) + # Set collection mmap to True + self.alter_collection_properties(client, collection_name, properties={"mmap.enabled": True}) + describe_res = self.describe_collection(client, collection_name)[0] + properties = describe_res.get("properties") + assert properties["mmap.enabled"] == 'True' + # Set index mmap to True + self.alter_index_properties(client, collection_name, "vector", properties={"mmap.enabled": True}) + # Load collection and perform search to verify functionality + self.load_collection(client, collection_name) + # Generate search vectors + search_data = cf.gen_vectors(default_nq, default_dim) + self.search(client, collection_name, search_data, + check_task=CheckTasks.check_search_results, + check_items={"nq": default_nq, "limit": default_limit}) + self.drop_collection(client, collection_name) + + @pytest.mark.tags(CaseLabel.L2) + def test_milvus_client_enable_mmap_after_drop_collection(self): + """ + target: enable mmap after deleting a collection + method: enable mmap after deleting a collection + expected: raise exception + """ + client = self._client() + collection_name = cf.gen_collection_name_by_testcase_name() + # Create and drop collection + self.create_collection(client, collection_name, default_dim) + self.drop_collection(client, collection_name) + # Try to enable mmap on dropped collection - should raise exception + error = {ct.err_code: 100, ct.err_msg: "collection not found"} + self.alter_collection_properties(client, collection_name, properties={"mmap.enabled": True}, + check_task=CheckTasks.err_res, check_items=error) + + + diff --git a/tests/python_client/milvus_client/test_milvus_client_partition.py b/tests/python_client/milvus_client/test_milvus_client_partition.py index fd467ccac6..ddb99de5e2 100644 --- a/tests/python_client/milvus_client/test_milvus_client_partition.py +++ b/tests/python_client/milvus_client/test_milvus_client_partition.py @@ -6,6 +6,8 @@ from common import common_func as cf from common import common_type as ct from common.common_type import CaseLabel, CheckTasks from utils.util_pymilvus import * +from pymilvus.client.types import LoadState + prefix = "milvus_client_api_partition" partition_prefix = "milvus_client_api_partition" @@ -560,6 +562,52 @@ class TestMilvusClientReleasePartitionInvalid(TestMilvusClientV2Base): self.release_partitions(client, collection_name, partition_name, check_task=CheckTasks.err_res, check_items=error) + @pytest.mark.tags(CaseLabel.L2) + def test_milvus_client_release_partition_after_disconnect(self): + """ + target: test release partition without connection + method: release partition with correct params, with a disconnected instance + expected: release raise exception + """ + client_temp = self._client(alias="client_release_partition") + collection_name = cf.gen_collection_name_by_testcase_name() + partition_name = cf.gen_unique_str("partition") + # Create collection and partition + self.create_collection(client_temp, collection_name, default_dim) + self.create_partition(client_temp, collection_name, partition_name) + # Load partition first + self.load_partitions(client_temp, collection_name, [partition_name]) + # Close the client connection + self.close(client_temp) + # Try to release partition after disconnect - should raise exception + error = {ct.err_code: 1, ct.err_msg: 'should create connection first'} + self.release_partitions(client_temp, collection_name, partition_name, + check_task=CheckTasks.err_res, check_items=error) + + @pytest.mark.tags(CaseLabel.L0) + def test_milvus_client_load_release_partition_after_collection_drop(self): + """ + target: test load and release partition after collection drop + method: load and release the partition after the collection has been dropped + expected: raise exception + """ + client = self._client() + collection_name = cf.gen_collection_name_by_testcase_name() + partition_name = cf.gen_unique_str(partition_prefix) + description = cf.gen_unique_str("desc_") + # 1. Create collection and partition + self.create_collection(client, collection_name, default_dim) + self.create_partition(client, collection_name, partition_name, description=description) + # 2. Drop the collection + self.drop_collection(client, collection_name) + # 3. Try to load partition after collection drop - should raise exception + error = {ct.err_code: 100, ct.err_msg: "collection not found"} + self.load_partitions(client, collection_name, partition_name, + check_task=CheckTasks.err_res, check_items=error) + # 4. Try to release partition after collection drop - should raise exception + self.release_partitions(client, collection_name, partition_name, + check_task=CheckTasks.err_res, check_items=error) + class TestMilvusClientReleasePartitionValid(TestMilvusClientV2Base): """ Test case of search interface """ @@ -600,9 +648,9 @@ class TestMilvusClientReleasePartitionValid(TestMilvusClientV2Base): @pytest.mark.tags(CaseLabel.L2) def test_milvus_client_partition_release_unloaded_partition(self): """ - target: test fast create collection normal case - method: create collection - expected: create collection with default schema, index, and load successfully + target: test releasing a partition that has not been loaded + method: create a collection and a partition, do not load the partition, then release the partition twice + expected: releasing an unloaded partition should succeed and be idempotent """ client = self._client() collection_name = cf.gen_unique_str(prefix) @@ -618,9 +666,11 @@ class TestMilvusClientReleasePartitionValid(TestMilvusClientV2Base): @pytest.mark.tags(CaseLabel.L2) def test_milvus_client_partition_release_unloaded_collection(self): """ - target: test fast create collection normal case - method: create collection - expected: create collection with default schema, index, and load successfully + target: Test releasing partitions after the collection has been released (unloaded) + method: 1. Create a collection and a partition + 2. Release the entire collection (unload) + 3. Attempt to release the partition after the collection is unloaded + expected: Releasing a partition after the collection is unloaded should succeed and be idempotent """ client = self._client() collection_name = cf.gen_unique_str(prefix) @@ -942,9 +992,9 @@ class TestMilvusClientLoadPartitionInvalid(TestMilvusClientV2Base): @pytest.mark.tags(CaseLabel.L2) def test_milvus_client_load_partitions_partition_not_existed(self): """ - target: test fast create collection normal case - method: create collection - expected: drop successfully + target: test loading a non-existent partition + method: create a collection, then attempt to load a partition that does not exist + expected: returns an error indicating the partition is not found """ client = self._client() collection_name = cf.gen_unique_str(prefix) @@ -996,15 +1046,58 @@ class TestMilvusClientLoadPartitionInvalid(TestMilvusClientV2Base): self.load_partitions(client, collection_name, partition_name, check_task=CheckTasks.err_res, check_items=error) + @pytest.mark.tags(CaseLabel.L2) + def test_milvus_client_load_partition_after_disconnect(self): + """ + target: test load partition without connection + method: load partition with correct params, with a disconnected instance + expected: load raises exception + """ + client_temp = self._client(alias="client_load_partition") + collection_name = cf.gen_collection_name_by_testcase_name() + partition_name = cf.gen_unique_str("partition") + # Create collection and partition + self.create_collection(client_temp, collection_name, default_dim) + self.create_partition(client_temp, collection_name, partition_name) + # Load partition first + self.load_partitions(client_temp, collection_name, [partition_name]) + # Close the client connection + self.close(client_temp) + # Try to release partition after disconnect - should raise exception + error = {ct.err_code: 1, ct.err_msg: 'should create connection first'} + self.load_partitions(client_temp, collection_name, partition_name, + check_task=CheckTasks.err_res, check_items=error) -class TestMilvusClientLoadPartitionInvalid(TestMilvusClientV2Base): - """ Test case of search interface """ + @pytest.mark.tags(CaseLabel.L2) + def test_milvus_client_load_release_partition_after_drop(self): + """ + target: test load and release partition after drop + method: drop partition and then load and release it + expected: raise exception + """ + client = self._client() + collection_name = cf.gen_collection_name_by_testcase_name() + partition_name = cf.gen_unique_str(partition_prefix) + description = cf.gen_unique_str("desc_") + # 1. Create collection and partition + self.create_collection(client, collection_name, default_dim) + self.create_partition(client, collection_name, partition_name, description=description) + # 2. Drop the partition + self.load_collection(client, collection_name) + self.release_partitions(client, collection_name, partition_name) + self.drop_partition(client, collection_name, partition_name) + # 3. Try to load partition after drop - should raise exception + error = {ct.err_code: 200, ct.err_msg: f'partition not found[partition={partition_name}]'} + self.load_partitions(client, collection_name, partition_name, + check_task=CheckTasks.err_res, check_items=error) + # 4. Try to release partition after drop - should also raise exception + self.release_partitions(client, collection_name, partition_name, + check_task=CheckTasks.err_res, check_items=error) + self.drop_collection(client, collection_name) - """ - ****************************************************************** - # The following are invalid base cases - ****************************************************************** - """ + +class TestMilvusClientLoadPartitionValid(TestMilvusClientV2Base): + """ Test case of load partition valid cases """ @pytest.mark.tags(CaseLabel.L1) def test_milvus_client_load_multiple_partition(self): @@ -1061,3 +1154,55 @@ class TestMilvusClientLoadPartitionInvalid(TestMilvusClientV2Base): self.load_partitions(client, collection_name, partition_name) self.load_collection(client, collection_name) self.load_partitions(client, collection_name, partition_name) + + @pytest.mark.tags(CaseLabel.L0) + @pytest.mark.parametrize('binary_index_type', ct.binary_supported_index_types) + @pytest.mark.parametrize('metric_type', ct.binary_metrics) + def test_milvus_client_load_partition_after_index_binary(self, binary_index_type, metric_type): + """ + target: test load binary collection partition after index created + method: insert and create index, load binary collection partition with correct params + expected: no error raised + """ + client = self._client() + collection_name = cf.gen_collection_name_by_testcase_name() + partition_name = cf.gen_unique_str(partition_prefix) + # 1. Create collection with binary vector + schema = self.create_schema(client, enable_dynamic_field=False)[0] + schema.add_field(ct.default_int64_field_name, DataType.INT64, is_primary=True, auto_id=False) + schema.add_field(ct.default_float_field_name, DataType.FLOAT) + schema.add_field(ct.default_binary_vec_field_name, DataType.BINARY_VECTOR, dim=default_dim) + self.create_collection(client, collection_name, schema=schema) + # 2. Create partition + self.create_partition(client, collection_name, partition_name) + # 3. Insert some data + schema_info = self.describe_collection(client, collection_name)[0] + data = cf.gen_row_data_by_schema(nb=default_nb, schema=schema_info) + self.insert(client, collection_name, data, partition_name=partition_name) + self.flush(client, collection_name) + # 4. Create binary index + index_params = self.prepare_index_params(client)[0] + binary_index = {"index_type": binary_index_type, "metric_type": metric_type, "params": {"nlist": 128}} + # Handle special case for BIN_IVF_FLAT with structure metrics + if binary_index_type == "BIN_IVF_FLAT" and metric_type in ct.structure_metrics: + # This combination should raise an error, so create with default instead + error = {ct.err_code: 65535, + ct.err_msg: f"metric type {metric_type} not found or not supported, supported: [HAMMING JACCARD]"} + index_params.add_index(field_name=ct.default_binary_vec_field_name, + index_type=binary_index_type, metric_type=metric_type, params={"nlist": 128}) + self.create_index(client, collection_name, index_params, + check_task=CheckTasks.err_res, check_items=error) + # Create with default binary index instead + index_params = self.prepare_index_params(client)[0] + index_params.add_index(field_name=ct.default_binary_vec_field_name, **ct.default_bin_flat_index) + self.create_index(client, collection_name, index_params) + else: + index_params.add_index(field_name=ct.default_binary_vec_field_name, **binary_index) + self.create_index(client, collection_name, index_params) + # 5. Load the partition + self.release_collection(client, collection_name) + self.load_partitions(client, collection_name, [partition_name]) + # 6. Verify partition is loaded by checking load state + load_state = self.get_load_state(client, collection_name)[0] + assert load_state["state"] == LoadState.Loaded + self.drop_collection(client, collection_name) diff --git a/tests/python_client/testcases/test_collection.py b/tests/python_client/testcases/test_collection.py index 1f4ac7b1ae..81219673f2 100644 --- a/tests/python_client/testcases/test_collection.py +++ b/tests/python_client/testcases/test_collection.py @@ -93,39 +93,6 @@ class TestCollectionParams(TestcaseBase): ct.err_msg: f"Collection '{c_name}' not exist, or you can pass in schema to create one."} self.collection_wrap.init_collection(c_name, schema=None, check_task=CheckTasks.err_res, check_items=error) - @pytest.mark.tags(CaseLabel.L2) - def test_collection_multi_float_vectors(self): - """ - target: test collection with multi float vectors - method: create collection with two float-vec fields - expected: Collection created successfully - """ - # 1. connect - self._connect() - # 2. create collection with multiple vectors - c_name = cf.gen_unique_str(prefix) - fields = [cf.gen_int64_field(is_primary=True), cf.gen_float_field(), - cf.gen_float_vec_field(dim=default_dim), cf.gen_float_vec_field(name="tmp", dim=default_dim)] - schema = cf.gen_collection_schema(fields=fields) - self.collection_wrap.init_collection(c_name, schema=schema, - check_task=CheckTasks.check_collection_property, - check_items={exp_name: c_name, exp_schema: schema}) - - @pytest.mark.tags(CaseLabel.L1) - def test_collection_mix_vectors(self): - """ - target: test collection with mix vectors - method: create with float and binary vec - expected: Collection created successfully - """ - self._connect() - c_name = cf.gen_unique_str(prefix) - fields = [cf.gen_int64_field(is_primary=True), cf.gen_float_vec_field(), cf.gen_binary_vec_field()] - schema = cf.gen_collection_schema(fields=fields, auto_id=True) - self.collection_wrap.init_collection(c_name, schema=schema, - check_task=CheckTasks.check_collection_property, - check_items={exp_name: c_name, exp_schema: schema}) - class TestCollectionDataframe(TestcaseBase): """ @@ -723,61 +690,6 @@ class TestLoadCollection(TestcaseBase): check_items={'exp_res': [{"count(*)": ct.default_nb}]}) -class TestReleaseAdvanced(TestcaseBase): - - @pytest.mark.tags(CaseLabel.L2) - def test_release_partition_during_searching(self): - """ - target: test release partition during searching - method: insert entities into partition, flush and load partition, release partition during searching - expected: raise exception - """ - self._connect() - partition_num = 1 - collection_w = self.init_collection_general(prefix, True, 10, partition_num, is_index=False)[0] - collection_w.create_index(ct.default_float_vec_field_name, index_params=ct.default_flat_index) - par = collection_w.partitions - par_name = par[partition_num].name - par[partition_num].load() - limit = 10 - collection_w.search(vectors, default_search_field, - default_search_params, limit, default_search_exp, - [par_name]) - par[partition_num].release() - collection_w.search(vectors, default_search_field, - default_search_params, limit, default_search_exp, - [par_name], - check_task=CheckTasks.err_res, - check_items={"err_code": 65535, - "err_msg": "collection not loaded"}) - - @pytest.mark.tags(CaseLabel.L0) - def test_release_indexed_collection_during_searching(self): - """ - target: test release indexed collection during searching - method: insert entities into partition, flush and load partition, release collection during searching - expected: raise exception - """ - self._connect() - partition_num = 1 - collection_w = self.init_collection_general(prefix, True, 10, partition_num, is_index=False)[0] - collection_w.create_index(ct.default_float_vec_field_name, index_params=ct.default_flat_index) - par = collection_w.partitions - par_name = par[partition_num].name - par[partition_num].load() - limit = 10 - collection_w.search(vectors, default_search_field, - default_search_params, limit, default_search_exp, - [par_name], _async=True) - collection_w.release() - error = {ct.err_code: 65535, ct.err_msg: "collection not loaded"} - collection_w.search(vectors, default_search_field, - default_search_params, limit, default_search_exp, - [par_name], - check_task=CheckTasks.err_res, - check_items=error) - - class TestLoadPartition(TestcaseBase): """ ****************************************************************** @@ -795,207 +707,6 @@ class TestLoadPartition(TestcaseBase): else: pytest.skip("Skip index Temporary") - @pytest.mark.tags(CaseLabel.L0) - @pytest.mark.parametrize('binary_index', gen_binary_index()) - @pytest.mark.parametrize('metric_type', ct.binary_metrics) - def test_load_partition_after_index_binary(self, binary_index, metric_type): - """ - target: test load binary_collection, after index created - method: insert and create index, load binary_collection with correct params - expected: no error raised - """ - self._connect() - partition_num = 1 - collection_w = self.init_collection_general(prefix, True, ct.default_nb, partition_num, - is_binary=True, is_index=False)[0] - - # for metric_type in ct.binary_metrics: - binary_index["metric_type"] = metric_type - if binary_index["index_type"] == "BIN_IVF_FLAT" and metric_type in ct.structure_metrics: - error = {ct.err_code: 65535, - ct.err_msg: f"metric type {metric_type} not found or not supported, supported: [HAMMING JACCARD]"} - collection_w.create_index(ct.default_binary_vec_field_name, binary_index, - check_task=CheckTasks.err_res, check_items=error) - collection_w.create_index(ct.default_binary_vec_field_name, ct.default_bin_flat_index) - else: - collection_w.create_index(ct.default_binary_vec_field_name, binary_index) - par = collection_w.partitions - par[partition_num].load() - - @pytest.mark.tags(CaseLabel.L2) - def test_load_partition_dis_connect(self): - """ - target: test load partition, without connection - method: load partition with correct params, with a disconnected instance - expected: load raise exception - """ - self._connect() - collection_w = self.init_collection_wrap() - partition_name = cf.gen_unique_str(prefix) - description = cf.gen_unique_str("desc_") - partition_w = self.init_partition_wrap(collection_w, partition_name, - description=description, - check_task=CheckTasks.check_partition_property, - check_items={"name": partition_name, "description": description, - "is_empty": True, "num_entities": 0} - ) - collection_w.create_index(ct.default_float_vec_field_name, index_params=ct.default_flat_index) - partition_w.load() - self.connection_wrap.remove_connection(ct.default_alias) - res_list, _ = self.connection_wrap.list_connections() - assert ct.default_alias not in res_list - error = {ct.err_code: 999, ct.err_msg: 'should create connection first.'} - partition_w.load(check_task=CheckTasks.err_res, check_items=error) - - @pytest.mark.tags(CaseLabel.L2) - def test_release_partition_dis_connect(self): - """ - target: test release collection, without connection - method: release collection with correct params, with a disconnected instance - expected: release raise exception - """ - self._connect() - collection_w = self.init_collection_wrap() - partition_name = cf.gen_unique_str(prefix) - description = cf.gen_unique_str("desc_") - partition_w = self.init_partition_wrap(collection_w, partition_name, - description=description, - check_task=CheckTasks.check_partition_property, - check_items={"name": partition_name, "description": description, - "is_empty": True, "num_entities": 0} - ) - collection_w.create_index(ct.default_float_vec_field_name, index_params=ct.default_flat_index) - partition_w.load() - self.connection_wrap.remove_connection(ct.default_alias) - res_list, _ = self.connection_wrap.list_connections() - assert ct.default_alias not in res_list - error = {ct.err_code: 1, ct.err_msg: 'should create connection first.'} - partition_w.release(check_task=CheckTasks.err_res, check_items=error) - - @pytest.mark.tags(CaseLabel.L2) - def test_load_partition_not_existed(self): - """ - target: test load partition for invalid scenario - method: load not existed partition - expected: raise exception and report the error - """ - self._connect() - collection_w = self.init_collection_wrap() - partition_name = cf.gen_unique_str(prefix) - description = cf.gen_unique_str("desc_") - partition_w = self.init_partition_wrap(collection_w, partition_name, - description=description, - check_task=CheckTasks.check_partition_property, - check_items={"name": partition_name, "description": description, - "is_empty": True, "num_entities": 0}) - collection_w.create_index(ct.default_float_vec_field_name, index_params=ct.default_flat_index) - partition_w.drop() - error = {ct.err_code: 200, ct.err_msg: 'partition not found[partition=%s]' % partition_name} - partition_w.load(check_task=CheckTasks.err_res, check_items=error) - - @pytest.mark.tags(CaseLabel.L0) - def test_release_partition_not_load(self): - """ - target: test release partition without load - method: release partition without load - expected: release success - """ - self._connect() - collection_w = self.init_collection_wrap() - partition_name = cf.gen_unique_str(prefix) - description = cf.gen_unique_str("desc_") - partition_w = self.init_partition_wrap(collection_w, partition_name, - description=description, - check_task=CheckTasks.check_partition_property, - check_items={"name": partition_name, "description": description, - "is_empty": True, "num_entities": 0}) - partition_w.release() - - @pytest.mark.tags(CaseLabel.L2) - def test_load_release_after_drop(self): - """ - target: test load and release partition after drop - method: drop partition and then load and release it - expected: raise exception - """ - self._connect() - collection_w = self.init_collection_wrap() - partition_name = cf.gen_unique_str(prefix) - description = cf.gen_unique_str("desc_") - partition_w = self.init_partition_wrap(collection_w, partition_name, - description=description, - check_task=CheckTasks.check_partition_property, - check_items={"name": partition_name, "description": description, - "is_empty": True, "num_entities": 0}) - partition_w.drop() - collection_w.create_index(ct.default_float_vec_field_name) - error = {ct.err_code: 200, ct.err_msg: 'partition not found[partition=%s]' % partition_name} - partition_w.load(check_task=CheckTasks.err_res, check_items=error) - partition_w.release(check_task=CheckTasks.err_res, check_items=error) - - @pytest.mark.tags(CaseLabel.L0) - def test_release_partition_after_drop(self): - """ - target: test release collection after drop - method: insert and flush, then release collection after load and drop - expected: raise exception - """ - self._connect() - collection_w = self.init_collection_wrap() - partition_name = cf.gen_unique_str(prefix) - description = cf.gen_unique_str("desc_") - partition_w = self.init_partition_wrap(collection_w, partition_name, - description=description, - check_task=CheckTasks.check_partition_property, - check_items={"name": partition_name, "description": description, - "is_empty": True, "num_entities": 0}) - partition_w.drop() - error = {ct.err_code: 200, ct.err_msg: 'partition not found[partition=%s]' % partition_name} - partition_w.release(check_task=CheckTasks.err_res, check_items=error) - - @pytest.mark.tags(CaseLabel.L0) - def test_load_release_after_collection_drop(self): - """ - target: test release collection after drop - method: insert and flush, then release collection after load and drop - expected: raise exception - """ - self._connect() - collection_w = self.init_collection_wrap() - name = collection_w.name - partition_name = cf.gen_unique_str(prefix) - description = cf.gen_unique_str("desc_") - partition_w = self.init_partition_wrap(collection_w, partition_name, - description=description, - check_task=CheckTasks.check_partition_property, - check_items={"name": partition_name, "description": description, - "is_empty": True, "num_entities": 0}) - collection_w.drop() - error = {ct.err_code: 0, ct.err_msg: "collection not found"} - partition_w.load(check_task=CheckTasks.err_res, check_items=error) - partition_w.release(check_task=CheckTasks.err_res, check_items=error) - - @pytest.mark.tags(CaseLabel.L2) - def test_load_collection_after_load_loaded_partition(self): - """ - target: test load partition after load partition - method: 1. load partition - 2. load the partition again - 3. query on the non-loaded partition - 4. load collection - expected: No exception - """ - collection_w = self.init_collection_general(prefix, is_index=False)[0] - collection_w.create_index(default_search_field) - partition_w1 = self.init_partition_wrap(collection_w, partition1) - partition_w2 = self.init_partition_wrap(collection_w, partition2) - partition_w1.load() - partition_w1.load() - error = {ct.err_code: 65538, ct.err_msg: 'partition not loaded'} - collection_w.query(default_term_expr, partition_names=[partition2], - check_task=CheckTasks.err_res, check_items=error) - collection_w.load() - @pytest.mark.tags(CaseLabel.L2) def test_load_collection_after_load_unloaded_partition(self): """ @@ -1015,23 +726,6 @@ class TestLoadPartition(TestcaseBase): collection_w.query(default_term_expr) collection_w.load() - @pytest.mark.tags(CaseLabel.L2) - def test_load_collection_after_load_one_partition(self): - """ - target: test load partition after load partition - method: 1. load partition - 2. load collection - 3. query on the partitions - expected: No exception - """ - collection_w = self.init_collection_general(prefix, is_index=False)[0] - collection_w.create_index(default_search_field) - partition_w1 = self.init_partition_wrap(collection_w, partition1) - partition_w2 = self.init_partition_wrap(collection_w, partition2) - partition_w1.load() - collection_w.load() - collection_w.query(default_term_expr, partition_names=[partition1, partition2]) - @pytest.mark.tags(CaseLabel.L0) def test_load_partitions_release_collection(self): """ @@ -1317,768 +1011,3 @@ class TestLoadPartition(TestcaseBase): collection_w.load() collection_w.query(default_term_expr) - @pytest.mark.tags(CaseLabel.L2) - def test_release_unloaded_partition(self): - """ - target: test load collection after load and drop partition - method: 1. load partition - 2. release the other partition - 3. query on the first partition - expected: no exception - """ - collection_w = self.init_collection_general(prefix, is_index=False)[0] - collection_w.create_index(default_search_field) - partition_w1 = self.init_partition_wrap(collection_w, partition1) - partition_w2 = self.init_partition_wrap(collection_w, partition2) - partition_w1.load() - partition_w2.release() - collection_w.query(default_term_expr, partition_names=[partition1]) - - -class TestCollectionString(TestcaseBase): - """ - ****************************************************************** - The following cases are used to test about string - ****************************************************************** - """ - - @pytest.mark.tags(CaseLabel.L1) - def test_collection_string_field_is_primary(self): - """ - target: test create collection with string field - method: 1. create collection with string field and vector field - 2. set string fields is_primary=True - expected: Create collection successfully - """ - self._connect() - c_name = cf.gen_unique_str(prefix) - schema = cf.gen_string_pk_default_collection_schema() - self.collection_wrap.init_collection(name=c_name, schema=schema, - check_task=CheckTasks.check_collection_property, - check_items={exp_name: c_name, exp_schema: schema}) - - @pytest.mark.tags(CaseLabel.L1) - def test_collection_with_muti_string_fields(self): - """ - target: test create collection with muti string fields - method: 1. create collection with primary string field and not primary string field - 2. string fields is_primary=True - expected: Create collection successfully - """ - self._connect() - c_name = cf.gen_unique_str(prefix) - int_field = cf.gen_int64_field() - vec_field = cf.gen_float_vec_field() - string_field_1 = cf.gen_string_field(is_primary=True) - string_field_2 = cf.gen_string_field(name=c_name) - schema = cf.gen_collection_schema(fields=[int_field, string_field_1, string_field_2, vec_field]) - self.collection_wrap.init_collection(name=c_name, schema=schema, - check_task=CheckTasks.check_collection_property, - check_items={exp_name: c_name, exp_schema: schema}) - - @pytest.mark.tags(CaseLabel.L1) - def test_collection_only_string_field(self): - """ - target: test create collection with one string field - method: create collection with only string field - expected: Raise exception - """ - self._connect() - string_field = cf.gen_string_field(is_primary=True) - schema = cf.gen_collection_schema([string_field]) - error = {ct.err_code: 0, ct.err_msg: "No vector field is found"} - self.collection_wrap.init_collection(name=cf.gen_unique_str(prefix), schema=schema, - check_task=CheckTasks.err_res, check_items=error) - - @pytest.mark.tags(CaseLabel.L1) - def test_collection_string_field_with_exceed_max_len(self): - """ - target: test create collection with string field - method: 1. create collection with string field - 2. String field max_length exceeds maximum - expected: Raise exception - """ - self._connect() - c_name = cf.gen_unique_str(prefix) - int_field = cf.gen_int64_field(is_primary=True) - vec_field = cf.gen_float_vec_field() - max_length = 65535 + 1 - string_field = cf.gen_string_field(max_length=max_length) - schema = cf.gen_collection_schema([int_field, string_field, vec_field]) - error = {ct.err_code: 65535, ct.err_msg: f"the maximum length specified for the field({ct.default_string_field_name}) should be in (0, 65535]"} - self.collection_wrap.init_collection(name=c_name, schema=schema, - check_task=CheckTasks.err_res, check_items=error) - - @pytest.mark.tags(CaseLabel.L1) - def test_collection_invalid_string_field_dtype(self): - """ - target: test create collection with string field - method: create collection with string field, the string field datatype is invaild - expected: Raise exception - """ - self._connect() - string_field = self.field_schema_wrap.init_field_schema(name="string", dtype=DataType.STRING)[0] - int_field = cf.gen_int64_field(is_primary=True) - vec_field = cf.gen_float_vec_field() - schema = cf.gen_collection_schema(fields=[int_field, string_field, vec_field]) - error = {ct.err_code: 0, ct.err_msg: "string data type not supported yet, please use VarChar type instead"} - self.collection_wrap.init_collection(name=cf.gen_unique_str(prefix), schema=schema, - check_task=CheckTasks.err_res, check_items=error) - - @pytest.mark.tags(CaseLabel.L1) - def test_collection_string_field_is_primary_and_auto_id(self): - """ - target: test create collection with string field - method: create collection with string field, the string field primary and auto id are true - expected: Create collection successfully - """ - self._connect() - int_field = cf.gen_int64_field() - vec_field = cf.gen_float_vec_field() - string_field = cf.gen_string_field(is_primary=True, auto_id=True) - fields = [int_field, string_field, vec_field] - schema = self.collection_schema_wrap.init_collection_schema(fields=fields)[0] - self.init_collection_wrap(schema=schema, check_task=CheckTasks.check_collection_property, - check_items={"schema": schema, "primary": ct.default_string_field_name}) - - -class TestCollectionJSON(TestcaseBase): - """ - ****************************************************************** - The following cases are used to test about json - ****************************************************************** - """ - @pytest.mark.tags(CaseLabel.L1) - @pytest.mark.parametrize("auto_id", [True, False]) - def test_collection_json_field_as_primary_key(self, auto_id): - """ - target: test create collection with JSON field as primary key - method: 1. create collection with one JSON field, and vector field - 2. set json field is_primary=true - 3. set auto_id as true - expected: Raise exception (not supported) - """ - self._connect() - int_field = cf.gen_int64_field() - vec_field = cf.gen_float_vec_field() - string_field = cf.gen_string_field() - # 1. create json field as primary key through field schema api - error = {ct.err_code: 1, ct.err_msg: "Primary key type must be DataType.INT64 or DataType.VARCHAR"} - json_field = cf.gen_json_field(is_primary=True, auto_id=auto_id) - fields = [int_field, string_field, json_field, vec_field] - self.collection_schema_wrap.init_collection_schema(fields=fields, - check_task=CheckTasks.err_res, check_items=error) - # 2. create json field as primary key through collection schema api - json_field = cf.gen_json_field() - fields = [int_field, string_field, json_field, vec_field] - self.collection_schema_wrap.init_collection_schema(fields=fields, primary_field=ct.default_json_field_name, - check_task=CheckTasks.err_res, check_items=error) - - @pytest.mark.tags(CaseLabel.L2) - @pytest.mark.parametrize("primary_field", [ct.default_float_field_name, ct.default_json_field_name]) - def test_collection_json_field_partition_key(self, primary_field): - """ - target: test create collection with multiple JSON fields - method: 1. create collection with multiple JSON fields, primary key field and vector field - 2. set json field is_primary=false - expected: Raise exception - """ - self._connect() - cf.gen_unique_str(prefix) - error = {ct.err_code: 1, ct.err_msg: "Primary key type must be DataType.INT64 or DataType.VARCHAR"} - cf.gen_json_default_collection_schema(primary_field=primary_field, is_partition_key=True, - check_task=CheckTasks.err_res, check_items=error) - - @pytest.mark.tags(CaseLabel.L0) - @pytest.mark.parametrize("primary_field", [ct.default_int64_field_name, ct.default_string_field_name]) - def test_collection_json_field_supported_primary_key(self, primary_field): - """ - target: test create collection with one JSON field - method: 1. create collection with one JSON field, primary key field and vector field - 2. set json field is_primary=false - expected: Create collection successfully - """ - self._connect() - c_name = cf.gen_unique_str(prefix) - schema = cf.gen_json_default_collection_schema(primary_field=primary_field) - self.collection_wrap.init_collection(name=c_name, schema=schema, - check_task=CheckTasks.check_collection_property, - check_items={exp_name: c_name, exp_schema: schema}) - - @pytest.mark.tags(CaseLabel.L2) - @pytest.mark.parametrize("primary_field", [ct.default_int64_field_name, ct.default_string_field_name]) - def test_collection_multiple_json_fields_supported_primary_key(self, primary_field): - """ - target: test create collection with multiple JSON fields - method: 1. create collection with multiple JSON fields, primary key field and vector field - 2. set json field is_primary=false - expected: Create collection successfully - """ - self._connect() - c_name = cf.gen_unique_str(prefix) - schema = cf.gen_multiple_json_default_collection_schema(primary_field=primary_field) - self.collection_wrap.init_collection(name=c_name, schema=schema, - check_task=CheckTasks.check_collection_property, - check_items={exp_name: c_name, exp_schema: schema}) - - -class TestCollectionARRAY(TestcaseBase): - """ - ****************************************************************** - The following cases are used to test about array - ****************************************************************** - """ - - @pytest.mark.tags(CaseLabel.L2) - def test_collection_array_field_element_type_not_exist(self): - """ - target: test create collection with ARRAY field without element type - method: create collection with one array field without element type - expected: Raise exception - """ - int_field = cf.gen_int64_field(is_primary=True) - vec_field = cf.gen_float_vec_field() - array_field = cf.gen_array_field(element_type=None) - array_schema = cf.gen_collection_schema([int_field, vec_field, array_field]) - self.init_collection_wrap(schema=array_schema, check_task=CheckTasks.err_res, - check_items={ct.err_code: 65535, ct.err_msg: "element data type None is not valid"}) - - @pytest.mark.tags(CaseLabel.L2) - @pytest.mark.parametrize("element_type", [1001, 'a', [], (), {1}, DataType.BINARY_VECTOR, - DataType.FLOAT_VECTOR, DataType.JSON, DataType.ARRAY]) - def test_collection_array_field_element_type_invalid(self, element_type): - """ - target: Create a field with invalid element_type - method: Create a field with invalid element_type - 1. Type not in DataType: 1, 'a', ... - 2. Type in DataType: binary_vector, float_vector, json_field, array_field - expected: Raise exception - """ - int_field = cf.gen_int64_field(is_primary=True) - vec_field = cf.gen_float_vec_field() - array_field = cf.gen_array_field(element_type=element_type) - array_schema = cf.gen_collection_schema([int_field, vec_field, array_field]) - error = {ct.err_code: 999, ct.err_msg: f"element type {element_type} is not supported"} - if element_type in ['a', {1}]: - error = {ct.err_code: 999, ct.err_msg: "Unexpected error"} - if element_type in [[], ()]: - error = {ct.err_code: 65535, ct.err_msg: "element data type None is not valid"} - if element_type in [DataType.BINARY_VECTOR, DataType.FLOAT_VECTOR, DataType.JSON, DataType.ARRAY]: - data_type = element_type.name - if element_type == DataType.BINARY_VECTOR: - data_type = "BinaryVector" - if element_type == DataType.FLOAT_VECTOR: - data_type = "FloatVector" - if element_type == DataType.ARRAY: - data_type = "Array" - error = {ct.err_code: 999, ct.err_msg: f"element type {data_type} is not supported"} - self.init_collection_wrap(schema=array_schema, check_task=CheckTasks.err_res, check_items=error) - - @pytest.mark.tags(CaseLabel.L2) - @pytest.mark.skip("https://github.com/milvus-io/pymilvus/issues/2041") - def test_collection_array_field_no_capacity(self): - """ - target: Create a field without giving max_capacity - method: Create a field without giving max_capacity - expected: Raise exception - """ - int_field = cf.gen_int64_field(is_primary=True) - vec_field = cf.gen_float_vec_field() - array_field = cf.gen_array_field(max_capacity=None) - array_schema = cf.gen_collection_schema([int_field, vec_field, array_field]) - self.init_collection_wrap(schema=array_schema, check_task=CheckTasks.err_res, - check_items={ct.err_code: 65535, - ct.err_msg: "the value of max_capacity must be an integer"}) - - @pytest.mark.tags(CaseLabel.L2) - @pytest.mark.skip("https://github.com/milvus-io/pymilvus/issues/2041") - @pytest.mark.parametrize("max_capacity", [[], 'a', (), -1, 4097]) - def test_collection_array_field_invalid_capacity(self, max_capacity): - """ - target: Create a field with invalid max_capacity - method: Create a field with invalid max_capacity - 1. Type invalid: [], 'a', () - 2. Value invalid: <0, >max_capacity(4096) - expected: Raise exception - """ - int_field = cf.gen_int64_field(is_primary=True) - vec_field = cf.gen_float_vec_field() - array_field = cf.gen_array_field(max_capacity=max_capacity) - array_schema = cf.gen_collection_schema([int_field, vec_field, array_field]) - self.init_collection_wrap(schema=array_schema, check_task=CheckTasks.err_res, - check_items={ct.err_code: 65535, - ct.err_msg: "the maximum capacity specified for a " - "Array should be in (0, 4096]"}) - - @pytest.mark.tags(CaseLabel.L2) - def test_collection_string_array_without_max_length(self): - """ - target: Create string array without giving max length - method: Create string array without giving max length - expected: Raise exception - """ - int_field = cf.gen_int64_field(is_primary=True) - vec_field = cf.gen_float_vec_field() - array_field = cf.gen_array_field(element_type=DataType.VARCHAR) - array_schema = cf.gen_collection_schema([int_field, vec_field, array_field]) - self.init_collection_wrap(schema=array_schema, check_task=CheckTasks.err_res, - check_items={ct.err_code: 65535, - ct.err_msg: "type param(max_length) should be specified for the " - "field(int_array)"}) - - @pytest.mark.tags(CaseLabel.L2) - @pytest.mark.skip("https://github.com/milvus-io/pymilvus/issues/2041") - @pytest.mark.parametrize("max_length", [[], 'a', (), -1, 65536]) - def test_collection_string_array_max_length_invalid(self, max_length): - """ - target: Create string array with invalid max length - method: Create string array with invalid max length - 1. Type invalid: [], 'a', () - 2. Value invalid: <0, >max_length(65535) - expected: Raise exception - """ - int_field = cf.gen_int64_field(is_primary=True) - vec_field = cf.gen_float_vec_field() - array_field = cf.gen_array_field(element_type=DataType.VARCHAR, max_length=max_length) - array_schema = cf.gen_collection_schema([int_field, vec_field, array_field]) - self.init_collection_wrap(schema=array_schema, check_task=CheckTasks.err_res, - check_items={ct.err_code: 65535, - ct.err_msg: "the maximum length specified for a VarChar " - "should be in (0, 65535]"}) - - @pytest.mark.tags(CaseLabel.L2) - def test_collection_array_field_all_datatype(self): - """ - target: test create collection with ARRAY field all data type - method: 1. Create field respectively: int8, int16, int32, int64, varchar, bool, float, double - 2. Insert data respectively: int8, int16, int32, int64, varchar, bool, float, double - expected: Raise exception - """ - # Create field respectively - nb = ct.default_nb - pk_field = cf.gen_int64_field(is_primary=True) - vec_field = cf.gen_float_vec_field() - int8_array = cf.gen_array_field(name="int8_array", element_type=DataType.INT8, max_capacity=nb) - int16_array = cf.gen_array_field(name="int16_array", element_type=DataType.INT16, max_capacity=nb) - int32_array = cf.gen_array_field(name="int32_array", element_type=DataType.INT32, max_capacity=nb) - int64_array = cf.gen_array_field(name="int64_array", element_type=DataType.INT64, max_capacity=nb) - bool_array = cf.gen_array_field(name="bool_array", element_type=DataType.BOOL, max_capacity=nb) - float_array = cf.gen_array_field(name="float_array", element_type=DataType.FLOAT, max_capacity=nb) - double_array = cf.gen_array_field(name="double_array", element_type=DataType.DOUBLE, max_capacity=nb) - string_array = cf.gen_array_field(name="string_array", element_type=DataType.VARCHAR, max_capacity=nb, - max_length=100) - array_schema = cf.gen_collection_schema([pk_field, vec_field, int8_array, int16_array, int32_array, - int64_array, bool_array, float_array, double_array, string_array]) - collection_w = self.init_collection_wrap(schema=array_schema, - check_task=CheckTasks.check_collection_property, - check_items={exp_schema: array_schema}) - - # check array in collection.describe() - res = collection_w.describe()[0] - log.info(res) - fields = [ - {"field_id": 100, "name": "int64", "description": "", "type": 5, "params": {}, - "element_type": 0, "is_primary": True}, - {"field_id": 101, "name": "float_vector", "description": "", "type": 101, - "params": {"dim": ct.default_dim}, "element_type": 0}, - {"field_id": 102, "name": "int8_array", "description": "", "type": 22, - "params": {"max_capacity": 2000}, "element_type": 2}, - {"field_id": 103, "name": "int16_array", "description": "", "type": 22, - "params": {"max_capacity": 2000}, "element_type": 3}, - {"field_id": 104, "name": "int32_array", "description": "", "type": 22, - "params": {"max_capacity": 2000}, "element_type": 4}, - {"field_id": 105, "name": "int64_array", "description": "", "type": 22, - "params": {"max_capacity": 2000}, "element_type": 5}, - {"field_id": 106, "name": "bool_array", "description": "", "type": 22, - "params": {"max_capacity": 2000}, "element_type": 1}, - {"field_id": 107, "name": "float_array", "description": "", "type": 22, - "params": {"max_capacity": 2000}, "element_type": 10}, - {"field_id": 108, "name": "double_array", "description": "", "type": 22, - "params": {"max_capacity": 2000}, "element_type": 11}, - {"field_id": 109, "name": "string_array", "description": "", "type": 22, - "params": {"max_length": 100, "max_capacity": 2000}, "element_type": 21} - ] - # assert res["fields"] == fields - - # Insert data respectively - nb = 10 - pk_values = [i for i in range(nb)] - float_vec = cf.gen_vectors(nb, ct.default_dim) - int8_values = [[numpy.int8(j) for j in range(nb)] for i in range(nb)] - int16_values = [[numpy.int16(j) for j in range(nb)] for i in range(nb)] - int32_values = [[numpy.int32(j) for j in range(nb)] for i in range(nb)] - int64_values = [[numpy.int64(j) for j in range(nb)] for i in range(nb)] - bool_values = [[numpy.bool_(j) for j in range(nb)] for i in range(nb)] - float_values = [[numpy.float32(j) for j in range(nb)] for i in range(nb)] - double_values = [[numpy.double(j) for j in range(nb)] for i in range(nb)] - string_values = [[str(j) for j in range(nb)] for i in range(nb)] - data = [pk_values, float_vec, int8_values, int16_values, int32_values, int64_values, - bool_values, float_values, double_values, string_values] - collection_w.insert(data) - - # check insert successfully - collection_w.flush() - collection_w.num_entities == nb - - -class TestCollectionMultipleVectorValid(TestcaseBase): - """ - ****************************************************************** - # The followings are valid cases - ****************************************************************** - """ - - @pytest.mark.tags(CaseLabel.L1) - @pytest.mark.parametrize("primary_key", [cf.gen_int64_field(is_primary=True), cf.gen_string_field(is_primary=True)]) - @pytest.mark.parametrize("auto_id", [True, False]) - @pytest.mark.parametrize("shards_num", [1, 3]) - def test_create_collection_multiple_vectors_all_supported_field_type(self, primary_key, auto_id, shards_num): - """ - target: test create collection with multiple vector fields - method: create collection with multiple vector fields - expected: no exception - """ - self._connect() - c_name = cf.gen_unique_str(prefix) - int_fields = [] - vector_limit_num = max_vector_field_num - 2 - # add multiple vector fields - for i in range(vector_limit_num): - vector_field_name = cf.gen_unique_str("field_name") - field = cf.gen_float_vec_field(name=vector_field_name) - int_fields.append(field) - # add other vector fields to maximum fields num - int_fields.append(cf.gen_int8_field()) - int_fields.append(cf.gen_int16_field()) - int_fields.append(cf.gen_int32_field()) - int_fields.append(cf.gen_float_field()) - int_fields.append(cf.gen_double_field()) - int_fields.append(cf.gen_string_field(cf.gen_unique_str("vchar_field_name"))) - int_fields.append(cf.gen_json_field()) - int_fields.append(cf.gen_bool_field()) - int_fields.append(cf.gen_array_field()) - int_fields.append(cf.gen_binary_vec_field()) - int_fields.append(primary_key) - schema = cf.gen_collection_schema(fields=int_fields, auto_id=auto_id, shards_num=shards_num) - self.collection_wrap.init_collection(c_name, schema=schema, check_task=CheckTasks.check_collection_property, - check_items={exp_name: c_name, exp_schema: schema}) - self.collection_wrap.init_collection(c_name, schema=schema, check_task=CheckTasks.check_collection_property, - check_items={exp_name: c_name, exp_schema: schema}) - - @pytest.mark.tags(CaseLabel.L2) - @pytest.mark.parametrize("primary_key", [ct.default_int64_field_name, ct.default_string_field_name]) - @pytest.mark.parametrize("auto_id", [True, False]) - @pytest.mark.parametrize("enable_dynamic_field", [True, False]) - def test_create_collection_multiple_vectors_different_dim(self, primary_key, auto_id, enable_dynamic_field): - """ - target: test create collection with multiple vector fields (different dim) - method: create collection with multiple vector fields - expected: no exception - """ - self._connect() - c_name = cf.gen_unique_str(prefix) - another_dim = ct.min_dim - schema = cf.gen_default_collection_schema(primary_field=primary_key, auto_id=auto_id, dim=ct.max_dim, - enable_dynamic_field=enable_dynamic_field, - multiple_dim_array=[another_dim]) - self.collection_wrap.init_collection(c_name, schema=schema, check_task=CheckTasks.check_collection_property, - check_items={exp_name: c_name, exp_schema: schema}) - - @pytest.mark.tags(CaseLabel.L2) - @pytest.mark.parametrize("primary_key", [ct.default_int64_field_name, ct.default_string_field_name]) - def test_create_collection_multiple_vectors_maximum_dim(self, primary_key): - """ - target: test create collection with multiple vector fields - method: create collection with multiple vector fields - expected: no exception - """ - self._connect() - c_name = cf.gen_unique_str(prefix) - schema = cf.gen_default_collection_schema(primary_field=primary_key, dim=ct.max_dim, - multiple_dim_array=[ct.max_dim]) - self.collection_wrap.init_collection(c_name, schema=schema, check_task=CheckTasks.check_collection_property, - check_items={exp_name: c_name, exp_schema: schema}) - - @pytest.mark.tags(CaseLabel.L2) - @pytest.mark.parametrize("primary_key", [cf.gen_int64_field(is_primary=True), cf.gen_string_field(is_primary=True)]) - @pytest.mark.parametrize("auto_id", [True, False]) - @pytest.mark.parametrize("par_key_field", [ct.default_int64_field_name, ct.default_string_field_name]) - def test_create_collection_multiple_vectors_partition_key(self, primary_key, auto_id, par_key_field): - """ - target: test create collection with multiple vector fields - method: create collection with multiple vector fields - expected: no exception - """ - self._connect() - c_name = cf.gen_unique_str(prefix) - int_fields = [] - vector_limit_num = max_vector_field_num - 2 - # add multiple vector fields - for i in range(vector_limit_num): - vector_field_name = cf.gen_unique_str("field_name") - field = cf.gen_float_vec_field(name=vector_field_name) - int_fields.append(field) - # add other vector fields to maximum fields num - int_fields.append(cf.gen_int8_field()) - int_fields.append(cf.gen_int16_field()) - int_fields.append(cf.gen_int32_field()) - int_fields.append(cf.gen_int64_field(cf.gen_unique_str("int_field_name"), - is_partition_key=(par_key_field == ct.default_int64_field_name))) - int_fields.append(cf.gen_float_field()) - int_fields.append(cf.gen_double_field()) - int_fields.append(cf.gen_string_field(cf.gen_unique_str("vchar_field_name"), - is_partition_key=(par_key_field == ct.default_string_field_name))) - int_fields.append(cf.gen_json_field()) - int_fields.append(cf.gen_bool_field()) - int_fields.append(cf.gen_array_field()) - int_fields.append(cf.gen_binary_vec_field()) - int_fields.append(primary_key) - schema = cf.gen_collection_schema(fields=int_fields, auto_id=auto_id) - collection_w = \ - self.collection_wrap.init_collection(c_name, schema=schema, check_task=CheckTasks.check_collection_property, - check_items={exp_name: c_name, exp_schema: schema})[0] - assert len(collection_w.partitions) == ct.default_partition_num - - -class TestCollectionMultipleVectorInvalid(TestcaseBase): - """ Test case of search interface """ - - @pytest.fixture(scope="function", params=ct.invalid_dims) - def get_invalid_dim(self, request): - yield request.param - - """ - ****************************************************************** - # The followings are invalid cases - ****************************************************************** - """ - - @pytest.mark.tags(CaseLabel.L1) - @pytest.mark.parametrize("primary_key", [cf.gen_int64_field(is_primary=True), cf.gen_string_field(is_primary=True)]) - def test_create_collection_multiple_vectors_same_vector_field_name(self, primary_key): - """ - target: test create collection with multiple vector fields - method: create collection with multiple vector fields - expected: no exception - """ - self._connect() - c_name = cf.gen_unique_str(prefix) - int_fields = [] - vector_limit_num = max_vector_field_num - 2 - # add multiple vector fields - for i in range(vector_limit_num): - field = cf.gen_float_vec_field() - int_fields.append(field) - # add other vector fields to maximum fields num - int_fields.append(cf.gen_int8_field()) - int_fields.append(cf.gen_int16_field()) - int_fields.append(cf.gen_int32_field()) - int_fields.append(cf.gen_float_field()) - int_fields.append(cf.gen_double_field()) - int_fields.append(cf.gen_string_field(cf.gen_unique_str("vchar_field_name"))) - int_fields.append(cf.gen_json_field()) - int_fields.append(cf.gen_bool_field()) - int_fields.append(cf.gen_array_field()) - int_fields.append(cf.gen_binary_vec_field()) - int_fields.append(primary_key) - schema = cf.gen_collection_schema(fields=int_fields) - error = {ct.err_code: 65535, ct.err_msg: "duplicated field name"} - self.collection_wrap.init_collection(c_name, schema=schema, check_task=CheckTasks.err_res, check_items=error) - - @pytest.mark.tags(CaseLabel.L1) - @pytest.mark.parametrize("invalid_vector_name", ["12-s", "12 s", "(mn)", "中文", "%$#", "a".join("a" for i in range(256))]) - def test_create_collection_multiple_vectors_invalid_part_vector_field_name(self, invalid_vector_name): - """ - target: test create collection with multiple vector fields - method: create collection with multiple vector fields - expected: no exception - """ - self._connect() - c_name = cf.gen_unique_str(prefix) - int_fields = [] - # add multiple vector fields - vector_field_1 = cf.gen_float_vec_field(name=invalid_vector_name) - int_fields.append(vector_field_1) - vector_field_2 = cf.gen_float_vec_field(name="valid_field_name") - int_fields.append(vector_field_2) - # add other vector fields to maximum fields num - int_fields.append(cf.gen_int64_field(is_primary=True)) - schema = cf.gen_collection_schema(fields=int_fields) - error = {ct.err_code: 1701, ct.err_msg: "Invalid field name: %s" % invalid_vector_name} - self.collection_wrap.init_collection(c_name, schema=schema, check_task=CheckTasks.err_res, check_items=error) - - @pytest.mark.tags(CaseLabel.L1) - @pytest.mark.parametrize("invalid_vector_name", ["12-s", "12 s", "(mn)", "中文", "%$#", "a".join("a" for i in range(256))]) - def test_create_collection_multiple_vectors_invalid_all_vector_field_name(self, invalid_vector_name): - """ - target: test create collection with multiple vector fields - method: create collection with multiple vector fields - expected: no exception - """ - self._connect() - c_name = cf.gen_unique_str(prefix) - int_fields = [] - # add multiple vector fields - vector_field_1 = cf.gen_float_vec_field(name=invalid_vector_name) - int_fields.append(vector_field_1) - vector_field_2 = cf.gen_float_vec_field(name=invalid_vector_name + " ") - int_fields.append(vector_field_2) - # add other vector fields to maximum fields num - int_fields.append(cf.gen_int64_field(is_primary=True)) - schema = cf.gen_collection_schema(fields=int_fields) - error = {ct.err_code: 1701, ct.err_msg: "Invalid field name: %s" % invalid_vector_name} - self.collection_wrap.init_collection(c_name, schema=schema, check_task=CheckTasks.err_res, check_items=error) - - @pytest.mark.tags(CaseLabel.L1) - @pytest.mark.skip("issue #37543") - def test_create_collection_multiple_vectors_invalid_dim(self, get_invalid_dim): - """ - target: test create collection with multiple vector fields - method: create collection with multiple vector fields - expected: no exception - """ - self._connect() - c_name = cf.gen_unique_str(prefix) - int_fields = [] - # add multiple vector fields - vector_field_1 = cf.gen_float_vec_field(dim=get_invalid_dim) - int_fields.append(vector_field_1) - vector_field_2 = cf.gen_float_vec_field(name="float_vec_field") - int_fields.append(vector_field_2) - # add other vector fields to maximum fields num - int_fields.append(cf.gen_int64_field(is_primary=True)) - schema = cf.gen_collection_schema(fields=int_fields) - error = {ct.err_code: 65535, ct.err_msg: "invalid dimension"} - self.collection_wrap.init_collection(c_name, schema=schema, check_task=CheckTasks.err_res, check_items=error) - - -class TestCollectionMmap(TestcaseBase): - @pytest.mark.tags(CaseLabel.L1) - def test_describe_collection_mmap(self): - """ - target: enable or disable mmap in the collection - method: enable or disable mmap in the collection - expected: description information contains mmap - """ - self._connect() - c_name = cf.gen_unique_str(prefix) - collection_w, _ = self.collection_wrap.init_collection(c_name, schema=default_schema) - collection_w.set_properties({'mmap.enabled': True}) - pro = collection_w.describe().get("properties") - assert "mmap.enabled" in pro.keys() - assert pro["mmap.enabled"] == 'True' - collection_w.set_properties({'mmap.enabled': False}) - pro = collection_w.describe().get("properties") - assert pro["mmap.enabled"] == 'False' - collection_w.set_properties({'mmap.enabled': True}) - pro = collection_w.describe().get("properties") - assert pro["mmap.enabled"] == 'True' - - @pytest.mark.tags(CaseLabel.L1) - def test_load_mmap_collection(self): - """ - target: after loading, enable mmap for the collection - method: 1. data preparation and create index - 2. load collection - 3. enable mmap on collection - expected: raise exception - """ - c_name = cf.gen_unique_str(prefix) - collection_w = self.init_collection_wrap(c_name, schema=default_schema) - collection_w.insert(cf.gen_default_list_data()) - collection_w.create_index(ct.default_float_vec_field_name, index_params=ct.default_flat_index, - index_name=ct.default_index_name) - collection_w.set_properties({'mmap.enabled': True}) - pro = collection_w.describe()[0].get("properties") - assert pro["mmap.enabled"] == 'True' - collection_w.load() - collection_w.set_properties({'mmap.enabled': True}, - check_task=CheckTasks.err_res, - check_items={ct.err_code: 104, - ct.err_msg: f"collection already loaded"}) - - @pytest.mark.tags(CaseLabel.L2) - def test_drop_mmap_collection(self): - """ - target: set mmap on collection - method: 1. set mmap on collection - 2. drop collection - 3. describe collection - expected: description information contains mmap - """ - self._connect() - c_name = "coll_rand" - collection_w, _ = self.collection_wrap.init_collection(c_name, schema=default_schema) - collection_w.set_properties({'mmap.enabled': True}) - collection_w.drop() - collection_w, _ = self.collection_wrap.init_collection(c_name, schema=default_schema) - pro = collection_w.describe().get("properties") - assert "mmap.enabled" not in pro.keys() - - @pytest.mark.tags(CaseLabel.L2) - def test_multiple_collections_enable_mmap(self): - """ - target: enabling mmap for multiple collections in a single instance - method: enabling mmap for multiple collections in a single instance - expected: the collection description message for mmap is normal - """ - self._connect() - c_name = "coll_1" - c_name2 = "coll_2" - c_name3 = "coll_3" - collection_w, _ = self.collection_wrap.init_collection(c_name, schema=default_schema) - collection_w2, _ = self.collection_wrap.init_collection(c_name2, schema=default_schema) - collection_w3, _ = self.collection_wrap.init_collection(c_name3, schema=default_schema) - collection_w.set_properties({'mmap.enabled': True}) - collection_w2.set_properties({'mmap.enabled': True}) - pro = collection_w.describe().get("properties") - pro2 = collection_w2.describe().get("properties") - assert pro["mmap.enabled"] == 'True' - assert pro2["mmap.enabled"] == 'True' - collection_w3.set_properties({'mmap.enabled': True}) - pro3 = collection_w3.describe().get("properties") - assert pro3["mmap.enabled"] == 'True' - - @pytest.mark.tags(CaseLabel.L2) - def test_flush_collection_mmap(self): - """ - target: after flush, collection enables mmap - method: after flush, collection enables mmap - expected: the collection description message for mmap is normal - """ - self._connect() - c_name = cf.gen_unique_str(prefix) - collection_w, _ = self.collection_wrap.init_collection(c_name, schema=default_schema) - collection_w.insert(cf.gen_default_list_data()) - collection_w.create_index(ct.default_float_vec_field_name, index_params=ct.default_flat_index, - index_name=ct.default_index_name) - collection_w.alter_index(ct.default_index_name, {'mmap.enabled': False}) - collection_w.flush() - collection_w.set_properties({'mmap.enabled': True}) - pro = collection_w.describe().get("properties") - assert pro["mmap.enabled"] == 'True' - collection_w.alter_index(ct.default_index_name, {'mmap.enabled': True}) - collection_w.load() - vectors = [[random.random() for _ in range(default_dim)] for _ in range(default_nq)] - collection_w.search(vectors[:default_nq], default_search_field, - default_search_params, default_limit, - default_search_exp, - check_task=CheckTasks.check_search_results, - check_items={"nq": default_nq, - "limit": default_limit}) - - @pytest.mark.tags(CaseLabel.L2) - def test_enable_mmap_after_drop_collection(self): - """ - target: enable mmap after deleting a collection - method: enable mmap after deleting a collection - expected: raise exception - """ - collection_w = self.init_collection_general(prefix, True, is_binary=True, is_index=False)[0] - collection_w.drop() - collection_w.set_properties({'mmap.enabled': True}, check_task=CheckTasks.err_res, - check_items={ct.err_code: 100, - ct.err_msg: f"collection not found"}) - - - \ No newline at end of file