This is an automated email from the ASF dual-hosted git repository.

JingsongLi 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 f88febf0c5 [vector][python] Read vindex vector indexes (#8293)
f88febf0c5 is described below

commit f88febf0c51a3e1ef187929f28b5d10b97484320
Author: Jingsong Lee <[email protected]>
AuthorDate: Sat Jun 20 10:00:36 2026 +0800

    [vector][python] Read vindex vector indexes (#8293)
    
    This PR flattens `paimon-vector` into a single jar module backed by
    `paimon-vector-index-java` 0.1.0, removing the old `paimon-vector-jni`
    submodule. It also adds Python read support for vindex vector indexes
    through `paimon-vindex` 0.1.0 and covers Java-write/Python-read interop.
---
 .github/workflows/utcase-vector-index.yml          |  74 --------
 .../lumina/index/LuminaVectorIndexOptions.java     |   4 +-
 paimon-python/dev/requirements-dev.txt             |   2 +
 paimon-python/dev/run_mixed_tests.sh               |  79 +++++++-
 .../pypaimon/globalindex/vindex/__init__.py        |  18 ++
 .../vindex/vindex_vector_global_index_reader.py    | 198 ++++++++++++++++++++
 .../pypaimon/table/source/vector_search_read.py    |   8 +
 .../pypaimon/tests/e2e/java_py_read_write_test.py  |  33 ++++
 .../pypaimon/tests/vindex_vector_index_test.py     |  41 +++++
 paimon-python/setup.py                             |   3 +
 paimon-vector/paimon-vector-index/pom.xml          | 200 ---------------------
 paimon-vector/paimon-vector-jni/pom.xml            |  69 -------
 .../apache/paimon/index/vector/NativeLoader.java   |  83 ---------
 .../paimon/index/vector/VectorIndexInput.java      |  23 ---
 .../paimon/index/vector/VectorIndexMetadata.java   |  94 ----------
 .../paimon/index/vector/VectorIndexNative.java     |  62 -------
 .../paimon/index/vector/VectorIndexReader.java     | 196 --------------------
 .../paimon/index/vector/VectorIndexWriter.java     | 131 --------------
 .../index/vector/VectorSearchBatchResult.java      | 100 -----------
 .../paimon/index/vector/VectorSearchResult.java    |  62 -------
 paimon-vector/pom.xml                              | 175 +++++++++++++++++-
 .../index/IvfFlatVectorGlobalIndexerFactory.java   |   2 +-
 .../IvfHnswFlatVectorGlobalIndexerFactory.java     |   2 +-
 .../index/IvfHnswSqVectorGlobalIndexerFactory.java |   2 +-
 .../IvfPqAlgorithmVectorGlobalIndexerFactory.java  |   2 +-
 .../index/NativeVectorGlobalIndexReader.java}      |  16 +-
 .../index/NativeVectorGlobalIndexWriter.java}      |  11 +-
 .../vector/index/NativeVectorGlobalIndexer.java}   |  11 +-
 .../index/NativeVectorGlobalIndexerFactory.java}   |   6 +-
 .../vector/index/NativeVectorIndexLoader.java      | 112 ++++++++++++
 .../paimon/vector/index/VectorIndexMeta.java       |   0
 ....apache.paimon.globalindex.GlobalIndexerFactory |   0
 .../test/java/org/apache/paimon/JavaPyE2ETest.java | 200 +++++++++++++++++++++
 .../vector/index/NativeVectorGlobalIndexTest.java} | 117 +++++++-----
 .../NativeVectorGlobalIndexerFactoryTest.java}     |  20 +--
 .../index/SeekableStreamVectorIndexInputTest.java  |  10 +-
 36 files changed, 983 insertions(+), 1183 deletions(-)

diff --git a/.github/workflows/utcase-vector-index.yml 
b/.github/workflows/utcase-vector-index.yml
deleted file mode 100644
index fa3da08567..0000000000
--- a/.github/workflows/utcase-vector-index.yml
+++ /dev/null
@@ -1,74 +0,0 @@
-################################################################################
-#  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.
-################################################################################
-
-name: UTCase Vector Index
-
-on:
-  push:
-    paths:
-      - 'paimon-vector/**'
-  pull_request:
-    paths:
-      - 'paimon-vector/**'
-
-env:
-  JDK_VERSION: 8
-  MAVEN_OPTS: -Dmaven.wagon.httpconnectionManager.ttlSeconds=30 
-Dmaven.wagon.http.retryHandler.requestSentEnabled=true
-
-concurrency:
-  group: ${{ github.workflow }}-${{ github.event_name }}-${{ 
github.event.number || github.run_id }}
-  cancel-in-progress: true
-
-jobs:
-  vector_index_test:
-    runs-on: ubuntu-latest
-
-    steps:
-      - name: Checkout code
-        uses: actions/checkout@v6
-
-      - name: Set up JDK ${{ env.JDK_VERSION }}
-        uses: actions/setup-java@v5
-        with:
-          java-version: ${{ env.JDK_VERSION }}
-          distribution: 'temurin'
-
-      - name: Install Rust toolchain
-        run: |
-          curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- 
-y --default-toolchain stable --profile minimal
-          echo "$HOME/.cargo/bin" >> $GITHUB_PATH
-
-      - name: Clone and build paimon-vector-index native library
-        run: |
-          git clone --depth 1 
https://github.com/apache/paimon-vector-index.git /tmp/paimon-vector-index
-          cd /tmp/paimon-vector-index
-          cargo build --release -p paimon-vindex-jni
-
-      - name: Copy native library to resources
-        run: |
-          
RESOURCE_DIR=paimon-vector/paimon-vector-jni/src/main/resources/native/linux-amd64
-          mkdir -p ${RESOURCE_DIR}
-          cp /tmp/paimon-vector-index/target/release/libpaimon_vindex_jni.so 
${RESOURCE_DIR}/
-
-      - name: Build and test vector index modules
-        timeout-minutes: 30
-        run: |
-          mvn -T 2C -B -ntp clean install -DskipTests
-          mvn -B -ntp verify -pl 
paimon-vector/paimon-vector-jni,paimon-vector/paimon-vector-index 
-Dcheckstyle.skip=true -Dspotless.check.skip=true
-        env:
-          MAVEN_OPTS: -Xmx4096m
diff --git 
a/paimon-lumina/src/main/java/org/apache/paimon/lumina/index/LuminaVectorIndexOptions.java
 
