prepare more efficient query completion
adding an index that answers the question given a query "a=b and c=", what are possible values for c.
This commit is contained in:
26
block-storage/src/main/java/org/lucares/pdb/map/Empty.java
Normal file
26
block-storage/src/main/java/org/lucares/pdb/map/Empty.java
Normal file
@@ -0,0 +1,26 @@
|
||||
package org.lucares.pdb.map;
|
||||
|
||||
import org.lucares.pdb.map.PersistentMap.EncoderDecoder;
|
||||
|
||||
/**
|
||||
* Used to denote empty values in {@link PersistentMap}.
|
||||
* <p>
|
||||
* Use {@link PersistentMap#EMPTY_ENCODER} as {@link EncoderDecoder}.
|
||||
* <p>
|
||||
* Implementation note: We cannot use {@link Void}, because {@link Void} cannot
|
||||
* be instantiated. A {@link PersistentMap PersistentMap<<String, Void>}
|
||||
* would have to return {@code null} for {@link PersistentMap#getValue(Object)}
|
||||
* which would make it impossible to know whether the key existed or not.<br>
|
||||
* {@link Empty} solves this by providing a single unmodifiable value.
|
||||
*/
|
||||
public final class Empty {
|
||||
public static final Empty INSTANCE = new Empty();
|
||||
|
||||
private Empty() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "<empty>";
|
||||
}
|
||||
}
|
||||
@@ -8,6 +8,7 @@ import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.Objects;
|
||||
import java.util.Stack;
|
||||
import java.util.UUID;
|
||||
|
||||
@@ -87,9 +88,28 @@ public class PersistentMap<K, V> implements AutoCloseable {
|
||||
}
|
||||
}
|
||||
|
||||
private static final class EmptyCoder implements EncoderDecoder<Empty> {
|
||||
|
||||
private static final byte[] EMPTY_BYTE_ARRAY = new byte[0];
|
||||
|
||||
@Override
|
||||
public byte[] encode(final Empty __) {
|
||||
return EMPTY_BYTE_ARRAY;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Empty decode(final byte[] bytes) {
|
||||
|
||||
Preconditions.checkEqual(bytes.length, 0, "");
|
||||
|
||||
return Empty.INSTANCE;
|
||||
}
|
||||
}
|
||||
|
||||
public static final EncoderDecoder<Long> LONG_CODER = new LongCoder();
|
||||
public static final EncoderDecoder<UUID> UUID_ENCODER = new UUIDCoder();
|
||||
public static final EncoderDecoder<String> STRING_CODER = new StringCoder();
|
||||
public static final EncoderDecoder<Empty> EMPTY_ENCODER = new EmptyCoder();
|
||||
|
||||
static final int BLOCK_SIZE = 4096;
|
||||
static final long NODE_OFFSET_TO_ROOT_NODE = 8;
|
||||
@@ -180,17 +200,27 @@ public class PersistentMap<K, V> implements AutoCloseable {
|
||||
final byte[] value) throws IOException {
|
||||
final PersistentMapDiskNode node = getNode(nodeOffest);
|
||||
|
||||
final var entry = node.getNodeEntryTo(key);
|
||||
final NodeEntry entry = node.getNodeEntryTo(key);
|
||||
if (entry == null || entry.isDataNode()) {
|
||||
|
||||
final byte[] oldValue;
|
||||
if (entry == null) {
|
||||
oldValue = null;
|
||||
} else {
|
||||
// found a NodeEntry that is either equal to key, or it is at the insertion
|
||||
// point
|
||||
final boolean entryIsForKey = entry.equal(key);
|
||||
|
||||
oldValue = entryIsForKey ? entry.getValue() : null;
|
||||
|
||||
// Early exit, if the oldValue equals the new value.
|
||||
// We do not have to replace the value, because it would not change anything
|
||||
// (just cause unnecessary write operations). But we return the oldValue so that
|
||||
// the caller thinks we replaced the value.
|
||||
if (Objects.equals(oldValue, value)) {
|
||||
return oldValue;
|
||||
}
|
||||
|
||||
if (entryIsForKey) {
|
||||
node.removeKey(key);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user