@Override
  public void open(ICursorInitialState initialState, ISearchPredicate searchPred)
      throws HyracksDataException, IndexException {
    LSMBTreeCursorInitialState lsmInitialState = (LSMBTreeCursorInitialState) initialState;
    cmp = lsmInitialState.getOriginalKeyComparator();
    includeMemComponent = lsmInitialState.getIncludeMemComponent();
    operationalComponents = lsmInitialState.getOperationalComponents();
    lsmHarness = lsmInitialState.getLSMHarness();
    searchCallback = lsmInitialState.getSearchOperationCallback();
    memBTreeAccessor = lsmInitialState.getMemBTreeAccessor();
    predicate = (RangePredicate) lsmInitialState.getSearchPredicate();
    reusablePred.setLowKeyComparator(cmp);
    reusablePred.setHighKey(predicate.getHighKey(), predicate.isHighKeyInclusive());
    reusablePred.setHighKeyComparator(predicate.getHighKeyComparator());

    int numBTrees = lsmInitialState.getNumBTrees();
    rangeCursors = new IIndexCursor[numBTrees];
    for (int i = 0; i < numBTrees; i++) {
      IBTreeLeafFrame leafFrame =
          (IBTreeLeafFrame) lsmInitialState.getLeafFrameFactory().createFrame();
      rangeCursors[i] = new BTreeRangeSearchCursor(leafFrame, false);
    }
    setPriorityQueueComparator();

    int cursorIx = 0;
    ListIterator<ILSMComponent> btreesIter = operationalComponents.listIterator();
    if (includeMemComponent) {
      // Open cursor of in-memory BTree at index 0.
      memBTreeAccessor.search(rangeCursors[cursorIx], searchPred);
      // Skip 0 because it is the in-memory BTree.
      ++cursorIx;
      btreesIter.next();
    }

    // Open cursors of on-disk BTrees.
    int numDiskComponents = includeMemComponent ? numBTrees - 1 : numBTrees;
    ITreeIndexAccessor[] diskBTreeAccessors = new ITreeIndexAccessor[numDiskComponents];
    int diskBTreeIx = 0;
    while (btreesIter.hasNext()) {
      BTree diskBTree = (BTree) ((LSMBTreeImmutableComponent) btreesIter.next()).getBTree();
      diskBTreeAccessors[diskBTreeIx] =
          diskBTree.createAccessor(NoOpOperationCallback.INSTANCE, NoOpOperationCallback.INSTANCE);
      diskBTreeAccessors[diskBTreeIx].search(rangeCursors[cursorIx], searchPred);
      cursorIx++;
      diskBTreeIx++;
    }
    initPriorityQueue();
    proceed = true;
  }
  @Override
  public void nextFrame(ByteBuffer buffer) throws HyracksDataException {
    accessor.reset(buffer);

    int tupleCount = accessor.getTupleCount();
    try {
      for (int i = 0; i < tupleCount; i++) {
        if (lowKey != null) lowKey.reset(accessor, i);
        if (highKey != null) highKey.reset(accessor, i);
        rangePred.setLowKey(lowKey, lowKeyInclusive);
        rangePred.setHighKey(highKey, highKeyInclusive);

        cursor.reset();
        indexAccessor.search(cursor, rangePred);
        writeSearchResults(accessor, i);
      }
    } catch (Exception e) {
      throw new HyracksDataException(e);
    }
  }
  protected void checkPriorityQueue() throws HyracksDataException, IndexException {
    while (!outputPriorityQueue.isEmpty() || needPush == true) {
      if (!outputPriorityQueue.isEmpty()) {
        PriorityQueueElement checkElement = outputPriorityQueue.peek();
        if (proceed && !searchCallback.proceed(checkElement.getTuple())) {
          if (includeMemComponent) {
            PriorityQueueElement inMemElement = null;
            boolean inMemElementFound = false;
            // scan the PQ for the in-memory component's element
            Iterator<PriorityQueueElement> it = outputPriorityQueue.iterator();
            while (it.hasNext()) {
              inMemElement = it.next();
              if (inMemElement.getCursorIndex() == 0) {
                inMemElementFound = true;
                it.remove();
                break;
              }
            }
            if (inMemElementFound) {
              // copy the in-mem tuple
              if (tupleBuilder == null) {
                tupleBuilder = new ArrayTupleBuilder(cmp.getKeyFieldCount());
              }
              TupleUtils.copyTuple(tupleBuilder, inMemElement.getTuple(), cmp.getKeyFieldCount());
              copyTuple.reset(tupleBuilder.getFieldEndOffsets(), tupleBuilder.getByteArray());

              // unlatch/unpin
              rangeCursors[0].reset();

              // reconcile
              if (checkElement.getCursorIndex() == 0) {
                searchCallback.reconcile(copyTuple);
              } else {
                searchCallback.reconcile(checkElement.getTuple());
              }
              // retraverse
              reusablePred.setLowKey(copyTuple, true);
              memBTreeAccessor.search(rangeCursors[0], reusablePred);
              pushIntoPriorityQueue(inMemElement);
              if (cmp.compare(copyTuple, inMemElement.getTuple()) != 0) {
                searchCallback.cancel(copyTuple);
                continue;
              }
            } else {
              // the in-memory cursor is exhausted
              searchCallback.reconcile(checkElement.getTuple());
            }
          } else {
            searchCallback.reconcile(checkElement.getTuple());
          }
        }
        // If there is no previous tuple or the previous tuple can be ignored
        if (outputElement == null) {
          if (isDeleted(checkElement)) {
            // If the key has been deleted then pop it and set needPush to true.
            // We cannot push immediately because the tuple may be
            // modified if hasNext() is called
            outputElement = outputPriorityQueue.poll();
            searchCallback.cancel(checkElement.getTuple());
            needPush = true;
            proceed = false;
          } else {
            break;
          }
        } else {
          // Compare the previous tuple and the head tuple in the PQ
          if (compare(cmp, outputElement.getTuple(), checkElement.getTuple()) == 0) {
            // If the previous tuple and the head tuple are
            // identical
            // then pop the head tuple and push the next tuple from
            // the tree of head tuple

            // the head element of PQ is useless now
            PriorityQueueElement e = outputPriorityQueue.poll();
            pushIntoPriorityQueue(e);
          } else {
            // If the previous tuple and the head tuple are different
            // the info of previous tuple is useless
            if (needPush == true) {
              pushIntoPriorityQueue(outputElement);
              needPush = false;
            }
            proceed = true;
            outputElement = null;
          }
        }
      } else {
        // the priority queue is empty and needPush
        pushIntoPriorityQueue(outputElement);
        needPush = false;
        outputElement = null;
        proceed = true;
      }
    }
  }
 protected void setCursor() {
   cursor = indexAccessor.createSearchCursor(false);
 }