diff --git a/build/ci/jenkins/NightlyCI.groovy b/build/ci/jenkins/NightlyCI.groovy index 51f30e943f..b78014303f 100644 --- a/build/ci/jenkins/NightlyCI.groovy +++ b/build/ci/jenkins/NightlyCI.groovy @@ -5,7 +5,7 @@ String cron_timezone = "TZ=Asia/Shanghai" String cron_string = BRANCH_NAME == "master" ? "50 22 * * * " : "" -int total_timeout_minutes = 90 +int total_timeout_minutes = 120 int e2e_timeout_seconds = 60 * 60 pipeline { diff --git a/tests/python_test/entity/test_insert.py b/tests/python_test/entity/test_insert.py index 780e53bdf1..7c354da648 100644 --- a/tests/python_test/entity/test_insert.py +++ b/tests/python_test/entity/test_insert.py @@ -610,6 +610,7 @@ class TestInsertBinary: assert stats[row_count] == default_nb @pytest.mark.tags(CaseLabel.L2) + @pytest.mark.skip(reason="issue 7027") def test_insert_binary_multi_times(self, connect, binary_collection): ''' target: test insert entities multi times and final flush diff --git a/tests/python_test/pytest.ini b/tests/python_test/pytest.ini index fc59b1e9f3..537ace219f 100644 --- a/tests/python_test/pytest.ini +++ b/tests/python_test/pytest.ini @@ -4,7 +4,7 @@ log_date_format = %Y-%m-%d %H:%M:%S log_cli = true log_level = 20 -addopts = -x +#addopts = -x timeout = 360 diff --git a/tests/scripts/e2e.sh b/tests/scripts/e2e.sh index 372ee6efd1..92b9d62262 100755 --- a/tests/scripts/e2e.sh +++ b/tests/scripts/e2e.sh @@ -72,11 +72,12 @@ pushd "${ROOT}/tests/docker" else if [[ "${MILVUS_CLIENT}" == "pymilvus" ]]; then export MILVUS_PYTEST_WORKSPACE="/milvus/tests/python_test" - docker-compose run --rm pytest /bin/bash -c "pytest -n ${PARALLEL_NUM} --ip ${MILVUS_SERVICE_IP} --port ${MILVUS_SERVICE_PORT} ${@:-}" + docker-compose run --rm pytest /bin/bash -c "pytest -n ${PARALLEL_NUM} --ip ${MILVUS_SERVICE_IP} \ + --port ${MILVUS_SERVICE_PORT} ${@:-} -x" elif [[ "${MILVUS_CLIENT}" == "pymilvus-orm" ]]; then export MILVUS_PYTEST_WORKSPACE="/milvus/tests20/python_client" docker-compose run --rm pytest /bin/bash -c "pytest -n ${PARALLEL_NUM} --host ${MILVUS_SERVICE_IP} --port ${MILVUS_SERVICE_PORT} \ - --html=\${CI_LOG_PATH}/report.html --self-contained-html ${@:-}" + --html=\${CI_LOG_PATH}/report.html --self-contained-html ${@:-} -x" fi fi popd diff --git a/tests20/python_client/base/utility_wrapper.py b/tests20/python_client/base/utility_wrapper.py index 63ad8ffc9c..33a58f3cc1 100644 --- a/tests20/python_client/base/utility_wrapper.py +++ b/tests20/python_client/base/utility_wrapper.py @@ -81,3 +81,14 @@ class ApiUtilityWrapper: check_result = ResponseChecker(res, func_name, check_task, check_items, is_succ, timeout=timeout, using=using).run() return res, check_result + + def calc_distance(self, vectors_left, vectors_right, params=None, timeout=None, + using="default", check_task=None, check_items=None): + timeout = TIMEOUT if timeout is None else timeout + + func_name = sys._getframe().f_code.co_name + res, is_succ = api_request([self.ut.calc_distance, vectors_left, vectors_right, + params, timeout, using]) + check_result = ResponseChecker(res, func_name, check_task, check_items, is_succ, + timeout=timeout, using=using).run() + return res, check_result diff --git a/tests20/python_client/check/func_check.py b/tests20/python_client/check/func_check.py index 8661ab9dee..b1a57374c2 100644 --- a/tests20/python_client/check/func_check.py +++ b/tests20/python_client/check/func_check.py @@ -1,5 +1,6 @@ from utils.util_log import test_log as log from common import common_type as ct +from common import common_func as cf from common.common_type import CheckTasks, Connect_Object_Name # from common.code_mapping import ErrorCode, ErrorMessage from pymilvus_orm import Collection, Partition @@ -53,6 +54,10 @@ class ResponseChecker: # Query interface of collection and partition that response check result = self.check_query_results(self.response, self.func_name, self.check_items) + elif self.check_task == CheckTasks.check_distance: + # Calculate distance interface that response check + result = self.check_distance(self.response, self.func_name, self.check_items) + # Add check_items here if something new need verify return result @@ -212,3 +217,20 @@ class ResponseChecker: # for i in range(len(exp_res)): # assert_entity_equal(exp=exp_res[i], actual=query_res[i]) + @staticmethod + def check_distance(distance_res, func_name, check_items): + if func_name != 'calc_distance': + log.warning("The function name is {} rather than {}".format(func_name, "calc_distance")) + if not isinstance(distance_res, list): + raise Exception("The distance result to check isn't list type object") + if len(check_items) == 0: + raise Exception("No expect values found in the check task") + vectors_l = check_items["vectors_l"] + vectors_r = check_items["vectors_r"] + metric = check_items.get("metric", "L2") + sqrt = check_items.get("sqrt", False) + cf.compare_distance_2d_vector(vectors_l, vectors_r, + distance_res, + metric, sqrt) + + return True diff --git a/tests20/python_client/common/common_func.py b/tests20/python_client/common/common_func.py index 45483feb79..ba49de8bdc 100644 --- a/tests20/python_client/common/common_func.py +++ b/tests20/python_client/common/common_func.py @@ -1,5 +1,6 @@ import os import random +import math import string import numpy as np import pandas as pd @@ -13,7 +14,7 @@ import threading import traceback """" Methods of processing data """ -l2 = lambda x, y: np.linalg.norm(np.array(x) - np.array(y)) +#l2 = lambda x, y: np.linalg.norm(np.array(x) - np.array(y)) def gen_unique_str(str_value=None): @@ -313,7 +314,7 @@ def gen_search_param(index_type, metric_type="L2"): search_params.append(nsg_search_param) elif index_type == "ANNOY": for search_k in [1000, 5000]: - annoy_search_param = {"metric_type": metric_type, "params": {"search_length": search_k}} + annoy_search_param = {"metric_type": metric_type, "params": {"search_k": search_k}} search_params.append(annoy_search_param) else: log.error("Invalid index_type.") @@ -353,6 +354,14 @@ def gen_normal_expressions_field(field): return expressions +def l2(x, y): + return np.linalg.norm(np.array(x) - np.array(y)) + + +def ip(x, y): + return np.inner(np.array(x), np.array(y)) + + def jaccard(x, y): x = np.asarray(x, np.bool) y = np.asarray(y, np.bool) @@ -382,6 +391,27 @@ def superstructure(x, y): y = np.asarray(y, np.bool) return 1 - np.double(np.bitwise_and(x, y).sum()) / np.count_nonzero(x) +def compare_distance_2d_vector(x, y, distance, metric, sqrt): + for i in range(len(x)): + for j in range(len(y)): + if metric == "L2": + distance_i = l2(x[i], y[j]) + if not sqrt: + distance_i = math.pow(distance_i, 2) + elif metric == "IP": + distance_i = ip(x[i], y[j]) + elif metric == "HAMMING": + distance_i = hamming(x[i], y[j]) + elif metric == "TANIMOTO": + distance_i = tanimoto(x[i], y[j]) + elif metric == "JACCARD": + distance_i = jaccard(x[i], y[j]) + else: + raise Exception("metric type is invalid") + assert abs(distance_i - distance[i][j]) < ct.epsilon + + return True + def modify_file(file_path_list, is_modify=False, input_content=""): """ diff --git a/tests20/python_client/common/common_type.py b/tests20/python_client/common/common_type.py index 49cd5dbadf..2aad841e64 100644 --- a/tests20/python_client/common/common_type.py +++ b/tests20/python_client/common/common_type.py @@ -114,6 +114,21 @@ get_invalid_ints = [ "a".join("a" for i in range(256)) ] +get_invalid_dict = [ + [], + 1, + [1, "2", 3], + (1,), + None, + "", + " ", + "12-s", + {1: 1}, + {"中文": 1}, + {"%$#": ["a"]}, + {"a".join("a" for i in range(256)): "a"} +] + get_dict_without_host_port = [ {"host": "host"}, {"port": "port"}, @@ -158,6 +173,7 @@ class CheckTasks: check_partition_property = "check_partition_property" check_search_results = "check_search_results" check_query_results = "check_query_results" + check_distance = "check_distance" class CaseLabel: diff --git a/tests20/python_client/conftest.py b/tests20/python_client/conftest.py index c8c69c74b3..0dbf46e4ca 100644 --- a/tests20/python_client/conftest.py +++ b/tests20/python_client/conftest.py @@ -199,6 +199,11 @@ def get_invalid_partition_name(request): yield request.param +@pytest.fixture(params=ct.get_invalid_dict) +def get_invalid_vector_dict(request): + yield request.param + + # for test exit in the future # @pytest.hookimpl(hookwrapper=True, tryfirst=True) # def pytest_runtest_makereport(): diff --git a/tests20/python_client/pytest.ini b/tests20/python_client/pytest.ini index 8c41081ae4..c9561202de 100644 --- a/tests20/python_client/pytest.ini +++ b/tests20/python_client/pytest.ini @@ -1,7 +1,7 @@ [pytest] -addopts = --host localhost --html=/tmp/ci_logs/report.html --self-contained-html -v -x +addopts = --host localhost --html=/tmp/ci_logs/report.html --self-contained-html -v # -;addopts = --host 172.28.255.155 --html=/tmp/report.html # python3 -W ignore -m pytest diff --git a/tests20/python_client/testcases/test_search.py b/tests20/python_client/testcases/test_search.py index 422d9746ea..d96d5170c4 100644 --- a/tests20/python_client/testcases/test_search.py +++ b/tests20/python_client/testcases/test_search.py @@ -525,6 +525,43 @@ class TestCollectionSearchInvalid(TestcaseBase): check_items={"err_code": 1, "err_msg": "PartitonName: %s not found" % deleted_par_name}) + @pytest.mark.tags(CaseLabel.L2) + @pytest.mark.xfail(reason="issue 6731") + @pytest.mark.parametrize("index, params", + zip(ct.all_index_types[:9], + ct.default_index_params[:9])) + def test_search_different_index_invalid_params(self, nq, dim, index, params, auto_id, _async): + """ + target: test search with different index + method: test search with different index + expected: searched successfully + """ + # 1. initialize with data + collection_w, _, _, insert_ids = self.init_collection_general(prefix, True, 5000, + partition_num=1, + auto_id=auto_id, + dim=dim, is_index=True) + vectors = [[random.random() for _ in range(dim)] for _ in range(nq)] + # 2. create different index + if params.get("m"): + if (dim % params["m"]) != 0: + params["m"] = dim//4 + log.info("test_search_different_index_invalid_params: Creating index-%s" % index) + default_index = {"index_type": index, "params": params, "metric_type": "L2"} + collection_w.create_index("float_vector", default_index) + log.info("test_search_different_index_invalid_params: Created index-%s" % index) + collection_w.load() + # 3. search + log.info("test_search_different_index_invalid_params: Searching after creating index-%s" % index) + collection_w.search(vectors[:nq], default_search_field, + default_search_params, default_limit, + default_search_exp, _async=_async, + check_task=CheckTasks.check_search_results, + check_items={"nq": nq, + "ids": insert_ids, + "limit": default_limit, + "_async": _async}) + @pytest.mark.tags(CaseLabel.L1) def test_search_index_partition_not_existed(self): """ @@ -902,6 +939,7 @@ class TestCollectionSearch(TestcaseBase): "_async": _async}) @pytest.mark.tags(CaseLabel.L2) + @pytest.mark.xfail(reason="issue 6997") def test_search_partition_after_release_load(self, nb, nq, dim, auto_id, _async): """ target: search the pre-released collection after load @@ -1044,44 +1082,6 @@ class TestCollectionSearch(TestcaseBase): "_async": _async}) @pytest.mark.tags(CaseLabel.L2) - @pytest.mark.xfail(reason="issue 6731") - @pytest.mark.parametrize("index, params", - zip(ct.all_index_types[:9], - ct.default_index_params[:9])) - def test_search_after_different_index(self, nq, dim, index, params, auto_id, _async): - """ - target: test search with different index - method: test search with different index - expected: searched successfully - """ - # 1. initialize with data - collection_w, _, _, insert_ids = self.init_collection_general(prefix, True, 5000, - partition_num=1, - auto_id=auto_id, - dim=dim, is_index=True) - vectors = [[random.random() for _ in range(dim)] for _ in range(nq)] - # 2. create different index - if params.get("m"): - if (dim % params["m"]) != 0: - params["m"] = dim//4 - log.info("test_search_after_different_index: Creating index-%s" % index) - default_index = {"index_type": index, "params": params, "metric_type": "L2"} - collection_w.create_index("float_vector", default_index) - log.info("test_search_after_different_index: Created index-%s" % index) - collection_w.load() - # 3. search - log.info("test_search_after_different_index: Searching after creating index-%s" % index) - collection_w.search(vectors[:nq], default_search_field, - default_search_params, default_limit, - default_search_exp, _async=_async, - check_task=CheckTasks.check_search_results, - check_items={"nq": nq, - "ids": insert_ids, - "limit": default_limit, - "_async": _async}) - - @pytest.mark.tags(CaseLabel.L2) - @pytest.mark.xfail(reason="issue 6731") @pytest.mark.parametrize("index, params", zip(ct.all_index_types[:9], ct.default_index_params[:9])) @@ -1100,6 +1100,9 @@ class TestCollectionSearch(TestcaseBase): if params.get("m"): if (dim % params["m"]) != 0: params["m"] = dim//4 + if params.get("PQM"): + if (dim % params["PQM"]) != 0: + params["PQM"] = dim//4 default_index = {"index_type": index, "params": params, "metric_type": "L2"} collection_w.create_index("float_vector", default_index) collection_w.load() @@ -1118,11 +1121,10 @@ class TestCollectionSearch(TestcaseBase): "_async": _async}) @pytest.mark.tags(CaseLabel.L2) - @pytest.mark.xfail(reason="issue 6731") @pytest.mark.parametrize("index, params", zip(ct.all_index_types[:9], ct.default_index_params[:9])) - def test_search_after_index_different_metric_type(self, nq, dim, index, params, auto_id, _async): + def test_search_after_index_different_metric_type(self, dim, index, params, auto_id, _async): """ target: test search with different metric type method: test search with different metric type @@ -1133,11 +1135,13 @@ class TestCollectionSearch(TestcaseBase): partition_num=1, auto_id=auto_id, dim=dim, is_index=True) - vectors = [[random.random() for _ in range(dim)] for _ in range(nq)] # 2. create different index if params.get("m"): if (dim % params["m"]) != 0: params["m"] = dim//4 + if params.get("PQM"): + if (dim % params["PQM"]) != 0: + params["PQM"] = dim//4 log.info("test_search_after_index_different_metric_type: Creating index-%s" % index) default_index = {"index_type": index, "params": params, "metric_type": "IP"} collection_w.create_index("float_vector", default_index) @@ -1145,6 +1149,7 @@ class TestCollectionSearch(TestcaseBase): collection_w.load() # 3. search search_params = cf.gen_search_param(index, "IP") + vectors = [[random.random() for _ in range(dim)] for _ in range(default_nq)] for search_param in search_params: log.info("Searching with search params: {}".format(search_param)) collection_w.search(vectors[:default_nq], default_search_field, @@ -1465,7 +1470,6 @@ class TestCollectionSearch(TestcaseBase): assert abs(res[0]._distances[0] - min(distance_0, distance_1)) <= epsilon @pytest.mark.tags(CaseLabel.L2) - @pytest.mark.xfail(reason="issue 6569") def test_search_binary_tanimoto_flat_index(self, nq, dim, auto_id, _async): """ target: search binary_collection, and check the result: distance diff --git a/tests20/python_client/testcases/test_utility.py b/tests20/python_client/testcases/test_utility.py index 8f60387eec..8090c771bd 100644 --- a/tests20/python_client/testcases/test_utility.py +++ b/tests20/python_client/testcases/test_utility.py @@ -8,14 +8,16 @@ from common.common_type import CaseLabel, CheckTasks prefix = "utility" default_schema = cf.gen_default_collection_schema() +default_int64_field_name = ct.default_int64_field_name default_field_name = ct.default_float_vec_field_name default_index_params = {"index_type": "IVF_SQ8", "metric_type": "L2", "params": {"nlist": 64}} +default_dim = ct.default_dim +default_nb = ct.default_nb class TestUtilityParams(TestcaseBase): """ Test case of index interface """ - @pytest.mark.tags(CaseLabel.L1) def test_has_collection_name_invalid(self, get_invalid_collection_name): """ @@ -139,10 +141,126 @@ class TestUtilityParams(TestcaseBase): log.error(str(ex)) assert "invalid" or "illegal" in str(ex) + @pytest.mark.tags(CaseLabel.L1) + def test_calc_distance_left_vector_invalid_type(self, get_invalid_vector_dict): + """ + target: test calculated distance with invalid vectors + method: input invalid vectors type + expected: raise exception + """ + self._connect() + invalid_vector = get_invalid_vector_dict + if not isinstance(invalid_vector, dict): + self.utility_wrap.calc_distance(invalid_vector, invalid_vector, + check_task=CheckTasks.err_res, + check_items={"err_code": 1, + "err_msg": "Left vectors array is invalid"}) + + @pytest.mark.tags(CaseLabel.L1) + @pytest.mark.xfail(reason="issue 7038") + def test_calc_distance_left_vector_invalid_value(self, get_invalid_vector_dict): + """ + target: test calculated distance with invalid vectors + method: input invalid vectors value + expected: raise exception + """ + self._connect() + invalid_vector = get_invalid_vector_dict + if isinstance(invalid_vector, dict): + self.utility_wrap.calc_distance(invalid_vector, invalid_vector, + check_task=CheckTasks.err_res, + check_items={"err_code": 1, + "err_msg": "Left vectors array is empty"}) + + @pytest.mark.tags(CaseLabel.L1) + def test_calc_distance_right_vector_invalid_type(self, get_invalid_vector_dict): + """ + target: test calculated distance with invalid vectors + method: input invalid vectors type + expected: raise exception + """ + self._connect() + invalid_vector = get_invalid_vector_dict + vector = cf.gen_vectors(default_nb, default_dim) + op_l = {"float_vectors": vector} + if not isinstance(invalid_vector, dict): + self.utility_wrap.calc_distance(op_l, invalid_vector, + check_task=CheckTasks.err_res, + check_items={"err_code": 1, + "err_msg": "Right vectors array is invalid"}) + + @pytest.mark.tags(CaseLabel.L1) + def test_calc_distance_right_vector_invalid_value(self, get_invalid_vector_dict): + """ + target: test calculated distance with invalid vectors + method: input invalid vectors value + expected: raise exception + """ + self._connect() + invalid_vector = get_invalid_vector_dict + vector = cf.gen_vectors(default_nb, default_dim) + op_l = {"float_vectors": vector} + if isinstance(invalid_vector, dict): + self.utility_wrap.calc_distance(op_l, invalid_vector, + check_task=CheckTasks.err_res, + check_items={"err_code": 1, + "err_msg": "Cannot calculate distance between " + "vectors with different dimension"}) + + @pytest.mark.tags(CaseLabel.L2) + def test_calc_distance_invalid_using(self): + """ + target: test calculated distance with invalid using + method: input invalid using + expected: raise exception + """ + self._connect() + vectors_l = cf.gen_vectors(5, 64) + vectors_r = cf.gen_vectors(10, 64) + op_l = {"float_vectors": vectors_l} + op_r = {"float_vectors": vectors_r} + params = {"metric": "L2", "sqrt": True} + using = "empty" + self.utility_wrap.calc_distance(op_l, op_r, params, using=using, + check_task=CheckTasks.err_res, + check_items={"err_code": 1, + "err_msg": "should create connect"}) + + @pytest.mark.tags(CaseLabel.L1) + def test_calc_distance_not_match_dim(self): + """ + target: test calculated distance with invalid vectors + method: input invalid vectors type and value + expected: raise exception + """ + self._connect() + dim = 129 + vector_l = cf.gen_vectors(default_nb, default_dim) + vector_r = cf.gen_vectors(default_nb, dim) + op_l = {"float_vectors": vector_l} + op_r = {"float_vectors": vector_r} + ut = ApiUtilityWrapper() + ut.calc_distance(op_l, op_r, + check_task=CheckTasks.err_res, + check_items={"err_code": 1, + "err_msg": "Cannot calculate distance between " + "vectors with different dimension"}) class TestUtilityBase(TestcaseBase): """ Test case of index interface """ + @pytest.fixture(scope="function", params=[True, False]) + def sqrt(self, request): + yield request.param + + @pytest.fixture(scope="function", params=["L2", "IP"]) + def metric(self, request): + yield request.param + + @pytest.fixture(scope="function", params=["HAMMING", "TANIMOTO", "JACCARD"]) + def metric_binary(self, request): + yield request.param + @pytest.mark.tags(CaseLabel.L1) def test_has_collection(self): """ @@ -386,6 +504,299 @@ class TestUtilityBase(TestcaseBase): res, _ = self.utility_wrap.index_building_progress(c_name) assert res["indexed_rows"] == nb + @pytest.mark.tags(CaseLabel.L1) + def test_calc_distance_default(self): + """ + target: test calculated distance with default params + method: calculated distance between two random vectors + expected: distance calculated successfully + """ + self._connect() + vectors_l = cf.gen_vectors(default_nb, default_dim) + vectors_r = cf.gen_vectors(default_nb, default_dim) + op_l = {"float_vectors": vectors_l} + op_r = {"float_vectors": vectors_r} + self.utility_wrap.calc_distance(op_l, op_r, + check_task=CheckTasks.check_distance, + check_items={"vectors_l": vectors_l, + "vectors_r": vectors_r}) + + @pytest.mark.tags(CaseLabel.L2) + def test_calc_distance_default_sqrt(self, metric): + """ + target: test calculated distance with default param + method: calculated distance with default sqrt + expected: distance calculated successfully + """ + self._connect() + vectors_l = cf.gen_vectors(default_nb, default_dim) + vectors_r = cf.gen_vectors(default_nb, default_dim) + op_l = {"float_vectors": vectors_l} + op_r = {"float_vectors": vectors_r} + params = {"metric": metric} + self.utility_wrap.calc_distance(op_l, op_r, params, + check_task=CheckTasks.check_distance, + check_items={"vectors_l": vectors_l, + "vectors_r": vectors_r, + "metric": metric}) + + @pytest.mark.tags(CaseLabel.L2) + def test_calc_distance_default_metric(self, sqrt): + """ + target: test calculated distance with default param + method: calculated distance with default metric + expected: distance calculated successfully + """ + self._connect() + vectors_l = cf.gen_vectors(default_nb, default_dim) + vectors_r = cf.gen_vectors(default_nb, default_dim) + op_l = {"float_vectors": vectors_l} + op_r = {"float_vectors": vectors_r} + params = {"sqrt": sqrt} + self.utility_wrap.calc_distance(op_l, op_r, params, + check_task=CheckTasks.check_distance, + check_items={"vectors_l": vectors_l, + "vectors_r": vectors_r, + "sqrt": sqrt}) + + @pytest.mark.tags(CaseLabel.L2) + @pytest.mark.xfail(reason="issue 7064") + def test_calc_distance_binary_metric(self, metric_binary): + """ + target: test calculate distance with binary vectors + method: calculate distance between binary vectors + expected: distance calculated successfully + """ + self._connect() + nb = 10 + raw_vectors_l, vectors_l = cf.gen_binary_vectors(nb, default_dim) + raw_vectors_r, vectors_r = cf.gen_binary_vectors(nb, default_dim) + op_l = {"bin_vectors": vectors_l} + op_r = {"bin_vectors": vectors_r} + params = {"metric": metric_binary} + if metric_binary == "HAMMING": + vectors_l = raw_vectors_l + vectors_r = raw_vectors_r + self.utility_wrap.calc_distance(op_l, op_r, params, + check_task=CheckTasks.check_distance, + check_items={"vectors_l": vectors_l, + "vectors_r": vectors_r, + "metric": metric_binary}) + + @pytest.mark.tags(CaseLabel.L1) + def test_calc_distance_from_collection_ids(self, metric, sqrt): + """ + target: test calculated distance from collection entities + method: both left and right vectors are from collection + expected: distance calculated successfully + """ + self._connect() + nb = 10 + collection_w, vectors, _, insert_ids = self.init_collection_general(prefix, True, nb) + middle = len(insert_ids) // 2 + vectors = vectors[0].loc[:, default_field_name] + vectors_l = vectors[:middle] + vectors_r = [] + for i in range(middle): + vectors_r.append(vectors[middle+i]) + op_l = {"ids": insert_ids[:middle], "collection": collection_w.name, + "field": default_field_name} + op_r = {"ids": insert_ids[middle:], "collection": collection_w.name, + "field": default_field_name} + params = {"metric": metric, "sqrt": sqrt} + self.utility_wrap.calc_distance(op_l, op_r, params, + check_task=CheckTasks.check_distance, + check_items={"vectors_l": vectors_l, + "vectors_r": vectors_r, + "metric": metric, + "sqrt": sqrt}) + + @pytest.mark.tags(CaseLabel.L2) + def test_calc_distance_from_collections(self, metric, sqrt): + """ + target: test calculated distance between entities from collections + method: calculated distance between entities from two collections + expected: distance calculated successfully + """ + self._connect() + nb = 10 + prefix_1 = "utility_distance" + collection_w, vectors, _, insert_ids = self.init_collection_general(prefix, True, nb) + collection_w_1, vectors_1, _, insert_ids_1 = self.init_collection_general(prefix_1, True, nb) + vectors_l = vectors[0].loc[:, default_field_name] + vectors_r = vectors_1[0].loc[:, default_field_name] + op_l = {"ids": insert_ids, "collection": collection_w.name, + "field": default_field_name} + op_r = {"ids": insert_ids_1, "collection": collection_w_1.name, + "field": default_field_name} + params = {"metric": metric, "sqrt": sqrt} + self.utility_wrap.calc_distance(op_l, op_r, params, + check_task=CheckTasks.check_distance, + check_items={"vectors_l": vectors_l, + "vectors_r": vectors_r, + "metric": metric, + "sqrt": sqrt}) + + @pytest.mark.tags(CaseLabel.L2) + def test_calc_distance_left_vector_and_collection_ids(self, metric, sqrt): + """ + target: test calculated distance from collection entities + method: set left vectors as random vectors, right vectors from collection + expected: distance calculated successfully + """ + self._connect() + nb = 10 + collection_w, vectors, _, insert_ids = self.init_collection_general(prefix, True, nb) + middle = len(insert_ids) // 2 + vectors = vectors[0].loc[:, default_field_name] + vectors_l = cf.gen_vectors(nb, default_dim) + vectors_r = [] + for i in range(middle): + vectors_r.append(vectors[middle + i]) + op_l = {"float_vectors": vectors_l} + op_r = {"ids": insert_ids[middle:], "collection": collection_w.name, + "field": default_field_name} + params = {"metric": metric, "sqrt": sqrt} + self.utility_wrap.calc_distance(op_l, op_r, params, + check_task=CheckTasks.check_distance, + check_items={"vectors_l": vectors_l, + "vectors_r": vectors_r, + "metric": metric, + "sqrt": sqrt}) + + @pytest.mark.tags(CaseLabel.L2) + def test_calc_distance_right_vector_and_collection_ids(self, metric, sqrt): + """ + target: test calculated distance from collection entities + method: set right vectors as random vectors, left vectors from collection + expected: distance calculated successfully + """ + self._connect() + nb = 10 + collection_w, vectors, _, insert_ids = self.init_collection_general(prefix, True, nb) + middle = len(insert_ids) // 2 + vectors = vectors[0].loc[:, default_field_name] + vectors_l = vectors[:middle] + vectors_r = cf.gen_vectors(nb, default_dim) + op_l = {"ids": insert_ids[:middle], "collection": collection_w.name, + "field": default_field_name} + op_r = {"float_vectors": vectors_r} + params = {"metric": metric, "sqrt": sqrt} + self.utility_wrap.calc_distance(op_l, op_r, params, + check_task=CheckTasks.check_distance, + check_items={"vectors_l": vectors_l, + "vectors_r": vectors_r, + "metric": metric, + "sqrt": sqrt}) + + @pytest.mark.tags(CaseLabel.L2) + @pytest.mark.xfail(reason="issue 7046") + def test_calc_distance_from_partition_ids(self, metric, sqrt): + """ + target: test calculated distance from one partition entities + method: both left and right vectors are from partition + expected: distance calculated successfully + """ + self._connect() + nb = 10 + collection_w, vectors, _, insert_ids = self.init_collection_general(prefix, True, nb, partition_num=1) + partitions = collection_w.partitions + middle = len(insert_ids) // 2 + params = {"metric": metric, "sqrt": sqrt} + for i in range(len(partitions)): + vectors_l = vectors[i].loc[:, default_field_name] + vectors_r = vectors[i].loc[:, default_field_name] + op_l = {"ids": insert_ids[:middle], "collection": collection_w.name, + "partition": partitions[i].name, "field": default_field_name} + op_r = {"ids": insert_ids[middle:], "collection": collection_w.name, + "partition": partitions[i].name, "field": default_field_name} + self.utility_wrap.calc_distance(op_l, op_r, params, + check_task=CheckTasks.check_distance, + check_items={"vectors_l": vectors_l, + "vectors_r": vectors_r, + "metric": metric, + "sqrt": sqrt}) + + @pytest.mark.tags(CaseLabel.L2) + @pytest.mark.xfail(reason="issue 7046") + def test_calc_distance_from_partitions(self, metric, sqrt): + """ + target: test calculated distance between entities from partitions + method: calculate distance between entities from two partitions + expected: distance calculated successfully + """ + self._connect() + nb = 10 + collection_w, vectors, _, insert_ids = self.init_collection_general(prefix, True, nb, partition_num=1) + partitions = collection_w.partitions + middle = len(insert_ids) // 2 + params = {"metric": metric, "sqrt": sqrt} + vectors_l = vectors[0].loc[:, default_field_name] + vectors_r = vectors[1].loc[:, default_field_name] + op_l = {"ids": insert_ids[:middle], "collection": collection_w.name, + "partition": partitions[0].name, "field": default_field_name} + op_r = {"ids": insert_ids[middle:], "collection": collection_w.name, + "partition": partitions[1].name, "field": default_field_name} + self.utility_wrap.calc_distance(op_l, op_r, params, + check_task=CheckTasks.check_distance, + check_items={"vectors_l": vectors_l, + "vectors_r": vectors_r, + "metric": metric, + "sqrt": sqrt}) + + @pytest.mark.tags(CaseLabel.L2) + @pytest.mark.xfail(reason="issue 7046") + def test_calc_distance_left_vectors_and_partition_ids(self, metric, sqrt): + """ + target: test calculated distance between vectors and partition entities + method: set left vectors as random vectors, right vectors are entities + expected: distance calculated successfully + """ + self._connect() + nb = 10 + collection_w, vectors, _, insert_ids = self.init_collection_general(prefix, True, nb, partition_num=1) + middle = len(insert_ids) // 2 + partitions = collection_w.partitions + vectors_l = cf.gen_vectors(nb // 2, default_dim) + op_l = {"float_vectors": vectors_l} + params = {"metric": metric, "sqrt": sqrt} + for i in range(len(partitions)): + vectors_r = vectors[i].loc[:, default_field_name] + op_r = {"ids": insert_ids[middle:], "collection": collection_w.name, + "partition": partitions[i].name, "field": default_field_name} + self.utility_wrap.calc_distance(op_l, op_r, params, + check_task=CheckTasks.check_distance, + check_items={"vectors_l": vectors_l, + "vectors_r": vectors_r, + "metric": metric, + "sqrt": sqrt}) + + @pytest.mark.tags(CaseLabel.L2) + @pytest.mark.xfail(reason="issue 7046") + def test_calc_distance_right_vectors_and_partition_ids(self, metric, sqrt): + """ + target: test calculated distance between vectors and partition entities + method: set right vectors as random vectors, left vectors are entities + expected: distance calculated successfully + """ + self._connect() + nb = 10 + collection_w, vectors, _, insert_ids = self.init_collection_general(prefix, True, nb, partition_num=1) + middle = len(insert_ids) // 2 + partitions = collection_w.partitions + vectors_r = cf.gen_vectors(nb // 2, default_dim) + op_r = {"float_vectors": vectors_r} + params = {"metric": metric, "sqrt": sqrt} + for i in range(len(partitions)): + vectors_l = vectors[i].loc[:, default_field_name] + op_l = {"ids": insert_ids[middle:], "collection": collection_w.name, + "partition": partitions[i].name, "field": default_field_name} + self.utility_wrap.calc_distance(op_l, op_r, params, + check_task=CheckTasks.check_distance, + check_items={"vectors_l": vectors_l, + "vectors_r": vectors_r, + "metric": metric, + "sqrt": sqrt}) class TestUtilityAdvanced(TestcaseBase): """ Test case of index interface """