Package com.orientechnologies.orient.core.iterator

Source Code of com.orientechnologies.orient.core.iterator.ORecordIteratorClusters

/*
  *
  *  *  Copyright 2014 Orient Technologies LTD (info(at)orientechnologies.com)
  *  *
  *  *  Licensed 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.
  *  *
  *  * For more information: http://www.orientechnologies.com
  *
  */
package com.orientechnologies.orient.core.iterator;

import java.util.Arrays;
import java.util.NoSuchElementException;

import com.orientechnologies.orient.core.db.record.ODatabaseRecordInternal;
import com.orientechnologies.orient.core.db.record.ORecordOperation;
import com.orientechnologies.orient.core.id.OClusterPosition;
import com.orientechnologies.orient.core.id.OClusterPositionFactory;
import com.orientechnologies.orient.core.id.ORID;
import com.orientechnologies.orient.core.record.ORecord;
import com.orientechnologies.orient.core.storage.OStorage;

/**
* Iterator to browse multiple clusters forward and backward. Once browsed in a direction, the iterator cannot change it. This
* iterator with "live updates" set is able to catch updates to the cluster sizes while browsing. This is the case when concurrent
* clients/threads insert and remove item in any cluster the iterator is browsing. If the cluster are hot removed by from the
* database the iterator could be invalid and throw exception of cluster not found.
*
* @author Luca Garulli
*/
public class ORecordIteratorClusters<REC extends ORecord> extends OIdentifiableIterator<REC> {
  protected int[]   clusterIds;
  protected int     currentClusterIdx;
  protected ORecord currentRecord;

  protected ORID    beginRange;
  protected ORID    endRange;

  public ORecordIteratorClusters(final ODatabaseRecordInternal iDatabase, final ODatabaseRecordInternal iLowLevelDatabase,
      final int[] iClusterIds, final boolean iUseCache, final boolean iterateThroughTombstones,
      final OStorage.LOCKING_STRATEGY iLockingStrategy) {
    super(iDatabase, iLowLevelDatabase, iUseCache, iterateThroughTombstones, iLockingStrategy);
    clusterIds = iClusterIds;

    Arrays.sort(clusterIds);

    config();
  }

  protected ORecordIteratorClusters(final ODatabaseRecordInternal iDatabase, final ODatabaseRecordInternal iLowLevelDatabase,
      final boolean iUseCache, final boolean iterateThroughTombstones, final OStorage.LOCKING_STRATEGY iLockingStrategy) {
    super(iDatabase, iLowLevelDatabase, iUseCache, iterateThroughTombstones, iLockingStrategy);
  }

  public ORecordIteratorClusters<REC> setRange(final ORID iBegin, final ORID iEnd) {
    beginRange = iBegin;
    endRange = iEnd;
    if (currentRecord != null && outsideOfTheRange(currentRecord.getIdentity())) {
      currentRecord = null;
    }

    updateClusterRange();
    begin();
    return this;
  }

  @Override
  public boolean hasPrevious() {
    checkDirection(false);

    if (currentRecord != null)
      return true;

    if (limit > -1 && browsedRecords >= limit)
      // LIMIT REACHED
      return false;

    if (browsedRecords >= totalAvailableRecords)
      return false;

    if (liveUpdated)
      updateClusterRange();

    ORecord record = getRecord();

    // ITERATE UNTIL THE PREVIOUS GOOD RECORD
    while (currentClusterIdx > -1) {
      while (prevPosition()) {
        currentRecord = readCurrentRecord(record, 0);

        if (currentRecord != null)
          if (include(currentRecord))
            // FOUND
            return true;
      }

      // CLUSTER EXHAUSTED, TRY WITH THE PREVIOUS ONE
      currentClusterIdx--;

      updateClusterRange();
    }

    if (txEntries != null && txEntries.size() - (currentTxEntryPosition + 1) > 0)
      return true;

    currentRecord = null;
    return false;
  }

