diff --git a/java/vortex-jni/src/main/java/dev/vortex/api/Expression.java b/java/vortex-jni/src/main/java/dev/vortex/api/Expression.java index 613d02fc3e7..922c6ae3f07 100644 --- a/java/vortex-jni/src/main/java/dev/vortex/api/Expression.java +++ b/java/vortex-jni/src/main/java/dev/vortex/api/Expression.java @@ -41,6 +41,16 @@ public static Expression root() { return new Expression(NativeExpression.root()); } + /** + * The row-index expression. When evaluated as part of a Vortex scan it yields, as a non-nullable {@code u64}, each + * row's index in the file before filtering: the index is assigned to the unfiltered rows, so filtered-out + * rows leave gaps and the surviving rows keep their original positions rather than being renumbered. It cannot be + * evaluated outside of a scan. + */ + public static Expression rowIdx() { + return new Expression(NativeExpression.rowIdx()); + } + /** Access a named field from a struct expression. */ public static Expression getItem(String fieldName, Expression child) { return new Expression(NativeExpression.getItem(fieldName, child.nativePointer())); diff --git a/java/vortex-jni/src/main/java/dev/vortex/jni/NativeExpression.java b/java/vortex-jni/src/main/java/dev/vortex/jni/NativeExpression.java index bd14baea18c..068927bdc79 100644 --- a/java/vortex-jni/src/main/java/dev/vortex/jni/NativeExpression.java +++ b/java/vortex-jni/src/main/java/dev/vortex/jni/NativeExpression.java @@ -13,6 +13,8 @@ private NativeExpression() {} public static native long root(); + public static native long rowIdx(); + public static native long getItem(String fieldName, long childPointer); public static native long select(String[] fieldNames, long childPointer); diff --git a/java/vortex-jni/src/test/java/dev/vortex/api/ExpressionTest.java b/java/vortex-jni/src/test/java/dev/vortex/api/ExpressionTest.java index 8a8cbcee6e8..f171267253b 100644 --- a/java/vortex-jni/src/test/java/dev/vortex/api/ExpressionTest.java +++ b/java/vortex-jni/src/test/java/dev/vortex/api/ExpressionTest.java @@ -4,6 +4,7 @@ package dev.vortex.api; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; @@ -18,6 +19,13 @@ public static void loadLibrary() { NativeLoader.loadJni(); } + @Test + public void rowIdxBuildsAndComposes() { + assertNotNull(Expression.rowIdx()); + // Mirrors `gt(row_idx(), lit(...))` on the Rust side: the row-index expression composes like any other. + assertNotNull(Expression.binary(Expression.BinaryOp.LT, Expression.rowIdx(), Expression.literal(5L))); + } + @Test public void literalDecimalRejectsValuesLargerThan32Bytes() { BigInteger tooLarge = BigInteger.ONE.shiftLeft(256); diff --git a/vortex-jni/src/expression.rs b/vortex-jni/src/expression.rs index e8f7171c51c..035990b14db 100644 --- a/vortex-jni/src/expression.rs +++ b/vortex-jni/src/expression.rs @@ -48,6 +48,7 @@ use vortex::extension::datetime::TimeUnit; use vortex::extension::datetime::Timestamp; use vortex::extension::uuid::Uuid; use vortex::extension::uuid::UuidMetadata; +use vortex::layout::layouts::row_idx::row_idx; use vortex::scalar::DecimalValue; use vortex::scalar::Scalar; use vortex::scalar::ScalarValue; @@ -116,6 +117,14 @@ pub extern "system" fn Java_dev_vortex_jni_NativeExpression_root( into_raw(root()) } +#[unsafe(no_mangle)] +pub extern "system" fn Java_dev_vortex_jni_NativeExpression_rowIdx( + _env: EnvUnowned, + _class: JClass, +) -> jlong { + into_raw(row_idx()) +} + #[unsafe(no_mangle)] pub extern "system" fn Java_dev_vortex_jni_NativeExpression_getItem( mut env: EnvUnowned,