org.apache.cassandra.index.sai.disk.v1.vector.OnDiskOrdinalsMap Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of cassandra-all Show documentation
Show all versions of cassandra-all Show documentation
The Apache Cassandra Project develops a highly scalable second-generation distributed database, bringing together Dynamo's fully distributed design and Bigtable's ColumnFamily-based data model.
/*
* 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.cassandra.index.sai.disk.v1.vector;
import java.io.IOException;
import java.util.HashSet;
import java.util.Set;
import com.google.common.base.Preconditions;
import io.github.jbellis.jvector.util.Bits;
import org.apache.cassandra.io.util.FileHandle;
import org.apache.cassandra.io.util.RandomAccessReader;
public class OnDiskOrdinalsMap implements AutoCloseable
{
private final FileHandle fh;
private final long ordToRowOffset;
private final long segmentEnd;
private final int size;
// the offset where we switch from recording ordinal -> rows, to row -> ordinal
private final long rowOrdinalOffset;
private final Set deletedOrdinals;
public OnDiskOrdinalsMap(FileHandle fh, long segmentOffset, long segmentLength)
{
deletedOrdinals = new HashSet<>();
this.segmentEnd = segmentOffset + segmentLength;
this.fh = fh;
try (var reader = fh.createReader())
{
reader.seek(segmentOffset);
int deletedCount = reader.readInt();
for (var i = 0; i < deletedCount; i++)
{
deletedOrdinals.add(reader.readInt());
}
this.ordToRowOffset = reader.getFilePointer();
this.size = reader.readInt();
reader.seek(segmentEnd - 8);
this.rowOrdinalOffset = reader.readLong();
assert rowOrdinalOffset < segmentEnd : "rowOrdinalOffset " + rowOrdinalOffset + " is not less than segmentEnd " + segmentEnd;
}
catch (Exception e)
{
throw new RuntimeException("Error initializing OnDiskOrdinalsMap at segment " + segmentOffset, e);
}
}
public RowIdsView getRowIdsView()
{
return new RowIdsView();
}
public Bits ignoringDeleted(Bits acceptBits)
{
return BitsUtil.bitsIgnoringDeleted(acceptBits, deletedOrdinals);
}
public class RowIdsView implements AutoCloseable
{
final RandomAccessReader reader = fh.createReader();
public int[] getSegmentRowIdsMatching(int vectorOrdinal) throws IOException
{
Preconditions.checkArgument(vectorOrdinal < size, "vectorOrdinal %s is out of bounds %s", vectorOrdinal, size);
// read index entry
try
{
reader.seek(ordToRowOffset + 4L + vectorOrdinal * 8L);
}
catch (Exception e)
{
throw new RuntimeException(String.format("Error seeking to index offset for ordinal %d with ordToRowOffset %d",
vectorOrdinal, ordToRowOffset), e);
}
var offset = reader.readLong();
// seek to and read rowIds
try
{
reader.seek(offset);
}
catch (Exception e)
{
throw new RuntimeException(String.format("Error seeking to rowIds offset for ordinal %d with ordToRowOffset %d",
vectorOrdinal, ordToRowOffset), e);
}
var postingsSize = reader.readInt();
var rowIds = new int[postingsSize];
for (var i = 0; i < rowIds.length; i++)
{
rowIds[i] = reader.readInt();
}
return rowIds;
}
@Override
public void close()
{
reader.close();
}
}
public OrdinalsView getOrdinalsView()
{
return new OrdinalsView();
}
public class OrdinalsView implements AutoCloseable
{
final RandomAccessReader reader = fh.createReader();
private final long high = (segmentEnd - 8 - rowOrdinalOffset) / 8;
/**
* @return order if given row id is found; otherwise return -1
*/
public int getOrdinalForRowId(int rowId) throws IOException
{
// Compute the offset of the start of the rowId to vectorOrdinal mapping
long index = DiskBinarySearch.searchInt(0, Math.toIntExact(high), rowId, i -> {
try
{
long offset = rowOrdinalOffset + i * 8;
reader.seek(offset);
return reader.readInt();
}
catch (IOException e)
{
throw new RuntimeException(e);
}
});
// not found
if (index < 0)
return -1;
return reader.readInt();
}
@Override
public void close()
{
reader.close();
}
}
@Override
public void close()
{
fh.close();
}
}