  public boolean hasNext() {
    checkDirection(true);

    if (Thread.interrupted())
      // INTERRUPTED
      return false;

    if (currentRecord != null)
      return true;

    if (limit > -1 && browsedRecords >= limit)
      // LIMIT REACHED
      return false;

    if (browsedRecords >= totalAvailableRecords)
      return false;

    // COMPUTE THE NUMBER OF RECORDS TO BROWSE
    if (liveUpdated)
      updateClusterRange();

    ORecord record = getRecord();

    // ITERATE UNTIL THE NEXT GOOD RECORD
    while (currentClusterIdx < clusterIds.length) {
      while (nextPosition()) {
        if (outsideOfTheRange(current))
          continue;

        currentRecord = readCurrentRecord(record, 0);

        if (currentRecord != null)
          if (include(currentRecord))
            // FOUND
            return true;
      }

      // CLUSTER EXHAUSTED, TRY WITH THE NEXT ONE
      currentClusterIdx++;
      if (currentClusterIdx >= clusterIds.length)
        break;

      updateClusterRange();
    }

    // CHECK IN TX IF ANY
    if (txEntries != null && txEntries.size() - (currentTxEntryPosition + 1) > 0)
      return true;

    currentRecord = null;
    return false;
  }

  private boolean outsideOfTheRange(ORID orid) {
    if (beginRange != null && orid.compareTo(beginRange) < 0)
      return true;

    if (endRange != null && orid.compareTo(endRange) > 0)
      return true;

    return false;
  }

  /**
   * Return the element at the current position and move forward the cursor to the next position available.
   *
   * @return the next record found, otherwise the NoSuchElementException exception is thrown when no more records are found.
   */
  @SuppressWarnings("unchecked")
  public REC next() {
    checkDirection(true);

    if (currentRecord != null)
      try {
        // RETURN LAST LOADED RECORD
        return (REC) currentRecord;
      } finally {
        currentRecord = null;
      }

    ORecord record;

    // MOVE FORWARD IN THE CURRENT CLUSTER
    while (hasNext()) {
      if (currentRecord != null)
        try {
          // RETURN LAST LOADED RECORD
          return (REC) currentRecord;
        } finally {
          currentRecord = null;
        }

      record = getTransactionEntry();
      if (record == null)
        record = readCurrentRecord(null, +1);

      if (record != null)
        // FOUND
        if (include(record))
          return (REC) record;
    }

    record = getTransactionEntry();
    if (record != null)
      return (REC) record;

    throw new NoSuchElementException("Direction: forward, last position was: " + current + ", range: " + beginRange + "-"
        + endRange);
  }

  /**
   * Return the element at the current position and move backward the cursor to the previous position available.
   *
   * @return the previous record found, otherwise the NoSuchElementException exception is thrown when no more records are found.
   */
  @SuppressWarnings("unchecked")
  @Override
  public REC previous() {
    checkDirection(false);

    if (currentRecord != null)
      try {
        // RETURN LAST LOADED RECORD
        return (REC) currentRecord;
      } finally {
        currentRecord = null;
      }

    ORecord record = getRecord();

    // MOVE BACKWARD IN THE CURRENT CLUSTER
    while (hasPrevious()) {
      if (currentRecord != null)
        try {
          // RETURN LAST LOADED RECORD
          return (REC) currentRecord;
        } finally {
          currentRecord = null;
        }

      if (record == null)
        record = readCurrentRecord(null, -1);

      if (record != null)
        // FOUND
        if (include(record))
          return (REC) record;
    }

    record = getTransactionEntry();
    if (record != null)
      return (REC) record;

    return null;
  }

  protected boolean include(final ORecord iRecord) {
    return true;
  }

