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 3bee09f21f [core] Unify single-column global index writer (#8275)
3bee09f21f is described below
commit 3bee09f21fbbee2e7f21852aa1a0800b22099d25
Author: Jingsong Lee <[email protected]>
AuthorDate: Thu Jun 18 12:15:57 2026 +0800
[core] Unify single-column global index writer (#8275)
This PR merges the previous singleton and parallel single-column global
index writer APIs into one `GlobalIndexSingleColumnWriter` interface.
Single-column index writers now receive the caller-provided
shard-relative row id through `write(@Nullable Object key, long
relativeRowId)`.
---
...ter.java => GlobalIndexSingleColumnWriter.java} | 10 ++-
.../globalindex/GlobalIndexSingletonWriter.java | 26 -------
.../paimon/globalindex/GlobalIndexWriter.java | 2 +-
.../paimon/globalindex/btree/BTreeIndexWriter.java | 11 ++-
.../globalindex/btree/AbstractIndexReaderTest.java | 4 +-
.../globalindex/btree/BTreeIndexMetaTest.java | 4 +-
.../globalindex/btree/BTreeThreadSafetyTest.java | 4 +-
.../btree/LazyFilteredBTreeIndexReaderTest.java | 4 +-
.../TestFullTextGlobalIndexReader.java | 18 +++--
.../TestFullTextGlobalIndexWriter.java | 38 ++++++----
.../testvector/TestVectorGlobalIndexReader.java | 14 +++-
.../testvector/TestVectorGlobalIndexWriter.java | 41 ++++++++---
.../globalindex/btree/BTreeGlobalIndexBuilder.java | 14 ++--
.../table/source/FullTextSearchBuilderTest.java | 22 +++---
.../table/source/VectorSearchBuilderTest.java | 43 ++++++------
.../paimon/flink/btree/BTreeIndexTopoBuilder.java | 4 +-
.../flink/globalindex/GenericIndexTopoBuilder.java | 7 +-
.../procedure/VectorSearchProcedureITCase.java | 10 +--
.../index/LuminaVectorGlobalIndexWriter.java | 37 +++++-----
.../paimon/lumina/index/JavaPyLuminaE2ETest.java | 23 +++---
.../paimon/lumina/index/LuminaVectorBenchmark.java | 2 +-
.../index/LuminaVectorGlobalIndexScanTest.java | 8 +--
.../lumina/index/LuminaVectorGlobalIndexTest.java | 81 ++++++++++++----------
.../index/LuminaVectorGlobalIndexWriterTest.java | 8 +--
.../globalindex/DefaultGlobalIndexBuilder.java | 22 +++---
.../index/TantivyFullTextGlobalIndexWriter.java | 24 +++----
.../paimon/tantivy/index/JavaPyTantivyE2ETest.java | 10 +--
.../index/TantivyFullTextGlobalIndexTest.java | 32 ++++-----
.../vector/index/VectorGlobalIndexWriter.java | 36 +++++-----
.../paimon/vector/index/VectorGlobalIndexTest.java | 39 ++++++-----
30 files changed, 318 insertions(+), 280 deletions(-)
diff --git
a/paimon-common/src/main/java/org/apache/paimon/globalindex/GlobalIndexParallelWriter.java
b/paimon-common/src/main/java/org/apache/paimon/globalindex/GlobalIndexSingleColumnWriter.java
similarity index 72%
rename from
paimon-common/src/main/java/org/apache/paimon/globalindex/GlobalIndexParallelWriter.java
rename to
paimon-common/src/main/java/org/apache/paimon/globalindex/GlobalIndexSingleColumnWriter.java
index 810998e6ed..67f5b95c07 100644
---
a/paimon-common/src/main/java/org/apache/paimon/globalindex/GlobalIndexParallelWriter.java
+++
b/paimon-common/src/main/java/org/apache/paimon/globalindex/GlobalIndexSingleColumnWriter.java
@@ -20,16 +20,14 @@ package org.apache.paimon.globalindex;
import javax.annotation.Nullable;
-/** Parallel Index writer for global index with relative row id (from 0 to
rowCnt - 1). */
-public interface GlobalIndexParallelWriter extends GlobalIndexWriter {
+/** Index writer for single-column global index with relative row id (from 0
to rowCnt - 1). */
+public interface GlobalIndexSingleColumnWriter extends GlobalIndexWriter {
/**
- * Write the indexed key and its related localRowId to the index File. The
input row id is
- * "local" which means it is calculated by the original row id minus the
start row id of current
- * index range.
+ * Write the indexed key and its related relative row id to the index file.
*
* @param key nullable index key
- * @param relativeRowId local row id calculated by {@code rowId -
rangeStart}.
+ * @param relativeRowId local row id calculated by {@code rowId -
rangeStart}
*/
void write(@Nullable Object key, long relativeRowId);
}
diff --git
a/paimon-common/src/main/java/org/apache/paimon/globalindex/GlobalIndexSingletonWriter.java
b/paimon-common/src/main/java/org/apache/paimon/globalindex/GlobalIndexSingletonWriter.java
deleted file mode 100644
index d8a06d874b..0000000000
---
a/paimon-common/src/main/java/org/apache/paimon/globalindex/GlobalIndexSingletonWriter.java
+++ /dev/null
@@ -1,26 +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.globalindex;
-
-import javax.annotation.Nullable;
-
-/** Index writer for global index. */
-public interface GlobalIndexSingletonWriter extends GlobalIndexWriter {
- void write(@Nullable Object key);
-}
diff --git
a/paimon-common/src/main/java/org/apache/paimon/globalindex/GlobalIndexWriter.java
b/paimon-common/src/main/java/org/apache/paimon/globalindex/GlobalIndexWriter.java
index 7e97f89319..89ab394c9d 100644
---
a/paimon-common/src/main/java/org/apache/paimon/globalindex/GlobalIndexWriter.java
+++
b/paimon-common/src/main/java/org/apache/paimon/globalindex/GlobalIndexWriter.java
@@ -20,7 +20,7 @@ package org.apache.paimon.globalindex;
import java.util.List;
-/** Parallel Index writer for global index with relative row id (from 0 to
rowCnt - 1). */
+/** Index writer for global index. */
public interface GlobalIndexWriter {
List<ResultEntry> finish();
diff --git
a/paimon-common/src/main/java/org/apache/paimon/globalindex/btree/BTreeIndexWriter.java
b/paimon-common/src/main/java/org/apache/paimon/globalindex/btree/BTreeIndexWriter.java
index fe4c14a449..5053b2dbb1 100644
---
a/paimon-common/src/main/java/org/apache/paimon/globalindex/btree/BTreeIndexWriter.java
+++
b/paimon-common/src/main/java/org/apache/paimon/globalindex/btree/BTreeIndexWriter.java
@@ -20,8 +20,7 @@ package org.apache.paimon.globalindex.btree;
import org.apache.paimon.compression.BlockCompressionFactory;
import org.apache.paimon.fs.PositionOutputStream;
-import org.apache.paimon.globalindex.GlobalIndexParallelWriter;
-import org.apache.paimon.globalindex.GlobalIndexSingletonWriter;
+import org.apache.paimon.globalindex.GlobalIndexSingleColumnWriter;
import org.apache.paimon.globalindex.ResultEntry;
import org.apache.paimon.globalindex.io.GlobalIndexFileWriter;
import org.apache.paimon.memory.MemorySlice;
@@ -42,9 +41,9 @@ import java.util.List;
import java.util.zip.CRC32;
/**
- * The {@link GlobalIndexSingletonWriter} implementation for BTree index. Note
that users must keep
- * written keys monotonically incremental. All null keys are stored in a
separate bitmap, which will
- * be serialized and appended to the file end on close. The layout is as below:
+ * The {@link GlobalIndexSingleColumnWriter} implementation for BTree index.
Note that users must
+ * keep written keys monotonically incremental. All null keys are stored in a
separate bitmap, which
+ * will be serialized and appended to the file end on close. The layout is as
below:
*
* <pre>
* +-----------------------------------+------+
@@ -67,7 +66,7 @@ import java.util.zip.CRC32;
* <p>For efficiency, we combine entries with the same keys and store a
compact list of row ids for
* each key.
*/
-public class BTreeIndexWriter implements GlobalIndexParallelWriter {
+public class BTreeIndexWriter implements GlobalIndexSingleColumnWriter {
private final String fileName;
private final PositionOutputStream out;
diff --git
a/paimon-common/src/test/java/org/apache/paimon/globalindex/btree/AbstractIndexReaderTest.java
b/paimon-common/src/test/java/org/apache/paimon/globalindex/btree/AbstractIndexReaderTest.java
index 1e81b7c744..b31cc248e0 100644
---
a/paimon-common/src/test/java/org/apache/paimon/globalindex/btree/AbstractIndexReaderTest.java
+++
b/paimon-common/src/test/java/org/apache/paimon/globalindex/btree/AbstractIndexReaderTest.java
@@ -26,9 +26,9 @@ import org.apache.paimon.fs.Path;
import org.apache.paimon.fs.PositionOutputStream;
import org.apache.paimon.fs.local.LocalFileIO;
import org.apache.paimon.globalindex.GlobalIndexIOMeta;
-import org.apache.paimon.globalindex.GlobalIndexParallelWriter;
import org.apache.paimon.globalindex.GlobalIndexReader;
import org.apache.paimon.globalindex.GlobalIndexResult;
+import org.apache.paimon.globalindex.GlobalIndexSingleColumnWriter;
import org.apache.paimon.globalindex.ResultEntry;
import org.apache.paimon.globalindex.io.GlobalIndexFileReader;
import org.apache.paimon.globalindex.io.GlobalIndexFileWriter;
@@ -277,7 +277,7 @@ public abstract class AbstractIndexReaderTest {
protected abstract GlobalIndexReader prepareDataAndCreateReader() throws
Exception;
protected GlobalIndexIOMeta writeData(List<Pair<Object, Long>> data)
throws IOException {
- GlobalIndexParallelWriter indexWriter =
globalIndexer.createWriter(fileWriter);
+ GlobalIndexSingleColumnWriter indexWriter =
globalIndexer.createWriter(fileWriter);
for (Pair<Object, Long> pair : data) {
indexWriter.write(pair.getKey(), pair.getValue());
}
diff --git
a/paimon-common/src/test/java/org/apache/paimon/globalindex/btree/BTreeIndexMetaTest.java
b/paimon-common/src/test/java/org/apache/paimon/globalindex/btree/BTreeIndexMetaTest.java
index 5b37aac50d..64467cdc63 100644
---
a/paimon-common/src/test/java/org/apache/paimon/globalindex/btree/BTreeIndexMetaTest.java
+++
b/paimon-common/src/test/java/org/apache/paimon/globalindex/btree/BTreeIndexMetaTest.java
@@ -23,7 +23,7 @@ import org.apache.paimon.fs.FileIO;
import org.apache.paimon.fs.Path;
import org.apache.paimon.fs.PositionOutputStream;
import org.apache.paimon.fs.local.LocalFileIO;
-import org.apache.paimon.globalindex.GlobalIndexParallelWriter;
+import org.apache.paimon.globalindex.GlobalIndexSingleColumnWriter;
import org.apache.paimon.globalindex.ResultEntry;
import org.apache.paimon.globalindex.io.GlobalIndexFileWriter;
import org.apache.paimon.memory.MemorySliceOutput;
@@ -155,7 +155,7 @@ class BTreeIndexMetaTest {
new Path(new Path(tempPath.toUri()),
fileName), true);
}
};
- GlobalIndexParallelWriter indexWriter =
+ GlobalIndexSingleColumnWriter indexWriter =
new BTreeGlobalIndexer(
new DataField(
1, "testField", new
VarCharType(VarCharType.MAX_LENGTH)),
diff --git
a/paimon-common/src/test/java/org/apache/paimon/globalindex/btree/BTreeThreadSafetyTest.java
b/paimon-common/src/test/java/org/apache/paimon/globalindex/btree/BTreeThreadSafetyTest.java
index af923d1f66..55d08432a6 100644
---
a/paimon-common/src/test/java/org/apache/paimon/globalindex/btree/BTreeThreadSafetyTest.java
+++
b/paimon-common/src/test/java/org/apache/paimon/globalindex/btree/BTreeThreadSafetyTest.java
@@ -23,9 +23,9 @@ import org.apache.paimon.fs.Path;
import org.apache.paimon.fs.PositionOutputStream;
import org.apache.paimon.fs.local.LocalFileIO;
import org.apache.paimon.globalindex.GlobalIndexIOMeta;
-import org.apache.paimon.globalindex.GlobalIndexParallelWriter;
import org.apache.paimon.globalindex.GlobalIndexReader;
import org.apache.paimon.globalindex.GlobalIndexResult;
+import org.apache.paimon.globalindex.GlobalIndexSingleColumnWriter;
import org.apache.paimon.globalindex.ResultEntry;
import org.apache.paimon.globalindex.io.GlobalIndexFileReader;
import org.apache.paimon.globalindex.io.GlobalIndexFileWriter;
@@ -353,7 +353,7 @@ public class BTreeThreadSafetyTest {
}
private GlobalIndexIOMeta writeData(List<Pair<Object, Long>> subData)
throws IOException {
- GlobalIndexParallelWriter indexWriter =
globalIndexer.createWriter(fileWriter);
+ GlobalIndexSingleColumnWriter indexWriter =
globalIndexer.createWriter(fileWriter);
for (Pair<Object, Long> pair : subData) {
indexWriter.write(pair.getKey(), pair.getValue());
}
diff --git
a/paimon-common/src/test/java/org/apache/paimon/globalindex/btree/LazyFilteredBTreeIndexReaderTest.java
b/paimon-common/src/test/java/org/apache/paimon/globalindex/btree/LazyFilteredBTreeIndexReaderTest.java
index 86e9e8227e..cad9861f40 100644
---
a/paimon-common/src/test/java/org/apache/paimon/globalindex/btree/LazyFilteredBTreeIndexReaderTest.java
+++
b/paimon-common/src/test/java/org/apache/paimon/globalindex/btree/LazyFilteredBTreeIndexReaderTest.java
@@ -20,9 +20,9 @@ package org.apache.paimon.globalindex.btree;
import org.apache.paimon.fs.Path;
import org.apache.paimon.globalindex.GlobalIndexIOMeta;
-import org.apache.paimon.globalindex.GlobalIndexParallelWriter;
import org.apache.paimon.globalindex.GlobalIndexReader;
import org.apache.paimon.globalindex.GlobalIndexResult;
+import org.apache.paimon.globalindex.GlobalIndexSingleColumnWriter;
import org.apache.paimon.globalindex.ResultEntry;
import org.apache.paimon.options.MemorySize;
import org.apache.paimon.options.Options;
@@ -233,7 +233,7 @@ public class LazyFilteredBTreeIndexReaderTest extends
AbstractIndexReaderTest {
private GlobalIndexIOMeta writeDataWithIndexer(
BTreeGlobalIndexer indexer, List<Pair<Object, Long>> subData)
throws IOException {
- GlobalIndexParallelWriter indexWriter =
indexer.createWriter(fileWriter);
+ GlobalIndexSingleColumnWriter indexWriter =
indexer.createWriter(fileWriter);
for (Pair<Object, Long> pair : subData) {
indexWriter.write(pair.getKey(), pair.getValue());
}
diff --git
a/paimon-common/src/test/java/org/apache/paimon/globalindex/testfulltext/TestFullTextGlobalIndexReader.java
b/paimon-common/src/test/java/org/apache/paimon/globalindex/testfulltext/TestFullTextGlobalIndexReader.java
index ec5c73cd7c..7e9684f267 100644
---
a/paimon-common/src/test/java/org/apache/paimon/globalindex/testfulltext/TestFullTextGlobalIndexReader.java
+++
b/paimon-common/src/test/java/org/apache/paimon/globalindex/testfulltext/TestFullTextGlobalIndexReader.java
@@ -54,6 +54,7 @@ public class TestFullTextGlobalIndexReader implements
GlobalIndexReader {
private final GlobalIndexIOMeta ioMeta;
private String[] documents;
+ private long[] rowIds;
private int count;
public TestFullTextGlobalIndexReader(
@@ -90,10 +91,10 @@ public class TestFullTextGlobalIndexReader implements
GlobalIndexReader {
continue;
}
if (topK.size() < effectiveK) {
- topK.offer(new ScoredRow(i, score));
+ topK.offer(new ScoredRow(rowIds[i], score));
} else if (score > topK.peek().score) {
topK.poll();
- topK.offer(new ScoredRow(i, score));
+ topK.offer(new ScoredRow(rowIds[i], score));
}
}
@@ -138,12 +139,14 @@ public class TestFullTextGlobalIndexReader implements
GlobalIndexReader {
// Read documents
documents = new String[count];
+ rowIds = new long[count];
for (int i = 0; i < count; i++) {
- byte[] lenBytes = new byte[4];
- readFully(in, lenBytes);
- ByteBuffer lenBuf = ByteBuffer.wrap(lenBytes);
- lenBuf.order(ByteOrder.LITTLE_ENDIAN);
- int textLen = lenBuf.getInt();
+ byte[] entryHeaderBytes = new byte[Long.BYTES + Integer.BYTES];
+ readFully(in, entryHeaderBytes);
+ ByteBuffer entryHeader = ByteBuffer.wrap(entryHeaderBytes);
+ entryHeader.order(ByteOrder.LITTLE_ENDIAN);
+ rowIds[i] = entryHeader.getLong();
+ int textLen = entryHeader.getInt();
byte[] textBytes = new byte[textLen];
readFully(in, textBytes);
@@ -170,6 +173,7 @@ public class TestFullTextGlobalIndexReader implements
GlobalIndexReader {
@Override
public void close() throws IOException {
documents = null;
+ rowIds = null;
}
// =================== unsupported predicate operations
=====================
diff --git
a/paimon-common/src/test/java/org/apache/paimon/globalindex/testfulltext/TestFullTextGlobalIndexWriter.java
b/paimon-common/src/test/java/org/apache/paimon/globalindex/testfulltext/TestFullTextGlobalIndexWriter.java
index cf8d2de24a..71ce5a096b 100644
---
a/paimon-common/src/test/java/org/apache/paimon/globalindex/testfulltext/TestFullTextGlobalIndexWriter.java
+++
b/paimon-common/src/test/java/org/apache/paimon/globalindex/testfulltext/TestFullTextGlobalIndexWriter.java
@@ -20,7 +20,7 @@ package org.apache.paimon.globalindex.testfulltext;
import org.apache.paimon.data.BinaryString;
import org.apache.paimon.fs.PositionOutputStream;
-import org.apache.paimon.globalindex.GlobalIndexSingletonWriter;
+import org.apache.paimon.globalindex.GlobalIndexSingleColumnWriter;
import org.apache.paimon.globalindex.ResultEntry;
import org.apache.paimon.globalindex.io.GlobalIndexFileWriter;
@@ -41,16 +41,18 @@ import java.util.List;
* <pre>
* [4 bytes] count (int)
* For each document:
+ * [8 bytes] relative row id (long)
* [4 bytes] text length in bytes (int)
* [N bytes] UTF-8 text
* </pre>
*/
-public class TestFullTextGlobalIndexWriter implements
GlobalIndexSingletonWriter {
+public class TestFullTextGlobalIndexWriter implements
GlobalIndexSingleColumnWriter {
private static final String FILE_NAME_PREFIX = "test-fulltext";
private final GlobalIndexFileWriter fileWriter;
- private final List<String> documents;
+ private final List<Document> documents;
+ private long rowCount;
public TestFullTextGlobalIndexWriter(GlobalIndexFileWriter fileWriter) {
this.fileWriter = fileWriter;
@@ -58,7 +60,8 @@ public class TestFullTextGlobalIndexWriter implements
GlobalIndexSingletonWriter
}
@Override
- public void write(Object fieldData) {
+ public void write(Object fieldData, long relativeRowId) {
+ rowCount++;
if (fieldData == null) {
throw new IllegalArgumentException("Text field data must not be
null");
}
@@ -72,7 +75,7 @@ public class TestFullTextGlobalIndexWriter implements
GlobalIndexSingletonWriter
throw new IllegalArgumentException(
"Unsupported text type: " +
fieldData.getClass().getName());
}
- documents.add(text);
+ documents.add(new Document(relativeRowId, text));
}
@Override
@@ -91,20 +94,31 @@ public class TestFullTextGlobalIndexWriter implements
GlobalIndexSingletonWriter
out.write(header.array());
// Documents
- for (String doc : documents) {
- byte[] textBytes = doc.getBytes(StandardCharsets.UTF_8);
- ByteBuffer lenBuf = ByteBuffer.allocate(4);
- lenBuf.order(ByteOrder.LITTLE_ENDIAN);
- lenBuf.putInt(textBytes.length);
- out.write(lenBuf.array());
+ for (Document doc : documents) {
+ byte[] textBytes =
doc.text.getBytes(StandardCharsets.UTF_8);
+ ByteBuffer entryHeader = ByteBuffer.allocate(Long.BYTES +
Integer.BYTES);
+ entryHeader.order(ByteOrder.LITTLE_ENDIAN);
+ entryHeader.putLong(doc.relativeRowId);
+ entryHeader.putInt(textBytes.length);
+ out.write(entryHeader.array());
out.write(textBytes);
}
out.flush();
}
- return Collections.singletonList(new ResultEntry(fileName,
documents.size(), null));
+ return Collections.singletonList(new ResultEntry(fileName,
rowCount, null));
} catch (IOException e) {
throw new RuntimeException("Failed to write test full-text index",
e);
}
}
+
+ private static class Document {
+ private final long relativeRowId;
+ private final String text;
+
+ private Document(long relativeRowId, String text) {
+ this.relativeRowId = relativeRowId;
+ this.text = text;
+ }
+ }
}
diff --git
a/paimon-common/src/test/java/org/apache/paimon/globalindex/testvector/TestVectorGlobalIndexReader.java
b/paimon-common/src/test/java/org/apache/paimon/globalindex/testvector/TestVectorGlobalIndexReader.java
index fd233ba06b..1e8cc432f2 100644
---
a/paimon-common/src/test/java/org/apache/paimon/globalindex/testvector/TestVectorGlobalIndexReader.java
+++
b/paimon-common/src/test/java/org/apache/paimon/globalindex/testvector/TestVectorGlobalIndexReader.java
@@ -60,6 +60,7 @@ public class TestVectorGlobalIndexReader implements
GlobalIndexReader {
private final String requiredOptionValue;
private float[][] vectors;
+ private long[] rowIds;
private int dimension;
private int count;
@@ -123,15 +124,15 @@ public class TestVectorGlobalIndexReader implements
GlobalIndexReader {
new PriorityQueue<>(effectiveK + 1,
Comparator.comparingDouble(s -> s.score));
for (int i = 0; i < count; i++) {
- if (includeRowIds != null && !includeRowIds.contains(i)) {
+ if (includeRowIds != null && !includeRowIds.contains(rowIds[i])) {
continue;
}
float score = computeScore(queryVector, vectors[i]);
if (topK.size() < effectiveK) {
- topK.offer(new ScoredRow(i, score));
+ topK.offer(new ScoredRow(rowIds[i], score));
} else if (score > topK.peek().score) {
topK.poll();
- topK.offer(new ScoredRow(i, score));
+ topK.offer(new ScoredRow(rowIds[i], score));
}
}
@@ -206,8 +207,14 @@ public class TestVectorGlobalIndexReader implements
GlobalIndexReader {
// Read vectors
vectors = new float[count][dimension];
+ rowIds = new long[count];
+ byte[] rowIdBytes = new byte[Long.BYTES];
byte[] vectorBytes = new byte[dimension * Float.BYTES];
for (int i = 0; i < count; i++) {
+ readFully(in, rowIdBytes);
+ ByteBuffer rowIdBuf = ByteBuffer.wrap(rowIdBytes);
+ rowIdBuf.order(ByteOrder.LITTLE_ENDIAN);
+ rowIds[i] = rowIdBuf.getLong();
readFully(in, vectorBytes);
ByteBuffer vectorBuf = ByteBuffer.wrap(vectorBytes);
vectorBuf.order(ByteOrder.LITTLE_ENDIAN);
@@ -236,6 +243,7 @@ public class TestVectorGlobalIndexReader implements
GlobalIndexReader {
@Override
public void close() throws IOException {
vectors = null;
+ rowIds = null;
}
// =================== unsupported predicate operations
=====================
diff --git
a/paimon-common/src/test/java/org/apache/paimon/globalindex/testvector/TestVectorGlobalIndexWriter.java
b/paimon-common/src/test/java/org/apache/paimon/globalindex/testvector/TestVectorGlobalIndexWriter.java
index ea8ad346cb..3bb3dde42f 100644
---
a/paimon-common/src/test/java/org/apache/paimon/globalindex/testvector/TestVectorGlobalIndexWriter.java
+++
b/paimon-common/src/test/java/org/apache/paimon/globalindex/testvector/TestVectorGlobalIndexWriter.java
@@ -20,7 +20,7 @@ package org.apache.paimon.globalindex.testvector;
import org.apache.paimon.data.InternalArray;
import org.apache.paimon.fs.PositionOutputStream;
-import org.apache.paimon.globalindex.GlobalIndexSingletonWriter;
+import org.apache.paimon.globalindex.GlobalIndexSingleColumnWriter;
import org.apache.paimon.globalindex.ResultEntry;
import org.apache.paimon.globalindex.io.GlobalIndexFileWriter;
@@ -40,16 +40,19 @@ import java.util.List;
* <pre>
* [4 bytes] dimension (int)
* [4 bytes] count (int)
- * [count * dim * 4 bytes] float vectors (row-major order)
+ * For each vector:
+ * [8 bytes] relative row id (long)
+ * [dim * 4 bytes] float vector
* </pre>
*/
-public class TestVectorGlobalIndexWriter implements GlobalIndexSingletonWriter
{
+public class TestVectorGlobalIndexWriter implements
GlobalIndexSingleColumnWriter {
private static final String FILE_NAME_PREFIX = "test-vector";
private final GlobalIndexFileWriter fileWriter;
private final int dimension;
- private final List<float[]> vectors;
+ private final List<VectorEntry> vectors;
+ private long rowCount;
public TestVectorGlobalIndexWriter(GlobalIndexFileWriter fileWriter, int
dimension) {
this.fileWriter = fileWriter;
@@ -58,7 +61,8 @@ public class TestVectorGlobalIndexWriter implements
GlobalIndexSingletonWriter {
}
@Override
- public void write(Object fieldData) {
+ public void write(Object fieldData, long relativeRowId) {
+ rowCount++;
if (fieldData == null) {
throw new IllegalArgumentException("Vector field data must not be
null");
}
@@ -83,14 +87,14 @@ public class TestVectorGlobalIndexWriter implements
GlobalIndexSingletonWriter {
int expectedDim =
dimension > 0
? dimension
- : (vectors.isEmpty() ? vector.length :
vectors.get(0).length);
+ : (vectors.isEmpty() ? vector.length :
vectors.get(0).vector.length);
if (vector.length != expectedDim) {
throw new IllegalArgumentException(
String.format(
"Vector dimension mismatch: expected %d, but got
%d",
expectedDim, vector.length));
}
- vectors.add(vector);
+ vectors.add(new VectorEntry(relativeRowId, vector));
}
@Override
@@ -99,7 +103,7 @@ public class TestVectorGlobalIndexWriter implements
GlobalIndexSingletonWriter {
return Collections.emptyList();
}
- int dim = dimension > 0 ? dimension : vectors.get(0).length;
+ int dim = dimension > 0 ? dimension : vectors.get(0).vector.length;
int count = vectors.size();
try {
@@ -113,21 +117,36 @@ public class TestVectorGlobalIndexWriter implements
GlobalIndexSingletonWriter {
out.write(header.array());
// Vector data
+ ByteBuffer rowIdBuf = ByteBuffer.allocate(Long.BYTES);
+ rowIdBuf.order(ByteOrder.LITTLE_ENDIAN);
ByteBuffer vectorBuf = ByteBuffer.allocate(dim * Float.BYTES);
vectorBuf.order(ByteOrder.LITTLE_ENDIAN);
- for (float[] vec : vectors) {
+ for (VectorEntry entry : vectors) {
+ rowIdBuf.clear();
+ rowIdBuf.putLong(entry.relativeRowId);
+ out.write(rowIdBuf.array());
vectorBuf.clear();
for (int i = 0; i < dim; i++) {
- vectorBuf.putFloat(vec[i]);
+ vectorBuf.putFloat(entry.vector[i]);
}
out.write(vectorBuf.array());
}
out.flush();
}
- return Collections.singletonList(new ResultEntry(fileName, count,
null));
+ return Collections.singletonList(new ResultEntry(fileName,
rowCount, null));
} catch (IOException e) {
throw new RuntimeException("Failed to write test vector index", e);
}
}
+
+ private static class VectorEntry {
+ private final long relativeRowId;
+ private final float[] vector;
+
+ private VectorEntry(long relativeRowId, float[] vector) {
+ this.relativeRowId = relativeRowId;
+ this.vector = vector;
+ }
+ }
}
diff --git
a/paimon-core/src/main/java/org/apache/paimon/globalindex/btree/BTreeGlobalIndexBuilder.java
b/paimon-core/src/main/java/org/apache/paimon/globalindex/btree/BTreeGlobalIndexBuilder.java
index ad68d83eb3..0fcbbcbb43 100644
---
a/paimon-core/src/main/java/org/apache/paimon/globalindex/btree/BTreeGlobalIndexBuilder.java
+++
b/paimon-core/src/main/java/org/apache/paimon/globalindex/btree/BTreeGlobalIndexBuilder.java
@@ -26,7 +26,7 @@ import org.apache.paimon.data.InternalRow;
import org.apache.paimon.data.InternalRow.FieldGetter;
import org.apache.paimon.disk.IOManager;
import org.apache.paimon.globalindex.DataEvolutionBatchScan;
-import org.apache.paimon.globalindex.GlobalIndexParallelWriter;
+import org.apache.paimon.globalindex.GlobalIndexSingleColumnWriter;
import org.apache.paimon.globalindex.GlobalIndexWriter;
import org.apache.paimon.globalindex.IndexedSplit;
import org.apache.paimon.globalindex.ResultEntry;
@@ -256,7 +256,7 @@ public class BTreeGlobalIndexBuilder implements
Serializable {
public List<CommitMessage> buildForSinglePartition(
Range rowRange, BinaryRow partition, Iterator<InternalRow> data)
throws IOException {
long counter = 0;
- GlobalIndexParallelWriter currentWriter = null;
+ GlobalIndexSingleColumnWriter currentWriter = null;
List<CommitMessage> commitMessages = new ArrayList<>();
FieldGetter indexFieldGetter =
InternalRow.createFieldGetter(indexField.type(), 0);
@@ -284,15 +284,15 @@ public class BTreeGlobalIndexBuilder implements
Serializable {
return commitMessages;
}
- public GlobalIndexParallelWriter createWriter() throws IOException {
- GlobalIndexParallelWriter currentWriter;
+ public GlobalIndexSingleColumnWriter createWriter() throws IOException {
+ GlobalIndexSingleColumnWriter currentWriter;
GlobalIndexWriter indexWriter = createIndexWriter(table, INDEX_TYPE,
indexField, options);
- if (!(indexWriter instanceof GlobalIndexParallelWriter)) {
+ if (!(indexWriter instanceof GlobalIndexSingleColumnWriter)) {
throw new RuntimeException(
- "Unexpected implementation, the index writer of BTree
should be an instance of GlobalIndexParallelWriter, but found: "
+ "Unexpected implementation, the index writer of BTree
should be an instance of GlobalIndexSingleColumnWriter, but found: "
+ indexWriter.getClass().getName());
}
- currentWriter = (GlobalIndexParallelWriter) indexWriter;
+ currentWriter = (GlobalIndexSingleColumnWriter) indexWriter;
return currentWriter;
}
diff --git
a/paimon-core/src/test/java/org/apache/paimon/table/source/FullTextSearchBuilderTest.java
b/paimon-core/src/test/java/org/apache/paimon/table/source/FullTextSearchBuilderTest.java
index 2d7b8c97fa..13f84d1b97 100644
---
a/paimon-core/src/test/java/org/apache/paimon/table/source/FullTextSearchBuilderTest.java
+++
b/paimon-core/src/test/java/org/apache/paimon/table/source/FullTextSearchBuilderTest.java
@@ -25,7 +25,7 @@ import org.apache.paimon.data.GenericRow;
import org.apache.paimon.data.InternalRow;
import org.apache.paimon.globalindex.GlobalIndexBuilderUtils;
import org.apache.paimon.globalindex.GlobalIndexResult;
-import org.apache.paimon.globalindex.GlobalIndexSingletonWriter;
+import org.apache.paimon.globalindex.GlobalIndexSingleColumnWriter;
import org.apache.paimon.globalindex.ResultEntry;
import org.apache.paimon.globalindex.ScoredGlobalIndexResult;
import
org.apache.paimon.globalindex.testfulltext.TestFullTextGlobalIndexerFactory;
@@ -361,15 +361,15 @@ public class FullTextSearchBuilderTest extends
TableTestBase {
Options options = table.coreOptions().toConfiguration();
DataField textField = table.rowType().getField(TEXT_FIELD_NAME);
- GlobalIndexSingletonWriter writer =
- (GlobalIndexSingletonWriter)
+ GlobalIndexSingleColumnWriter writer =
+ (GlobalIndexSingleColumnWriter)
GlobalIndexBuilderUtils.createIndexWriter(
table,
TestFullTextGlobalIndexerFactory.IDENTIFIER,
textField,
options);
- for (String doc : documents) {
- writer.write(doc);
+ for (int i = 0; i < documents.length; i++) {
+ writer.write(documents[i], i);
}
List<ResultEntry> entries = writer.finish();
@@ -404,15 +404,15 @@ public class FullTextSearchBuilderTest extends
TableTestBase {
int mid = documents.length / 2;
// Build first index file covering rows [0, mid)
- GlobalIndexSingletonWriter writer1 =
- (GlobalIndexSingletonWriter)
+ GlobalIndexSingleColumnWriter writer1 =
+ (GlobalIndexSingleColumnWriter)
GlobalIndexBuilderUtils.createIndexWriter(
table,
TestFullTextGlobalIndexerFactory.IDENTIFIER,
textField,
options);
for (int i = 0; i < mid; i++) {
- writer1.write(documents[i]);
+ writer1.write(documents[i], i);
}
List<ResultEntry> entries1 = writer1.finish();
Range rowRange1 = new Range(0, mid - 1);
@@ -427,15 +427,15 @@ public class FullTextSearchBuilderTest extends
TableTestBase {
entries1);
// Build second index file covering rows [mid, end)
- GlobalIndexSingletonWriter writer2 =
- (GlobalIndexSingletonWriter)
+ GlobalIndexSingleColumnWriter writer2 =
+ (GlobalIndexSingleColumnWriter)
GlobalIndexBuilderUtils.createIndexWriter(
table,
TestFullTextGlobalIndexerFactory.IDENTIFIER,
textField,
options);
for (int i = mid; i < documents.length; i++) {
- writer2.write(documents[i]);
+ writer2.write(documents[i], i - mid);
}
List<ResultEntry> entries2 = writer2.finish();
Range rowRange2 = new Range(mid, documents.length - 1);
diff --git
a/paimon-core/src/test/java/org/apache/paimon/table/source/VectorSearchBuilderTest.java
b/paimon-core/src/test/java/org/apache/paimon/table/source/VectorSearchBuilderTest.java
index 75b6897e8e..fb5ca42764 100644
---
a/paimon-core/src/test/java/org/apache/paimon/table/source/VectorSearchBuilderTest.java
+++
b/paimon-core/src/test/java/org/apache/paimon/table/source/VectorSearchBuilderTest.java
@@ -25,9 +25,8 @@ import org.apache.paimon.data.GenericRow;
import org.apache.paimon.data.InternalRow;
import org.apache.paimon.data.serializer.InternalRowSerializer;
import org.apache.paimon.globalindex.GlobalIndexBuilderUtils;
-import org.apache.paimon.globalindex.GlobalIndexParallelWriter;
import org.apache.paimon.globalindex.GlobalIndexResult;
-import org.apache.paimon.globalindex.GlobalIndexSingletonWriter;
+import org.apache.paimon.globalindex.GlobalIndexSingleColumnWriter;
import org.apache.paimon.globalindex.ResultEntry;
import org.apache.paimon.globalindex.ScoredGlobalIndexResult;
import org.apache.paimon.globalindex.btree.BTreeGlobalIndexerFactory;
@@ -611,15 +610,15 @@ public class VectorSearchBuilderTest extends
TableTestBase {
Options options = table.coreOptions().toConfiguration();
DataField vectorField = table.rowType().getField(fieldName);
- GlobalIndexSingletonWriter writer =
- (GlobalIndexSingletonWriter)
+ GlobalIndexSingleColumnWriter writer =
+ (GlobalIndexSingleColumnWriter)
GlobalIndexBuilderUtils.createIndexWriter(
table,
TestVectorGlobalIndexerFactory.IDENTIFIER,
vectorField,
options);
- for (float[] vec : vectors) {
- writer.write(vec);
+ for (int i = 0; i < vectors.length; i++) {
+ writer.write(vectors[i], i);
}
List<ResultEntry> entries = writer.finish();
@@ -654,15 +653,15 @@ public class VectorSearchBuilderTest extends
TableTestBase {
int mid = vectors.length / 2;
// Build first index file covering rows [0, mid)
- GlobalIndexSingletonWriter writer1 =
- (GlobalIndexSingletonWriter)
+ GlobalIndexSingleColumnWriter writer1 =
+ (GlobalIndexSingleColumnWriter)
GlobalIndexBuilderUtils.createIndexWriter(
table,
TestVectorGlobalIndexerFactory.IDENTIFIER,
vectorField,
options);
for (int i = 0; i < mid; i++) {
- writer1.write(vectors[i]);
+ writer1.write(vectors[i], i);
}
List<ResultEntry> entries1 = writer1.finish();
Range rowRange1 = new Range(0, mid - 1);
@@ -677,15 +676,15 @@ public class VectorSearchBuilderTest extends
TableTestBase {
entries1);
// Build second index file covering rows [mid, end)
- GlobalIndexSingletonWriter writer2 =
- (GlobalIndexSingletonWriter)
+ GlobalIndexSingleColumnWriter writer2 =
+ (GlobalIndexSingleColumnWriter)
GlobalIndexBuilderUtils.createIndexWriter(
table,
TestVectorGlobalIndexerFactory.IDENTIFIER,
vectorField,
options);
for (int i = mid; i < vectors.length; i++) {
- writer2.write(vectors[i]);
+ writer2.write(vectors[i], i - mid);
}
List<ResultEntry> entries2 = writer2.finish();
Range rowRange2 = new Range(mid, vectors.length - 1);
@@ -819,15 +818,15 @@ public class VectorSearchBuilderTest extends
TableTestBase {
Options options = table.coreOptions().toConfiguration();
DataField vectorField = table.rowType().getField(VECTOR_FIELD_NAME);
- GlobalIndexSingletonWriter writer =
- (GlobalIndexSingletonWriter)
+ GlobalIndexSingleColumnWriter writer =
+ (GlobalIndexSingleColumnWriter)
GlobalIndexBuilderUtils.createIndexWriter(
table,
TestVectorGlobalIndexerFactory.IDENTIFIER,
vectorField,
options);
- for (float[] vec : vectors) {
- writer.write(vec);
+ for (int i = 0; i < vectors.length; i++) {
+ writer.write(vectors[i], i);
}
List<ResultEntry> entries = writer.finish();
@@ -859,8 +858,8 @@ public class VectorSearchBuilderTest extends TableTestBase {
Options options = table.coreOptions().toConfiguration();
DataField idField = table.rowType().getField("id");
- GlobalIndexParallelWriter writer =
- (GlobalIndexParallelWriter)
+ GlobalIndexSingleColumnWriter writer =
+ (GlobalIndexSingleColumnWriter)
GlobalIndexBuilderUtils.createIndexWriter(
table, BTreeGlobalIndexerFactory.IDENTIFIER,
idField, options);
for (int id : ids) {
@@ -898,15 +897,15 @@ public class VectorSearchBuilderTest extends
TableTestBase {
Options options = table.coreOptions().toConfiguration();
DataField vectorField = table.rowType().getField(VECTOR_FIELD_NAME);
- GlobalIndexSingletonWriter writer =
- (GlobalIndexSingletonWriter)
+ GlobalIndexSingleColumnWriter writer =
+ (GlobalIndexSingleColumnWriter)
GlobalIndexBuilderUtils.createIndexWriter(
table,
TestVectorGlobalIndexerFactory.IDENTIFIER,
vectorField,
options);
- for (float[] vec : vectors) {
- writer.write(vec);
+ for (int i = 0; i < vectors.length; i++) {
+ writer.write(vectors[i], i);
}
List<ResultEntry> entries = writer.finish();
diff --git
a/paimon-flink/paimon-flink-common/src/main/java/org/apache/paimon/flink/btree/BTreeIndexTopoBuilder.java
b/paimon-flink/paimon-flink-common/src/main/java/org/apache/paimon/flink/btree/BTreeIndexTopoBuilder.java
index 9542eea4a4..98697fae73 100644
---
a/paimon-flink/paimon-flink-common/src/main/java/org/apache/paimon/flink/btree/BTreeIndexTopoBuilder.java
+++
b/paimon-flink/paimon-flink-common/src/main/java/org/apache/paimon/flink/btree/BTreeIndexTopoBuilder.java
@@ -37,7 +37,7 @@ import org.apache.paimon.flink.sorter.TableSorter;
import org.apache.paimon.flink.utils.BoundedOneInputOperator;
import org.apache.paimon.flink.utils.JavaTypeInfo;
import org.apache.paimon.flink.utils.StreamExecutionEnvironmentUtils;
-import org.apache.paimon.globalindex.GlobalIndexParallelWriter;
+import org.apache.paimon.globalindex.GlobalIndexSingleColumnWriter;
import org.apache.paimon.globalindex.btree.BTreeGlobalIndexBuilder;
import org.apache.paimon.globalindex.btree.BTreeIndexOptions;
import org.apache.paimon.options.Options;
@@ -377,7 +377,7 @@ public class BTreeIndexTopoBuilder {
private transient long counter;
private transient BTreeBuildTask currentTask;
private transient BinaryRow currentPartition;
- private transient GlobalIndexParallelWriter currentWriter;
+ private transient GlobalIndexSingleColumnWriter currentWriter;
private transient List<CommitMessage> commitMessages;
private transient Map<Integer, BTreeBuildTask> buildTasksById;
private transient InternalRow.FieldGetter indexFieldGetter;
diff --git
a/paimon-flink/paimon-flink-common/src/main/java/org/apache/paimon/flink/globalindex/GenericIndexTopoBuilder.java
b/paimon-flink/paimon-flink-common/src/main/java/org/apache/paimon/flink/globalindex/GenericIndexTopoBuilder.java
index af256da8ec..39e6db0cf8 100644
---
a/paimon-flink/paimon-flink-common/src/main/java/org/apache/paimon/flink/globalindex/GenericIndexTopoBuilder.java
+++
b/paimon-flink/paimon-flink-common/src/main/java/org/apache/paimon/flink/globalindex/GenericIndexTopoBuilder.java
@@ -30,7 +30,7 @@ import org.apache.paimon.flink.utils.BoundedOneInputOperator;
import org.apache.paimon.flink.utils.JavaTypeInfo;
import org.apache.paimon.flink.utils.StreamExecutionEnvironmentUtils;
import org.apache.paimon.globalindex.GlobalIndexMultiColumnWriter;
-import org.apache.paimon.globalindex.GlobalIndexSingletonWriter;
+import org.apache.paimon.globalindex.GlobalIndexSingleColumnWriter;
import org.apache.paimon.globalindex.GlobalIndexWriter;
import org.apache.paimon.globalindex.ResultEntry;
import org.apache.paimon.index.IndexFileMeta;
@@ -684,13 +684,14 @@ public class GenericIndexTopoBuilder {
}
// Only write rows within this shard's range
if (currentRowId >= task.shardRange.from) {
+ long rowId = currentRowId - task.shardRange.from;
if (multiColumn) {
- long rowId = currentRowId -
task.shardRange.from;
((GlobalIndexMultiColumnWriter) indexWriter)
.write(rowId,
writerProjection.replaceRow(row));
} else {
Object fieldData =
indexFieldGetters[0].getFieldOrNull(row);
- ((GlobalIndexSingletonWriter)
indexWriter).write(fieldData);
+ ((GlobalIndexSingleColumnWriter) indexWriter)
+ .write(fieldData, rowId);
}
rowsSeen++;
}
diff --git
a/paimon-flink/paimon-flink-common/src/test/java/org/apache/paimon/flink/procedure/VectorSearchProcedureITCase.java
b/paimon-flink/paimon-flink-common/src/test/java/org/apache/paimon/flink/procedure/VectorSearchProcedureITCase.java
index 7beab44fdc..d71de22d2a 100644
---
a/paimon-flink/paimon-flink-common/src/test/java/org/apache/paimon/flink/procedure/VectorSearchProcedureITCase.java
+++
b/paimon-flink/paimon-flink-common/src/test/java/org/apache/paimon/flink/procedure/VectorSearchProcedureITCase.java
@@ -23,7 +23,7 @@ import org.apache.paimon.data.GenericArray;
import org.apache.paimon.data.GenericRow;
import org.apache.paimon.flink.CatalogITCaseBase;
import org.apache.paimon.globalindex.GlobalIndexBuilderUtils;
-import org.apache.paimon.globalindex.GlobalIndexSingletonWriter;
+import org.apache.paimon.globalindex.GlobalIndexSingleColumnWriter;
import org.apache.paimon.globalindex.ResultEntry;
import org.apache.paimon.globalindex.testvector.TestVectorGlobalIndexer;
import org.apache.paimon.globalindex.testvector.TestVectorGlobalIndexerFactory;
@@ -218,15 +218,15 @@ public class VectorSearchProcedureITCase extends
CatalogITCaseBase {
Options options = table.coreOptions().toConfiguration();
DataField vectorField = table.rowType().getField(VECTOR_FIELD);
- GlobalIndexSingletonWriter writer =
- (GlobalIndexSingletonWriter)
+ GlobalIndexSingleColumnWriter writer =
+ (GlobalIndexSingleColumnWriter)
GlobalIndexBuilderUtils.createIndexWriter(
table,
TestVectorGlobalIndexerFactory.IDENTIFIER,
vectorField,
options);
- for (float[] vec : vectors) {
- writer.write(vec);
+ for (int i = 0; i < vectors.length; i++) {
+ writer.write(vectors[i], i);
}
List<ResultEntry> entries = writer.finish();
diff --git
a/paimon-lumina/src/main/java/org/apache/paimon/lumina/index/LuminaVectorGlobalIndexWriter.java
b/paimon-lumina/src/main/java/org/apache/paimon/lumina/index/LuminaVectorGlobalIndexWriter.java
index 206da270a4..9d9ebeaad8 100644
---
a/paimon-lumina/src/main/java/org/apache/paimon/lumina/index/LuminaVectorGlobalIndexWriter.java
+++
b/paimon-lumina/src/main/java/org/apache/paimon/lumina/index/LuminaVectorGlobalIndexWriter.java
@@ -21,7 +21,7 @@ package org.apache.paimon.lumina.index;
import org.apache.paimon.data.InternalArray;
import org.apache.paimon.data.InternalVector;
import org.apache.paimon.fs.PositionOutputStream;
-import org.apache.paimon.globalindex.GlobalIndexSingletonWriter;
+import org.apache.paimon.globalindex.GlobalIndexSingleColumnWriter;
import org.apache.paimon.globalindex.ResultEntry;
import org.apache.paimon.globalindex.io.GlobalIndexFileWriter;
import org.apache.paimon.types.ArrayType;
@@ -50,16 +50,17 @@ import java.util.concurrent.ConcurrentHashMap;
/**
* Vector global index writer using Lumina. Builds a single index file per
shard.
*
- * <p>Vectors are spilled to a temporary file on disk as they arrive via
{@link #write(Object)},
- * keeping Java heap usage constant (~8 MB buffer) regardless of dataset size.
During index build,
- * the Lumina builder streams vectors from the temp file via the {@link
LuminaDataset} callback API.
+ * <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) regardless of
dataset size. During index
+ * build, the Lumina builder streams vectors from the temp file via the {@link
LuminaDataset}
+ * callback API.
*
* <p><b>Thread safety:</b> This class is <b>not</b> thread-safe. The
underlying {@code
* LuminaBuilder} must be used from a single thread or the caller must provide
external
* synchronization. The internal Lumina executor thread pool size is
controlled globally by the
* {@code LUMINA_EXECUTOR_THREAD_COUNT} environment variable and cannot be
configured per-instance.
*/
-public class LuminaVectorGlobalIndexWriter implements
GlobalIndexSingletonWriter, Closeable {
+public class LuminaVectorGlobalIndexWriter implements
GlobalIndexSingleColumnWriter, Closeable {
private static final String FILE_NAME_PREFIX = "lumina";
@@ -92,7 +93,7 @@ public class LuminaVectorGlobalIndexWriter implements
GlobalIndexSingletonWriter
private long count;
private boolean closed;
- private long logicalRowId;
+ private long rowCount;
public LuminaVectorGlobalIndexWriter(
GlobalIndexFileWriter fileWriter,
@@ -154,23 +155,23 @@ public class LuminaVectorGlobalIndexWriter implements
GlobalIndexSingletonWriter
}
@Override
- public void write(Object fieldData) {
+ public void write(Object fieldData, long relativeRowId) {
if (fieldData == null) {
- logicalRowId++;
+ rowCount++;
return;
}
// Validation must complete before any buffer/state mutation below
- float[] src = materializeAndValidate(fieldData);
+ float[] src = materializeAndValidate(fieldData, relativeRowId);
if (writeBuf.remaining() < recordSizeInBytes) {
flushWriteBuffer();
}
- writeBuf.putLong(logicalRowId);
+ writeBuf.putLong(relativeRowId);
for (int i = 0; i < dim; i++) {
writeBuf.putFloat(src[i]);
}
- logicalRowId++;
+ rowCount++;
count++;
}
@@ -179,12 +180,12 @@ public class LuminaVectorGlobalIndexWriter implements
GlobalIndexSingletonWriter
* input directly (zero-copy). For InternalVector/InternalArray, reads
into the reusable
* vectorBuf field.
*/
- private float[] materializeAndValidate(Object fieldData) {
+ private float[] materializeAndValidate(Object fieldData, long
relativeRowId) {
if (fieldData instanceof float[]) {
float[] vector = (float[]) fieldData;
checkDimension(vector.length);
for (int i = 0; i < dim; i++) {
- checkFinite(vector[i], i);
+ checkFinite(vector[i], relativeRowId, i);
}
return vector;
} else if (fieldData instanceof InternalVector) {
@@ -192,7 +193,7 @@ public class LuminaVectorGlobalIndexWriter implements
GlobalIndexSingletonWriter
checkDimension(vector.size());
for (int i = 0; i < dim; i++) {
float v = vector.getFloat(i);
- checkFinite(v, i);
+ checkFinite(v, relativeRowId, i);
vectorBuf[i] = v;
}
return vectorBuf;
@@ -204,7 +205,7 @@ public class LuminaVectorGlobalIndexWriter implements
GlobalIndexSingletonWriter
throw new IllegalArgumentException("Vector element at
index " + i + " is null");
}
float v = array.getFloat(i);
- checkFinite(v, i);
+ checkFinite(v, relativeRowId, i);
vectorBuf[i] = v;
}
return vectorBuf;
@@ -301,7 +302,7 @@ public class LuminaVectorGlobalIndexWriter implements
GlobalIndexSingletonWriter
LuminaIndexMeta meta = new LuminaIndexMeta(luminaOptions);
// rowCount = logical rows including nulls (not just indexed
vectors)
- return new ResultEntry(fileName, logicalRowId, meta.serialize());
+ return new ResultEntry(fileName, rowCount, meta.serialize());
}
}
@@ -365,12 +366,12 @@ public class LuminaVectorGlobalIndexWriter implements
GlobalIndexSingletonWriter
}
}
- private void checkFinite(float value, int elementIndex) {
+ private void checkFinite(float value, long relativeRowId, int
elementIndex) {
if (!Float.isFinite(value)) {
throw new IllegalArgumentException(
String.format(
"Vector element at rowId=%d, index=%d is %s",
- logicalRowId, elementIndex,
Float.toString(value)));
+ relativeRowId, elementIndex,
Float.toString(value)));
}
}
diff --git
a/paimon-lumina/src/test/java/org/apache/paimon/lumina/index/JavaPyLuminaE2ETest.java
b/paimon-lumina/src/test/java/org/apache/paimon/lumina/index/JavaPyLuminaE2ETest.java
index ce613dfd61..d402a40ac9 100644
---
a/paimon-lumina/src/test/java/org/apache/paimon/lumina/index/JavaPyLuminaE2ETest.java
+++
b/paimon-lumina/src/test/java/org/apache/paimon/lumina/index/JavaPyLuminaE2ETest.java
@@ -25,8 +25,7 @@ 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.GlobalIndexParallelWriter;
-import org.apache.paimon.globalindex.GlobalIndexSingletonWriter;
+import org.apache.paimon.globalindex.GlobalIndexSingleColumnWriter;
import org.apache.paimon.globalindex.ResultEntry;
import org.apache.paimon.globalindex.btree.BTreeGlobalIndexerFactory;
import org.apache.paimon.index.IndexFileMeta;
@@ -170,16 +169,16 @@ public class JavaPyLuminaE2ETest {
DataField embeddingField = table.rowType().getField("embedding");
Options indexOptions = table.coreOptions().toConfiguration();
- GlobalIndexSingletonWriter writer =
- (GlobalIndexSingletonWriter)
+ GlobalIndexSingleColumnWriter writer =
+ (GlobalIndexSingleColumnWriter)
GlobalIndexBuilderUtils.createIndexWriter(
table,
LuminaVectorGlobalIndexerFactory.IDENTIFIER,
embeddingField,
indexOptions);
- for (float[] vec : vectors) {
- writer.write(vec);
+ for (int i = 0; i < vectors.length; i++) {
+ writer.write(vectors[i], i);
}
List<ResultEntry> entries = writer.finish();
@@ -280,15 +279,15 @@ public class JavaPyLuminaE2ETest {
// Build Lumina vector index on "embedding".
DataField embeddingField = table.rowType().getField("embedding");
- GlobalIndexSingletonWriter vectorWriter =
- (GlobalIndexSingletonWriter)
+ GlobalIndexSingleColumnWriter vectorWriter =
+ (GlobalIndexSingleColumnWriter)
GlobalIndexBuilderUtils.createIndexWriter(
table,
LuminaVectorGlobalIndexerFactory.IDENTIFIER,
embeddingField,
indexOptions);
- for (float[] vec : vectors) {
- vectorWriter.write(vec);
+ for (int i = 0; i < vectors.length; i++) {
+ vectorWriter.write(vectors[i], i);
}
List<ResultEntry> vectorEntries = vectorWriter.finish();
List<IndexFileMeta> vectorIndexFiles =
@@ -303,8 +302,8 @@ public class JavaPyLuminaE2ETest {
// Build BTree global index on "id".
DataField idField = table.rowType().getField("id");
- GlobalIndexParallelWriter idWriter =
- (GlobalIndexParallelWriter)
+ GlobalIndexSingleColumnWriter idWriter =
+ (GlobalIndexSingleColumnWriter)
GlobalIndexBuilderUtils.createIndexWriter(
table, BTreeGlobalIndexerFactory.IDENTIFIER,
idField, indexOptions);
for (int i = 0; i < vectors.length; i++) {
diff --git
a/paimon-lumina/src/test/java/org/apache/paimon/lumina/index/LuminaVectorBenchmark.java
b/paimon-lumina/src/test/java/org/apache/paimon/lumina/index/LuminaVectorBenchmark.java
index 1458352bba..3511fba4a3 100644
---
a/paimon-lumina/src/test/java/org/apache/paimon/lumina/index/LuminaVectorBenchmark.java
+++
b/paimon-lumina/src/test/java/org/apache/paimon/lumina/index/LuminaVectorBenchmark.java
@@ -388,7 +388,7 @@ public class LuminaVectorBenchmark {
for (int d = 0; d < dimension; d++) {
vec[d] = (float) insertRandom.nextDouble() * 2 - 1;
}
- writer.write(vec);
+ writer.write(vec, i);
}
long writeEnd = System.currentTimeMillis();
System.out.printf(
diff --git
a/paimon-lumina/src/test/java/org/apache/paimon/lumina/index/LuminaVectorGlobalIndexScanTest.java
b/paimon-lumina/src/test/java/org/apache/paimon/lumina/index/LuminaVectorGlobalIndexScanTest.java
index 52100fb2e1..85d4ebe6be 100644
---
a/paimon-lumina/src/test/java/org/apache/paimon/lumina/index/LuminaVectorGlobalIndexScanTest.java
+++
b/paimon-lumina/src/test/java/org/apache/paimon/lumina/index/LuminaVectorGlobalIndexScanTest.java
@@ -212,8 +212,8 @@ public class LuminaVectorGlobalIndexScanTest {
LuminaVectorGlobalIndexWriter indexWriter =
new LuminaVectorGlobalIndexWriter(
fileWriter, new ArrayType(DataTypes.FLOAT()),
indexOptions);
- for (float[] vec : vectors) {
- indexWriter.write(vec);
+ for (int i = 0; i < vectors.length; i++) {
+ indexWriter.write(vectors[i], i);
}
List<ResultEntry> entries = indexWriter.finish();
@@ -302,8 +302,8 @@ public class LuminaVectorGlobalIndexScanTest {
LuminaVectorGlobalIndexWriter writer =
new LuminaVectorGlobalIndexWriter(
fileWriter, new ArrayType(DataTypes.FLOAT()),
indexOptions);
- for (float[] vec : vectors) {
- writer.write(vec);
+ for (int i = 0; i < vectors.length; i++) {
+ writer.write(vectors[i], i);
}
List<ResultEntry> entries = writer.finish();
diff --git
a/paimon-lumina/src/test/java/org/apache/paimon/lumina/index/LuminaVectorGlobalIndexTest.java
b/paimon-lumina/src/test/java/org/apache/paimon/lumina/index/LuminaVectorGlobalIndexTest.java
index ab37247295..b9ab7e1219 100644
---
a/paimon-lumina/src/test/java/org/apache/paimon/lumina/index/LuminaVectorGlobalIndexTest.java
+++
b/paimon-lumina/src/test/java/org/apache/paimon/lumina/index/LuminaVectorGlobalIndexTest.java
@@ -46,7 +46,6 @@ import org.junit.jupiter.api.io.TempDir;
import java.io.IOException;
import java.util.ArrayList;
-import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Random;
@@ -150,7 +149,7 @@ public class LuminaVectorGlobalIndexTest {
new LuminaVectorGlobalIndexWriter(fileWriter, vectorType,
indexOptions);
List<float[]> testVectors = generateRandomVectors(numVectors,
dimension);
- testVectors.forEach(writer::write);
+ writeVectors(writer, testVectors);
List<ResultEntry> results = writer.finish();
List<GlobalIndexIOMeta> metas = toIOMetas(results,
metricIndexPath);
@@ -185,7 +184,7 @@ public class LuminaVectorGlobalIndexTest {
int numVectors = 10;
List<float[]> testVectors = generateRandomVectors(numVectors,
dimension);
- testVectors.forEach(writer::write);
+ writeVectors(writer, testVectors);
List<ResultEntry> results = writer.finish();
List<GlobalIndexIOMeta> metas = toIOMetas(results, dimIndexPath);
@@ -216,7 +215,7 @@ public class LuminaVectorGlobalIndexTest {
new LuminaVectorGlobalIndexWriter(fileWriter, vectorType,
indexOptions);
float[] wrongDimVector = new float[32];
- assertThatThrownBy(() -> writer.write(wrongDimVector))
+ assertThatThrownBy(() -> writer.write(wrongDimVector, 0))
.isInstanceOf(IllegalArgumentException.class)
.hasMessageContaining("dimension mismatch");
}
@@ -236,7 +235,7 @@ public class LuminaVectorGlobalIndexTest {
LuminaVectorIndexOptions indexOptions = new
LuminaVectorIndexOptions(options);
LuminaVectorGlobalIndexWriter writer =
new LuminaVectorGlobalIndexWriter(fileWriter, vectorType,
indexOptions);
- Arrays.stream(vectors).forEach(writer::write);
+ writeVectors(writer, vectors);
List<ResultEntry> results = writer.finish();
List<GlobalIndexIOMeta> metas = toIOMetas(results, indexPath);
@@ -299,7 +298,7 @@ public class LuminaVectorGlobalIndexTest {
LuminaVectorIndexOptions indexOptions = new
LuminaVectorIndexOptions(options);
LuminaVectorGlobalIndexWriter writer =
new LuminaVectorGlobalIndexWriter(fileWriter, vectorType,
indexOptions);
- Arrays.stream(vectors).forEach(writer::write);
+ writeVectors(writer, vectors);
List<ResultEntry> results = writer.finish();
List<GlobalIndexIOMeta> metas = toIOMetas(results, indexPath);
@@ -366,7 +365,7 @@ public class LuminaVectorGlobalIndexTest {
int numVectors = 350;
List<float[]> testVectors = generateRandomVectors(numVectors,
dimension);
- testVectors.forEach(writer::write);
+ writeVectors(writer, testVectors);
List<ResultEntry> results = writer.finish();
List<GlobalIndexIOMeta> metas = toIOMetas(results, indexPath);
@@ -417,7 +416,7 @@ public class LuminaVectorGlobalIndexTest {
GlobalIndexFileWriter fileWriter = createFileWriter(indexPath);
LuminaVectorGlobalIndexWriter writer =
new LuminaVectorGlobalIndexWriter(fileWriter, vectorType,
writeIndexOptions);
- Arrays.stream(vectors).forEach(writer::write);
+ writeVectors(writer, vectors);
List<ResultEntry> results = writer.finish();
List<GlobalIndexIOMeta> metas = toIOMetas(results, indexPath);
@@ -467,8 +466,8 @@ public class LuminaVectorGlobalIndexTest {
new LuminaVectorGlobalIndexWriter(fileWriter, vecFieldType,
indexOptions);
// Write using BinaryVector (InternalVector)
- for (float[] vec : vectors) {
- writer.write(BinaryVector.fromPrimitiveArray(vec));
+ for (int i = 0; i < vectors.length; i++) {
+ writer.write(BinaryVector.fromPrimitiveArray(vectors[i]), i);
}
List<ResultEntry> results = writer.finish();
@@ -507,8 +506,8 @@ public class LuminaVectorGlobalIndexTest {
new float[] {0.0f, 1.0f},
new float[] {0.7f, 0.7f}
};
- for (float[] vec : vectors) {
- writer.write(vec);
+ for (int i = 0; i < vectors.length; i++) {
+ writer.write(vectors[i], i);
}
List<ResultEntry> results = writer.finish();
@@ -548,12 +547,12 @@ public class LuminaVectorGlobalIndexTest {
LuminaVectorGlobalIndexWriter writer =
new LuminaVectorGlobalIndexWriter(fileWriter, vectorType,
indexOptions);
- writer.write(vectors[0]); // row 0
- writer.write(null); // row 1 - null
- writer.write(vectors[1]); // row 2
- writer.write(null); // row 3 - null
- writer.write(null); // row 4 - null
- writer.write(vectors[2]); // row 5
+ writer.write(vectors[0], 0); // row 0
+ writer.write(null, 1); // row 1 - null
+ writer.write(vectors[1], 2); // row 2
+ writer.write(null, 3); // row 3 - null
+ writer.write(null, 4); // row 4 - null
+ writer.write(vectors[2], 5); // row 5
List<ResultEntry> results = writer.finish();
assertThat(results).hasSize(1);
@@ -591,9 +590,9 @@ public class LuminaVectorGlobalIndexTest {
LuminaVectorGlobalIndexWriter writer =
new LuminaVectorGlobalIndexWriter(fileWriter, vectorType,
indexOptions);
- writer.write(null);
- writer.write(null);
- writer.write(null);
+ writer.write(null, 0);
+ writer.write(null, 1);
+ writer.write(null, 2);
List<ResultEntry> results = writer.finish();
assertThat(results).isEmpty();
@@ -618,12 +617,12 @@ public class LuminaVectorGlobalIndexTest {
LuminaVectorGlobalIndexWriter writer =
new LuminaVectorGlobalIndexWriter(fileWriter, vectorType,
indexOptions);
- writer.write(vectors[0]); // row 0
- writer.write(null); // row 1 - null
- writer.write(vectors[1]); // row 2
- writer.write(vectors[2]); // row 3
- writer.write(null); // row 4 - null
- writer.write(vectors[3]); // row 5
+ writer.write(vectors[0], 0); // row 0
+ writer.write(null, 1); // row 1 - null
+ writer.write(vectors[1], 2); // row 2
+ writer.write(vectors[2], 3); // row 3
+ writer.write(null, 4); // row 4 - null
+ writer.write(vectors[3], 5); // row 5
List<ResultEntry> results = writer.finish();
List<GlobalIndexIOMeta> metas = toIOMetas(results, indexPath);
@@ -659,8 +658,8 @@ public class LuminaVectorGlobalIndexTest {
LuminaVectorGlobalIndexWriter writer =
new LuminaVectorGlobalIndexWriter(fileWriter, vectorType,
indexOptions);
- writer.write(null); // row 0 - null
- writer.write(new float[] {1.0f, 0.0f}); // row 1
+ writer.write(null, 0); // row 0 - null
+ writer.write(new float[] {1.0f, 0.0f}, 1); // row 1
List<ResultEntry> results = writer.finish();
assertThat(results).hasSize(1);
@@ -689,8 +688,8 @@ public class LuminaVectorGlobalIndexTest {
LuminaVectorGlobalIndexWriter writer =
new LuminaVectorGlobalIndexWriter(fileWriter, vectorType,
indexOptions);
- writer.write(new float[] {1.0f, 0.0f}); // row 0
- writer.write(null); // row 1 - null
+ writer.write(new float[] {1.0f, 0.0f}, 0); // row 0
+ writer.write(null, 1); // row 1 - null
List<ResultEntry> results = writer.finish();
assertThat(results).hasSize(1);
@@ -719,7 +718,7 @@ public class LuminaVectorGlobalIndexTest {
LuminaVectorGlobalIndexWriter writer =
new LuminaVectorGlobalIndexWriter(fileWriter, vectorType,
indexOptions);
- assertThatThrownBy(() -> writer.write(new float[] {1.0f, Float.NaN}))
+ assertThatThrownBy(() -> writer.write(new float[] {1.0f, Float.NaN},
0))
.isInstanceOf(IllegalArgumentException.class)
.hasMessageContaining("rowId=0")
.hasMessageContaining("index=1")
@@ -736,14 +735,14 @@ public class LuminaVectorGlobalIndexTest {
LuminaVectorGlobalIndexWriter writer =
new LuminaVectorGlobalIndexWriter(fileWriter, vectorType,
indexOptions);
- writer.write(null); // row 0 - null, advances logicalRowId
- assertThatThrownBy(() -> writer.write(new float[]
{Float.POSITIVE_INFINITY, 0.0f}))
+ writer.write(null, 0); // row 0 - null
+ assertThatThrownBy(() -> writer.write(new float[]
{Float.POSITIVE_INFINITY, 0.0f}, 1))
.isInstanceOf(IllegalArgumentException.class)
.hasMessageContaining("rowId=1")
.hasMessageContaining("index=0")
.hasMessageContaining("Infinity");
- assertThatThrownBy(() -> writer.write(new float[] {0.0f,
Float.NEGATIVE_INFINITY}))
+ assertThatThrownBy(() -> writer.write(new float[] {0.0f,
Float.NEGATIVE_INFINITY}, 1))
.isInstanceOf(IllegalArgumentException.class)
.hasMessageContaining("rowId=1")
.hasMessageContaining("index=1")
@@ -757,6 +756,18 @@ public class LuminaVectorGlobalIndexTest {
return options;
}
+ private void writeVectors(LuminaVectorGlobalIndexWriter writer,
List<float[]> vectors) {
+ for (int i = 0; i < vectors.size(); i++) {
+ writer.write(vectors.get(i), i);
+ }
+ }
+
+ private void writeVectors(LuminaVectorGlobalIndexWriter writer, float[][]
vectors) {
+ for (int i = 0; i < vectors.length; i++) {
+ writer.write(vectors[i], i);
+ }
+ }
+
private List<float[]> generateRandomVectors(int count, int dimension) {
Random random = new Random(42);
List<float[]> vectors = new ArrayList<>();
diff --git
a/paimon-lumina/src/test/java/org/apache/paimon/lumina/index/LuminaVectorGlobalIndexWriterTest.java
b/paimon-lumina/src/test/java/org/apache/paimon/lumina/index/LuminaVectorGlobalIndexWriterTest.java
index b0b1996e73..ecae030045 100644
---
a/paimon-lumina/src/test/java/org/apache/paimon/lumina/index/LuminaVectorGlobalIndexWriterTest.java
+++
b/paimon-lumina/src/test/java/org/apache/paimon/lumina/index/LuminaVectorGlobalIndexWriterTest.java
@@ -54,9 +54,9 @@ public class LuminaVectorGlobalIndexWriterTest {
LuminaVectorIndexOptions.DIMENSION),
"2");
- writer.write(new float[] {1.0f, 0.0f});
+ writer.write(new float[] {1.0f, 0.0f}, 0);
- assertThatThrownBy(() -> writer.write(new float[] {1.0f, 0.0f,
0.0f}))
+ assertThatThrownBy(() -> writer.write(new float[] {1.0f, 0.0f,
0.0f}, 1))
.isInstanceOf(IllegalArgumentException.class)
.hasMessageContaining("expected 2")
.hasMessageContaining("got 3");
@@ -94,9 +94,9 @@ public class LuminaVectorGlobalIndexWriterTest {
try (LuminaVectorGlobalIndexWriter writer =
new LuminaVectorGlobalIndexWriter(
createNoopFileWriter(), arrayFieldType, indexOptions))
{
- writer.write(new float[] {1.0f, 0.0f, 0.0f});
+ writer.write(new float[] {1.0f, 0.0f, 0.0f}, 0);
- assertThatThrownBy(() -> writer.write(new float[] {1.0f, 0.0f}))
+ assertThatThrownBy(() -> writer.write(new float[] {1.0f, 0.0f}, 1))
.isInstanceOf(IllegalArgumentException.class)
.hasMessageContaining("expected 3")
.hasMessageContaining("got 2");
diff --git
a/paimon-spark/paimon-spark-common/src/main/java/org/apache/paimon/spark/globalindex/DefaultGlobalIndexBuilder.java
b/paimon-spark/paimon-spark-common/src/main/java/org/apache/paimon/spark/globalindex/DefaultGlobalIndexBuilder.java
index ae87dc96a4..fd13f5ee0a 100644
---
a/paimon-spark/paimon-spark-common/src/main/java/org/apache/paimon/spark/globalindex/DefaultGlobalIndexBuilder.java
+++
b/paimon-spark/paimon-spark-common/src/main/java/org/apache/paimon/spark/globalindex/DefaultGlobalIndexBuilder.java
@@ -21,7 +21,7 @@ package org.apache.paimon.spark.globalindex;
import org.apache.paimon.data.BinaryRow;
import org.apache.paimon.data.InternalRow;
import org.apache.paimon.globalindex.GlobalIndexMultiColumnWriter;
-import org.apache.paimon.globalindex.GlobalIndexSingletonWriter;
+import org.apache.paimon.globalindex.GlobalIndexSingleColumnWriter;
import org.apache.paimon.globalindex.GlobalIndexWriter;
import org.apache.paimon.globalindex.ResultEntry;
import org.apache.paimon.index.IndexFileMeta;
@@ -165,16 +165,22 @@ public class DefaultGlobalIndexBuilder implements
Serializable {
rowCounter.add(1);
}
} else {
- GlobalIndexSingletonWriter singleWriter =
(GlobalIndexSingletonWriter) indexWriter;
+ GlobalIndexSingleColumnWriter singleWriter =
+ (GlobalIndexSingleColumnWriter) indexWriter;
InternalRow.FieldGetter getter =
InternalRow.createFieldGetter(
indexField.type(),
readType.getFieldIndex(indexField.name()));
- rows.forEachRemaining(
- row -> {
- Object indexO = getter.getFieldOrNull(row);
- singleWriter.write(indexO);
- rowCounter.add(1);
- });
+ int rowIdIndex =
readType.getFieldIndex(SpecialFields.ROW_ID.name());
+ while (rows.hasNext()) {
+ InternalRow row = rows.next();
+ long absRowId = row.getLong(rowIdIndex);
+ if (absRowId < rowRange.from || absRowId > rowRange.to) {
+ continue;
+ }
+ Object indexO = getter.getFieldOrNull(row);
+ singleWriter.write(indexO, absRowId - rowRange.from);
+ rowCounter.add(1);
+ }
}
return indexWriter.finish();
} finally {
diff --git
a/paimon-tantivy/paimon-tantivy-index/src/main/java/org/apache/paimon/tantivy/index/TantivyFullTextGlobalIndexWriter.java
b/paimon-tantivy/paimon-tantivy-index/src/main/java/org/apache/paimon/tantivy/index/TantivyFullTextGlobalIndexWriter.java
index 66b6c19f6d..052a8cf2a5 100644
---
a/paimon-tantivy/paimon-tantivy-index/src/main/java/org/apache/paimon/tantivy/index/TantivyFullTextGlobalIndexWriter.java
+++
b/paimon-tantivy/paimon-tantivy-index/src/main/java/org/apache/paimon/tantivy/index/TantivyFullTextGlobalIndexWriter.java
@@ -20,7 +20,7 @@ package org.apache.paimon.tantivy.index;
import org.apache.paimon.data.BinaryString;
import org.apache.paimon.fs.PositionOutputStream;
-import org.apache.paimon.globalindex.GlobalIndexSingletonWriter;
+import org.apache.paimon.globalindex.GlobalIndexSingleColumnWriter;
import org.apache.paimon.globalindex.ResultEntry;
import org.apache.paimon.globalindex.io.GlobalIndexFileWriter;
import org.apache.paimon.tantivy.TantivyIndexWriter;
@@ -48,7 +48,7 @@ import java.util.List;
* <p>Text data is written to a local Tantivy index via JNI. On {@link
#finish()}, the index
* directory is packed into a single file and written to the global index file
system.
*/
-public class TantivyFullTextGlobalIndexWriter implements
GlobalIndexSingletonWriter, Closeable {
+public class TantivyFullTextGlobalIndexWriter implements
GlobalIndexSingleColumnWriter, Closeable {
private static final String FILE_NAME_PREFIX = "tantivy";
private static final Logger LOG =
@@ -58,7 +58,7 @@ public class TantivyFullTextGlobalIndexWriter implements
GlobalIndexSingletonWri
private final TantivyFullTextIndexOptions indexOptions;
private File tempIndexDir;
private TantivyIndexWriter writer;
- private long rowId;
+ private long rowCount;
private boolean closed;
public TantivyFullTextGlobalIndexWriter(GlobalIndexFileWriter fileWriter) {
@@ -69,7 +69,7 @@ public class TantivyFullTextGlobalIndexWriter implements
GlobalIndexSingletonWri
GlobalIndexFileWriter fileWriter, TantivyFullTextIndexOptions
indexOptions) {
this.fileWriter = fileWriter;
this.indexOptions = indexOptions;
- this.rowId = 0;
+ this.rowCount = 0;
this.closed = false;
try {
@@ -84,9 +84,9 @@ public class TantivyFullTextGlobalIndexWriter implements
GlobalIndexSingletonWri
}
@Override
- public void write(Object fieldData) {
+ public void write(Object fieldData, long relativeRowId) {
if (fieldData == null) {
- rowId++;
+ rowCount++;
return;
}
@@ -100,14 +100,14 @@ public class TantivyFullTextGlobalIndexWriter implements
GlobalIndexSingletonWri
"Unsupported field type: " +
fieldData.getClass().getName());
}
- writer.addDocument(rowId, text);
- rowId++;
+ writer.addDocument(relativeRowId, text);
+ rowCount++;
}
@Override
public List<ResultEntry> finish() {
try {
- if (rowId == 0) {
+ if (rowCount == 0) {
return Collections.emptyList();
}
@@ -129,7 +129,7 @@ public class TantivyFullTextGlobalIndexWriter implements
GlobalIndexSingletonWri
}
private ResultEntry packIndex() throws IOException {
- LOG.info("Packing Tantivy index: {} documents", rowId);
+ LOG.info("Packing Tantivy index for {} rows", rowCount);
String fileName = fileWriter.newFileName(FILE_NAME_PREFIX);
try (PositionOutputStream out = fileWriter.newOutputStream(fileName)) {
@@ -170,8 +170,8 @@ public class TantivyFullTextGlobalIndexWriter implements
GlobalIndexSingletonWri
out.flush();
}
- LOG.info("Tantivy index packed: {} documents", rowId);
- return new ResultEntry(fileName, rowId, indexOptions.serialize());
+ LOG.info("Tantivy index packed for {} rows", rowCount);
+ return new ResultEntry(fileName, rowCount, indexOptions.serialize());
}
private static void writeInt(PositionOutputStream out, int value) throws
IOException {
diff --git
a/paimon-tantivy/paimon-tantivy-index/src/test/java/org/apache/paimon/tantivy/index/JavaPyTantivyE2ETest.java
b/paimon-tantivy/paimon-tantivy-index/src/test/java/org/apache/paimon/tantivy/index/JavaPyTantivyE2ETest.java
index fcb2ef7acb..f249227973 100644
---
a/paimon-tantivy/paimon-tantivy-index/src/test/java/org/apache/paimon/tantivy/index/JavaPyTantivyE2ETest.java
+++
b/paimon-tantivy/paimon-tantivy-index/src/test/java/org/apache/paimon/tantivy/index/JavaPyTantivyE2ETest.java
@@ -25,7 +25,7 @@ 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.GlobalIndexSingletonWriter;
+import org.apache.paimon.globalindex.GlobalIndexSingleColumnWriter;
import org.apache.paimon.globalindex.ResultEntry;
import org.apache.paimon.index.IndexFileMeta;
import org.apache.paimon.io.CompactIncrement;
@@ -199,8 +199,8 @@ public class JavaPyTantivyE2ETest {
indexOptions.set(TantivyFullTextIndexOptions.REMOVE_STOP_WORDS,
true);
}
- GlobalIndexSingletonWriter writer =
- (GlobalIndexSingletonWriter)
+ GlobalIndexSingleColumnWriter writer =
+ (GlobalIndexSingleColumnWriter)
GlobalIndexBuilderUtils.createIndexWriter(
table,
TantivyFullTextGlobalIndexerFactory.IDENTIFIER,
@@ -208,8 +208,8 @@ public class JavaPyTantivyE2ETest {
indexOptions);
// Write the same text data to the index.
- for (String content : contents) {
- writer.write(BinaryString.fromString(content));
+ for (int i = 0; i < contents.size(); i++) {
+ writer.write(BinaryString.fromString(contents.get(i)), i);
}
List<ResultEntry> entries = writer.finish();
diff --git
a/paimon-tantivy/paimon-tantivy-index/src/test/java/org/apache/paimon/tantivy/index/TantivyFullTextGlobalIndexTest.java
b/paimon-tantivy/paimon-tantivy-index/src/test/java/org/apache/paimon/tantivy/index/TantivyFullTextGlobalIndexTest.java
index de13f51645..5986ce70f3 100644
---
a/paimon-tantivy/paimon-tantivy-index/src/test/java/org/apache/paimon/tantivy/index/TantivyFullTextGlobalIndexTest.java
+++
b/paimon-tantivy/paimon-tantivy-index/src/test/java/org/apache/paimon/tantivy/index/TantivyFullTextGlobalIndexTest.java
@@ -125,9 +125,9 @@ public class TantivyFullTextGlobalIndexTest {
GlobalIndexFileWriter fileWriter = createFileWriter(indexPath);
TantivyFullTextGlobalIndexWriter writer = new
TantivyFullTextGlobalIndexWriter(fileWriter);
- writer.write(BinaryString.fromString("Apache Paimon is a streaming
data lake platform"));
- writer.write(BinaryString.fromString("Tantivy is a full-text search
engine in Rust"));
- writer.write(BinaryString.fromString("Paimon supports real-time data
ingestion"));
+ writer.write(BinaryString.fromString("Apache Paimon is a streaming
data lake platform"), 0);
+ writer.write(BinaryString.fromString("Tantivy is a full-text search
engine in Rust"), 1);
+ writer.write(BinaryString.fromString("Paimon supports real-time data
ingestion"), 2);
List<ResultEntry> results = writer.finish();
assertThat(results).hasSize(1);
@@ -171,7 +171,7 @@ public class TantivyFullTextGlobalIndexTest {
new TantivyFullTextIndexOptions(
TantivyFullTextGlobalIndexerFactory.removeTantivyPrefix(options)));
- writer.write(BinaryString.fromString("Apache Paimon supports Chinese
text"));
+ writer.write(BinaryString.fromString("Apache Paimon supports Chinese
text"), 0);
List<ResultEntry> results = writer.finish();
assertThat(results).hasSize(1);
@@ -194,8 +194,8 @@ public class TantivyFullTextGlobalIndexTest {
new TantivyFullTextIndexOptions(
TantivyFullTextGlobalIndexerFactory.removeTantivyPrefix(options)));
- writer.write(BinaryString.fromString("张华在百货公司当售货员"));
- writer.write(BinaryString.fromString("Apache Paimon supports full text
search"));
+ writer.write(BinaryString.fromString("张华在百货公司当售货员"), 0);
+ writer.write(BinaryString.fromString("Apache Paimon supports full text
search"), 1);
List<ResultEntry> results = writer.finish();
TantivyFullTextIndexOptions indexOptions =
@@ -222,8 +222,8 @@ public class TantivyFullTextGlobalIndexTest {
GlobalIndexFileWriter fileWriter = createFileWriter(indexPath);
TantivyFullTextGlobalIndexWriter writer = new
TantivyFullTextGlobalIndexWriter(fileWriter);
- writer.write(BinaryString.fromString("Hello world"));
- writer.write(BinaryString.fromString("Foo bar baz"));
+ writer.write(BinaryString.fromString("Hello world"), 0);
+ writer.write(BinaryString.fromString("Foo bar baz"), 1);
List<ResultEntry> results = writer.finish();
List<GlobalIndexIOMeta> metas = toIOMetas(results, indexPath);
@@ -245,9 +245,9 @@ public class TantivyFullTextGlobalIndexTest {
GlobalIndexFileWriter fileWriter = createFileWriter(indexPath);
TantivyFullTextGlobalIndexWriter writer = new
TantivyFullTextGlobalIndexWriter(fileWriter);
- writer.write(BinaryString.fromString("Paimon data lake"));
- writer.write(null); // row 1 is null, should be skipped
- writer.write(BinaryString.fromString("Paimon streaming"));
+ writer.write(BinaryString.fromString("Paimon data lake"), 0);
+ writer.write(null, 1); // row 1 is null, should be skipped
+ writer.write(BinaryString.fromString("Paimon streaming"), 2);
List<ResultEntry> results = writer.finish();
assertThat(results.get(0).rowCount()).isEqualTo(3);
@@ -291,7 +291,7 @@ public class TantivyFullTextGlobalIndexTest {
if (i % 10 == 0) {
text += " special_keyword";
}
- writer.write(BinaryString.fromString(text));
+ writer.write(BinaryString.fromString(text), i);
}
List<ResultEntry> results = writer.finish();
@@ -323,7 +323,7 @@ public class TantivyFullTextGlobalIndexTest {
TantivyFullTextGlobalIndexWriter writer = new
TantivyFullTextGlobalIndexWriter(fileWriter);
for (int i = 0; i < 20; i++) {
- writer.write(BinaryString.fromString("paimon document " + i));
+ writer.write(BinaryString.fromString("paimon document " + i), i);
}
List<ResultEntry> results = writer.finish();
@@ -346,8 +346,8 @@ public class TantivyFullTextGlobalIndexTest {
public void testPoolReuse() throws IOException {
GlobalIndexFileWriter fileWriter = createFileWriter(indexPath);
TantivyFullTextGlobalIndexWriter writer = new
TantivyFullTextGlobalIndexWriter(fileWriter);
- writer.write(BinaryString.fromString("Apache Paimon streaming lake"));
- writer.write(BinaryString.fromString("Tantivy full-text search"));
+ writer.write(BinaryString.fromString("Apache Paimon streaming lake"),
0);
+ writer.write(BinaryString.fromString("Tantivy full-text search"), 1);
List<ResultEntry> results = writer.finish();
List<GlobalIndexIOMeta> metas = toIOMetas(results, indexPath);
@@ -379,7 +379,7 @@ public class TantivyFullTextGlobalIndexTest {
TantivyFullTextGlobalIndexWriter writer =
(TantivyFullTextGlobalIndexWriter)
indexer.createWriter(fileWriter);
- writer.write(BinaryString.fromString("test via indexer factory"));
+ writer.write(BinaryString.fromString("test via indexer factory"), 0);
List<ResultEntry> results = writer.finish();
assertThat(results).hasSize(1);
diff --git
a/paimon-vector/paimon-vector-index/src/main/java/org/apache/paimon/vector/index/VectorGlobalIndexWriter.java
b/paimon-vector/paimon-vector-index/src/main/java/org/apache/paimon/vector/index/VectorGlobalIndexWriter.java
index 1958200958..0554bb7862 100644
---
a/paimon-vector/paimon-vector-index/src/main/java/org/apache/paimon/vector/index/VectorGlobalIndexWriter.java
+++
b/paimon-vector/paimon-vector-index/src/main/java/org/apache/paimon/vector/index/VectorGlobalIndexWriter.java
@@ -21,7 +21,7 @@ package org.apache.paimon.vector.index;
import org.apache.paimon.data.InternalArray;
import org.apache.paimon.data.InternalVector;
import org.apache.paimon.fs.PositionOutputStream;
-import org.apache.paimon.globalindex.GlobalIndexSingletonWriter;
+import org.apache.paimon.globalindex.GlobalIndexSingleColumnWriter;
import org.apache.paimon.globalindex.ResultEntry;
import org.apache.paimon.globalindex.io.GlobalIndexFileWriter;
import org.apache.paimon.index.vector.VectorIndexWriter;
@@ -48,13 +48,13 @@ import java.util.Map;
/**
* Vector global index writer using paimon-vector-index.
*
- * <p>Vectors are spilled to a temporary file on disk as they arrive via
{@link #write(Object)},
- * keeping Java heap usage constant (~8 MB buffer). During index build,
vectors are read back for
- * training and batch insertion.
+ * <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
+ * back for training and batch insertion.
*
* <p><b>Thread safety:</b> This class is <b>not</b> thread-safe.
*/
-public class VectorGlobalIndexWriter implements GlobalIndexSingletonWriter,
Closeable {
+public class VectorGlobalIndexWriter implements GlobalIndexSingleColumnWriter,
Closeable {
private static final String FILE_NAME_PREFIX = "vector";
@@ -77,7 +77,7 @@ public class VectorGlobalIndexWriter implements
GlobalIndexSingletonWriter, Clos
private long count;
private boolean closed;
- private long logicalRowId;
+ private long rowCount;
public VectorGlobalIndexWriter(
GlobalIndexFileWriter fileWriter,
@@ -129,31 +129,31 @@ public class VectorGlobalIndexWriter implements
GlobalIndexSingletonWriter, Clos
}
@Override
- public void write(Object fieldData) {
+ public void write(Object fieldData, long relativeRowId) {
if (fieldData == null) {
- logicalRowId++;
+ rowCount++;
return;
}
- float[] src = materializeAndValidate(fieldData);
+ float[] src = materializeAndValidate(fieldData, relativeRowId);
if (writeBuf.remaining() < recordSizeInBytes) {
flushWriteBuffer();
}
- writeBuf.putLong(logicalRowId);
+ writeBuf.putLong(relativeRowId);
for (int i = 0; i < dim; i++) {
writeBuf.putFloat(src[i]);
}
- logicalRowId++;
+ rowCount++;
count++;
}
- private float[] materializeAndValidate(Object fieldData) {
+ private float[] materializeAndValidate(Object fieldData, long
relativeRowId) {
if (fieldData instanceof float[]) {
float[] vector = (float[]) fieldData;
checkDimension(vector.length);
for (int i = 0; i < dim; i++) {
- checkFinite(vector[i], i);
+ checkFinite(vector[i], relativeRowId, i);
}
return vector;
} else if (fieldData instanceof InternalVector) {
@@ -161,7 +161,7 @@ public class VectorGlobalIndexWriter implements
GlobalIndexSingletonWriter, Clos
checkDimension(vector.size());
for (int i = 0; i < dim; i++) {
float v = vector.getFloat(i);
- checkFinite(v, i);
+ checkFinite(v, relativeRowId, i);
vectorBuf[i] = v;
}
return vectorBuf;
@@ -173,7 +173,7 @@ public class VectorGlobalIndexWriter implements
GlobalIndexSingletonWriter, Clos
throw new IllegalArgumentException("Vector element at
index " + i + " is null");
}
float v = array.getFloat(i);
- checkFinite(v, i);
+ checkFinite(v, relativeRowId, i);
vectorBuf[i] = v;
}
return vectorBuf;
@@ -262,7 +262,7 @@ public class VectorGlobalIndexWriter implements
GlobalIndexSingletonWriter, Clos
System.currentTimeMillis() - buildStart);
VectorIndexMeta meta = new VectorIndexMeta();
- return new ResultEntry(fileName, logicalRowId, meta.serialize());
+ return new ResultEntry(fileName, rowCount, meta.serialize());
}
}
@@ -364,12 +364,12 @@ public class VectorGlobalIndexWriter implements
GlobalIndexSingletonWriter, Clos
}
}
- private void checkFinite(float value, int elementIndex) {
+ private void checkFinite(float value, long relativeRowId, int
elementIndex) {
if (!Float.isFinite(value)) {
throw new IllegalArgumentException(
String.format(
"Vector element at rowId=%d, index=%d is %s",
- logicalRowId, elementIndex,
Float.toString(value)));
+ relativeRowId, elementIndex,
Float.toString(value)));
}
}
diff --git
a/paimon-vector/paimon-vector-index/src/test/java/org/apache/paimon/vector/index/VectorGlobalIndexTest.java
b/paimon-vector/paimon-vector-index/src/test/java/org/apache/paimon/vector/index/VectorGlobalIndexTest.java
index b10c843ef2..e82664f806 100644
---
a/paimon-vector/paimon-vector-index/src/test/java/org/apache/paimon/vector/index/VectorGlobalIndexTest.java
+++
b/paimon-vector/paimon-vector-index/src/test/java/org/apache/paimon/vector/index/VectorGlobalIndexTest.java
@@ -45,7 +45,6 @@ import org.junit.jupiter.api.io.TempDir;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
-import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
@@ -106,7 +105,7 @@ public class VectorGlobalIndexTest {
VectorGlobalIndexWriter writer = createIvfPqWriter(fileWriter,
vectorType, options);
float[] wrongDimVector = new float[32];
- assertThatThrownBy(() -> writer.write(wrongDimVector))
+ assertThatThrownBy(() -> writer.write(wrongDimVector, 0))
.isInstanceOf(IllegalArgumentException.class)
.hasMessageContaining("dimension mismatch");
}
@@ -130,7 +129,7 @@ public class VectorGlobalIndexTest {
GlobalIndexFileWriter fileWriter = createFileWriter(indexPath);
VectorGlobalIndexWriter writer = createIvfPqWriter(fileWriter,
vectorType, options);
- assertThatThrownBy(() -> writer.write(new float[] {1.0f, Float.NaN}))
+ assertThatThrownBy(() -> writer.write(new float[] {1.0f, Float.NaN},
0))
.isInstanceOf(IllegalArgumentException.class)
.hasMessageContaining("rowId=0")
.hasMessageContaining("index=1")
@@ -144,8 +143,8 @@ public class VectorGlobalIndexTest {
GlobalIndexFileWriter fileWriter = createFileWriter(indexPath);
VectorGlobalIndexWriter writer = createIvfPqWriter(fileWriter,
vectorType, options);
- writer.write(null); // row 0 - null, advances logicalRowId
- assertThatThrownBy(() -> writer.write(new float[]
{Float.POSITIVE_INFINITY, 0.0f}))
+ writer.write(null, 0); // row 0 - null
+ assertThatThrownBy(() -> writer.write(new float[]
{Float.POSITIVE_INFINITY, 0.0f}, 1))
.isInstanceOf(IllegalArgumentException.class)
.hasMessageContaining("rowId=1")
.hasMessageContaining("index=0")
@@ -159,9 +158,9 @@ public class VectorGlobalIndexTest {
GlobalIndexFileWriter fileWriter = createFileWriter(indexPath);
VectorGlobalIndexWriter writer = createIvfPqWriter(fileWriter,
vectorType, options);
- writer.write(null);
- writer.write(null);
- writer.write(null);
+ writer.write(null, 0);
+ writer.write(null, 1);
+ writer.write(null, 2);
List<ResultEntry> results = writer.finish();
assertThat(results).isEmpty();
@@ -223,7 +222,9 @@ public class VectorGlobalIndexTest {
GlobalIndexFileWriter fileWriter = createFileWriter(indexPath);
VectorGlobalIndexWriter writer = createIvfPqWriter(fileWriter,
vectorType, options);
- Arrays.stream(vectors).forEach(writer::write);
+ for (int i = 0; i < vectors.length; i++) {
+ writer.write(vectors[i], i);
+ }
List<ResultEntry> results = writer.finish();
List<GlobalIndexIOMeta> metas = toIOMetas(results, indexPath);
@@ -260,7 +261,9 @@ public class VectorGlobalIndexTest {
GlobalIndexFileWriter fileWriter = createFileWriter(indexPath);
VectorGlobalIndexWriter writer = createIvfPqWriter(fileWriter,
vectorType, options);
- Arrays.stream(vectors).forEach(writer::write);
+ for (int i = 0; i < vectors.length; i++) {
+ writer.write(vectors[i], i);
+ }
List<ResultEntry> results = writer.finish();
List<GlobalIndexIOMeta> metas = toIOMetas(results, indexPath);
@@ -300,12 +303,12 @@ public class VectorGlobalIndexTest {
GlobalIndexFileWriter fileWriter = createFileWriter(indexPath);
VectorGlobalIndexWriter writer = createIvfPqWriter(fileWriter,
vectorType, options);
- writer.write(vectors[0]); // row 0
- writer.write(null); // row 1 - null
- writer.write(vectors[1]); // row 2
- writer.write(null); // row 3 - null
- writer.write(null); // row 4 - null
- writer.write(vectors[2]); // row 5
+ writer.write(vectors[0], 0); // row 0
+ writer.write(null, 1); // row 1 - null
+ writer.write(vectors[1], 2); // row 2
+ writer.write(null, 3); // row 3 - null
+ writer.write(null, 4); // row 4 - null
+ writer.write(vectors[2], 5); // row 5
List<ResultEntry> results = writer.finish();
assertThat(results).hasSize(1);
@@ -352,7 +355,9 @@ public class VectorGlobalIndexTest {
GlobalIndexFileWriter fileWriter = createFileWriter(indexPath);
VectorGlobalIndexWriter writer = (VectorGlobalIndexWriter)
indexer.createWriter(fileWriter);
- Arrays.stream(vectors).forEach(writer::write);
+ for (int i = 0; i < vectors.length; i++) {
+ writer.write(vectors[i], i);
+ }
List<ResultEntry> results = writer.finish();
List<GlobalIndexIOMeta> metas = toIOMetas(results, indexPath);