From 9eeb3ec7d4ec7e6e720098e42b80a48a625bdd83 Mon Sep 17 00:00:00 2001 From: Anthony Mahanna <43019056+aMahanna@users.noreply.github.com> Date: Fri, 2 Feb 2024 15:42:42 -0500 Subject: [PATCH] DE-743 | MDI/ZKD Indexes (#316) * DE-743 | initial commit * fix: `format_index` * remove `sparse` assertion (zkd & mdi indexes don't support the `sparse` property` * cleanup * use `split` on `db_version` allows us to test with a nightly build, e.g `3.12.0-NIGHTLY.20240116` --- arango/collection.py | 83 ++++++++++++++++++++++++++++++++++++++++++++ arango/formatter.py | 2 ++ tests/conftest.py | 2 +- tests/test_index.py | 61 ++++++++++++++++++++++++++++++++ 4 files changed, 147 insertions(+), 1 deletion(-) diff --git a/arango/collection.py b/arango/collection.py index 930ef771..d483a5b4 100644 --- a/arango/collection.py +++ b/arango/collection.py @@ -1617,6 +1617,89 @@ def add_inverted_index( return self._add_index(data) + def add_zkd_index( + self, + fields: Sequence[str], + field_value_types: str = "double", + name: Optional[str] = None, + unique: Optional[bool] = None, + in_background: Optional[bool] = None, + ) -> Result[Json]: + """Create a new ZKD Index. + + :param fields: Document fields to index. Unlike for other indexes the + order of the fields does not matter. + :type fields: Sequence[str] + :param field_value_types: The type of the field values. The only allowed + value is "double" at the moment. Defaults to "double". + :type field_value_types: str + :param name: Optional name for the index. + :type name: str | None + :param unique: Whether the index is unique. + :type unique: bool | None + :param in_background: Do not hold the collection lock. + :type in_background: bool | None + :return: New index details. + :rtype: dict + :raise arango.exceptions.IndexCreateError: If create fails. + """ + data: Json = { + "type": "zkd", + "fields": fields, + "fieldValueTypes": field_value_types, + } + + if unique is not None: + data["unique"] = unique + if name is not None: + data["name"] = name + if in_background is not None: + data["inBackground"] = in_background + + return self._add_index(data) + + def add_mdi_index( + self, + fields: Sequence[str], + field_value_types: str = "double", + name: Optional[str] = None, + unique: Optional[bool] = None, + in_background: Optional[bool] = None, + ) -> Result[Json]: + """Create a new MDI index, previously known as ZKD index. This method + is only usable with ArangoDB 3.12 and later. + + :param fields: Document fields to index. Unlike for other indexes the + order of the fields does not matter. + :type fields: Sequence[str] + :param field_value_types: The type of the field values. The only allowed + value is "double" at the moment. Defaults to "double". + :type field_value_types: str + :param name: Optional name for the index. + :type name: str | None + :param unique: Whether the index is unique. + :type unique: bool | None + :param in_background: Do not hold the collection lock. + :type in_background: bool | None + :return: New index details. + :rtype: dict + :raise arango.exceptions.IndexCreateError: If create fails. + """ + data: Json = { + "type": "mdi", + "fields": fields, + "fieldValueTypes": field_value_types, + } + + if unique is not None: + data["unique"] = unique + if name is not None: + data["name"] = name + if in_background is not None: + data["inBackground"] = in_background + + return self._add_index(data) + def delete_index(self, index_id: str, ignore_missing: bool = False) -> Result[bool]: """Delete an index. diff --git a/arango/formatter.py b/arango/formatter.py index 2058b1d6..6a730b87 100644 --- a/arango/formatter.py +++ b/arango/formatter.py @@ -101,6 +101,8 @@ def format_index(body: Json) -> Json: result["writebuffer_active"] = body["writebufferActive"] if "writebufferSizeMax" in body: result["writebuffer_max_size"] = body["writebufferSizeMax"] + if "fieldValueTypes" in body: + result["field_value_types"] = body["fieldValueTypes"] return verify_format(body, result) diff --git a/tests/conftest.py b/tests/conftest.py index da95e2ef..184269d7 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -134,7 +134,7 @@ def pytest_configure(config): global_data.username = username global_data.password = password global_data.db_name = tst_db_name - global_data.db_version = version.parse(db_version) + global_data.db_version = version.parse(db_version.split("-")[0]) global_data.sys_db = sys_db global_data.tst_db = tst_db global_data.bad_db = bad_db diff --git a/tests/test_index.py b/tests/test_index.py index dbf235fa..840d744a 100644 --- a/tests/test_index.py +++ b/tests/test_index.py @@ -248,6 +248,67 @@ def test_add_inverted_index(icol, enterprise, db_version): icol.delete_index(result["id"]) +def test_add_zkd_index(icol, db_version): + result = icol.add_zkd_index( + name="zkd_index", + fields=["x", "y", "z"], + field_value_types="double", + in_background=False, + unique=False, + ) + + expected_index = { + "name": "zkd_index", + "type": "zkd", + "fields": ["x", "y", "z"], + "new": True, + "unique": False, + } + + for key, value in expected_index.items(): + assert result[key] == value + + assert result["id"] in extract("id", icol.indexes()) + + with assert_raises(IndexCreateError) as err: + icol.add_zkd_index(field_value_types="integer", fields=["x", "y", "z"]) + assert err.value.error_code == 10 + + icol.delete_index(result["id"]) + + +def test_add_mdi_index(icol, db_version): + if db_version < version.parse("3.12.0"): + pytest.skip("MDI indexes are usable with 3.12+ only") + + result = icol.add_mdi_index( + name="mdi_index", + fields=["x", "y", "z"], + field_value_types="double", + in_background=False, + unique=True, + ) + + expected_index = { + "name": "mdi_index", + "type": "mdi", + "fields": ["x", "y", "z"], + "new": True, + "unique": True, + } + + for key, value in expected_index.items(): + assert result[key] == value + + assert result["id"] in extract("id", icol.indexes()) + + with assert_raises(IndexCreateError) as err: + icol.add_mdi_index(field_value_types="integer", fields=["x", "y", "z"]) + assert err.value.error_code == 10 + + icol.delete_index(result["id"]) + + def test_delete_index(icol, bad_col): old_indexes = set(extract("id", icol.indexes())) icol.add_hash_index(["attr3", "attr4"], unique=True)