b/paimon-lumina/src/main/java/org/apache/paimon/lumina/index/LuminaVectorIndexOptions.java
index 843e5c3813..3c067ef93b 100644
--- 
a/paimon-lumina/src/main/java/org/apache/paimon/lumina/index/LuminaVectorIndexOptions.java
+++ 
b/paimon-lumina/src/main/java/org/apache/paimon/lumina/index/LuminaVectorIndexOptions.java
@@ -130,7 +130,7 @@ public class LuminaVectorIndexOptions {
     /**
      * Resolves per-field Lumina options for {@code fieldName} into an 
effective {@link Options}.
      *
-     * <p>Following the convention shared with {@code paimon-vector-index} (PR 
#8239), a field-level
+     * <p>Following the convention shared with {@code paimon-vector} (PR 
#8239), a field-level
      * option is written {@code fields.<fieldName>.<option>} — <b>without</b> 
the {@code lumina.}
      * index-type prefix — and overrides the column-agnostic {@code 
lumina.<option>} for that field
      * only. For example {@code fields.embed.distance.metric} overrides {@code
@@ -139,7 +139,7 @@ public class LuminaVectorIndexOptions {
      *
      * <p>Only recognized Lumina options (the keys in {@code 
FIELD_OVERRIDABLE_KEYS}) are accepted;
      * any other {@code fields.<fieldName>.*} key (e.g. a merge/aggregation 
option) is left
-     * untouched, mirroring how {@code paimon-vector-index} ignores keys it 
does not recognize.
+     * untouched, mirroring how {@code paimon-vector} ignores keys it does not 
recognize.
      *
      * <p>Each recognized field option is flattened back to its plain {@code 
lumina.*} form, so the
      * rest of this class still sees only {@code lumina.*} keys and the 
metadata produced from these
diff --git a/paimon-python/dev/requirements-dev.txt 
b/paimon-python/dev/requirements-dev.txt
index c83a2e44b8..c1c4e4eea9 100644
--- a/paimon-python/dev/requirements-dev.txt
+++ b/paimon-python/dev/requirements-dev.txt
@@ -32,3 +32,5 @@ vortex-data==0.70.0; python_version >= "3.11"
 datafusion>=52; python_version >= "3.10"
 # Lumina vector search (optional, for lumina index tests)
 lumina-data>=0.1.0
+# paimon-vindex vector search (optional, for vindex index tests)
+paimon-vindex==0.1.0; python_version >= "3.9"
diff --git a/paimon-python/dev/run_mixed_tests.sh 
b/paimon-python/dev/run_mixed_tests.sh
index b2c8aec7a4..c3d91893df 100755
--- a/paimon-python/dev/run_mixed_tests.sh
+++ b/paimon-python/dev/run_mixed_tests.sh
@@ -483,6 +483,64 @@ run_lumina_vector_btree_test() {
     fi
 }
 
+ensure_paimon_vindex() {
+    if python -c "import paimon_vindex" >/dev/null 2>&1; then
+        return 0
+    fi
+
+    echo "Installing Python paimon-vindex dependency..."
+    if python -m pip install 'paimon-vindex==0.1.0'; then
+        return 0
+    fi
+
+    echo -e "${YELLOW}Direct pip install failed; installing paimon-vindex into 
a temporary target directory...${NC}"
+    local target_dir="${TMPDIR:-/tmp}/paimon-vindex-site"
+    rm -rf "$target_dir"
+    if python -m pip install --target "$target_dir" 'paimon-vindex==0.1.0'; 
then
+        export PYTHONPATH="$target_dir:${PYTHONPATH:-}"
+        return 0
+    fi
+
+    if python -c "import numpy" >/dev/null 2>&1; then
+        echo -e "${YELLOW}Dependency install failed but numpy is already 
available; retrying paimon-vindex without dependencies...${NC}"
+        rm -rf "$target_dir"
+        if python -m pip install --target "$target_dir" --no-deps 
'paimon-vindex==0.1.0'; then
+            export PYTHONPATH="$target_dir:${PYTHONPATH:-}"
+            return 0
+        fi
+    fi
+
+    echo -e "${RED}✗ Failed to install paimon-vindex${NC}"
+    return 1
+}
+
+# Function to run paimon-vindex vector index test (Java write index, Python 
read and search)
+run_vindex_vector_test() {
+    echo -e "${YELLOW}=== Running paimon-vindex Vector Index Test (Java Write, 
Python Read) ===${NC}"
+
+    cd "$PROJECT_ROOT"
+
+    echo "Running Maven test for JavaPyE2ETest.testVindexVectorIndexWrite..."
+    if mvn test 
-Dtest=org.apache.paimon.JavaPyE2ETest#testVindexVectorIndexWrite -pl 
paimon-vector -am -q -DfailIfNoTests=false -Drun.e2e.tests=true; then
+        echo -e "${GREEN}✓ Java test completed successfully${NC}"
+    else
+        echo -e "${RED}✗ Java test failed${NC}"
+        return 1
+    fi
+    cd "$PAIMON_PYTHON_DIR"
+    if ! ensure_paimon_vindex; then
+        return 1
+    fi
+    echo "Running Python test for 
JavaPyReadWriteTest.test_read_vindex_vector_index..."
+    if python -m pytest 
java_py_read_write_test.py::JavaPyReadWriteTest::test_read_vindex_vector_index 
-v; then
+        echo -e "${GREEN}✓ Python test completed successfully${NC}"
+        return 0
+    else
+        echo -e "${RED}✗ Python test failed${NC}"
+        return 1
+    fi
+}
+
 run_compact_conflict_test() {
     echo -e "${YELLOW}=== Running Compact Conflict Test (Java Write Base, 
Python Shard Update + Java Compact) ===${NC}"
 
@@ -745,6 +803,7 @@ main() {
     local tantivy_fulltext_result=0
     local lumina_vector_result=0
     local lumina_vector_btree_result=0
+    local vindex_vector_result=0
     local compact_conflict_result=0
     local blob_compact_conflict_result=0
     local blob_alter_compact_result=0
@@ -903,6 +962,18 @@ main() {
 
     echo ""
 
+    # Run paimon-vindex vector index test (requires Python >= 3.9)
+    if [[ "$PYTHON_MINOR" -ge 9 ]]; then
+        if ! run_vindex_vector_test; then
+            vindex_vector_result=1
+        fi
+    else
+        echo -e "${YELLOW}⏭ Skipping paimon-vindex Vector Index Test (requires 
Python >= 3.9, current: $PYTHON_VERSION)${NC}"
+        vindex_vector_result=0
+    fi
+
+    echo ""
+
     # Run compact conflict test (Java write+compact, Python read)
     if ! run_compact_conflict_test; then
         compact_conflict_result=1
@@ -1068,6 +1139,12 @@ main() {
         echo -e "${RED}✗ Lumina Vector + BTree Pre-Filter Test (Java Write, 
Python Read): FAILED${NC}"
     fi
 
+    if [[ $vindex_vector_result -eq 0 ]]; then
+        echo -e "${GREEN}✓ paimon-vindex Vector Index Test (Java Write, Python 
Read): PASSED${NC}"
+    else
+        echo -e "${RED}✗ paimon-vindex Vector Index Test (Java Write, Python 
Read): FAILED${NC}"
+    fi
+
     if [[ $compact_conflict_result -eq 0 ]]; then
         echo -e "${GREEN}✓ Compact Conflict Test (Java Write+Compact, Python 
Read): PASSED${NC}"
     else
@@ -1121,7 +1198,7 @@ main() {
     # Clean up warehouse directory after all tests
     cleanup_warehouse
 
-    if [[ $java_write_result -eq 0 && $python_read_result -eq 0 && 
$python_write_result -eq 0 && $java_read_result -eq 0 && $pk_dv_result -eq 0 && 
$btree_index_result -eq 0 && $bitmap_index_result -eq 0 && 
$compressed_global_index_result -eq 0 && $compressed_text_result -eq 0 && 
$tantivy_fulltext_result -eq 0 && $lumina_vector_result -eq 0 && 
$lumina_vector_btree_result -eq 0 && $compact_conflict_result -eq 0 && 
$blob_compact_conflict_result -eq 0 && $blob_alter_compact_result -eq 0 && $ 
[...]
+    if [[ $java_write_result -eq 0 && $python_read_result -eq 0 && 
$python_write_result -eq 0 && $java_read_result -eq 0 && $pk_dv_result -eq 0 && 
$btree_index_result -eq 0 && $bitmap_index_result -eq 0 && 
$compressed_global_index_result -eq 0 && $compressed_text_result -eq 0 && 
$tantivy_fulltext_result -eq 0 && $lumina_vector_result -eq 0 && 
$lumina_vector_btree_result -eq 0 && $vindex_vector_result -eq 0 && 
$compact_conflict_result -eq 0 && $blob_compact_conflict_result -eq 0 && $blob_ 
[...]
         echo -e "${GREEN}🎉 All tests passed! Java-Python interoperability 
verified.${NC}"
         return 0
     else
diff --git a/paimon-python/pypaimon/globalindex/vindex/__init__.py 
b/paimon-python/pypaimon/globalindex/vindex/__init__.py
new file mode 100644
index 0000000000..76373df86c
--- /dev/null
+++ b/paimon-python/pypaimon/globalindex/vindex/__init__.py
@@ -0,0 +1,18 @@
+# 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.
+
+"""paimon-vindex based global index readers."""
diff --git 
a/paimon-python/pypaimon/globalindex/vindex/vindex_vector_global_index_reader.py
 
b/paimon-python/pypaimon/globalindex/vindex/vindex_vector_global_index_reader.py
new file mode 100644
index 0000000000..5b5c93a1b3
--- /dev/null
+++ 
b/paimon-python/pypaimon/globalindex/vindex/vindex_vector_global_index_reader.py
@@ -0,0 +1,198 @@
+# 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.
+
+"""Vector global index reader using paimon-vindex."""
+
+import os
+import threading
+
+import numpy as np
+
+from pypaimon.common.file_io import pread, supports_pread
+from pypaimon.globalindex.global_index_reader import GlobalIndexReader, 
_completed_future
+from pypaimon.globalindex.vector_search_result import 
DictBasedScoredIndexResult
+
+VINDEX_IDENTIFIERS = ("ivf-flat", "ivf-pq", "ivf-hnsw-flat", "ivf-hnsw-sq")
+
+NPROBE_PARAMETER = "ivf.nprobe"
+EF_SEARCH_PARAMETER = "hnsw.ef_search"
+DEFAULT_NPROBE = 16
+DEFAULT_EF_SEARCH = 0
+
+
+class PaimonVindexInput:
+    """Input adapter required by paimon_vindex.VectorIndexReader."""
+
+    def __init__(self, stream):
+        self._stream = stream
+        self._supports_pread = supports_pread(stream)
+        self._lock = threading.Lock()
+
+    def pread_many(self, ranges):
+        if self._supports_pread:
+            return [pread(self._stream, length, offset) for offset, length in 
ranges]
+
+        chunks = []
+        with self._lock:
+            for offset, length in ranges:
+                self._stream.seek(offset)
+                chunks.append(self._stream.read(length))
+        return chunks
+
+
+class VindexVectorGlobalIndexReader(GlobalIndexReader):
+    """Vector global index reader using paimon-vindex."""
+
+    def __init__(self, file_io, index_path, io_metas, options=None):
+        assert len(io_metas) == 1, "Expected exactly one index file per shard"
+        self._file_io = file_io
+        self._index_path = index_path
+        self._io_meta = io_metas[0]
+        self._options = dict(options or {})
+        self._stream = None
+        self._index_input = None
+        self._reader = None
+        self._metadata = None
+        self._load_lock = threading.Lock()
+
+    def visit_vector_search(self, vector_search):
+        self._ensure_loaded()
+
+        query = np.asarray(vector_search.vector, dtype=np.float32)
+        if query.ndim != 1:
+            raise ValueError("Query vector must be a one-dimensional float32 
array")
+        expected_dim = self._metadata.dimension
+        if query.shape[0] != expected_dim:
+            raise ValueError(
+                "Query vector dimension mismatch: expected %d, got %d"
+                % (expected_dim, query.shape[0]))
+
+        effective_k = self._effective_k(vector_search)
+        if effective_k <= 0:
+            return _completed_future(None)
+
+        options = vector_search.options or {}
+        nprobe = _int_parameter(options, NPROBE_PARAMETER, DEFAULT_NPROBE)
+        ef_search = _int_parameter(options, EF_SEARCH_PARAMETER, 
DEFAULT_EF_SEARCH)
+        filter_bytes = _filter_bytes(vector_search.include_row_ids)
+
+        ids, distances = self._reader.search(
+            query, effective_k, nprobe, ef_search, filter_bytes=filter_bytes)
+        id_to_scores = _build_scores(ids, distances, self._metadata.metric)
+        if not id_to_scores:
+            return _completed_future(None)
+        return _completed_future(DictBasedScoredIndexResult(id_to_scores))
+
+    def vector_metric(self):
+        self._ensure_loaded()
+        return self._metadata.metric
+
+    def _effective_k(self, vector_search):
+        limit = vector_search.limit
+        total_vectors = getattr(self._metadata, "total_vectors", limit)
+        effective_k = min(limit, int(total_vectors))
+        include_row_ids = vector_search.include_row_ids
+        if include_row_ids is not None:
+            cardinality = include_row_ids.cardinality()
+            if cardinality == 0:
+                return 0
+            effective_k = min(effective_k, cardinality)
+        return effective_k
+
+    def _ensure_loaded(self):
+        if self._reader is not None:
+            return
+
+        with self._load_lock:
+            if self._reader is not None:
+                return
+
+            try:
+                from paimon_vindex import VectorIndexReader
+            except ImportError as e:
+                raise ImportError(
+                    "paimon-vindex is required to read vindex vector indexes. "
+                    "Install paimon-vindex==0.1.0 or pypaimon[vindex].") from e
+
+            file_path = (self._io_meta.external_path
+                         if self._io_meta.external_path
+                         else os.path.join(self._index_path, 
self._io_meta.file_name))
+            stream = self._file_io.new_input_stream(file_path)
+            try:
+                index_input = PaimonVindexInput(stream)
+                reader = VectorIndexReader(index_input)
+                self._metadata = reader.metadata()
+                self._index_input = index_input
+                self._reader = reader
+                self._stream = stream
+            except Exception:
+                stream.close()
+                raise
+
+    def __enter__(self):
+        return self
+
+    def __exit__(self, exc_type, exc_val, exc_tb):
+        self.close()
+        return False
+
+    def close(self):
+        if self._reader is not None:
+            self._reader.close()
+            self._reader = None
+        if self._stream is not None:
+            self._stream.close()
+            self._stream = None
+
+
+def _filter_bytes(include_row_ids):
+    if include_row_ids is None:
+        return None
+    if include_row_ids.cardinality() == 0:
+        return None
+    return include_row_ids.serialize()
+
+
+def _build_scores(ids, distances, metric):
+    id_to_scores = {}
+    for row_id, distance in zip(ids, distances):
+        row_id = int(row_id)
+        if row_id < 0:
+            continue
+        id_to_scores[row_id] = _convert_distance_to_score(float(distance), 
metric)
+    return id_to_scores
+
+
+def _convert_distance_to_score(distance, metric):
+    if metric == "l2":
+        return 1.0 / (1.0 + distance)
+    if metric == "cosine":
+        return 1.0 - distance
+    if metric == "inner_product":
+        return -distance
+    raise ValueError("Unknown vector search metric: %s" % metric)
+
+
+def _int_parameter(options, key, default_value):
+    value = options.get(key)
+    if value is None:
+        return default_value
+    try:
+        return int(value)
+    except ValueError as e:
+        raise ValueError(
+            "Invalid value for '%s': %s. Must be an integer." % (key, value)) 
from e
diff --git a/paimon-python/pypaimon/table/source/vector_search_read.py 
b/paimon-python/pypaimon/table/source/vector_search_read.py
index 005a2ce85b..566b093cd6 100644
--- a/paimon-python/pypaimon/table/source/vector_search_read.py
+++ b/paimon-python/pypaimon/table/source/vector_search_read.py
@@ -225,8 +225,16 @@ def _create_vector_reader(index_type, file_io, index_path, 
index_io_meta_list, o
         LUMINA_IDENTIFIERS,
         LuminaVectorGlobalIndexReader,
     )
+    from pypaimon.globalindex.vindex.vindex_vector_global_index_reader import (
+        VINDEX_IDENTIFIERS,
+        VindexVectorGlobalIndexReader,
+    )
     if index_type in LUMINA_IDENTIFIERS:
         return LuminaVectorGlobalIndexReader(
             file_io, index_path, index_io_meta_list, options
         )
+    if index_type in VINDEX_IDENTIFIERS:
+        return VindexVectorGlobalIndexReader(
+            file_io, index_path, index_io_meta_list, options
+        )
     raise ValueError("Unsupported vector index type: '%s'" % index_type)
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 5b2929cfac..f940f38a69 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
@@ -1220,6 +1220,39 @@ class JavaPyReadWriteTest(unittest.TestCase):
                 print(f"Lumina vector search ({label}) matched rows: 
ids={ids}")
                 self.assertIn(0, ids)
 
+    def test_read_vindex_vector_index(self):
+        """Test reading a paimon-vindex vector index built by Java."""
+        if sys.version_info < (3, 9):
+            self.skipTest("paimon-vindex requires Python >= 3.9")
+        try:
+            import paimon_vindex  # noqa: F401
+        except ImportError:
+            self.skipTest("paimon-vindex is not installed")
+
+        table = self.catalog.get_table('default.test_vindex_vector')
+
+        builder = table.new_vector_search_builder()
+        builder.with_vector_column('embedding')
+        builder.with_query_vector([1.0, 0.0, 0.0, 0.0])
+        builder.with_limit(3)
+
+        result = builder.execute_local()
+        row_ids = sorted(list(result.results()))
+        print(f"paimon-vindex vector search for [1,0,0,0]: row_ids={row_ids}")
+        self.assertIn(0, row_ids)
+        self.assertEqual(len(row_ids), 3)
+
+        read_builder = table.new_read_builder()
+        scan = read_builder.new_scan().with_global_index_result(result)
+        plan = scan.plan()
+        table_read = read_builder.new_read()
+        pa_table = table_read.to_arrow(plan.splits())
+        pa_table = table_sort_by(pa_table, 'id')
+        self.assertEqual(pa_table.num_rows, 3)
+        ids = pa_table.column('id').to_pylist()
+        print(f"paimon-vindex vector search matched rows: ids={ids}")
+        self.assertIn(0, ids)
+
     def test_read_lumina_vector_with_btree_filter(self):
         """Vector search + btree scalar pre-filter, using a table that Java
         populated with both a Lumina vector index on `embedding` and a BTree
diff --git a/paimon-python/pypaimon/tests/vindex_vector_index_test.py 
b/paimon-python/pypaimon/tests/vindex_vector_index_test.py
new file mode 100644
index 0000000000..eae983ca42
--- /dev/null
+++ b/paimon-python/pypaimon/tests/vindex_vector_index_test.py
@@ -0,0 +1,41 @@
+# 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 pypaimon.globalindex.vector_search_result import 
DictBasedScoredIndexResult
+from pypaimon.globalindex.vindex.vindex_vector_global_index_reader import 
_build_scores
+
+
+class VindexVectorIndexTest(unittest.TestCase):
+
+    def test_inner_product_distance_converted_to_higher_is_better_score(self):
+        id_to_scores = _build_scores(
+            [10, 20, 30],
+            [-1.0, -0.5, -0.1],
+            "inner_product")
+
+        self.assertEqual(1.0, id_to_scores[10])
+        self.assertEqual(0.5, id_to_scores[20])
+        self.assertEqual(0.1, id_to_scores[30])
+
+        top1 = DictBasedScoredIndexResult(id_to_scores).top_k(1)
+        self.assertEqual([10], top1.results().to_list())
+
+
+if __name__ == '__main__':
+    unittest.main()
diff --git a/paimon-python/setup.py b/paimon-python/setup.py
index e4974bdc9b..c4a543f8fb 100644
--- a/paimon-python/setup.py
+++ b/paimon-python/setup.py
@@ -180,6 +180,9 @@ setup(
         'lumina': [
             'lumina-data>=0.1.0'
         ],
+        'vindex': [
+            'paimon-vindex==0.1.0; python_version>="3.9"',
+        ],
         'sql': [
             'pypaimon-rust; python_version>="3.10"',
             'datafusion>=52; python_version>="3.10"',
diff --git a/paimon-vector/paimon-vector-index/pom.xml 
b/paimon-vector/paimon-vector-index/pom.xml
deleted file mode 100644
index 6744de7c9e..0000000000
--- a/paimon-vector/paimon-vector-index/pom.xml
+++ /dev/null
@@ -1,200 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-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.
--->
-<project xmlns="http://maven.apache.org/POM/4.0.0";
-         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance";
-         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 
http://maven.apache.org/xsd/maven-4.0.0.xsd";>
-    <modelVersion>4.0.0</modelVersion>
-
-    <parent>
-        <artifactId>paimon-vector</artifactId>
-        <groupId>org.apache.paimon</groupId>
-        <version>1.5-SNAPSHOT</version>
-    </parent>
-
-    <artifactId>paimon-vector-index</artifactId>
-    <name>Paimon : Vector Index</name>
-
-    <dependencies>
-        <dependency>
-            <groupId>org.apache.paimon</groupId>
-            <artifactId>paimon-vector-jni</artifactId>
-            <version>${project.version}</version>
-        </dependency>
-
-        <dependency>
-            <groupId>org.apache.paimon</groupId>
-            <artifactId>paimon-common</artifactId>
-            <version>${project.version}</version>
-            <scope>provided</scope>
-        </dependency>
-
-        <dependency>
-            <groupId>org.apache.paimon</groupId>
-            <artifactId>paimon-shade-jackson-2</artifactId>
-            
<version>${paimon.shade.jackson.version}-${paimon.shade.version}</version>
-        </dependency>
-
-        <!-- test dependencies -->
-        <dependency>
-            <groupId>org.junit.jupiter</groupId>
-            <artifactId>junit-jupiter</artifactId>
-            <version>${junit5.version}</version>
-            <scope>test</scope>
-        </dependency>
-
-        <dependency>
-            <groupId>org.apache.paimon</groupId>
-            <artifactId>paimon-core</artifactId>
-            <version>${project.version}</version>
-            <scope>test</scope>
-        </dependency>
-
-        <dependency>
-            <groupId>org.apache.paimon</groupId>
-            <artifactId>paimon-core</artifactId>
-            <version>${project.version}</version>
-            <type>test-jar</type>
-            <scope>test</scope>
-        </dependency>
-
-        <dependency>
-            <groupId>org.apache.paimon</groupId>
-            <artifactId>paimon-common</artifactId>
-            <version>${project.version}</version>
-            <type>test-jar</type>
-            <scope>test</scope>
-        </dependency>
-
-        <dependency>
-            <groupId>org.apache.paimon</groupId>
-            <artifactId>paimon-format</artifactId>
-            <version>${project.version}</version>
-            <scope>test</scope>
-        </dependency>
-
-        <dependency>
-            <groupId>org.apache.paimon</groupId>
-            <artifactId>paimon-test-utils</artifactId>
-            <version>${project.version}</version>
-            <scope>test</scope>
-        </dependency>
-
-        <dependency>
-            <groupId>org.apache.hadoop</groupId>
-            <artifactId>hadoop-hdfs-client</artifactId>
-            <version>${hadoop.version}</version>
-            <scope>test</scope>
-            <exclusions>
-                <exclusion>
-                    <groupId>org.apache.avro</groupId>
-                    <artifactId>avro</artifactId>
-                </exclusion>
-                <exclusion>
-                    <groupId>log4j</groupId>
-                    <artifactId>log4j</artifactId>
-                </exclusion>
-                <exclusion>
-                    <groupId>org.slf4j</groupId>
-                    <artifactId>slf4j-log4j12</artifactId>
-                </exclusion>
-            </exclusions>
-        </dependency>
-
-        <dependency>
-            <groupId>org.apache.hadoop</groupId>
-            <artifactId>hadoop-common</artifactId>
-            <version>${hadoop.version}</version>
-            <scope>test</scope>
-            <exclusions>
-                <exclusion>
-                    <groupId>org.apache.avro</groupId>
-                    <artifactId>avro</artifactId>
-                </exclusion>
-                <exclusion>
-                    <groupId>log4j</groupId>
-                    <artifactId>log4j</artifactId>
-                </exclusion>
-                <exclusion>
-                    <groupId>org.slf4j</groupId>
-                    <artifactId>slf4j-log4j12</artifactId>
-                </exclusion>
-                <exclusion>
-                    <groupId>jdk.tools</groupId>
-                    <artifactId>jdk.tools</artifactId>
-                </exclusion>
-                <exclusion>
-                    <groupId>com.google.protobuf</groupId>
-                    <artifactId>protobuf-java</artifactId>
-                </exclusion>
-            </exclusions>
-        </dependency>
-
-        <dependency>
-            <groupId>org.apache.hadoop</groupId>
-            <artifactId>hadoop-mapreduce-client-core</artifactId>
-            <version>${hadoop.version}</version>
-            <scope>test</scope>
-            <exclusions>
-                <exclusion>
-                    <groupId>org.apache.avro</groupId>
-                    <artifactId>avro</artifactId>
-                </exclusion>
-                <exclusion>
-                    <groupId>com.google.protobuf</groupId>
-                    <artifactId>protobuf-java</artifactId>
-                </exclusion>
-                <exclusion>
-                    <groupId>ch.qos.reload4j</groupId>
-                    <artifactId>reload4j</artifactId>
-                </exclusion>
-                <exclusion>
-                    <groupId>org.slf4j</groupId>
-                    <artifactId>slf4j-reload4j</artifactId>
-                </exclusion>
-                <exclusion>
-                    <groupId>log4j</groupId>
-                    <artifactId>log4j</artifactId>
-                </exclusion>
-                <exclusion>
-                    <groupId>org.slf4j</groupId>
-                    <artifactId>slf4j-log4j12</artifactId>
-                </exclusion>
-                <exclusion>
-                    <groupId>jdk.tools</groupId>
-                    <artifactId>jdk.tools</artifactId>
-                </exclusion>
-            </exclusions>
-        </dependency>
-    </dependencies>
-
-    <build>
-        <plugins>
-            <plugin>
-                <groupId>org.apache.maven.plugins</groupId>
-                <artifactId>maven-surefire-plugin</artifactId>
-                <configuration>
-                    <forkCount>1</forkCount>
-                    <redirectTestOutputToFile>true</redirectTestOutputToFile>
-                    <parallel>none</parallel>
-                </configuration>
-            </plugin>
-        </plugins>
-    </build>
-</project>
diff --git a/paimon-vector/paimon-vector-jni/pom.xml 
b/paimon-vector/paimon-vector-jni/pom.xml
deleted file mode 100644
index 23d00d043a..0000000000
--- a/paimon-vector/paimon-vector-jni/pom.xml
+++ /dev/null
@@ -1,69 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-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.
--->
-<project xmlns="http://maven.apache.org/POM/4.0.0";
-         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance";
-         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 
http://maven.apache.org/xsd/maven-4.0.0.xsd";>
-    <modelVersion>4.0.0</modelVersion>
-
-    <parent>
-        <artifactId>paimon-vector</artifactId>
-        <groupId>org.apache.paimon</groupId>
-        <version>1.5-SNAPSHOT</version>
-    </parent>
-
-    <artifactId>paimon-vector-jni</artifactId>
-    <name>Paimon : Vector Index JNI</name>
-
-    <properties>
-        <target.java.version>1.8</target.java.version>
-        <spotless.check.skip>true</spotless.check.skip>
-        <spotless.apply.skip>true</spotless.apply.skip>
-        <checkstyle.skip>true</checkstyle.skip>
-    </properties>
-
-    <dependencies>
-        <dependency>
-            <groupId>org.apache.paimon</groupId>
-            <artifactId>paimon-shade-guava-30</artifactId>
-            
<version>${paimon.shade.guava.version}-${paimon.shade.version}</version>
-        </dependency>
-
-        <dependency>
-            <groupId>org.junit.jupiter</groupId>
-            <artifactId>junit-jupiter</artifactId>
-            <version>${junit5.version}</version>
-            <scope>test</scope>
-        </dependency>
-    </dependencies>
-
-    <build>
-        <plugins>
-            <plugin>
-                <groupId>org.apache.maven.plugins</groupId>
-                <artifactId>maven-surefire-plugin</artifactId>
-                <configuration>
-                    <forkCount>1</forkCount>
-                    <redirectTestOutputToFile>true</redirectTestOutputToFile>
-                    <parallel>none</parallel>
-                </configuration>
-            </plugin>
-        </plugins>
-    </build>
-</project>
diff --git 
a/paimon-vector/paimon-vector-jni/src/main/java/org/apache/paimon/index/vector/NativeLoader.java
 
b/paimon-vector/paimon-vector-jni/src/main/java/org/apache/paimon/index/vector/NativeLoader.java
deleted file mode 100644
index e667bbcb8f..0000000000
--- 
a/paimon-vector/paimon-vector-jni/src/main/java/org/apache/paimon/index/vector/NativeLoader.java
+++ /dev/null
@@ -1,83 +0,0 @@
-/*
- * 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.
- */
-
-package org.apache.paimon.index.vector;
-
-import org.apache.paimon.shade.guava30.com.google.common.io.ByteStreams;
-
-import java.io.File;
-import java.io.FileNotFoundException;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.util.Locale;
-
-/** Utility class for loading the native vector index JNI library. */
-public final class NativeLoader {
-    private static boolean loaded = false;
-
-    private NativeLoader() {}
-
-    public static synchronized void loadJni() {
-        if (loaded) {
-            return;
-        }
-
-        String osName = System.getProperty("os.name").toLowerCase(Locale.ROOT);
-        String osArch = System.getProperty("os.arch").toLowerCase(Locale.ROOT);
-        String libName = "libpaimon_vindex_jni";
-
-        String libExt;
-        String osShortName;
-        if (osName.contains("win")) {
-            osShortName = "win";
-            libExt = ".dll";
-            libName += libExt;
-        } else if (osName.contains("mac")) {
-            osShortName = "darwin";
-            libExt = ".dylib";
-            libName += libExt;
-        } else if (osName.contains("nix") || osName.contains("nux")) {
-            osShortName = "linux";
-            libExt = ".so";
-            libName += libExt;
-        } else {
-            throw new UnsupportedOperationException("Unsupported OS: " + 
osName);
-        }
-
-        String libPath = "/native/" + osShortName + "-" + osArch + "/" + 
libName;
-        try (InputStream in = NativeLoader.class.getResourceAsStream(libPath)) 
{
-            if (in == null) {
-                throw new FileNotFoundException("Library not found: " + 
libPath);
-            }
-            File tempFile = File.createTempFile("libpaimon_vindex_jni", 
libExt);
-            tempFile.deleteOnExit();
-
-            try (OutputStream out = new FileOutputStream(tempFile)) {
-                ByteStreams.copy(in, out);
-            }
-            libName = tempFile.getAbsolutePath();
-        } catch (IOException e) {
-            throw new RuntimeException("Failed to load library: " + 
e.getMessage(), e);
-        }
-
-        System.load(libName);
-        loaded = true;
-    }
-}
diff --git 
a/paimon-vector/paimon-vector-jni/src/main/java/org/apache/paimon/index/vector/VectorIndexInput.java
 
b/paimon-vector/paimon-vector-jni/src/main/java/org/apache/paimon/index/vector/VectorIndexInput.java
deleted file mode 100644
index dca4430181..0000000000
--- 
a/paimon-vector/paimon-vector-jni/src/main/java/org/apache/paimon/index/vector/VectorIndexInput.java
+++ /dev/null
@@ -1,23 +0,0 @@
-// 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.
-
-package org.apache.paimon.index.vector;
-
-public interface VectorIndexInput {
-
-    void pread(long[] positions, byte[][] buffers);
-}
diff --git 
a/paimon-vector/paimon-vector-jni/src/main/java/org/apache/paimon/index/vector/VectorIndexMetadata.java
 
b/paimon-vector/paimon-vector-jni/src/main/java/org/apache/paimon/index/vector/VectorIndexMetadata.java
deleted file mode 100644
index 4ffd89a4f3..0000000000
--- 
a/paimon-vector/paimon-vector-jni/src/main/java/org/apache/paimon/index/vector/VectorIndexMetadata.java
+++ /dev/null
@@ -1,94 +0,0 @@
-// 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.
-
-package org.apache.paimon.index.vector;
-
-public final class VectorIndexMetadata {
-
-    private final String indexType;
-    private final int dimension;
-    private final int nlist;
-    private final String metric;
-    private final long totalVectors;
-    private final int pqM;
-    private final int hnswM;
-    private final int hnswEfConstruction;
-    private final int hnswMaxLevel;
-
-    public VectorIndexMetadata(
-            String indexType,
-            int dimension,
-            int nlist,
-            String metric,
-            long totalVectors,
-            int pqM,
-            int hnswM,
-            int efConstruction,
-            int maxLevel) {
-        if (indexType == null) {
-            throw new NullPointerException("indexType");
-        }
-        if (metric == null) {
-            throw new NullPointerException("metric");
-        }
-        this.indexType = indexType;
-        this.dimension = dimension;
-        this.nlist = nlist;
-        this.metric = metric;
-        this.totalVectors = totalVectors;
-        this.pqM = pqM;
-        this.hnswM = hnswM;
-        this.hnswEfConstruction = efConstruction;
-        this.hnswMaxLevel = maxLevel;
-    }
-
-    public String indexType() {
-        return indexType;
-    }
-
-    public int dimension() {
-        return dimension;
-    }
-
-    public int nlist() {
-        return nlist;
-    }
-
-    public String metric() {
-        return metric;
-    }
-
-    public long totalVectors() {
-        return totalVectors;
-    }
-
-    public int pqM() {
-        return pqM;
-    }
-
-    public int hnswM() {
-        return hnswM;
-    }
-
-    public int hnswEfConstruction() {
-        return hnswEfConstruction;
-    }
-
-    public int hnswMaxLevel() {
-        return hnswMaxLevel;
-    }
-}
diff --git 
a/paimon-vector/paimon-vector-jni/src/main/java/org/apache/paimon/index/vector/VectorIndexNative.java
 
b/paimon-vector/paimon-vector-jni/src/main/java/org/apache/paimon/index/vector/VectorIndexNative.java
deleted file mode 100644
index b6a5bbf0a7..0000000000
--- 
a/paimon-vector/paimon-vector-jni/src/main/java/org/apache/paimon/index/vector/VectorIndexNative.java
+++ /dev/null
@@ -1,62 +0,0 @@
-// 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.
-
-package org.apache.paimon.index.vector;
-
-final class VectorIndexNative {
-
-    static {
-        NativeLoader.loadJni();
-    }
-
-    private VectorIndexNative() {}
-
-    static native long createWriter(String[] optionKeys, String[] 
optionValues);
-
-    static native int writerDimension(long ptr);
-
-    static native void train(long ptr, float[] data, int n);
-
-    static native void addVectors(long ptr, long[] ids, float[] data, int n);
-
-    static native void writeIndex(long ptr, Object streamOutput);
-
-    static native void freeWriter(long ptr);
-
-    static native long openReader(Object streamInput);
-
-    static native VectorIndexMetadata metadata(long ptr);
-
-    static native VectorSearchResult search(long ptr, float[] query, int k, 
int nprobe, int efSearch);
-
-    static native VectorSearchResult searchWithRoaringFilter(
-            long ptr, float[] query, int k, int nprobe, int efSearch, byte[] 
roaringFilter);
-
-    static native VectorSearchBatchResult searchBatch(
-            long ptr, float[] queries, int queryCount, int k, int nprobe, int 
efSearch);
-
-    static native VectorSearchBatchResult searchBatchWithRoaringFilter(
-            long ptr,
-            float[] queries,
-            int queryCount,
-            int k,
-            int nprobe,
-            int efSearch,
-            byte[] roaringFilter);
-
-    static native void freeReader(long ptr);
-}
diff --git 
a/paimon-vector/paimon-vector-jni/src/main/java/org/apache/paimon/index/vector/VectorIndexReader.java
 
b/paimon-vector/paimon-vector-jni/src/main/java/org/apache/paimon/index/vector/VectorIndexReader.java
deleted file mode 100644
index 34eefc7dca..0000000000
--- 
a/paimon-vector/paimon-vector-jni/src/main/java/org/apache/paimon/index/vector/VectorIndexReader.java
+++ /dev/null
@@ -1,196 +0,0 @@
-// 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.
-
-package org.apache.paimon.index.vector;
-
-public final class VectorIndexReader implements AutoCloseable {
-
-    private final Object nativeHandleLock = new Object();
-    private long nativePtr;
-    private Thread nativeHandleOwner;
-    private VectorIndexMetadata metadata;
-
-    public VectorIndexReader(VectorIndexInput input) {
-        if (input == null) {
-            throw new NullPointerException("input");
-        }
-        this.nativePtr = VectorIndexNative.openReader(input);
-    }
-
-    private VectorIndexReader(long nativePtr) {
-        this.nativePtr = nativePtr;
-    }
-
-    static VectorIndexReader fromNativePointerForTesting(long nativePtr) {
-        return new VectorIndexReader(nativePtr);
-    }
-
-    public VectorIndexMetadata metadata() {
-        synchronized (nativeHandleLock) {
-            enterNativeHandle();
-            try {
-                requireOpen();
-                if (metadata == null) {
-                    metadata = VectorIndexNative.metadata(nativePtr);
-                }
-                return metadata;
-            } finally {
-                exitNativeHandle();
-            }
-        }
-    }
-
-    public String indexType() {
-        return metadata().indexType();
-    }
-
-    public int dimension() {
-        return metadata().dimension();
-    }
-
-    public long totalVectors() {
-        return metadata().totalVectors();
-    }
-
-    public VectorSearchResult search(float[] query, int topK, int nprobe) {
-        return search(query, topK, nprobe, 0);
-    }
-
-    public VectorSearchResult search(float[] query, int topK, int nprobe, int 
efSearch) {
-        validateQuery(query);
-        synchronized (nativeHandleLock) {
-            enterNativeHandle();
-            try {
-                return VectorIndexNative.search(requireOpen(), query, topK, 
nprobe, efSearch);
-            } finally {
-                exitNativeHandle();
-            }
-        }
-    }
-
-    public VectorSearchResult search(float[] query, int topK, int nprobe, 
byte[] roaringFilter) {
-        return search(query, topK, nprobe, 0, roaringFilter);
-    }
-
-    public VectorSearchResult search(
-            float[] query, int topK, int nprobe, int efSearch, byte[] 
roaringFilter) {
-        validateQuery(query);
-        if (roaringFilter == null) {
-            throw new NullPointerException("roaringFilter");
-        }
-        synchronized (nativeHandleLock) {
-            enterNativeHandle();
-            try {
-                return VectorIndexNative.searchWithRoaringFilter(
-                        requireOpen(), query, topK, nprobe, efSearch, 
roaringFilter);
-            } finally {
-                exitNativeHandle();
-            }
-        }
-    }
-
-    public VectorSearchBatchResult searchBatch(
-            float[] queries, int queryCount, int topK, int nprobe) {
-        return searchBatch(queries, queryCount, topK, nprobe, 0);
-    }
-
-    public VectorSearchBatchResult searchBatch(
-            float[] queries, int queryCount, int topK, int nprobe, int 
efSearch) {
-        if (queries == null) {
-            throw new NullPointerException("queries");
-        }
-        synchronized (nativeHandleLock) {
-            enterNativeHandle();
-            try {
-                return VectorIndexNative.searchBatch(
-                        requireOpen(), queries, queryCount, topK, nprobe, 
efSearch);
-            } finally {
-                exitNativeHandle();
-            }
-        }
-    }
-
-    public VectorSearchBatchResult searchBatch(
-            float[] queries, int queryCount, int topK, int nprobe, byte[] 
roaringFilter) {
-        return searchBatch(queries, queryCount, topK, nprobe, 0, 
roaringFilter);
-    }
-
-    public VectorSearchBatchResult searchBatch(
-            float[] queries,
-            int queryCount,
-            int topK,
-            int nprobe,
-            int efSearch,
-            byte[] roaringFilter) {
-        if (queries == null) {
-            throw new NullPointerException("queries");
-        }
-        if (roaringFilter == null) {
-            throw new NullPointerException("roaringFilter");
-        }
-        synchronized (nativeHandleLock) {
-            enterNativeHandle();
-            try {
-                return VectorIndexNative.searchBatchWithRoaringFilter(
-                        requireOpen(), queries, queryCount, topK, nprobe, 
efSearch, roaringFilter);
-            } finally {
-                exitNativeHandle();
-            }
-        }
-    }
-
-    @Override
-    public void close() {
-        synchronized (nativeHandleLock) {
-            enterNativeHandle();
-            try {
-                long ptr = nativePtr;
-                nativePtr = 0L;
-                if (ptr != 0L) {
-                    VectorIndexNative.freeReader(ptr);
-                }
-            } finally {
-                exitNativeHandle();
-            }
-        }
-    }
-
-    private void validateQuery(float[] query) {
-        if (query == null) {
-            throw new NullPointerException("query");
-        }
-    }
-
-    private long requireOpen() {
-        if (nativePtr == 0L) {
-            throw new IllegalStateException("VectorIndexReader is closed");
-        }
-        return nativePtr;
-    }
-
-    private void enterNativeHandle() {
-        Thread current = Thread.currentThread();
-        if (nativeHandleOwner == current) {
-            throw new IllegalStateException("VectorIndexReader native handle 
is already in use");
-        }
-        nativeHandleOwner = current;
-    }
-
-    private void exitNativeHandle() {
-        nativeHandleOwner = null;
-    }
-}
diff --git 
a/paimon-vector/paimon-vector-jni/src/main/java/org/apache/paimon/index/vector/VectorIndexWriter.java
 
b/paimon-vector/paimon-vector-jni/src/main/java/org/apache/paimon/index/vector/VectorIndexWriter.java
deleted file mode 100644
index 0dda2a3eba..0000000000
--- 
a/paimon-vector/paimon-vector-jni/src/main/java/org/apache/paimon/index/vector/VectorIndexWriter.java
+++ /dev/null
@@ -1,131 +0,0 @@
-// 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.
-
-package org.apache.paimon.index.vector;
-
-import java.util.Map;
-
-public final class VectorIndexWriter implements AutoCloseable {
-
-    private final Object nativeHandleLock = new Object();
-    private long nativePtr;
-    private Thread nativeHandleOwner;
-
-    public VectorIndexWriter(Map<String, String> options) {
-        String[] keys = new String[options.size()];
-        String[] values = new String[options.size()];
-        int index = 0;
-        for (Map.Entry<String, String> entry : options.entrySet()) {
-            keys[index] = entry.getKey();
-            values[index] = entry.getValue();
-            index++;
-        }
-        this.nativePtr = VectorIndexNative.createWriter(keys, values);
-    }
-
-    private VectorIndexWriter(long nativePtr) {
-        this.nativePtr = nativePtr;
-    }
-
-    static VectorIndexWriter fromNativePointerForTesting(long nativePtr) {
-        return new VectorIndexWriter(nativePtr);
-    }
-
-    public int dimension() {
-        return VectorIndexNative.writerDimension(requireOpen());
-    }
-
-    public void train(float[] data, int vectorCount) {
-        if (data == null) {
-            throw new NullPointerException("data");
-        }
-        synchronized (nativeHandleLock) {
-            enterNativeHandle();
-            try {
-                VectorIndexNative.train(requireOpen(), data, vectorCount);
-            } finally {
-                exitNativeHandle();
-            }
-        }
-    }
-
-    public void addVectors(long[] ids, float[] data, int vectorCount) {
-        if (ids == null) {
-            throw new NullPointerException("ids");
-        }
-        if (data == null) {
-            throw new NullPointerException("data");
-        }
-        synchronized (nativeHandleLock) {
-            enterNativeHandle();
-            try {
-                VectorIndexNative.addVectors(requireOpen(), ids, data, 
vectorCount);
-            } finally {
-                exitNativeHandle();
-            }
-        }
-    }
-
-    public void writeIndex(Object output) {
-        if (output == null) {
-            throw new NullPointerException("output");
-        }
-        synchronized (nativeHandleLock) {
-            enterNativeHandle();
-            try {
-                VectorIndexNative.writeIndex(requireOpen(), output);
-            } finally {
-                exitNativeHandle();
-            }
-        }
-    }
-
-    @Override
-    public void close() {
-        synchronized (nativeHandleLock) {
-            enterNativeHandle();
-            try {
-                long ptr = nativePtr;
-                nativePtr = 0L;
-                if (ptr != 0L) {
-                    VectorIndexNative.freeWriter(ptr);
-                }
-            } finally {
-                exitNativeHandle();
-            }
-        }
-    }
-
-    private long requireOpen() {
-        if (nativePtr == 0L) {
-            throw new IllegalStateException("VectorIndexWriter is closed");
-        }
-        return nativePtr;
-    }
-
-    private void enterNativeHandle() {
-        Thread current = Thread.currentThread();
-        if (nativeHandleOwner == current) {
-            throw new IllegalStateException("VectorIndexWriter native handle 
is already in use");
-        }
-        nativeHandleOwner = current;
-    }
-
-    private void exitNativeHandle() {
-        nativeHandleOwner = null;
-    }
-}
diff --git 
a/paimon-vector/paimon-vector-jni/src/main/java/org/apache/paimon/index/vector/VectorSearchBatchResult.java
 
b/paimon-vector/paimon-vector-jni/src/main/java/org/apache/paimon/index/vector/VectorSearchBatchResult.java
deleted file mode 100644
index 12952e932e..0000000000
--- 
a/paimon-vector/paimon-vector-jni/src/main/java/org/apache/paimon/index/vector/VectorSearchBatchResult.java
+++ /dev/null
@@ -1,100 +0,0 @@
-// 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.
-
-package org.apache.paimon.index.vector;
-
-import java.util.Arrays;
-
-public final class VectorSearchBatchResult {
-
-    private final long[] ids;
-    private final float[] distances;
-    private final int queryCount;
-    private final int topK;
-
-    public VectorSearchBatchResult(long[] ids, float[] distances, int 
queryCount, int topK) {
-        if (ids == null) {
-            throw new NullPointerException("ids");
-        }
-        if (distances == null) {
-            throw new NullPointerException("distances");
-        }
-        if (queryCount < 0) {
-            throw new IllegalArgumentException("queryCount must be >= 0");
-        }
-        if (topK < 0) {
-            throw new IllegalArgumentException("topK must be >= 0");
-        }
-        int expectedLength = checkedResultLength(queryCount, topK);
-        if (ids.length != expectedLength) {
-            throw new IllegalArgumentException(
-                    "ids length " + ids.length + " != queryCount * topK " + 
expectedLength);
-        }
-        if (distances.length != expectedLength) {
-            throw new IllegalArgumentException(
-                    "distances length "
-                            + distances.length
-                            + " != queryCount * topK "
-                            + expectedLength);
-        }
-        this.ids = ids.clone();
-        this.distances = distances.clone();
-        this.queryCount = queryCount;
-        this.topK = topK;
-    }
-
-    public int queryCount() {
-        return queryCount;
-    }
-
-    public int topK() {
-        return topK;
-    }
-
-    public long[] ids() {
-        return ids.clone();
-    }
-
-    public float[] distances() {
-        return distances.clone();
-    }
-
-    public long[] idsForQuery(int queryIndex) {
-        checkQueryIndex(queryIndex);
-        return Arrays.copyOfRange(ids, queryIndex * topK, (queryIndex + 1) * 
topK);
-    }
-
-    public float[] distancesForQuery(int queryIndex) {
-        checkQueryIndex(queryIndex);
-        return Arrays.copyOfRange(distances, queryIndex * topK, (queryIndex + 
1) * topK);
-    }
-
-    private void checkQueryIndex(int queryIndex) {
-        if (queryIndex < 0 || queryIndex >= queryCount) {
-            throw new IndexOutOfBoundsException(
-                    "queryIndex " + queryIndex + " out of range [0, " + 
queryCount + ')');
-        }
-    }
-
-    private static int checkedResultLength(int queryCount, int topK) {
-        long length = (long) queryCount * (long) topK;
-        if (length > Integer.MAX_VALUE) {
-            throw new IllegalArgumentException("queryCount * topK overflows 
int");
-        }
-        return (int) length;
-    }
-}
diff --git 
a/paimon-vector/paimon-vector-jni/src/main/java/org/apache/paimon/index/vector/VectorSearchResult.java
 
b/paimon-vector/paimon-vector-jni/src/main/java/org/apache/paimon/index/vector/VectorSearchResult.java
deleted file mode 100644
index 870aa49ce1..0000000000
--- 
a/paimon-vector/paimon-vector-jni/src/main/java/org/apache/paimon/index/vector/VectorSearchResult.java
+++ /dev/null
@@ -1,62 +0,0 @@
-// 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.
-
-package org.apache.paimon.index.vector;
-
-import java.util.Arrays;
-
-public final class VectorSearchResult {
-
-    private final long[] ids;
-    private final float[] distances;
-
-    public VectorSearchResult(long[] ids, float[] distances) {
-        if (ids == null) {
-            throw new NullPointerException("ids");
-        }
-        if (distances == null) {
-            throw new NullPointerException("distances");
-        }
-        if (ids.length != distances.length) {
-            throw new IllegalArgumentException(
-                    "ids length " + ids.length + " != distances length " + 
distances.length);
-        }
-        this.ids = ids.clone();
-        this.distances = distances.clone();
-    }
-
-    public int size() {
-        return ids.length;
-    }
-
-    public long[] ids() {
-        return ids.clone();
-    }
-
-    public float[] distances() {
-        return distances.clone();
-    }
-
-    @Override
-    public String toString() {
-        return "VectorSearchResult{ids="
-                + Arrays.toString(ids)
-                + ", distances="
-                + Arrays.toString(distances)
-                + '}';
-    }
-}
diff --git a/paimon-vector/pom.xml b/paimon-vector/pom.xml
index 5cbb01a35a..9fd20e915d 100644
--- a/paimon-vector/pom.xml
+++ b/paimon-vector/pom.xml
@@ -30,10 +30,175 @@ under the License.
 
     <artifactId>paimon-vector</artifactId>
     <name>Paimon : Vector Index</name>
-    <packaging>pom</packaging>
 
-    <modules>
-        <module>paimon-vector-jni</module>
-        <module>paimon-vector-index</module>
-    </modules>
+    <properties>
+        
<paimon-vector-index-java.version>0.1.0</paimon-vector-index-java.version>
+    </properties>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.apache.paimon</groupId>
+            <artifactId>paimon-vector-index-java</artifactId>
+            <version>${paimon-vector-index-java.version}</version>
+        </dependency>
+
+        <dependency>
+            <groupId>org.apache.paimon</groupId>
+            <artifactId>paimon-common</artifactId>
+            <version>${project.version}</version>
+            <scope>provided</scope>
+        </dependency>
+
+        <dependency>
+            <groupId>org.apache.paimon</groupId>
+            <artifactId>paimon-shade-jackson-2</artifactId>
+            
<version>${paimon.shade.jackson.version}-${paimon.shade.version}</version>
+        </dependency>
+
+        <!-- test dependencies -->
+        <dependency>
+            <groupId>org.junit.jupiter</groupId>
+            <artifactId>junit-jupiter</artifactId>
+            <version>${junit5.version}</version>
+            <scope>test</scope>
+        </dependency>
+
+        <dependency>
+            <groupId>org.apache.paimon</groupId>
+            <artifactId>paimon-core</artifactId>
+            <version>${project.version}</version>
+            <scope>test</scope>
+        </dependency>
+
+        <dependency>
+            <groupId>org.apache.paimon</groupId>
+            <artifactId>paimon-core</artifactId>
+            <version>${project.version}</version>
+            <type>test-jar</type>
+            <scope>test</scope>
+        </dependency>
+
+        <dependency>
+            <groupId>org.apache.paimon</groupId>
+            <artifactId>paimon-common</artifactId>
+            <version>${project.version}</version>
+            <type>test-jar</type>
+            <scope>test</scope>
+        </dependency>
+
+        <dependency>
+            <groupId>org.apache.paimon</groupId>
+            <artifactId>paimon-format</artifactId>
+            <version>${project.version}</version>
+            <scope>test</scope>
+        </dependency>
+
+        <dependency>
+            <groupId>org.apache.paimon</groupId>
+            <artifactId>paimon-test-utils</artifactId>
+            <version>${project.version}</version>
+            <scope>test</scope>
+        </dependency>
+
+        <dependency>
+            <groupId>org.apache.hadoop</groupId>
+            <artifactId>hadoop-hdfs-client</artifactId>
+            <version>${hadoop.version}</version>
+            <scope>test</scope>
+            <exclusions>
+                <exclusion>
+                    <groupId>org.apache.avro</groupId>
+                    <artifactId>avro</artifactId>
+                </exclusion>
+                <exclusion>
+                    <groupId>log4j</groupId>
+                    <artifactId>log4j</artifactId>
+                </exclusion>
+                <exclusion>
+                    <groupId>org.slf4j</groupId>
+                    <artifactId>slf4j-log4j12</artifactId>
+                </exclusion>
+            </exclusions>
+        </dependency>
+
+        <dependency>
+            <groupId>org.apache.hadoop</groupId>
+            <artifactId>hadoop-common</artifactId>
+            <version>${hadoop.version}</version>
+            <scope>test</scope>
+            <exclusions>
+                <exclusion>
+                    <groupId>org.apache.avro</groupId>
+                    <artifactId>avro</artifactId>
+                </exclusion>
+                <exclusion>
+                    <groupId>log4j</groupId>
+                    <artifactId>log4j</artifactId>
+                </exclusion>
+                <exclusion>
+                    <groupId>org.slf4j</groupId>
+                    <artifactId>slf4j-log4j12</artifactId>
+                </exclusion>
+                <exclusion>
+                    <groupId>jdk.tools</groupId>
+                    <artifactId>jdk.tools</artifactId>
+                </exclusion>
+                <exclusion>
+                    <groupId>com.google.protobuf</groupId>
+                    <artifactId>protobuf-java</artifactId>
+                </exclusion>
+            </exclusions>
+        </dependency>
+
+        <dependency>
+            <groupId>org.apache.hadoop</groupId>
+            <artifactId>hadoop-mapreduce-client-core</artifactId>
+            <version>${hadoop.version}</version>
+            <scope>test</scope>
+            <exclusions>
+                <exclusion>
+                    <groupId>org.apache.avro</groupId>
+                    <artifactId>avro</artifactId>
+                </exclusion>
+                <exclusion>
+                    <groupId>com.google.protobuf</groupId>
+                    <artifactId>protobuf-java</artifactId>
+                </exclusion>
+                <exclusion>
+                    <groupId>ch.qos.reload4j</groupId>
+                    <artifactId>reload4j</artifactId>
+                </exclusion>
+                <exclusion>
+                    <groupId>org.slf4j</groupId>
+                    <artifactId>slf4j-reload4j</artifactId>
+                </exclusion>
+                <exclusion>
+                    <groupId>log4j</groupId>
+                    <artifactId>log4j</artifactId>
+                </exclusion>
+                <exclusion>
+                    <groupId>org.slf4j</groupId>
+                    <artifactId>slf4j-log4j12</artifactId>
+                </exclusion>
+                <exclusion>
+                    <groupId>jdk.tools</groupId>
+                    <artifactId>jdk.tools</artifactId>
+                </exclusion>
+            </exclusions>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-surefire-plugin</artifactId>
+                <configuration>
+                    <forkCount>1</forkCount>
+                    <redirectTestOutputToFile>true</redirectTestOutputToFile>
+                    <parallel>none</parallel>
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
 </project>
diff --git 
a/paimon-vector/paimon-vector-index/src/main/java/org/apache/paimon/vector/index/IvfFlatVectorGlobalIndexerFactory.java
 
b/paimon-vector/src/main/java/org/apache/paimon/vector/index/IvfFlatVectorGlobalIndexerFactory.java
similarity index 92%
rename from 
paimon-vector/paimon-vector-index/src/main/java/org/apache/paimon/vector/index/IvfFlatVectorGlobalIndexerFactory.java
rename to 
paimon-vector/src/main/java/org/apache/paimon/vector/index/IvfFlatVectorGlobalIndexerFactory.java
index 572c7cf4ed..12da3c121d 100644
--- 
a/paimon-vector/paimon-vector-index/src/main/java/org/apache/paimon/vector/index/IvfFlatVectorGlobalIndexerFactory.java
+++ 
b/paimon-vector/src/main/java/org/apache/paimon/vector/index/IvfFlatVectorGlobalIndexerFactory.java
@@ -19,7 +19,7 @@
 package org.apache.paimon.vector.index;
 
 /** Factory for the {@code ivf-flat} vector index identifier. */
-public class IvfFlatVectorGlobalIndexerFactory extends 
VectorGlobalIndexerFactory {
+public class IvfFlatVectorGlobalIndexerFactory extends 
NativeVectorGlobalIndexerFactory {
 
     public static final String IDENTIFIER = "ivf-flat";
 
diff --git 
a/paimon-vector/paimon-vector-index/src/main/java/org/apache/paimon/vector/index/IvfHnswFlatVectorGlobalIndexerFactory.java
 
b/paimon-vector/src/main/java/org/apache/paimon/vector/index/IvfHnswFlatVectorGlobalIndexerFactory.java
similarity index 91%
rename from 
paimon-vector/paimon-vector-index/src/main/java/org/apache/paimon/vector/index/IvfHnswFlatVectorGlobalIndexerFactory.java
rename to 
paimon-vector/src/main/java/org/apache/paimon/vector/index/IvfHnswFlatVectorGlobalIndexerFactory.java
index 159e7af6f1..764920d5d7 100644
--- 
a/paimon-vector/paimon-vector-index/src/main/java/org/apache/paimon/vector/index/IvfHnswFlatVectorGlobalIndexerFactory.java
+++ 
b/paimon-vector/src/main/java/org/apache/paimon/vector/index/IvfHnswFlatVectorGlobalIndexerFactory.java
@@ -19,7 +19,7 @@
 package org.apache.paimon.vector.index;
 
 /** Factory for the {@code ivf-hnsw-flat} vector index identifier. */
-public class IvfHnswFlatVectorGlobalIndexerFactory extends 
VectorGlobalIndexerFactory {
+public class IvfHnswFlatVectorGlobalIndexerFactory extends 
NativeVectorGlobalIndexerFactory {
 
     public static final String IDENTIFIER = "ivf-hnsw-flat";
 
diff --git 
a/paimon-vector/paimon-vector-index/src/main/java/org/apache/paimon/vector/index/IvfHnswSqVectorGlobalIndexerFactory.java
 
b/paimon-vector/src/main/java/org/apache/paimon/vector/index/IvfHnswSqVectorGlobalIndexerFactory.java
similarity index 92%
rename from 
paimon-vector/paimon-vector-index/src/main/java/org/apache/paimon/vector/index/IvfHnswSqVectorGlobalIndexerFactory.java
rename to 
paimon-vector/src/main/java/org/apache/paimon/vector/index/IvfHnswSqVectorGlobalIndexerFactory.java
index 51c72cd8f3..3f09984db5 100644
--- 
a/paimon-vector/paimon-vector-index/src/main/java/org/apache/paimon/vector/index/IvfHnswSqVectorGlobalIndexerFactory.java
+++ 
b/paimon-vector/src/main/java/org/apache/paimon/vector/index/IvfHnswSqVectorGlobalIndexerFactory.java
@@ -19,7 +19,7 @@
 package org.apache.paimon.vector.index;
 
 /** Factory for the {@code ivf-hnsw-sq} vector index identifier. */
-public class IvfHnswSqVectorGlobalIndexerFactory extends 
VectorGlobalIndexerFactory {
+public class IvfHnswSqVectorGlobalIndexerFactory extends 
NativeVectorGlobalIndexerFactory {
 
     public static final String IDENTIFIER = "ivf-hnsw-sq";
 
diff --git 
a/paimon-vector/paimon-vector-index/src/main/java/org/apache/paimon/vector/index/IvfPqAlgorithmVectorGlobalIndexerFactory.java
 
b/paimon-vector/src/main/java/org/apache/paimon/vector/index/IvfPqAlgorithmVectorGlobalIndexerFactory.java
similarity index 91%
rename from 
paimon-vector/paimon-vector-index/src/main/java/org/apache/paimon/vector/index/IvfPqAlgorithmVectorGlobalIndexerFactory.java
rename to 
paimon-vector/src/main/java/org/apache/paimon/vector/index/IvfPqAlgorithmVectorGlobalIndexerFactory.java
index f3932de46e..f00adfcbfc 100644
--- 
a/paimon-vector/paimon-vector-index/src/main/java/org/apache/paimon/vector/index/IvfPqAlgorithmVectorGlobalIndexerFactory.java
+++ 
b/paimon-vector/src/main/java/org/apache/paimon/vector/index/IvfPqAlgorithmVectorGlobalIndexerFactory.java
@@ -19,7 +19,7 @@
 package org.apache.paimon.vector.index;
 
 /** Factory for the {@code ivf-pq} vector index identifier. */
-public class IvfPqAlgorithmVectorGlobalIndexerFactory extends 
VectorGlobalIndexerFactory {
+public class IvfPqAlgorithmVectorGlobalIndexerFactory extends 
NativeVectorGlobalIndexerFactory {
 
     public static final String IDENTIFIER = "ivf-pq";
 
diff --git 
a/paimon-vector/paimon-vector-index/src/main/java/org/apache/paimon/vector/index/VectorGlobalIndexReader.java
 
b/paimon-vector/src/main/java/org/apache/paimon/vector/index/NativeVectorGlobalIndexReader.java
similarity index 97%
rename from 
paimon-vector/paimon-vector-index/src/main/java/org/apache/paimon/vector/index/VectorGlobalIndexReader.java
rename to 
paimon-vector/src/main/java/org/apache/paimon/vector/index/NativeVectorGlobalIndexReader.java
index cde8d2a83d..1f0b46fca9 100644
--- 
a/paimon-vector/paimon-vector-index/src/main/java/org/apache/paimon/vector/index/VectorGlobalIndexReader.java
+++ 
b/paimon-vector/src/main/java/org/apache/paimon/vector/index/NativeVectorGlobalIndexReader.java
@@ -55,12 +55,12 @@ import java.util.concurrent.ExecutorService;
 import static org.apache.paimon.utils.Preconditions.checkArgument;
 
 /**
- * Vector global index reader using paimon-vector-index.
+ * Vector global index reader using paimon-vector-index-java.
  *
  * <p>Each shard has exactly one vector index file. The reader lazily opens 
the index and performs
  * vector similarity search.
  */
-public class VectorGlobalIndexReader implements GlobalIndexReader {
+public class NativeVectorGlobalIndexReader implements GlobalIndexReader {
 
     private static final String NPROBE_PARAMETER = "ivf.nprobe";
     private static final String EF_SEARCH_PARAMETER = "hnsw.ef_search";
@@ -78,7 +78,7 @@ public class VectorGlobalIndexReader implements 
GlobalIndexReader {
     private volatile VectorIndexReader vectorReader;
     private SeekableInputStream openStream;
 
-    public VectorGlobalIndexReader(
+    public NativeVectorGlobalIndexReader(
             GlobalIndexFileReader fileReader,
             List<GlobalIndexIOMeta> ioMetas,
             DataType fieldType,
@@ -198,7 +198,7 @@ public class VectorGlobalIndexReader implements 
GlobalIndexReader {
         return buildScoredResult(result.ids(), result.distances(), 
metric).orElse(null);
     }
 
-    private static Optional<ScoredGlobalIndexResult> buildScoredResult(
+    static Optional<ScoredGlobalIndexResult> buildScoredResult(
             long[] ids, float[] distances, String metric) {
         if (ids.length == 0) {
             return Optional.empty();
@@ -274,7 +274,7 @@ public class VectorGlobalIndexReader implements 
GlobalIndexReader {
         } else if ("cosine".equals(metric)) {
             return 1.0f - distance;
         } else if ("inner_product".equals(metric)) {
-            return distance;
+            return -distance;
         }
         throw new IllegalArgumentException("Unknown metric: " + metric);
     }
@@ -331,9 +331,11 @@ public class VectorGlobalIndexReader implements 
GlobalIndexReader {
                 if (vectorReader == null) {
                     SeekableInputStream in = fileReader.getInputStream(ioMeta);
                     try {
-                        vectorReader =
+                        NativeVectorIndexLoader.loadJni();
+                        VectorIndexReader reader =
                                 new VectorIndexReader(new 
SeekableStreamVectorIndexInput(in));
-                        nativeMeta = vectorReader.metadata();
+                        nativeMeta = reader.metadata();
+                        vectorReader = reader;
                         openStream = in;
                     } catch (Exception e) {
                         IOUtils.closeQuietly(in);
diff --git 
a/paimon-vector/paimon-vector-index/src/main/java/org/apache/paimon/vector/index/VectorGlobalIndexWriter.java
 
b/paimon-vector/src/main/java/org/apache/paimon/vector/index/NativeVectorGlobalIndexWriter.java
similarity index 97%
rename from 
paimon-vector/paimon-vector-index/src/main/java/org/apache/paimon/vector/index/VectorGlobalIndexWriter.java
rename to 
paimon-vector/src/main/java/org/apache/paimon/vector/index/NativeVectorGlobalIndexWriter.java
index 0554bb7862..89aa004bee 100644
--- 
a/paimon-vector/paimon-vector-index/src/main/java/org/apache/paimon/vector/index/VectorGlobalIndexWriter.java
+++ 
b/paimon-vector/src/main/java/org/apache/paimon/vector/index/NativeVectorGlobalIndexWriter.java
@@ -46,7 +46,7 @@ import java.util.List;
 import java.util.Map;
 
 /**
- * Vector global index writer using paimon-vector-index.
+ * Vector global index writer using paimon-vector-index-java.
  *
  * <p>Vectors are spilled to a temporary file on disk as they arrive via 
{@link #write(Object,
  * long)}, keeping Java heap usage constant (~8 MB buffer). During index 
build, vectors are read
@@ -54,11 +54,11 @@ import java.util.Map;
  *
  * <p><b>Thread safety:</b> This class is <b>not</b> thread-safe.
  */
-public class VectorGlobalIndexWriter implements GlobalIndexSingleColumnWriter, 
Closeable {
+public class NativeVectorGlobalIndexWriter implements 
GlobalIndexSingleColumnWriter, Closeable {
 
     private static final String FILE_NAME_PREFIX = "vector";
 
-    private static final Logger LOG = 
LoggerFactory.getLogger(VectorGlobalIndexWriter.class);
+    private static final Logger LOG = 
LoggerFactory.getLogger(NativeVectorGlobalIndexWriter.class);
 
     private static final int IO_BUFFER_SIZE = 8 * 1024 * 1024;
     private static final int ADD_BATCH_SIZE = 10000;
@@ -79,7 +79,7 @@ public class VectorGlobalIndexWriter implements 
GlobalIndexSingleColumnWriter, C
 
     private long rowCount;
 
-    public VectorGlobalIndexWriter(
+    public NativeVectorGlobalIndexWriter(
             GlobalIndexFileWriter fileWriter,
             DataType fieldType,
             Map<String, String> options,
@@ -95,7 +95,7 @@ public class VectorGlobalIndexWriter implements 
GlobalIndexSingleColumnWriter, C
         this.vectorBuf = new float[dim];
 
         try {
-            this.tempVectorFile = 
File.createTempFile("paimon-vector-index-vectors-", ".bin");
+            this.tempVectorFile = 
File.createTempFile("paimon-vector-vectors-", ".bin");
             this.tempVectorFile.deleteOnExit();
             @SuppressWarnings("resource")
             RandomAccessFile raf = new RandomAccessFile(tempVectorFile, "rw");
@@ -223,6 +223,7 @@ public class VectorGlobalIndexWriter implements 
GlobalIndexSingleColumnWriter, C
         LOG.info("{} vector index build started: {} vectors, dim={}", 
identifier, count, dim);
         long buildStart = System.currentTimeMillis();
 
+        NativeVectorIndexLoader.loadJni();
         try (VectorIndexWriter writer = new VectorIndexWriter(nativeOptions)) {
 
             // Phase 1: Train
diff --git 
a/paimon-vector/paimon-vector-index/src/main/java/org/apache/paimon/vector/index/VectorGlobalIndexer.java
 
b/paimon-vector/src/main/java/org/apache/paimon/vector/index/NativeVectorGlobalIndexer.java
similarity index 81%
rename from 
paimon-vector/paimon-vector-index/src/main/java/org/apache/paimon/vector/index/VectorGlobalIndexer.java
rename to 
paimon-vector/src/main/java/org/apache/paimon/vector/index/NativeVectorGlobalIndexer.java
index a3f3bf51fb..7095b5bd2e 100644
--- 
a/paimon-vector/paimon-vector-index/src/main/java/org/apache/paimon/vector/index/VectorGlobalIndexer.java
+++ 
b/paimon-vector/src/main/java/org/apache/paimon/vector/index/NativeVectorGlobalIndexer.java
@@ -31,14 +31,15 @@ import java.util.Map;
 import java.util.Objects;
 import java.util.concurrent.ExecutorService;
 
-/** Vector global indexer backed by paimon-vector-index. */
-public class VectorGlobalIndexer implements GlobalIndexer {
+/** Native vector global indexer backed by paimon-vector-index-java. */
+public class NativeVectorGlobalIndexer implements GlobalIndexer {
 
     private final DataType fieldType;
     private final Map<String, String> options;
     private final String identifier;
 
-    public VectorGlobalIndexer(DataType fieldType, Map<String, String> 
options, String identifier) {
+    public NativeVectorGlobalIndexer(
+            DataType fieldType, Map<String, String> options, String 
identifier) {
         this.fieldType = fieldType;
         this.options = Objects.requireNonNull(options, "options must not be 
null");
         this.identifier = Objects.requireNonNull(identifier, "identifier must 
not be null");
@@ -46,7 +47,7 @@ public class VectorGlobalIndexer implements GlobalIndexer {
 
     @Override
     public GlobalIndexWriter createWriter(GlobalIndexFileWriter fileWriter) {
-        return new VectorGlobalIndexWriter(fileWriter, fieldType, options, 
identifier);
+        return new NativeVectorGlobalIndexWriter(fileWriter, fieldType, 
options, identifier);
     }
 
     @Override
@@ -54,6 +55,6 @@ public class VectorGlobalIndexer implements GlobalIndexer {
             GlobalIndexFileReader fileReader,
             List<GlobalIndexIOMeta> files,
             ExecutorService executor) {
-        return new VectorGlobalIndexReader(fileReader, files, fieldType, 
executor);
+        return new NativeVectorGlobalIndexReader(fileReader, files, fieldType, 
executor);
     }
 }
diff --git 
a/paimon-vector/paimon-vector-index/src/main/java/org/apache/paimon/vector/index/VectorGlobalIndexerFactory.java
 
b/paimon-vector/src/main/java/org/apache/paimon/vector/index/NativeVectorGlobalIndexerFactory.java
similarity index 96%
rename from 
paimon-vector/paimon-vector-index/src/main/java/org/apache/paimon/vector/index/VectorGlobalIndexerFactory.java
rename to 
paimon-vector/src/main/java/org/apache/paimon/vector/index/NativeVectorGlobalIndexerFactory.java
index 9367723114..8e4daa030f 100644
--- 
a/paimon-vector/paimon-vector-index/src/main/java/org/apache/paimon/vector/index/VectorGlobalIndexerFactory.java
+++ 
b/paimon-vector/src/main/java/org/apache/paimon/vector/index/NativeVectorGlobalIndexerFactory.java
@@ -28,15 +28,15 @@ import org.apache.paimon.types.VectorType;
 import java.util.LinkedHashMap;
 import java.util.Map;
 
-/** Factory for creating vector indexes backed by paimon-vector-index. */
-public abstract class VectorGlobalIndexerFactory implements 
GlobalIndexerFactory {
+/** Factory for creating vector indexes backed by paimon-vector-index-java. */
+public abstract class NativeVectorGlobalIndexerFactory implements 
GlobalIndexerFactory {
 
     private static final int DEFAULT_DIMENSION = 128;
 
     @Override
     public GlobalIndexer create(DataField field, Options options) {
         String identifier = identifier();
-        return new VectorGlobalIndexer(
+        return new NativeVectorGlobalIndexer(
                 field.type(),
                 nativeOptions(field.type(), options, identifier, field.name()),
                 identifier);
diff --git 
a/paimon-vector/src/main/java/org/apache/paimon/vector/index/NativeVectorIndexLoader.java
 
b/paimon-vector/src/main/java/org/apache/paimon/vector/index/NativeVectorIndexLoader.java
new file mode 100644
index 0000000000..3c55fda25f
--- /dev/null
+++ 
b/paimon-vector/src/main/java/org/apache/paimon/vector/index/NativeVectorIndexLoader.java
@@ -0,0 +1,112 @@
+/*
+ * 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.
+ */
+
+package org.apache.paimon.vector.index;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Locale;
+
+/** Loads native libraries packaged by paimon-vector-index-java. */
+final class NativeVectorIndexLoader {
+
+    private static volatile boolean loaded;
+
+    private NativeVectorIndexLoader() {}
+
+    static void loadJni() {
+        if (loaded) {
+            return;
+        }
+        synchronized (NativeVectorIndexLoader.class) {
+            if (loaded) {
+                return;
+            }
+            loadFromResource();
+            loaded = true;
+        }
+    }
+
+    private static void loadFromResource() {
+        String libraryPath = nativeLibraryPath();
+        try (InputStream input = 
NativeVectorIndexLoader.class.getResourceAsStream(libraryPath)) {
+            if (input == null) {
+                throw new UnsupportedOperationException(
+                        "Vector index native library resource not found: " + 
libraryPath);
+            }
+
+            File temp = File.createTempFile("paimon-vector-", librarySuffix());
+            temp.deleteOnExit();
+            try (FileOutputStream output = new FileOutputStream(temp)) {
+                byte[] buffer = new byte[8192];
+                int len;
+                while ((len = input.read(buffer)) >= 0) {
+                    output.write(buffer, 0, len);
+                }
+            }
+            System.load(temp.getAbsolutePath());
+        } catch (IOException e) {
+            throw new RuntimeException(
+                    "Failed to load vector index native library: " + 
libraryPath, e);
+        }
+    }
+
+    private static String nativeLibraryPath() {
+        return "/native/" + os() + "/" + arch() + "/" + libraryName();
+    }
+
+    private static String os() {
+        String os = System.getProperty("os.name").toLowerCase(Locale.ROOT);
+        if (os.contains("mac")) {
+            return "macos";
+        } else if (os.contains("linux")) {
+            return "linux";
+        } else if (os.contains("windows")) {
+            return "windows";
+        }
+        throw new UnsupportedOperationException("Unsupported vector index 
native OS: " + os);
+    }
+
+    private static String arch() {
+        String arch = System.getProperty("os.arch").toLowerCase(Locale.ROOT);
+        if ("amd64".equals(arch) || "x86_64".equals(arch)) {
+            return "x86_64";
+        } else if ("aarch64".equals(arch) || "arm64".equals(arch)) {
+            return "aarch64";
+        }
+        throw new UnsupportedOperationException("Unsupported vector index 
native arch: " + arch);
+    }
+
+    private static String libraryName() {
+        if ("windows".equals(os())) {
+            return "paimon_vindex_jni.dll";
+        }
+        return "libpaimon_vindex_jni" + librarySuffix();
+    }
+
+    private static String librarySuffix() {
+        if ("macos".equals(os())) {
+            return ".dylib";
+        } else if ("windows".equals(os())) {
+            return ".dll";
+        }
+        return ".so";
+    }
+}
diff --git 
a/paimon-vector/paimon-vector-index/src/main/java/org/apache/paimon/vector/index/VectorIndexMeta.java
 
b/paimon-vector/src/main/java/org/apache/paimon/vector/index/VectorIndexMeta.java
similarity index 100%
rename from 
paimon-vector/paimon-vector-index/src/main/java/org/apache/paimon/vector/index/VectorIndexMeta.java
rename to 
paimon-vector/src/main/java/org/apache/paimon/vector/index/VectorIndexMeta.java
diff --git 
a/paimon-vector/paimon-vector-index/src/main/resources/META-INF/services/org.apache.paimon.globalindex.GlobalIndexerFactory
 
b/paimon-vector/src/main/resources/META-INF/services/org.apache.paimon.globalindex.GlobalIndexerFactory
similarity index 100%
rename from 
paimon-vector/paimon-vector-index/src/main/resources/META-INF/services/org.apache.paimon.globalindex.GlobalIndexerFactory
rename to 
paimon-vector/src/main/resources/META-INF/services/org.apache.paimon.globalindex.GlobalIndexerFactory
diff --git a/paimon-vector/src/test/java/org/apache/paimon/JavaPyE2ETest.java 
b/paimon-vector/src/test/java/org/apache/paimon/JavaPyE2ETest.java
new file mode 100644
index 0000000000..033245f9eb
--- /dev/null
+++ b/paimon-vector/src/test/java/org/apache/paimon/JavaPyE2ETest.java
@@ -0,0 +1,200 @@
+/*
+ * 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.
+ */
+
+package org.apache.paimon;
+
+import org.apache.paimon.data.BinaryRow;
+import org.apache.paimon.data.GenericArray;
+import org.apache.paimon.data.GenericRow;
+import org.apache.paimon.fs.FileIOFinder;
+import org.apache.paimon.fs.Path;
+import org.apache.paimon.fs.local.LocalFileIO;
+import org.apache.paimon.globalindex.GlobalIndexBuilderUtils;
+import org.apache.paimon.globalindex.GlobalIndexSingleColumnWriter;
+import org.apache.paimon.globalindex.ResultEntry;
+import org.apache.paimon.index.IndexFileMeta;
+import org.apache.paimon.io.CompactIncrement;
+import org.apache.paimon.io.DataIncrement;
+import org.apache.paimon.options.Options;
+import org.apache.paimon.schema.Schema;
+import org.apache.paimon.schema.SchemaManager;
+import org.apache.paimon.schema.SchemaUtils;
+import org.apache.paimon.schema.TableSchema;
+import org.apache.paimon.table.AppendOnlyFileStoreTable;
+import org.apache.paimon.table.CatalogEnvironment;
+import org.apache.paimon.table.sink.BatchTableCommit;
+import org.apache.paimon.table.sink.BatchTableWrite;
+import org.apache.paimon.table.sink.BatchWriteBuilder;
+import org.apache.paimon.table.sink.CommitMessage;
+import org.apache.paimon.table.sink.CommitMessageImpl;
+import org.apache.paimon.types.ArrayType;
+import org.apache.paimon.types.DataField;
+import org.apache.paimon.types.DataType;
+import org.apache.paimon.types.DataTypes;
+import org.apache.paimon.types.FloatType;
+import org.apache.paimon.types.RowType;
+import org.apache.paimon.utils.Range;
+import org.apache.paimon.vector.index.IvfFlatVectorGlobalIndexerFactory;
+
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.condition.EnabledIfSystemProperty;
+
+import java.nio.file.Files;
+import java.nio.file.Paths;
+import java.util.Collections;
+import java.util.List;
+
+import static org.apache.paimon.CoreOptions.DATA_EVOLUTION_ENABLED;
+import static org.apache.paimon.CoreOptions.GLOBAL_INDEX_ENABLED;
+import static org.apache.paimon.CoreOptions.PATH;
+import static org.apache.paimon.CoreOptions.ROW_TRACKING_ENABLED;
+import static org.assertj.core.api.Assertions.assertThat;
+
+/**
+ * Mixed language E2E test for Java paimon-vindex building and Python reading.
+ *
+ * <p>Java writes data and builds an ivf-flat vector index, then Python reads 
and searches it.
+ */
+public class JavaPyE2ETest {
+
+    java.nio.file.Path tempDir = 
Paths.get("../paimon-python/pypaimon/tests/e2e").toAbsolutePath();
+
+    protected Path warehouse;
+
+    @BeforeEach
+    public void before() throws Exception {
+        if (!Files.exists(tempDir.resolve("warehouse"))) {
+            Files.createDirectories(tempDir.resolve("warehouse"));
+        }
+        warehouse = new Path(tempDir.resolve("warehouse").toUri());
+    }
+
+    @Test
+    @EnabledIfSystemProperty(named = "run.e2e.tests", matches = "true")
+    public void testVindexVectorIndexWrite() throws Exception {
+        String tableName = "test_vindex_vector";
+        Path tablePath = new Path(warehouse.toString() + "/default.db/" + 
tableName);
+        LocalFileIO fileIO = LocalFileIO.create();
+        if (fileIO.exists(tablePath)) {
+            fileIO.delete(tablePath, true);
+        }
+
+        int dimension = 4;
+
+        RowType rowType =
+                RowType.of(
+                        new DataType[] {DataTypes.INT(), new ArrayType(new 
FloatType())},
+                        new String[] {"id", "embedding"});
+
+        Options options = new Options();
+        options.set(PATH, tablePath.toString());
+        options.set(ROW_TRACKING_ENABLED, true);
+        options.set(DATA_EVOLUTION_ENABLED, true);
+        options.set(GLOBAL_INDEX_ENABLED, true);
+        options.setString(
+                IvfFlatVectorGlobalIndexerFactory.IDENTIFIER + ".dimension",
+                String.valueOf(dimension));
+        options.setString(IvfFlatVectorGlobalIndexerFactory.IDENTIFIER + 
".metric", "l2");
+        options.setString(IvfFlatVectorGlobalIndexerFactory.IDENTIFIER + 
".nlist", "2");
+
+        TableSchema tableSchema =
+                SchemaUtils.forceCommit(
+                        new SchemaManager(fileIO, tablePath),
+                        new Schema(
+                                rowType.getFields(),
+                                Collections.emptyList(),
+                                Collections.emptyList(),
+                                options.toMap(),
+                                ""));
+
+        AppendOnlyFileStoreTable table =
+                new AppendOnlyFileStoreTable(
+                        FileIOFinder.find(tablePath),
+                        tablePath,
+                        tableSchema,
+                        CatalogEnvironment.empty());
+
+        float[][] vectors =
+                new float[][] {
+                    new float[] {1.0f, 0.0f, 0.0f, 0.0f},
+                    new float[] {0.9f, 0.1f, 0.0f, 0.0f},
+                    new float[] {0.0f, 1.0f, 0.0f, 0.0f},
+                    new float[] {0.0f, 0.0f, 1.0f, 0.0f},
+                    new float[] {0.0f, 0.0f, 0.0f, 1.0f},
+                    new float[] {0.95f, 0.05f, 0.0f, 0.0f}
+                };
+
+        BatchWriteBuilder writeBuilder = table.newBatchWriteBuilder();
+        try (BatchTableWrite write = writeBuilder.newWrite();
+                BatchTableCommit commit = writeBuilder.newCommit()) {
+            for (int i = 0; i < vectors.length; i++) {
+                write.write(GenericRow.of(i, new GenericArray(vectors[i])));
+            }
+            commit.commit(write.prepareCommit());
+        }
+
+        DataField embeddingField = table.rowType().getField("embedding");
+        Options indexOptions = table.coreOptions().toConfiguration();
+
+        GlobalIndexSingleColumnWriter writer =
+                (GlobalIndexSingleColumnWriter)
+                        GlobalIndexBuilderUtils.createIndexWriter(
+                                table,
+                                IvfFlatVectorGlobalIndexerFactory.IDENTIFIER,
+                                embeddingField,
+                                indexOptions);
+
+        for (int i = 0; i < vectors.length; i++) {
+            writer.write(vectors[i], i);
+        }
+
+        List<ResultEntry> entries = writer.finish();
+        assertThat(entries).hasSize(1);
+        assertThat(entries.get(0).rowCount()).isEqualTo(vectors.length);
+
+        Range rowRange = new Range(0, vectors.length - 1);
+        List<IndexFileMeta> indexFiles =
+                GlobalIndexBuilderUtils.toIndexFileMetas(
+                        table.fileIO(),
+                        table.store().pathFactory().globalIndexFileFactory(),
+                        table.coreOptions(),
+                        rowRange,
+                        embeddingField.id(),
+                        IvfFlatVectorGlobalIndexerFactory.IDENTIFIER,
+                        entries);
+
+        DataIncrement dataIncrement = DataIncrement.indexIncrement(indexFiles);
+        CommitMessage message =
+                new CommitMessageImpl(
+                        BinaryRow.EMPTY_ROW,
+                        0,
+                        null,
+                        dataIncrement,
+                        CompactIncrement.emptyIncrement());
+        try (BatchTableCommit commit = writeBuilder.newCommit()) {
+            commit.commit(Collections.singletonList(message));
+        }
+
+        List<org.apache.paimon.manifest.IndexManifestEntry> indexEntries =
+                
table.indexManifestFileReader().read(table.latestSnapshot().get().indexManifest());
+        assertThat(indexEntries).hasSize(1);
+        assertThat(indexEntries.get(0).indexFile().indexType())
+                .isEqualTo(IvfFlatVectorGlobalIndexerFactory.IDENTIFIER);
+    }
+}
diff --git 
a/paimon-vector/paimon-vector-index/src/test/java/org/apache/paimon/vector/index/VectorGlobalIndexTest.java
 
b/paimon-vector/src/test/java/org/apache/paimon/vector/index/NativeVectorGlobalIndexTest.java
similarity index 81%
rename from 
paimon-vector/paimon-vector-index/src/test/java/org/apache/paimon/vector/index/VectorGlobalIndexTest.java
rename to 
paimon-vector/src/test/java/org/apache/paimon/vector/index/NativeVectorGlobalIndexTest.java
index f08c072984..72303f5bf0 100644
--- 
a/paimon-vector/paimon-vector-index/src/test/java/org/apache/paimon/vector/index/VectorGlobalIndexTest.java
+++ 
b/paimon-vector/src/test/java/org/apache/paimon/vector/index/NativeVectorGlobalIndexTest.java
@@ -27,7 +27,6 @@ import org.apache.paimon.globalindex.ResultEntry;
 import org.apache.paimon.globalindex.ScoredGlobalIndexResult;
 import org.apache.paimon.globalindex.io.GlobalIndexFileReader;
 import org.apache.paimon.globalindex.io.GlobalIndexFileWriter;
-import org.apache.paimon.index.vector.NativeLoader;
 import org.apache.paimon.options.Options;
 import org.apache.paimon.predicate.BatchVectorSearch;
 import org.apache.paimon.predicate.VectorSearch;
@@ -58,8 +57,8 @@ import java.util.concurrent.Executors;
 import static org.assertj.core.api.Assertions.assertThat;
 import static org.assertj.core.api.Assertions.assertThatThrownBy;
 
-/** Tests for {@link VectorGlobalIndexWriter} and {@link 
VectorGlobalIndexReader}. */
-public class VectorGlobalIndexTest {
+/** Tests for {@link NativeVectorGlobalIndexWriter} and {@link 
NativeVectorGlobalIndexReader}. */
+public class NativeVectorGlobalIndexTest {
 
     @TempDir java.nio.file.Path tempDir;
 
@@ -73,7 +72,20 @@ public class VectorGlobalIndexTest {
 
     private static boolean isNativeAvailable() {
         try {
-            NativeLoader.loadJni();
+            NativeVectorIndexLoader.loadJni();
+            Options options = new Options();
+            options.setInteger("ivf-flat.dimension", 2);
+            options.setString("ivf-flat.metric", "l2");
+            options.setInteger("ivf-flat.nlist", 1);
+            try (org.apache.paimon.index.vector.VectorIndexWriter ignored =
+                    new org.apache.paimon.index.vector.VectorIndexWriter(
+                            NativeVectorGlobalIndexerFactory.nativeOptions(
+                                    new ArrayType(new FloatType()),
+                                    options,
+                                    
IvfFlatVectorGlobalIndexerFactory.IDENTIFIER,
+                                    "vec"))) {
+                // Closed immediately; constructing the writer is enough to 
validate JNI loading.
+            }
             return true;
         } catch (Throwable t) {
             return false;
@@ -104,7 +116,7 @@ public class VectorGlobalIndexTest {
     public void testDimensionMismatch() {
         Options options = createDefaultOptions(64);
         GlobalIndexFileWriter fileWriter = createFileWriter(indexPath);
-        VectorGlobalIndexWriter writer = createIvfPqWriter(fileWriter, 
vectorType, options);
+        NativeVectorGlobalIndexWriter writer = createIvfPqWriter(fileWriter, 
vectorType, options);
 
         float[] wrongDimVector = new float[32];
         assertThatThrownBy(() -> writer.write(wrongDimVector, 0))
@@ -129,7 +141,7 @@ public class VectorGlobalIndexTest {
         Options options = createDefaultOptions(2);
         options.setInteger("ivf-pq.pq.m", 1);
         GlobalIndexFileWriter fileWriter = createFileWriter(indexPath);
-        VectorGlobalIndexWriter writer = createIvfPqWriter(fileWriter, 
vectorType, options);
+        NativeVectorGlobalIndexWriter writer = createIvfPqWriter(fileWriter, 
vectorType, options);
 
         assertThatThrownBy(() -> writer.write(new float[] {1.0f, Float.NaN}, 
0))
                 .isInstanceOf(IllegalArgumentException.class)
@@ -143,7 +155,7 @@ public class VectorGlobalIndexTest {
         Options options = createDefaultOptions(2);
         options.setInteger("ivf-pq.pq.m", 1);
         GlobalIndexFileWriter fileWriter = createFileWriter(indexPath);
-        VectorGlobalIndexWriter writer = createIvfPqWriter(fileWriter, 
vectorType, options);
+        NativeVectorGlobalIndexWriter writer = createIvfPqWriter(fileWriter, 
vectorType, options);
 
         writer.write(null, 0); // row 0 - null
         assertThatThrownBy(() -> writer.write(new float[] 
{Float.POSITIVE_INFINITY, 0.0f}, 1))
@@ -158,7 +170,7 @@ public class VectorGlobalIndexTest {
         Options options = createDefaultOptions(2);
         options.setInteger("ivf-pq.pq.m", 1);
         GlobalIndexFileWriter fileWriter = createFileWriter(indexPath);
-        VectorGlobalIndexWriter writer = createIvfPqWriter(fileWriter, 
vectorType, options);
+        NativeVectorGlobalIndexWriter writer = createIvfPqWriter(fileWriter, 
vectorType, options);
 
         writer.write(null, 0);
         writer.write(null, 1);
@@ -185,22 +197,42 @@ public class VectorGlobalIndexTest {
         parameters.put("hnsw.ef_search", "80");
         parameters.put("ignored", "bad");
 
-        assertThat(VectorGlobalIndexReader.nprobe(parameters)).isEqualTo(24);
-        assertThat(VectorGlobalIndexReader.efSearch(parameters)).isEqualTo(80);
-        
assertThat(VectorGlobalIndexReader.nprobe(Collections.emptyMap())).isEqualTo(16);
-        
assertThat(VectorGlobalIndexReader.efSearch(Collections.emptyMap())).isEqualTo(0);
+        
assertThat(NativeVectorGlobalIndexReader.nprobe(parameters)).isEqualTo(24);
+        
assertThat(NativeVectorGlobalIndexReader.efSearch(parameters)).isEqualTo(80);
+        
assertThat(NativeVectorGlobalIndexReader.nprobe(Collections.emptyMap())).isEqualTo(16);
+        
assertThat(NativeVectorGlobalIndexReader.efSearch(Collections.emptyMap())).isEqualTo(0);
     }
 
     @Test
     public void testVectorSearchParameterRangeValidationDelegatedToNative() {
-        
assertThat(VectorGlobalIndexReader.nprobe(Collections.singletonMap("ivf.nprobe",
 "0")))
+        assertThat(
+                        NativeVectorGlobalIndexReader.nprobe(
+                                Collections.singletonMap("ivf.nprobe", "0")))
                 .isEqualTo(0);
         assertThat(
-                        VectorGlobalIndexReader.efSearch(
+                        NativeVectorGlobalIndexReader.efSearch(
                                 Collections.singletonMap("hnsw.ef_search", 
"-1")))
                 .isEqualTo(-1);
     }
 
+    @Test
+    public void testInnerProductDistanceConvertedToHigherIsBetterScore() {
+        ScoredGlobalIndexResult result =
+                NativeVectorGlobalIndexReader.buildScoredResult(
+                                new long[] {10L, 20L, 30L},
+                                new float[] {-1.0f, -0.5f, -0.1f},
+                                "inner_product")
+                        .get();
+
+        assertThat(result.scoreGetter().score(10L)).isEqualTo(1.0f);
+        assertThat(result.scoreGetter().score(20L)).isEqualTo(0.5f);
+        assertThat(result.scoreGetter().score(30L)).isEqualTo(0.1f);
+
+        ScoredGlobalIndexResult top1 = result.topK(1);
+        assertThat(top1.results().getLongCardinality()).isEqualTo(1);
+        assertThat(top1.results().contains(10L)).isTrue();
+    }
+
     // =================== Tests that NEED native library =====================
 
     @Test
@@ -223,7 +255,7 @@ public class VectorGlobalIndexTest {
                 };
 
         GlobalIndexFileWriter fileWriter = createFileWriter(indexPath);
-        VectorGlobalIndexWriter writer = createIvfPqWriter(fileWriter, 
vectorType, options);
+        NativeVectorGlobalIndexWriter writer = createIvfPqWriter(fileWriter, 
vectorType, options);
         for (int i = 0; i < vectors.length; i++) {
             writer.write(vectors[i], i);
         }
@@ -231,8 +263,8 @@ public class VectorGlobalIndexTest {
         List<GlobalIndexIOMeta> metas = toIOMetas(results, indexPath);
 
         GlobalIndexFileReader fileReader = createFileReader(indexPath);
-        try (VectorGlobalIndexReader reader =
-                new VectorGlobalIndexReader(fileReader, metas, vectorType, 
executor)) {
+        try (NativeVectorGlobalIndexReader reader =
+                new NativeVectorGlobalIndexReader(fileReader, metas, 
vectorType, executor)) {
             VectorSearch vectorSearch = new VectorSearch(vectors[0], 3, 
fieldName);
             ScoredGlobalIndexResult result = 
reader.visitVectorSearch(vectorSearch).join().get();
             assertThat(result.results().getLongCardinality()).isEqualTo(3);
@@ -262,7 +294,7 @@ public class VectorGlobalIndexTest {
                 };
 
         GlobalIndexFileWriter fileWriter = createFileWriter(indexPath);
-        VectorGlobalIndexWriter writer = createIvfPqWriter(fileWriter, 
vectorType, options);
+        NativeVectorGlobalIndexWriter writer = createIvfPqWriter(fileWriter, 
vectorType, options);
         for (int i = 0; i < vectors.length; i++) {
             writer.write(vectors[i], i);
         }
@@ -270,8 +302,8 @@ public class VectorGlobalIndexTest {
         List<GlobalIndexIOMeta> metas = toIOMetas(results, indexPath);
 
         GlobalIndexFileReader fileReader = createFileReader(indexPath);
-        try (VectorGlobalIndexReader reader =
-                new VectorGlobalIndexReader(fileReader, metas, vectorType, 
executor)) {
+        try (NativeVectorGlobalIndexReader reader =
+                new NativeVectorGlobalIndexReader(fileReader, metas, 
vectorType, executor)) {
 
             // Filter to rows {1, 4} only
             RoaringNavigableMap64 filter = new RoaringNavigableMap64();
@@ -303,7 +335,7 @@ public class VectorGlobalIndexTest {
                 };
 
         GlobalIndexFileWriter fileWriter = createFileWriter(indexPath);
-        VectorGlobalIndexWriter writer = createIvfPqWriter(fileWriter, 
vectorType, options);
+        NativeVectorGlobalIndexWriter writer = createIvfPqWriter(fileWriter, 
vectorType, options);
 
         writer.write(vectors[0], 0); // row 0
         writer.write(null, 1); // row 1 - null
@@ -318,8 +350,8 @@ public class VectorGlobalIndexTest {
 
         List<GlobalIndexIOMeta> metas = toIOMetas(results, indexPath);
         GlobalIndexFileReader fileReader = createFileReader(indexPath);
-        try (VectorGlobalIndexReader reader =
-                new VectorGlobalIndexReader(fileReader, metas, vectorType, 
executor)) {
+        try (NativeVectorGlobalIndexReader reader =
+                new NativeVectorGlobalIndexReader(fileReader, metas, 
vectorType, executor)) {
             VectorSearch vectorSearch = new VectorSearch(vectors[0], 3, 
fieldName);
             ScoredGlobalIndexResult result = 
reader.visitVectorSearch(vectorSearch).join().get();
             assertThat(result.results().getLongCardinality()).isEqualTo(3);
@@ -348,15 +380,16 @@ public class VectorGlobalIndexTest {
                     new float[] {0.7f, 0.7f}
                 };
 
-        VectorGlobalIndexer indexer =
-                new VectorGlobalIndexer(
+        NativeVectorGlobalIndexer indexer =
+                new NativeVectorGlobalIndexer(
                         vectorType,
-                        VectorGlobalIndexerFactory.nativeOptions(
+                        NativeVectorGlobalIndexerFactory.nativeOptions(
                                 vectorType, options, IVF_PQ_IDENTIFIER, 
fieldName),
                         IVF_PQ_IDENTIFIER);
 
         GlobalIndexFileWriter fileWriter = createFileWriter(indexPath);
-        VectorGlobalIndexWriter writer = (VectorGlobalIndexWriter) 
indexer.createWriter(fileWriter);
+        NativeVectorGlobalIndexWriter writer =
+                (NativeVectorGlobalIndexWriter) 
indexer.createWriter(fileWriter);
         for (int i = 0; i < vectors.length; i++) {
             writer.write(vectors[i], i);
         }
@@ -364,8 +397,8 @@ public class VectorGlobalIndexTest {
         List<GlobalIndexIOMeta> metas = toIOMetas(results, indexPath);
 
         GlobalIndexFileReader fileReader = createFileReader(indexPath);
-        try (VectorGlobalIndexReader reader =
-                (VectorGlobalIndexReader) indexer.createReader(fileReader, 
metas, executor)) {
+        try (NativeVectorGlobalIndexReader reader =
+                (NativeVectorGlobalIndexReader) 
indexer.createReader(fileReader, metas, executor)) {
             VectorSearch vectorSearch = new VectorSearch(vectors[0], 2, 
fieldName);
             ScoredGlobalIndexResult result = 
reader.visitVectorSearch(vectorSearch).join().get();
             assertThat(result.results().getLongCardinality()).isEqualTo(2);
@@ -393,14 +426,14 @@ public class VectorGlobalIndexTest {
                 };
 
         GlobalIndexFileWriter fileWriter = createFileWriter(indexPath);
-        VectorGlobalIndexWriter writer = createIvfPqWriter(fileWriter, 
vectorType, options);
+        NativeVectorGlobalIndexWriter writer = createIvfPqWriter(fileWriter, 
vectorType, options);
         writeVectors(writer, vectors);
         List<ResultEntry> results = writer.finish();
         List<GlobalIndexIOMeta> metas = toIOMetas(results, indexPath);
 
         GlobalIndexFileReader fileReader = createFileReader(indexPath);
-        try (VectorGlobalIndexReader reader =
-                new VectorGlobalIndexReader(fileReader, metas, vectorType, 
executor)) {
+        try (NativeVectorGlobalIndexReader reader =
+                new NativeVectorGlobalIndexReader(fileReader, metas, 
vectorType, executor)) {
             float[][] queryVectors =
                     new float[][] {
                         new float[] {1.0f, 0.0f},
@@ -445,14 +478,14 @@ public class VectorGlobalIndexTest {
                 };
 
         GlobalIndexFileWriter fileWriter = createFileWriter(indexPath);
-        VectorGlobalIndexWriter writer = createIvfPqWriter(fileWriter, 
vectorType, options);
+        NativeVectorGlobalIndexWriter writer = createIvfPqWriter(fileWriter, 
vectorType, options);
         writeVectors(writer, vectors);
         List<ResultEntry> results = writer.finish();
         List<GlobalIndexIOMeta> metas = toIOMetas(results, indexPath);
 
         GlobalIndexFileReader fileReader = createFileReader(indexPath);
-        try (VectorGlobalIndexReader reader =
-                new VectorGlobalIndexReader(fileReader, metas, vectorType, 
executor)) {
+        try (NativeVectorGlobalIndexReader reader =
+                new NativeVectorGlobalIndexReader(fileReader, metas, 
vectorType, executor)) {
             float[][] queryVectors =
                     new float[][] {new float[] {1.0f, 0.0f}, new float[] 
{-1.0f, 0.0f}};
 
@@ -500,14 +533,14 @@ public class VectorGlobalIndexTest {
                 };
 
         GlobalIndexFileWriter fileWriter = createFileWriter(indexPath);
-        VectorGlobalIndexWriter writer = createIvfPqWriter(fileWriter, 
vectorType, options);
+        NativeVectorGlobalIndexWriter writer = createIvfPqWriter(fileWriter, 
vectorType, options);
         writeVectors(writer, vectors);
         List<ResultEntry> results = writer.finish();
         List<GlobalIndexIOMeta> metas = toIOMetas(results, indexPath);
 
         GlobalIndexFileReader fileReader = createFileReader(indexPath);
-        try (VectorGlobalIndexReader reader =
-                new VectorGlobalIndexReader(fileReader, metas, vectorType, 
executor)) {
+        try (NativeVectorGlobalIndexReader reader =
+                new NativeVectorGlobalIndexReader(fileReader, metas, 
vectorType, executor)) {
             float[][] queryVectors =
                     new float[][] {
                         new float[] {1.0f, 0.0f},
@@ -541,12 +574,12 @@ public class VectorGlobalIndexTest {
 
     // =================== Helpers =====================
 
-    private VectorGlobalIndexWriter createIvfPqWriter(
+    private NativeVectorGlobalIndexWriter createIvfPqWriter(
             GlobalIndexFileWriter fileWriter, DataType fieldType, Options 
options) {
-        return new VectorGlobalIndexWriter(
+        return new NativeVectorGlobalIndexWriter(
                 fileWriter,
                 fieldType,
-                VectorGlobalIndexerFactory.nativeOptions(
+                NativeVectorGlobalIndexerFactory.nativeOptions(
                         fieldType, options, IVF_PQ_IDENTIFIER, fieldName),
                 IVF_PQ_IDENTIFIER);
     }
@@ -558,7 +591,7 @@ public class VectorGlobalIndexTest {
         return options;
     }
 
-    private void writeVectors(VectorGlobalIndexWriter writer, float[][] 
vectors) {
+    private void writeVectors(NativeVectorGlobalIndexWriter writer, float[][] 
vectors) {
         for (int i = 0; i < vectors.length; i++) {
             writer.write(vectors[i], i);
         }
diff --git 
a/paimon-vector/paimon-vector-index/src/test/java/org/apache/paimon/vector/index/VectorGlobalIndexerFactoryTest.java
 
b/paimon-vector/src/test/java/org/apache/paimon/vector/index/NativeVectorGlobalIndexerFactoryTest.java
similarity index 92%
rename from 
paimon-vector/paimon-vector-index/src/test/java/org/apache/paimon/vector/index/VectorGlobalIndexerFactoryTest.java
rename to 
paimon-vector/src/test/java/org/apache/paimon/vector/index/NativeVectorGlobalIndexerFactoryTest.java
index b0f33d7706..92c56b6485 100644
--- 
a/paimon-vector/paimon-vector-index/src/test/java/org/apache/paimon/vector/index/VectorGlobalIndexerFactoryTest.java
+++ 
b/paimon-vector/src/test/java/org/apache/paimon/vector/index/NativeVectorGlobalIndexerFactoryTest.java
@@ -32,7 +32,7 @@ import static org.assertj.core.api.Assertions.assertThat;
 import static org.assertj.core.api.Assertions.assertThatThrownBy;
 
 /** Tests for vector global indexer factory SPI registration. */
-public class VectorGlobalIndexerFactoryTest {
+public class NativeVectorGlobalIndexerFactoryTest {
 
     @Test
     public void testIdentifier() {
@@ -67,7 +67,7 @@ public class VectorGlobalIndexerFactoryTest {
         options.setString("ivf-pq.nlist", "256");
 
         Map<String, String> nativeOptions =
-                VectorGlobalIndexerFactory.nativeOptions(
+                NativeVectorGlobalIndexerFactory.nativeOptions(
                         new ArrayType(new FloatType()),
                         options,
                         IvfFlatVectorGlobalIndexerFactory.IDENTIFIER,
@@ -90,7 +90,7 @@ public class VectorGlobalIndexerFactoryTest {
         options.setString("ivf-flat.dimension", "32");
 
         Map<String, String> nativeOptions =
-                VectorGlobalIndexerFactory.nativeOptions(
+                NativeVectorGlobalIndexerFactory.nativeOptions(
                         new VectorType(8, new FloatType()),
                         options,
                         IvfFlatVectorGlobalIndexerFactory.IDENTIFIER,
@@ -106,7 +106,7 @@ public class VectorGlobalIndexerFactoryTest {
 
         assertThatThrownBy(
                         () ->
-                                VectorGlobalIndexerFactory.nativeOptions(
+                                NativeVectorGlobalIndexerFactory.nativeOptions(
                                         new ArrayType(new FloatType()),
                                         options,
                                         
IvfFlatVectorGlobalIndexerFactory.IDENTIFIER,
@@ -124,7 +124,7 @@ public class VectorGlobalIndexerFactoryTest {
         options.setString("fields.vec.nlist", "256");
 
         Map<String, String> nativeOptions =
-                VectorGlobalIndexerFactory.nativeOptions(
+                NativeVectorGlobalIndexerFactory.nativeOptions(
                         new ArrayType(new FloatType()),
                         options,
                         IvfFlatVectorGlobalIndexerFactory.IDENTIFIER,
@@ -143,7 +143,7 @@ public class VectorGlobalIndexerFactoryTest {
         options.setString("fields.vec.dimension", "64");
 
         Map<String, String> nativeOptions =
-                VectorGlobalIndexerFactory.nativeOptions(
+                NativeVectorGlobalIndexerFactory.nativeOptions(
                         new ArrayType(new FloatType()),
                         options,
                         IvfFlatVectorGlobalIndexerFactory.IDENTIFIER,
@@ -159,7 +159,7 @@ public class VectorGlobalIndexerFactoryTest {
         options.setString("fields.vec.nlist", "256");
 
         Map<String, String> nativeOptions =
-                VectorGlobalIndexerFactory.nativeOptions(
+                NativeVectorGlobalIndexerFactory.nativeOptions(
                         new ArrayType(new FloatType()),
                         options,
                         IvfFlatVectorGlobalIndexerFactory.IDENTIFIER,
@@ -175,7 +175,7 @@ public class VectorGlobalIndexerFactoryTest {
         options.setString("fields.vec_extra.nlist", "512");
 
         Map<String, String> nativeOptions =
-                VectorGlobalIndexerFactory.nativeOptions(
+                NativeVectorGlobalIndexerFactory.nativeOptions(
                         new ArrayType(new FloatType()),
                         options,
                         IvfFlatVectorGlobalIndexerFactory.IDENTIFIER,
@@ -190,7 +190,7 @@ public class VectorGlobalIndexerFactoryTest {
         options.setString("fields.vec.distance.metric", "cosine");
 
         Map<String, String> nativeOptions =
-                VectorGlobalIndexerFactory.nativeOptions(
+                NativeVectorGlobalIndexerFactory.nativeOptions(
                         new ArrayType(new FloatType()),
                         options,
                         IvfFlatVectorGlobalIndexerFactory.IDENTIFIER,
@@ -207,7 +207,7 @@ public class VectorGlobalIndexerFactoryTest {
         options.setString("fields.vec.aggregate-function", "sum");
 
         Map<String, String> nativeOptions =
-                VectorGlobalIndexerFactory.nativeOptions(
+                NativeVectorGlobalIndexerFactory.nativeOptions(
                         new ArrayType(new FloatType()),
                         options,
                         IvfFlatVectorGlobalIndexerFactory.IDENTIFIER,
diff --git 
a/paimon-vector/paimon-vector-index/src/test/java/org/apache/paimon/vector/index/SeekableStreamVectorIndexInputTest.java
 
b/paimon-vector/src/test/java/org/apache/paimon/vector/index/SeekableStreamVectorIndexInputTest.java
similarity index 92%
rename from 
paimon-vector/paimon-vector-index/src/test/java/org/apache/paimon/vector/index/SeekableStreamVectorIndexInputTest.java
rename to 
paimon-vector/src/test/java/org/apache/paimon/vector/index/SeekableStreamVectorIndexInputTest.java
index ddb29eab6e..82eeee198e 100644
--- 
a/paimon-vector/paimon-vector-index/src/test/java/org/apache/paimon/vector/index/SeekableStreamVectorIndexInputTest.java
+++ 
b/paimon-vector/src/test/java/org/apache/paimon/vector/index/SeekableStreamVectorIndexInputTest.java
@@ -30,15 +30,15 @@ import java.util.concurrent.atomic.AtomicInteger;
 
 import static org.assertj.core.api.Assertions.assertThat;
 
-/** Tests for {@link VectorGlobalIndexReader.SeekableStreamVectorIndexInput}. 
*/
+/** Tests for {@link 
NativeVectorGlobalIndexReader.SeekableStreamVectorIndexInput}. */
 public class SeekableStreamVectorIndexInputTest {
 
     @Test
     public void testVectoredReadableInputUsesParallelPositionReads() throws 
Exception {
         byte[] data = data(128 * 1024);
         TestVectoredSeekableInputStream input = new 
TestVectoredSeekableInputStream(data, 2);
-        VectorGlobalIndexReader.SeekableStreamVectorIndexInput indexInput =
-                new 
VectorGlobalIndexReader.SeekableStreamVectorIndexInput(input);
+        NativeVectorGlobalIndexReader.SeekableStreamVectorIndexInput 
indexInput =
+                new 
NativeVectorGlobalIndexReader.SeekableStreamVectorIndexInput(input);
 
         byte[][] buffers = new byte[][] {new byte[64], new byte[64]};
         indexInput.pread(new long[] {0, 32 * 1024}, buffers);
@@ -54,8 +54,8 @@ public class SeekableStreamVectorIndexInputTest {
     public void testFallbackToSequentialReadWhenRangesOverlap() {
         byte[] data = data(1024);
         TestVectoredSeekableInputStream input = new 
TestVectoredSeekableInputStream(data, 0);
-        VectorGlobalIndexReader.SeekableStreamVectorIndexInput indexInput =
-                new 
VectorGlobalIndexReader.SeekableStreamVectorIndexInput(input);
+        NativeVectorGlobalIndexReader.SeekableStreamVectorIndexInput 
indexInput =
+                new 
NativeVectorGlobalIndexReader.SeekableStreamVectorIndexInput(input);
 
         byte[][] buffers = new byte[][] {new byte[64], new byte[64]};
         indexInput.pread(new long[] {0, 32}, buffers);

Reply via email to