  /**
   * Move the iterator to the begin of the range. If no range was specified move to the first record of the cluster.
   *
   * @return The object itself
   */
  @Override
  public ORecordIteratorClusters<REC> begin() {
    if (clusterIds.length == 0)
      return this;

    browsedRecords = 0;
    currentClusterIdx = 0;
    current.clusterId = clusterIds[currentClusterIdx];

    if (liveUpdated)
      updateClusterRange();

    resetCurrentPosition();
    nextPosition();

    final ORecord record = getRecord();
    currentRecord = readCurrentRecord(record, 0);

    if (currentRecord != null && !include(currentRecord)) {
      currentRecord = null;
      hasNext();
    }

    return this;
  }

  /**
   * Move the iterator to the end of the range. If no range was specified move to the last record of the cluster.
   *
   * @return The object itself
   */
  @Override
  public ORecordIteratorClusters<REC> last() {
    if (clusterIds.length == 0)
      return this;

    browsedRecords = 0;
    currentClusterIdx = clusterIds.length - 1;
    if (liveUpdated)
      updateClusterRange();

    current.clusterId = clusterIds[currentClusterIdx];

    resetCurrentPosition();
    prevPosition();

    final ORecord record = getRecord();
    currentRecord = readCurrentRecord(record, 0);

    if (currentRecord != null && !include(currentRecord)) {
      currentRecord = null;
      hasPrevious();
    }

    return this;
  }

  /**
   * Tell to the iterator that the upper limit must be checked at every cycle. Useful when concurrent deletes or additions change
   * the size of the cluster while you're browsing it. Default is false.
   *
   * @param iLiveUpdated
   *          True to activate it, otherwise false (default)
   * @see #isLiveUpdated()
   */
  @Override
  public ORecordIteratorClusters<REC> setLiveUpdated(boolean iLiveUpdated) {
    super.setLiveUpdated(iLiveUpdated);

    if (iLiveUpdated) {
      firstClusterEntry = OClusterPositionFactory.INSTANCE.valueOf(0);
      lastClusterEntry = OClusterPositionFactory.INSTANCE.getMaxValue();
    } else {
      updateClusterRange();
    }

    return this;
  }

  protected void updateClusterRange() {
    if (clusterIds.length == 0)
      return;

    current.clusterId = clusterIds[currentClusterIdx];
    final OClusterPosition[] range = database.getStorage().getClusterDataRange(current.clusterId);

    if (beginRange != null && beginRange.getClusterId() == current.clusterId
        && beginRange.getClusterPosition().compareTo(range[0]) > 0)
      firstClusterEntry = beginRange.getClusterPosition();
    else
      firstClusterEntry = range[0];

    if (endRange != null && endRange.getClusterId() == current.clusterId && endRange.getClusterPosition().compareTo(range[1]) < 0)
      lastClusterEntry = endRange.getClusterPosition();
    else
      lastClusterEntry = range[1];

    resetCurrentPosition();
  }

  protected void config() {
    if (clusterIds.length == 0)
      return;

    currentClusterIdx = 0; // START FROM THE FIRST CLUSTER

    updateClusterRange();

    totalAvailableRecords = database.countClusterElements(clusterIds, isIterateThroughTombstones());

    txEntries = database.getTransaction().getNewRecordEntriesByClusterIds(clusterIds);

    if (txEntries != null)
      // ADJUST TOTAL ELEMENT BASED ON CURRENT TRANSACTION'S ENTRIES
      for (ORecordOperation entry : txEntries) {
        if (entry.getRecord().getIdentity().isTemporary() && entry.type != ORecordOperation.DELETED)
          totalAvailableRecords++;
        else if (entry.type == ORecordOperation.DELETED)
          totalAvailableRecords--;
      }

    begin();
  }

  @Override
  public String toString() {
    return String.format("ORecordIteratorCluster.clusters(%s).currentRecord(%s).range(%s-%s)", Arrays.toString(clusterIds),
        currentRecord, beginRange, endRange);
  }
}
TOP

Related Classes of com.orientechnologies.orient.core.iterator.ORecordIteratorClusters

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.