blob: 0b504a6dcb1c0f2ee5901569d1e1619b9cb3b551 [file] [log] [blame]
/*
* 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.hyracks.storage.am.lsm.invertedindex.ondisk;
import java.io.DataOutput;
import java.io.IOException;
import org.apache.hyracks.api.context.IHyracksTaskContext;
import org.apache.hyracks.api.dataflow.value.IBinaryComparatorFactory;
import org.apache.hyracks.api.dataflow.value.ITypeTraits;
import org.apache.hyracks.api.exceptions.ErrorCode;
import org.apache.hyracks.api.exceptions.HyracksDataException;
import org.apache.hyracks.api.io.FileReference;
import org.apache.hyracks.api.util.HyracksConstants;
import org.apache.hyracks.data.std.primitive.IntegerPointable;
import org.apache.hyracks.dataflow.common.comm.io.ArrayTupleBuilder;
import org.apache.hyracks.dataflow.common.comm.io.ArrayTupleReference;
import org.apache.hyracks.dataflow.common.data.accessors.ITupleReference;
import org.apache.hyracks.dataflow.common.utils.TupleUtils;
import org.apache.hyracks.storage.am.btree.frames.BTreeLeafFrameType;
import org.apache.hyracks.storage.am.btree.impls.BTree;
import org.apache.hyracks.storage.am.btree.impls.DiskBTree;
import org.apache.hyracks.storage.am.btree.impls.RangePredicate;
import org.apache.hyracks.storage.am.btree.util.BTreeUtils;
import org.apache.hyracks.storage.am.common.api.IIndexOperationContext;
import org.apache.hyracks.storage.am.common.api.IPageManagerFactory;
import org.apache.hyracks.storage.am.common.impls.NoOpIndexAccessParameters;
import org.apache.hyracks.storage.am.common.tuples.PermutingTupleReference;
import org.apache.hyracks.storage.am.lsm.invertedindex.api.IInPlaceInvertedIndex;
import org.apache.hyracks.storage.am.lsm.invertedindex.api.IInvertedIndexAccessor;
import org.apache.hyracks.storage.am.lsm.invertedindex.api.IInvertedIndexSearcher;
import org.apache.hyracks.storage.am.lsm.invertedindex.api.IInvertedListBuilder;
import org.apache.hyracks.storage.am.lsm.invertedindex.api.InvertedListCursor;
import org.apache.hyracks.storage.am.lsm.invertedindex.impls.LSMInvertedIndexSearchCursorInitialState;
import org.apache.hyracks.storage.am.lsm.invertedindex.search.InvertedIndexSearchPredicate;
import org.apache.hyracks.storage.am.lsm.invertedindex.search.TOccurrenceSearcher;
import org.apache.hyracks.storage.am.lsm.invertedindex.tuples.TokenKeyPairTuple;
import org.apache.hyracks.storage.common.IIndexAccessParameters;
import org.apache.hyracks.storage.common.IIndexAccessor;
import org.apache.hyracks.storage.common.IIndexBulkLoader;
import org.apache.hyracks.storage.common.IIndexCursor;
import org.apache.hyracks.storage.common.ISearchPredicate;
import org.apache.hyracks.storage.common.MultiComparator;
import org.apache.hyracks.storage.common.buffercache.IBufferCache;
import org.apache.hyracks.storage.common.buffercache.ICachedPage;
import org.apache.hyracks.storage.common.buffercache.IFIFOPageQueue;
import org.apache.hyracks.storage.common.buffercache.PageWriteFailureCallback;
import org.apache.hyracks.storage.common.file.BufferedFileHandle;
/**
* An inverted index consists of two files: 1. a file storing (paginated)
* inverted lists 2. a BTree-file mapping from tokens to inverted lists.
* Implemented features: bulk loading and searching (based on T-Occurrence) Not
* implemented features: updates (insert/update/delete) Limitations: a query
* cannot exceed the size of a Hyracks frame.
*/
public class OnDiskInvertedIndex implements IInPlaceInvertedIndex {
// Schema of BTree tuples, set in constructor.
protected final int invListStartPageIdField;
protected final int invListEndPageIdField;
protected final int invListStartOffField;
protected final int invListNumElementsField;
// Type traits to be appended to the token type trait which finally form the BTree field type traits.
protected static final ITypeTraits[] btreeValueTypeTraits = new ITypeTraits[4];
static {
// startPageId
btreeValueTypeTraits[0] = IntegerPointable.TYPE_TRAITS;
// endPageId
btreeValueTypeTraits[1] = IntegerPointable.TYPE_TRAITS;
// startOff
btreeValueTypeTraits[2] = IntegerPointable.TYPE_TRAITS;
// numElements
btreeValueTypeTraits[3] = IntegerPointable.TYPE_TRAITS;
}
protected DiskBTree btree;
protected int rootPageId = 0;
protected IBufferCache bufferCache;
protected int fileId = -1;
protected final ITypeTraits[] invListTypeTraits;
protected final IBinaryComparatorFactory[] invListCmpFactories;
protected final ITypeTraits[] tokenTypeTraits;
protected final IBinaryComparatorFactory[] tokenCmpFactories;
protected final IInvertedListBuilder invListBuilder;
protected final int numTokenFields;
protected final int numInvListKeys;
protected final FileReference invListsFile;
// Last page id of inverted-lists file (inclusive). Set during bulk load.
protected int invListsMaxPageId = -1;
protected boolean isOpen = false;
protected boolean wasOpen = false;
public OnDiskInvertedIndex(IBufferCache bufferCache, IInvertedListBuilder invListBuilder,
ITypeTraits[] invListTypeTraits, IBinaryComparatorFactory[] invListCmpFactories,
ITypeTraits[] tokenTypeTraits, IBinaryComparatorFactory[] tokenCmpFactories, FileReference btreeFile,
FileReference invListsFile, IPageManagerFactory pageManagerFactory) throws HyracksDataException {
this.bufferCache = bufferCache;
this.invListBuilder = invListBuilder;
this.invListTypeTraits = invListTypeTraits;
this.invListCmpFactories = invListCmpFactories;
this.tokenTypeTraits = tokenTypeTraits;
this.tokenCmpFactories = tokenCmpFactories;
this.btree = BTreeUtils.createDiskBTree(bufferCache, getBTreeTypeTraits(tokenTypeTraits), tokenCmpFactories,
BTreeLeafFrameType.REGULAR_NSM, btreeFile, pageManagerFactory.createPageManager(bufferCache), false);
this.numTokenFields = btree.getComparatorFactories().length;
this.numInvListKeys = invListCmpFactories.length;
this.invListsFile = invListsFile;
this.invListStartPageIdField = numTokenFields;
this.invListEndPageIdField = numTokenFields + 1;
this.invListStartOffField = numTokenFields + 2;
this.invListNumElementsField = numTokenFields + 3;
}
@Override
public synchronized void create() throws HyracksDataException {
if (isOpen) {
throw new HyracksDataException("Failed to create since index is already open.");
}
btree.create();
fileId = bufferCache.createFile(invListsFile);
}
@Override
public synchronized void activate() throws HyracksDataException {
if (isOpen) {
throw new HyracksDataException("Failed to activate the index since it is already activated.");
}
btree.activate();
if (fileId >= 0) {
bufferCache.openFile(fileId);
} else {
fileId = bufferCache.openFile(invListsFile);
}
isOpen = true;
wasOpen = true;
}
@Override
public synchronized void deactivate() throws HyracksDataException {
if (!isOpen && wasOpen) {
throw new HyracksDataException("Failed to deactivate the index since it is already deactivated.");
}
btree.deactivate();
bufferCache.closeFile(fileId);
isOpen = false;
}
@Override
public synchronized void destroy() throws HyracksDataException {
if (isOpen) {
throw new HyracksDataException("Failed to destroy since index is already open.");
}
btree.destroy();
bufferCache.deleteFile(invListsFile);
}
@Override
public synchronized void clear() throws HyracksDataException {
if (!isOpen) {
throw new HyracksDataException("Failed to clear since index is not open.");
}
btree.clear();
bufferCache.closeFile(fileId);
bufferCache.deleteFile(fileId);
fileId = bufferCache.createFile(invListsFile);
bufferCache.openFile(fileId);
}
@Override
public InvertedListCursor createInvertedListCursor(IHyracksTaskContext ctx) throws HyracksDataException {
return new FixedSizeElementInvertedListCursor(bufferCache, fileId, invListTypeTraits, ctx);
}
@Override
public InvertedListCursor createInvertedListRangeSearchCursor() throws HyracksDataException {
return new FixedSizeElementInvertedListScanCursor(bufferCache, fileId, invListTypeTraits);
}
@Override
public void openInvertedListCursor(InvertedListCursor listCursor, ITupleReference searchKey,
IIndexOperationContext ictx) throws HyracksDataException {
OnDiskInvertedIndexOpContext ctx = (OnDiskInvertedIndexOpContext) ictx;
ctx.getBtreePred().setLowKeyComparator(ctx.getSearchCmp());
ctx.getBtreePred().setHighKeyComparator(ctx.getSearchCmp());
ctx.getBtreePred().setLowKey(searchKey, true);
ctx.getBtreePred().setHighKey(searchKey, true);
ctx.getBtreeAccessor().search(ctx.getBtreeCursor(), ctx.getBtreePred());
try {
if (ctx.getBtreeCursor().hasNext()) {
ctx.getBtreeCursor().next();
openInvertedListCursor(ctx.getBtreeCursor().getTuple(), listCursor, ctx);
} else {
LSMInvertedIndexSearchCursorInitialState initState = ctx.getCursorInitialState();
initState.setInvertedListInfo(0, 0, 0, 0);
listCursor.open(initState, null);
}
} finally {
ctx.getBtreeCursor().close();
}
}
public void openInvertedListCursor(ITupleReference btreeTuple, InvertedListCursor listCursor,
OnDiskInvertedIndexOpContext opCtx) throws HyracksDataException {
int startPageId = IntegerPointable.getInteger(btreeTuple.getFieldData(invListStartPageIdField),
btreeTuple.getFieldStart(invListStartPageIdField));
int endPageId = IntegerPointable.getInteger(btreeTuple.getFieldData(invListEndPageIdField),
btreeTuple.getFieldStart(invListEndPageIdField));
int startOff = IntegerPointable.getInteger(btreeTuple.getFieldData(invListStartOffField),
btreeTuple.getFieldStart(invListStartOffField));
int numElements = IntegerPointable.getInteger(btreeTuple.getFieldData(invListNumElementsField),
btreeTuple.getFieldStart(invListNumElementsField));
LSMInvertedIndexSearchCursorInitialState initState = opCtx.getCursorInitialState();
initState.setInvertedListInfo(startPageId, endPageId, startOff, numElements);
listCursor.open(initState, null);
}
public abstract class AbstractOnDiskInvertedIndexBulkLoader extends PageWriteFailureCallback
implements IIndexBulkLoader {
protected final ArrayTupleBuilder btreeTupleBuilder;
protected final ArrayTupleReference btreeTupleReference;
protected final IIndexBulkLoader btreeBulkloader;
protected int currentInvListStartPageId;
protected int currentInvListStartOffset;
protected final ArrayTupleBuilder lastTupleBuilder;
protected final ArrayTupleReference lastTuple;
protected int currentPageId;
protected ICachedPage currentPage;
protected final MultiComparator invListCmp;
protected final boolean verifyInput;
protected final MultiComparator allCmp;
protected final IFIFOPageQueue queue;
public AbstractOnDiskInvertedIndexBulkLoader(float btreeFillFactor, boolean verifyInput, long numElementsHint,
boolean checkIfEmptyIndex, int startPageId) throws HyracksDataException {
this.verifyInput = verifyInput;
this.invListCmp = MultiComparator.create(invListCmpFactories);
if (verifyInput) {
allCmp = MultiComparator.create(btree.getComparatorFactories(), invListCmpFactories);
} else {
allCmp = null;
}
this.btreeTupleBuilder = new ArrayTupleBuilder(btree.getFieldCount());
this.btreeTupleReference = new ArrayTupleReference();
this.lastTupleBuilder = new ArrayTupleBuilder(numTokenFields + numInvListKeys);
this.lastTuple = new ArrayTupleReference();
this.btreeBulkloader =
btree.createBulkLoader(btreeFillFactor, verifyInput, numElementsHint, checkIfEmptyIndex);
currentPageId = startPageId;
currentPage = bufferCache.confiscatePage(BufferedFileHandle.getDiskPageId(fileId, currentPageId));
invListBuilder.setTargetBuffer(currentPage.getBuffer().array(), 0);
queue = bufferCache.createFIFOQueue();
}
protected void pinNextPage() throws HyracksDataException {
queue.put(currentPage, this);
currentPageId++;
currentPage = bufferCache.confiscatePage(BufferedFileHandle.getDiskPageId(fileId, currentPageId));
}
protected void insertBTreeTuple() throws HyracksDataException {
// Build tuple.
DataOutput output = btreeTupleBuilder.getDataOutput();
// Add inverted-list 'pointer' value fields.
try {
output.writeInt(currentInvListStartPageId);
btreeTupleBuilder.addFieldEndOffset();
output.writeInt(currentPageId);
btreeTupleBuilder.addFieldEndOffset();
output.writeInt(currentInvListStartOffset);
btreeTupleBuilder.addFieldEndOffset();
output.writeInt(invListBuilder.getListSize());
btreeTupleBuilder.addFieldEndOffset();
} catch (IOException e) {
throw HyracksDataException.create(e);
}
// Reset tuple reference and add it into the BTree load.
btreeTupleReference.reset(btreeTupleBuilder.getFieldEndOffsets(), btreeTupleBuilder.getByteArray());
btreeBulkloader.add(btreeTupleReference);
btreeTupleBuilder.reset();
}
protected void startNewList(ITupleReference tokenTuple) throws HyracksDataException {
if (!invListBuilder.startNewList(tokenTuple, numTokenFields)) {
pinNextPage();
invListBuilder.setTargetBuffer(currentPage.getBuffer().array(), 0);
if (!invListBuilder.startNewList(tokenTuple, numTokenFields)) {
throw new IllegalStateException("Failed to create first inverted list.");
}
}
currentInvListStartPageId = currentPageId;
currentInvListStartOffset = invListBuilder.getPos();
}
protected void appendInvertedList(ITupleReference keyTuple, int startField) throws HyracksDataException {
if (!invListBuilder.appendElement(keyTuple, startField, numInvListKeys)) {
pinNextPage();
invListBuilder.setTargetBuffer(currentPage.getBuffer().array(), 0);
if (!invListBuilder.appendElement(keyTuple, startField, numInvListKeys)) {
throw new IllegalStateException(
"Failed to append element to inverted list after switching to a new page.");
}
}
}
protected void verifyTuple(ITupleReference tuple) throws HyracksDataException {
if (lastTupleBuilder.getSize() > 0 && allCmp.compare(tuple, lastTuple) <= 0) {
HyracksDataException.create(ErrorCode.UNSORTED_LOAD_INPUT);
}
}
protected void saveLastTuple(ITupleReference tuple) throws HyracksDataException {
lastTupleBuilder.reset();
for (int i = 0; i < tuple.getFieldCount(); i++) {
lastTupleBuilder.addField(tuple.getFieldData(i), tuple.getFieldStart(i), tuple.getFieldLength(i));
}
lastTuple.reset(lastTupleBuilder.getFieldEndOffsets(), lastTupleBuilder.getByteArray());
}
protected void copyTokenToBTreeTuple(ITupleReference tokenTuple) throws HyracksDataException {
for (int i = 0; i < numTokenFields; i++) {
btreeTupleBuilder.addField(tokenTuple.getFieldData(i), tokenTuple.getFieldStart(i),
tokenTuple.getFieldLength(i));
}
}
@Override
public void end() throws HyracksDataException {
if (btreeTupleBuilder.getSize() != 0) {
insertBTreeTuple();
}
btreeBulkloader.end();
if (currentPage != null) {
queue.put(currentPage, this);
}
invListsMaxPageId = currentPageId;
bufferCache.finishQueue();
if (hasFailed()) {
throw HyracksDataException.create(getFailure());
}
}
@Override
public void abort() throws HyracksDataException {
if (btreeBulkloader != null) {
btreeBulkloader.abort();
}
}
}
public class OnDiskInvertedIndexMergeBulkLoader extends AbstractOnDiskInvertedIndexBulkLoader {
public OnDiskInvertedIndexMergeBulkLoader(float btreeFillFactor, boolean verifyInput, long numElementsHint,
boolean checkIfEmptyIndex, int startPageId) throws HyracksDataException {
super(btreeFillFactor, verifyInput, numElementsHint, checkIfEmptyIndex, startPageId);
}
@Override
public void add(ITupleReference tuple) throws HyracksDataException {
TokenKeyPairTuple pairTuple = (TokenKeyPairTuple) tuple;
ITupleReference tokenTuple = pairTuple.getTokenTuple();
ITupleReference keyTuple = pairTuple.getKeyTuple();
boolean startNewList = pairTuple.isNewToken();
if (startNewList) {
if (btreeTupleBuilder.getSize() > 0) {
insertBTreeTuple();
}
startNewList(tokenTuple);
copyTokenToBTreeTuple(tokenTuple);
}
appendInvertedList(keyTuple, 0);
if (verifyInput) {
verifyTuple(tuple);
saveLastTuple(tuple);
}
}
}
public class OnDiskInvertedIndexBulkLoader extends AbstractOnDiskInvertedIndexBulkLoader {
public OnDiskInvertedIndexBulkLoader(float btreeFillFactor, boolean verifyInput, long numElementsHint,
boolean checkIfEmptyIndex, int startPageId) throws HyracksDataException {
super(btreeFillFactor, verifyInput, numElementsHint, checkIfEmptyIndex, startPageId);
}
@Override
public void add(ITupleReference tuple) throws HyracksDataException {
boolean firstElement = btreeTupleBuilder.getSize() == 0;
boolean startNewList = firstElement;
if (!firstElement) {
// If the current and the last token don't match, we start a new list.
startNewList = !TupleUtils.equalTuples(tuple, lastTuple, numTokenFields);
}
if (startNewList) {
if (!firstElement) {
// Create entry in btree for last inverted list.
insertBTreeTuple();
}
startNewList(tuple);
copyTokenToBTreeTuple(tuple);
} else {
if (invListCmp.compare(tuple, lastTuple, numTokenFields) == 0) {
// Duplicate inverted-list element.
return;
}
}
appendInvertedList(tuple, numTokenFields);
if (verifyInput) {
verifyTuple(tuple);
}
saveLastTuple(tuple);
}
}
@Override
public IBufferCache getBufferCache() {
return bufferCache;
}
public int getInvListsFileId() {
return fileId;
}
public int getInvListsMaxPageId() {
return invListsMaxPageId;
}
@Override
public IBinaryComparatorFactory[] getInvListCmpFactories() {
return invListCmpFactories;
}
@Override
public ITypeTraits[] getInvListTypeTraits() {
return invListTypeTraits;
}
public BTree getBTree() {
return btree;
}
public FileReference getInvListsFile() {
return invListsFile;
}
public class OnDiskInvertedIndexAccessor implements IInvertedIndexAccessor {
protected final OnDiskInvertedIndex index;
protected final IIndexOperationContext opCtx;
protected final IHyracksTaskContext ctx;
protected IInvertedIndexSearcher searcher;
private boolean destroyed = false;
public OnDiskInvertedIndexAccessor(OnDiskInvertedIndex index, IHyracksTaskContext ctx)
throws HyracksDataException {
this.index = index;
this.ctx = ctx;
this.opCtx = new OnDiskInvertedIndexOpContext(btree);
}
@Override
public IIndexCursor createSearchCursor(boolean exclusive) throws HyracksDataException {
if (searcher == null) {
searcher = new TOccurrenceSearcher(index, ctx);
}
return new OnDiskInvertedIndexSearchCursor(searcher);
}
@Override
public void search(IIndexCursor cursor, ISearchPredicate searchPred) throws HyracksDataException {
if (searcher == null) {
searcher = new TOccurrenceSearcher(index, ctx);
}
searcher.search(cursor, (InvertedIndexSearchPredicate) searchPred, opCtx);
}
@Override
public InvertedListCursor createInvertedListCursor() throws HyracksDataException {
return index.createInvertedListCursor(ctx);
}
@Override
public void openInvertedListCursor(InvertedListCursor listCursor, ITupleReference searchKey)
throws HyracksDataException {
index.openInvertedListCursor(listCursor, searchKey, opCtx);
}
@Override
public IIndexCursor createRangeSearchCursor() throws HyracksDataException {
return new OnDiskInvertedIndexRangeSearchCursor(index, opCtx);
}
@Override
public void rangeSearch(IIndexCursor cursor, ISearchPredicate searchPred) throws HyracksDataException {
OnDiskInvertedIndexRangeSearchCursor rangeSearchCursor = (OnDiskInvertedIndexRangeSearchCursor) cursor;
rangeSearchCursor.open(null, searchPred);
}
@Override
public void insert(ITupleReference tuple) throws HyracksDataException {
throw new UnsupportedOperationException("Insert not supported by inverted index.");
}
@Override
public void update(ITupleReference tuple) throws HyracksDataException {
throw new UnsupportedOperationException("Update not supported by inverted index.");
}
@Override
public void delete(ITupleReference tuple) throws HyracksDataException {
throw new UnsupportedOperationException("Delete not supported by inverted index.");
}
@Override
public void upsert(ITupleReference tuple) throws HyracksDataException {
throw new UnsupportedOperationException("Upsert not supported by inverted index.");
}
@Override
public void destroy() throws HyracksDataException {
if (destroyed) {
return;
}
destroyed = true;
opCtx.destroy();
}
}
@Override
public OnDiskInvertedIndexAccessor createAccessor(IIndexAccessParameters iap) throws HyracksDataException {
return new OnDiskInvertedIndexAccessor(this,
(IHyracksTaskContext) iap.getParameters().get(HyracksConstants.HYRACKS_TASK_CONTEXT));
}
@Override
public IIndexBulkLoader createBulkLoader(float fillFactor, boolean verifyInput, long numElementsHint,
boolean checkIfEmptyIndex) throws HyracksDataException {
return new OnDiskInvertedIndexBulkLoader(fillFactor, verifyInput, numElementsHint, checkIfEmptyIndex,
rootPageId);
}
public IIndexBulkLoader createMergeBulkLoader(float fillFactor, boolean verifyInput, long numElementsHint,
boolean checkIfEmptyIndex) throws HyracksDataException {
return new OnDiskInvertedIndexMergeBulkLoader(fillFactor, verifyInput, numElementsHint, checkIfEmptyIndex,
rootPageId);
}
@Override
public void validate() throws HyracksDataException {
btree.validate();
// Scan the btree and validate the order of elements in each inverted-list.
IIndexAccessor btreeAccessor = btree.createAccessor(NoOpIndexAccessParameters.INSTANCE);
try {
MultiComparator btreeCmp = MultiComparator.create(btree.getComparatorFactories());
RangePredicate rangePred = new RangePredicate(null, null, true, true, btreeCmp, btreeCmp);
IIndexCursor btreeCursor = btreeAccessor.createSearchCursor(false);
try {
btreeAccessor.search(btreeCursor, rangePred);
try {
doValidate(btreeCursor);
} finally {
btreeCursor.close();
}
} finally {
btreeCursor.destroy();
}
} finally {
btreeAccessor.destroy();
}
}
private void doValidate(IIndexCursor btreeCursor) throws HyracksDataException {
int[] fieldPermutation = new int[tokenTypeTraits.length];
for (int i = 0; i < tokenTypeTraits.length; i++) {
fieldPermutation[i] = i;
}
PermutingTupleReference tokenTuple = new PermutingTupleReference(fieldPermutation);
IIndexOperationContext opCtx = new OnDiskInvertedIndexOpContext(btree);
// Search key for finding an inverted-list in the actual index.
ArrayTupleBuilder prevBuilder = new ArrayTupleBuilder(invListTypeTraits.length);
ArrayTupleReference prevTuple = new ArrayTupleReference();
IInvertedIndexAccessor invIndexAccessor = createAccessor(NoOpIndexAccessParameters.INSTANCE);
try {
InvertedListCursor invListCursor = createInvertedListRangeSearchCursor();
MultiComparator invListCmp = MultiComparator.create(invListCmpFactories);
while (btreeCursor.hasNext()) {
btreeCursor.next();
tokenTuple.reset(btreeCursor.getTuple());
// Validate inverted list by checking that the elements are totally ordered.
openInvertedListCursor(invListCursor, tokenTuple, opCtx);
invListCursor.prepareLoadPages();
invListCursor.loadPages();
try {
if (invListCursor.hasNext()) {
invListCursor.next();
ITupleReference invListElement = invListCursor.getTuple();
// Initialize prev tuple.
TupleUtils.copyTuple(prevBuilder, invListElement, invListElement.getFieldCount());
prevTuple.reset(prevBuilder.getFieldEndOffsets(), prevBuilder.getByteArray());
}
while (invListCursor.hasNext()) {
invListCursor.next();
ITupleReference invListElement = invListCursor.getTuple();
// Compare with previous element.
validateWithPrevious(invListCmp, invListElement, prevTuple);
// Set new prevTuple.
TupleUtils.copyTuple(prevBuilder, invListElement, invListElement.getFieldCount());
prevTuple.reset(prevBuilder.getFieldEndOffsets(), prevBuilder.getByteArray());
}
} finally {
invListCursor.unloadPages();
invListCursor.close();
}
}
} finally {
invIndexAccessor.destroy();
}
}
private void validateWithPrevious(MultiComparator invListCmp, ITupleReference invListElement,
ArrayTupleReference prevTuple) throws HyracksDataException {
if (invListCmp.compare(invListElement, prevTuple) <= 0) {
throw new HyracksDataException("Index validation failed.");
}
}
@Override
public long getMemoryAllocationSize() {
return 0;
}
protected static ITypeTraits[] getBTreeTypeTraits(ITypeTraits[] tokenTypeTraits) {
ITypeTraits[] btreeTypeTraits = new ITypeTraits[tokenTypeTraits.length + btreeValueTypeTraits.length];
// Set key type traits.
for (int i = 0; i < tokenTypeTraits.length; i++) {
btreeTypeTraits[i] = tokenTypeTraits[i];
}
// Set value type traits.
for (int i = 0; i < btreeValueTypeTraits.length; i++) {
btreeTypeTraits[i + tokenTypeTraits.length] = btreeValueTypeTraits[i];
}
return btreeTypeTraits;
}
@Override
public ITypeTraits[] getTokenTypeTraits() {
return tokenTypeTraits;
}
@Override
public IBinaryComparatorFactory[] getTokenCmpFactories() {
return tokenCmpFactories;
}
@Override
public int getNumOfFilterFields() {
return 0;
}
@Override
public synchronized void purge() throws HyracksDataException {
if (isOpen) {
throw HyracksDataException.create(ErrorCode.CANNOT_PURGE_ACTIVE_INDEX);
}
btree.purge();
bufferCache.purgeHandle(fileId);
fileId = -1;
}
}