This is an automated email from the ASF dual-hosted git repository.
jshao pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/gravitino.git
The following commit(s) were added to refs/heads/main by this push:
new 9afa4cd6b0 [#5199] feat(client-python): add index DTO (#8262)
9afa4cd6b0 is described below
commit 9afa4cd6b0d7fc8e245fc745b5422a724631d61f
Author: George T. C. Lai <[email protected]>
AuthorDate: Tue Aug 26 13:58:56 2025 +0800
[#5199] feat(client-python): add index DTO (#8262)
### What changes were proposed in this pull request?
This PR is aimed at implementing the class `IndexDTO` corresponding to
the Java client.
### Why are the changes needed?
We need to support table partitioning, bucketing and sort ordering and
indexes.
#5199
### Does this PR introduce _any_ user-facing change?
No
### How was this patch tested?
Unit tests
---------
Signed-off-by: George T. C. Lai <[email protected]>
Co-authored-by: Jerry Shao <[email protected]>
---
.../gravitino/api/expressions/indexes/index.py | 6 +-
.../gravitino/api/expressions/indexes/indexes.py | 9 ++-
.../gravitino/dto/rel/indexes/__init__.py | 16 +++++
.../gravitino/dto/rel/indexes/index_dto.py | 74 ++++++++++++++++++++++
.../tests/unittests/dto/rel/test_index_dto.py | 72 +++++++++++++++++++++
5 files changed, 171 insertions(+), 6 deletions(-)
diff --git a/clients/client-python/gravitino/api/expressions/indexes/index.py
b/clients/client-python/gravitino/api/expressions/indexes/index.py
index 82d0931d8f..777e3b4813 100644
--- a/clients/client-python/gravitino/api/expressions/indexes/index.py
+++ b/clients/client-python/gravitino/api/expressions/indexes/index.py
@@ -18,7 +18,7 @@
from abc import ABC, abstractmethod
from enum import Enum, unique
-from typing import List
+from typing import List, Optional
class Index(ABC):
@@ -60,11 +60,11 @@ class Index(ABC):
pass # pragma: no cover
@abstractmethod
- def name(self) -> str:
+ def name(self) -> Optional[str]:
"""Returns the name of the index.
Returns:
- str: The name of the index.
+ Optional[str]: The name of the index.
"""
pass # pragma: no cover
diff --git a/clients/client-python/gravitino/api/expressions/indexes/indexes.py
b/clients/client-python/gravitino/api/expressions/indexes/indexes.py
index f2a75ba768..720e2b46fe 100644
--- a/clients/client-python/gravitino/api/expressions/indexes/indexes.py
+++ b/clients/client-python/gravitino/api/expressions/indexes/indexes.py
@@ -16,7 +16,7 @@
# under the License.
-from typing import ClassVar, List, final
+from typing import ClassVar, List, Optional, final
from gravitino.api.expressions.indexes.index import Index
@@ -50,7 +50,10 @@ class Indexes:
@final
class IndexImpl(Index):
def __init__(
- self, index_type: Index.IndexType, name: str, field_names:
List[List[str]]
+ self,
+ index_type: Index.IndexType,
+ name: Optional[str],
+ field_names: List[List[str]],
):
self._index_type = index_type
self._name = name
@@ -59,7 +62,7 @@ class Indexes:
def type(self) -> Index.IndexType:
return self._index_type
- def name(self) -> str:
+ def name(self) -> Optional[str]:
return self._name
def field_names(self) -> List[List[str]]:
diff --git a/clients/client-python/gravitino/dto/rel/indexes/__init__.py
b/clients/client-python/gravitino/dto/rel/indexes/__init__.py
new file mode 100644
index 0000000000..13a83393a9
--- /dev/null
+++ b/clients/client-python/gravitino/dto/rel/indexes/__init__.py
@@ -0,0 +1,16 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
diff --git a/clients/client-python/gravitino/dto/rel/indexes/index_dto.py
b/clients/client-python/gravitino/dto/rel/indexes/index_dto.py
new file mode 100644
index 0000000000..d78843ab22
--- /dev/null
+++ b/clients/client-python/gravitino/dto/rel/indexes/index_dto.py
@@ -0,0 +1,74 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+from functools import reduce
+from typing import ClassVar, List, Optional
+
+from gravitino.api.expressions.indexes.index import Index
+from gravitino.utils.precondition import Precondition
+
+
+class IndexDTO(Index):
+ """Data transfer object representing index information.
+
+ Attributes:
+ EMPTY_INDEXES (List[IndexDTO]): An empty array of indexes.
+ """
+
+ EMPTY_INDEXES: ClassVar[List["IndexDTO"]] = []
+
+ def __init__(
+ self,
+ index_type: Index.IndexType,
+ name: Optional[str],
+ field_names: List[List[str]],
+ ):
+ Precondition.check_argument(index_type is not None, "Index type cannot
be null")
+ Precondition.check_argument(
+ field_names is not None and len(field_names) > 0,
+ "The index must be set with corresponding column names",
+ )
+
+ self._index_type = index_type
+ self._name = name
+ self._field_names = field_names
+
+ def type(self) -> Index.IndexType:
+ return self._index_type
+
+ def name(self) -> Optional[str]:
+ return self._name
+
+ def field_names(self) -> List[List[str]]:
+ return self._field_names
+
+ def __eq__(self, other) -> bool:
+ if not isinstance(other, IndexDTO):
+ return False
+ return (
+ self._index_type is other.type()
+ and self._name is other.name()
+ and self._field_names == other.field_names()
+ )
+
+ def __hash__(self) -> int:
+ initial_hash = hash((self._index_type, self._name))
+ return reduce(
+ lambda result, field_name: 31 * result + hash(tuple(field_name)),
+ self._field_names,
+ initial_hash,
+ )
diff --git a/clients/client-python/tests/unittests/dto/rel/test_index_dto.py
b/clients/client-python/tests/unittests/dto/rel/test_index_dto.py
new file mode 100644
index 0000000000..5bb9fa31dc
--- /dev/null
+++ b/clients/client-python/tests/unittests/dto/rel/test_index_dto.py
@@ -0,0 +1,72 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+import unittest
+
+from gravitino.api.expressions.indexes.index import Index
+from gravitino.dto.rel.indexes.index_dto import IndexDTO
+from gravitino.exceptions.base import IllegalArgumentException
+
+
+class TestIndexDTO(unittest.TestCase):
+ def test_index_dto_invalid_init(self):
+ self.assertRaisesRegex(
+ IllegalArgumentException,
+ "Index type cannot be null",
+ IndexDTO,
+ None,
+ "invalid_index_type",
+ [["field1", "field2"]],
+ )
+ self.assertRaisesRegex(
+ IllegalArgumentException,
+ "The index must be set with corresponding column names",
+ IndexDTO,
+ Index.IndexType.PRIMARY_KEY,
+ "invalid_field_names",
+ None,
+ )
+ self.assertRaisesRegex(
+ IllegalArgumentException,
+ "The index must be set with corresponding column names",
+ IndexDTO,
+ Index.IndexType.PRIMARY_KEY,
+ "invalid_field_names",
+ [],
+ )
+
+ def test_index_dto(self):
+ names = (None, "", "index_name")
+ field_names = [["col1"], ["col2"]]
+ dto_dict = {}
+ index_dto = None
+ for idx, name in enumerate(names):
+ index_dto = IndexDTO(Index.IndexType.PRIMARY_KEY, name,
field_names)
+ dto_dict[index_dto] = idx
+
+ self.assertIs(index_dto.type(), Index.IndexType.PRIMARY_KEY)
+ self.assertEqual(index_dto.name(), name)
+ self.assertListEqual(index_dto.field_names(), field_names)
+
+ slack_dto = IndexDTO(Index.IndexType.PRIMARY_KEY, "index_name",
field_names)
+ self.assertTrue(index_dto == slack_dto)
+ self.assertFalse(index_dto == "index_dto")
+ self.assertEqual(dto_dict[slack_dto], len(names) - 1)
+
+ dto_dict[slack_dto] = len(names)
+ self.assertEqual(len(dto_dict), len(names))
+ self.assertEqual(dto_dict[slack_dto], len(names))