Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions lucene/CHANGES.txt
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,8 @@ Improvements

Optimizations
---------------------
* GITHUB#16407: Binary search unequal-length suffixes in leaf term blocks. (zhouhui)

* GITHUB#16382: WildcardQuery.toAutomaton now collapses consecutive unescaped '*'
wildcards into a single any-string automaton, avoiding a quadratic blow-up in
automaton size for patterns with many repeated asterisks. (Vismay Tiwari)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ final class SegmentTermsEnumFrame {

byte[] suffixLengthBytes;
final ByteArrayDataInput suffixLengthsReader;
int[] suffixOffsets;

byte[] statBytes;
int statsSingletonRunLength = 0;
Expand Down Expand Up @@ -218,6 +219,16 @@ void loadBlock() throws IOException {
ste.in.readBytes(suffixLengthBytes, 0, numSuffixLengthBytes);
}
suffixLengthsReader.reset(suffixLengthBytes, 0, numSuffixLengthBytes);
if (isLeafBlock && allEqual == false) {
if (suffixOffsets == null || suffixOffsets.length < entCount + 1) {
suffixOffsets = new int[ArrayUtil.oversize(entCount + 1, Integer.BYTES)];
}
suffixOffsets[0] = 0;
for (int i = 0; i < entCount; i++) {
suffixOffsets[i + 1] = suffixOffsets[i] + suffixLengthsReader.readVInt();
}
suffixLengthsReader.setPosition(0);
}
totalSuffixBytes = ste.in.getFilePointer() - startSuffixFP;

/*if (DEBUG) {
Expand Down Expand Up @@ -332,9 +343,15 @@ public void nextLeaf() {
// " entCount=" + entCount);
assert nextEnt != -1 && nextEnt < entCount
: "nextEnt=" + nextEnt + " entCount=" + entCount + " fp=" + fp;
nextEnt++;
suffixLength = suffixLengthsReader.readVInt();
startBytePos = suffixesReader.getPosition();
final int currentEnt = nextEnt++;
if (allEqual) {
suffixLength = suffixLengthsReader.readVInt();
startBytePos = suffixesReader.getPosition();
} else {
startBytePos = suffixOffsets[currentEnt];
suffixLength = suffixOffsets[currentEnt + 1] - startBytePos;
suffixesReader.setPosition(startBytePos);
}
ste.term.setLength(prefixLength + suffixLength);
ste.term.grow(ste.term.length());
suffixesReader.readBytes(ste.term.bytes(), prefixLength, suffixLength);
Expand Down Expand Up @@ -558,11 +575,7 @@ public void scanToSubBlock(long subFP) {
// NOTE: sets startBytePos/suffix as a side effect
public SeekStatus scanToTerm(BytesRef target, boolean exactOnly) throws IOException {
if (isLeafBlock) {
if (allEqual) {
return binarySearchTermLeaf(target, exactOnly);
} else {
return scanToTermLeaf(target, exactOnly);
}
return binarySearchTermLeaf(target, exactOnly);
} else {
return scanToTermNonLeaf(target, exactOnly);
}
Expand All @@ -573,14 +586,22 @@ public SeekStatus scanToTerm(BytesRef target, boolean exactOnly) throws IOExcept
private long subCode;
CompressionAlgorithm compressionAlg = CompressionAlgorithm.NO_COMPRESSION;

// Target's prefix matches this block's prefix; we
// scan the entries to check if the suffix matches.
public SeekStatus scanToTermLeaf(BytesRef target, boolean exactOnly) throws IOException {
// Target's prefix matches this block's prefix. We binary search the entries to check if the
// suffix matches.
public SeekStatus binarySearchTermLeaf(BytesRef target, boolean exactOnly) throws IOException {
return allEqual
? binarySearchTermLeafAllEqual(target, exactOnly)
: binarySearchTermLeafUnEqual(target, exactOnly);
}

// if (DEBUG) System.out.println(" scanToTermLeaf: block fp=" + fp + " prefix=" + prefix +
// " nextEnt=" + nextEnt + " (of " + entCount + ") target=" +
// ToStringUtils.bytesRefToString(target) +
// " term=" + ToStringUtils.bytesRefToString(term));
// Target's prefix matches this leaf block's prefix. Since all suffixes have the same
// length, we can binary search by computing each suffix's start offset from its ordinal.
private SeekStatus binarySearchTermLeafAllEqual(BytesRef target, boolean exactOnly)
throws IOException {
// if (DEBUG) System.out.println(" binarySearchTermLeaf: block fp=" + fp + " prefix=" +
// prefix + "
// nextEnt=" + nextEnt + " (of " + entCount + ") target=" + brToString(target) + " term=" +
// brToString(term));

assert nextEnt != -1;

Expand All @@ -596,59 +617,42 @@ public SeekStatus scanToTermLeaf(BytesRef target, boolean exactOnly) throws IOEx

assert prefixMatches(target);

// Loop over each entry (term or sub-block) in this block:
do {
nextEnt++;

suffixLength = suffixLengthsReader.readVInt();

// if (DEBUG) {
// BytesRef suffixBytesRef = new BytesRef();
// suffixBytesRef.bytes = suffixBytes;
// suffixBytesRef.offset = suffixesReader.getPosition();
// suffixBytesRef.length = suffix;
// System.out.println(" cycle: term " + (nextEnt-1) + " (of " + entCount + ") suffix="
// + ToStringUtils.bytesRefToString(suffixBytesRef));
// }

startBytePos = suffixesReader.getPosition();
suffixesReader.skipBytes(suffixLength);
suffixLength = suffixLengthsReader.readVInt();
// TODO early terminate when target length unequals suffix + prefix.
// But we need to keep the same status with scanToTermLeaf.
int start = nextEnt;
int end = entCount - 1;
// Binary search the entries (terms) in this leaf block:
int cmp = 0;
while (start <= end) {
int mid = (start + end) >>> 1;
nextEnt = mid + 1;
startBytePos = mid * suffixLength;

// Compare suffix and target.
final int cmp =
cmp =
Arrays.compareUnsigned(
suffixBytes,
startBytePos,
startBytePos + suffixLength,
target.bytes,
target.offset + prefixLength,
target.offset + target.length);

if (cmp < 0) {
// Current entry is still before the target;
// keep scanning
start = mid + 1;
} else if (cmp > 0) {
// Done! Current entry is after target --
// return NOT_FOUND:
fillTerm();

// if (DEBUG) System.out.println(" not found");
return SeekStatus.NOT_FOUND;
end = mid - 1;
} else {
// Exact match!

// This cannot be a sub-block because we
// would have followed the index to this
// sub-block from the start:

suffixesReader.setPosition(startBytePos + suffixLength);
fillTerm();
// if (DEBUG) System.out.println(" found!");
return SeekStatus.FOUND;
}
} while (nextEnt < entCount);
}

// It is possible (and OK) that terms index pointed us
// at this block, but, we scanned the entire block and
// at this block, but, we searched the entire block and
// did not find the term to position to. This happens
// when the target is after the last term in the block
// (but, before the next term in the index). EG
Expand All @@ -657,22 +661,36 @@ public SeekStatus scanToTermLeaf(BytesRef target, boolean exactOnly) throws IOEx
// was fooz (and, eg, first term in the next block will
// bee fop).
// if (DEBUG) System.out.println(" block end");
if (exactOnly) {
SeekStatus seekStatus;
if (end < entCount - 1) {
seekStatus = SeekStatus.NOT_FOUND;
// If binary search ended at the less term, and greater term exists.
// We need to advance to the greater term.
if (cmp < 0) {
startBytePos += suffixLength;
nextEnt++;
}
suffixesReader.setPosition(startBytePos + suffixLength);
fillTerm();
} else {
seekStatus = SeekStatus.END;
suffixesReader.setPosition(startBytePos + suffixLength);
if (exactOnly) {
fillTerm();
}
}

// TODO: not consistent that in the
// not-exact case we don't next() into the next
// frame here
return SeekStatus.END;
return seekStatus;
}

// Target's prefix matches this block's prefix;
// And all suffixes have the same length in this block,
// we binary search the entries to check if the suffix matches.
public SeekStatus binarySearchTermLeaf(BytesRef target, boolean exactOnly) throws IOException {
// if (DEBUG) System.out.println(" binarySearchTermLeaf: block fp=" + fp + " prefix=" +
// prefix + "
// Target's prefix matches this leaf block's prefix. Even though suffixes have different lengths,
// we use suffixOffsets to randomly access suffix bytes during binary search.
private SeekStatus binarySearchTermLeafUnEqual(BytesRef target, boolean exactOnly)
throws IOException {
// if (DEBUG) System.out.println(" binarySearchTermLeafUnEqual: block fp=" + fp + " prefix="
// + prefix + "
// nextEnt=" + nextEnt + " (of " + entCount + ") target=" + brToString(target) + " term=" +
// brToString(term));

Expand All @@ -689,18 +707,17 @@ public SeekStatus binarySearchTermLeaf(BytesRef target, boolean exactOnly) throw
}

assert prefixMatches(target);
assert suffixOffsets != null;

suffixLength = suffixLengthsReader.readVInt();
// TODO early terminate when target length unequals suffix + prefix.
// But we need to keep the same status with scanToTermLeaf.
int start = nextEnt;
int end = entCount - 1;
// Binary search the entries (terms) in this leaf block:
int cmp = 0;
while (start <= end) {
int mid = (start + end) >>> 1;
nextEnt = mid + 1;
startBytePos = mid * suffixLength;
startBytePos = suffixOffsets[mid];
suffixLength = suffixOffsets[mid + 1] - startBytePos;

// Compare suffix and target.
cmp =
Expand Down Expand Up @@ -740,14 +757,13 @@ public SeekStatus binarySearchTermLeaf(BytesRef target, boolean exactOnly) throw
// If binary search ended at the less term, and greater term exists.
// We need to advance to the greater term.
if (cmp < 0) {
startBytePos += suffixLength;
nextEnt++;
}
suffixesReader.setPosition(startBytePos + suffixLength);
setSuffixesReaderUnEqual();
fillTerm();
} else {
seekStatus = SeekStatus.END;
suffixesReader.setPosition(startBytePos + suffixLength);
setSuffixesReaderUnEqual();
if (exactOnly) {
fillTerm();
}
Expand All @@ -758,6 +774,14 @@ public SeekStatus binarySearchTermLeaf(BytesRef target, boolean exactOnly) throw
return seekStatus;
}

private void setSuffixesReaderUnEqual() {
int currentEnt = nextEnt - 1;

startBytePos = suffixOffsets[currentEnt];
suffixLength = suffixOffsets[currentEnt + 1] - startBytePos;
suffixesReader.setPosition(startBytePos + suffixLength);
}

// Target's prefix matches this block's prefix; we
// scan the entries to check if the suffix matches.
public SeekStatus scanToTermNonLeaf(BytesRef target, boolean exactOnly) throws IOException {
Expand Down
Loading