This is an automated email from the ASF dual-hosted git repository.
lzljs3620320 pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/paimon.git
The following commit(s) were added to refs/heads/master by this push:
new f6bee4e29e [python] Python BTree support between predicate (#7193)
f6bee4e29e is described below
commit f6bee4e29e0adbb5ed18f9e131317aa7568ea182
Author: Jingsong Lee <[email protected]>
AuthorDate: Tue Feb 3 19:20:56 2026 +0800
[python] Python BTree support between predicate (#7193)
---
.../globalindex/btree/btree_index_reader.py | 11 +++++++++++
.../pypaimon/globalindex/global_index_evaluator.py | 2 ++
.../pypaimon/globalindex/global_index_reader.py | 4 ++++
.../pypaimon/tests/e2e/java_py_read_write_test.py | 22 ++++++++++++++++++++++
4 files changed, 39 insertions(+)
diff --git a/paimon-python/pypaimon/globalindex/btree/btree_index_reader.py
b/paimon-python/pypaimon/globalindex/btree/btree_index_reader.py
index 2c64d3a4a2..034e220108 100644
--- a/paimon-python/pypaimon/globalindex/btree/btree_index_reader.py
+++ b/paimon-python/pypaimon/globalindex/btree/btree_index_reader.py
@@ -406,6 +406,17 @@ class BTreeIndexReader(GlobalIndexReader):
return GlobalIndexResult.create(supplier)
+ def visit_between(self, field_ref: FieldRef, min_v: object, max_v: object)
-> Optional[GlobalIndexResult]:
+ """Visit a between predicate."""
+
+ def supplier():
+ try:
+ return self._range_query(min_v, max_v, True, True)
+ except Exception as e:
+ raise RuntimeError("fail to read btree index file.", e)
+
+ return GlobalIndexResult.create(supplier)
+
def close(self) -> None:
"""Close the reader and release resources."""
if self.input_stream is not None:
diff --git a/paimon-python/pypaimon/globalindex/global_index_evaluator.py
b/paimon-python/pypaimon/globalindex/global_index_evaluator.py
index 46bb5cd67d..1692113d64 100644
--- a/paimon-python/pypaimon/globalindex/global_index_evaluator.py
+++ b/paimon-python/pypaimon/globalindex/global_index_evaluator.py
@@ -186,6 +186,8 @@ class GlobalIndexEvaluator:
return reader.visit_ends_with(field_ref, literals[0])
elif method == 'contains':
return reader.visit_contains(field_ref, literals[0])
+ elif method == 'between':
+ return reader.visit_between(field_ref, literals[0], literals[1])
return None
diff --git a/paimon-python/pypaimon/globalindex/global_index_reader.py
b/paimon-python/pypaimon/globalindex/global_index_reader.py
index a5006bddf5..ce70b5d6c1 100644
--- a/paimon-python/pypaimon/globalindex/global_index_reader.py
+++ b/paimon-python/pypaimon/globalindex/global_index_reader.py
@@ -102,6 +102,10 @@ class GlobalIndexReader(ABC):
"""Visit a like predicate."""
return None
+ def visit_between(self, field_ref: FieldRef, min_v: object, max_v: object)
-> Optional['GlobalIndexResult']:
+ """Visit a between predicate."""
+ return None
+
@abstractmethod
def close(self) -> None:
"""Close the reader and release resources."""
diff --git a/paimon-python/pypaimon/tests/e2e/java_py_read_write_test.py
b/paimon-python/pypaimon/tests/e2e/java_py_read_write_test.py
index 3180d7b699..320ee6c6e2 100644
--- a/paimon-python/pypaimon/tests/e2e/java_py_read_write_test.py
+++ b/paimon-python/pypaimon/tests/e2e/java_py_read_write_test.py
@@ -363,6 +363,28 @@ class JavaPyReadWriteTest(unittest.TestCase):
})
self.assertEqual(expected, actual)
+ # read between index
+ read_builder.with_filter(predicate_builder.between('k', 'k990',
'k995'))
+ table_read = read_builder.new_read()
+ splits = read_builder.new_scan().plan().splits()
+ actual = table_sort_by(table_read.to_arrow(splits), 'k')
+ expected = pa.Table.from_pydict({
+ 'k': ["k990", "k991", "k992", "k993", "k994", "k995"],
+ 'v': ["v990", "v991", "v992", "v993", "v994", "v995"]
+ })
+ self.assertEqual(expected, actual)
+
+ # read in index
+ read_builder.with_filter(predicate_builder.is_in('k', ['k990',
'k995']))
+ table_read = read_builder.new_read()
+ splits = read_builder.new_scan().plan().splits()
+ actual = table_sort_by(table_read.to_arrow(splits), 'k')
+ expected = pa.Table.from_pydict({
+ 'k': ["k990", "k995"],
+ 'v': ["v990", "v995"]
+ })
+ self.assertEqual(expected, actual)
+
def _test_read_btree_index_null(self):
table = self.catalog.get_table('default.test_btree_index_null')