@Override
  public Object executeIndexQuery(
      OCommandContext iContext,
      OIndex<?> index,
      final INDEX_OPERATION_TYPE iOperationType,
      List<Object> keyParams,
      int fetchLimit) {
    final OIndexDefinition indexDefinition = index.getDefinition();

    final OIndexInternal<?> internalIndex = index.getInternal();
    if (!internalIndex.canBeUsedInEqualityOperators()) return null;

    final Object result;

    if (indexDefinition.getParamCount() == 1) {
      final Object key;
      if (indexDefinition instanceof OIndexDefinitionMultiValue)
        key = ((OIndexDefinitionMultiValue) indexDefinition).createSingleValue(keyParams.get(0));
      else key = indexDefinition.createValue(keyParams);

      if (key == null) return null;

      final Object indexResult;
      if (iOperationType == INDEX_OPERATION_TYPE.GET) indexResult = index.get(key);
      else indexResult = index.count(key);

      result = convertIndexResult(indexResult);
    } else {
      // in case of composite keys several items can be returned in case of we perform search
      // using part of composite key stored in index.

      final OCompositeIndexDefinition compositeIndexDefinition =
          (OCompositeIndexDefinition) indexDefinition;

      final Object keyOne = compositeIndexDefinition.createSingleValue(keyParams);

      if (keyOne == null) return null;

      final Object keyTwo = compositeIndexDefinition.createSingleValue(keyParams);

      if (internalIndex.hasRangeQuerySupport()) {
        if (INDEX_OPERATION_TYPE.COUNT.equals(iOperationType)) {
          result = index.count(keyOne, true, keyTwo, true, fetchLimit);
        } else if (fetchLimit > -1)
          result = index.getValuesBetween(keyOne, true, keyTwo, true, fetchLimit);
        else result = index.getValuesBetween(keyOne, true, keyTwo, true);
      } else {
        if (indexDefinition.getParamCount() == keyParams.size()) {
          final Object indexResult;
          if (iOperationType == INDEX_OPERATION_TYPE.GET) indexResult = index.get(keyOne);
          else indexResult = index.count(keyOne);

          result = convertIndexResult(indexResult);
        } else return null;
      }
    }

    updateProfiler(iContext, index, keyParams, indexDefinition);
    return result;
  }
 @Override
 protected void handleObject(OIndex<?> object) {
   indexName = object.getName();
   OIndexDefinition indexDefinition = object.getDefinition();
   if (indexDefinition != null) {
     String className = indexDefinition.getClassName();
     if (className != null) classModel = new OClassModel(className);
   }
 }
 /**
  * Make type conversion of keys for specific index.
  *
  * @param index - index for which keys prepared for.
  * @param keys - which should be prepared.
  * @return keys converted to necessary type.
  */
 private Set<Comparable> prepareKeys(OIndex<?> index, Object keys) {
   final OIndexDefinition indexDefinition = index.getDefinition();
   if (keys instanceof Collection) {
     final Set<Comparable> newKeys = new TreeSet<Comparable>();
     for (Object o : ((Collection) keys)) {
       newKeys.add((Comparable) indexDefinition.createValue(o));
     }
     return newKeys;
   } else {
     return Collections.singleton((Comparable) indexDefinition.createValue(keys));
   }
 }
  /**
   * Register statistic information about usage of index in {@link OProfilerStub}.
   *
   * @param index which usage is registering.
   */
  private void updateStatistic(OIndex<?> index) {

    final OProfiler profiler = Orient.instance().getProfiler();
    if (profiler.isRecording()) {
      Orient.instance()
          .getProfiler()
          .updateCounter(
              profiler.getDatabaseMetric(index.getDatabaseName(), "query.indexUsed"),
              "Used index in query",
              +1);

      final int paramCount = index.getDefinition().getParamCount();
      if (paramCount > 1) {
        final String profiler_prefix =
            profiler.getDatabaseMetric(index.getDatabaseName(), "query.compositeIndexUsed");
        profiler.updateCounter(profiler_prefix, "Used composite index in query", +1);
        profiler.updateCounter(
            profiler_prefix + "." + paramCount,
            "Used composite index in query with " + paramCount + " params",
            +1);
      }
    }
  }
 private static boolean isComposite(OIndex<?> currentIndex) {
   return currentIndex.getDefinition().getParamCount() > 1;
 }
 /** {@inheritDoc} */
 public OIndexDefinition getDefinition() {
   return lastIndex.getDefinition();
 }
  public void commit() {
    checkTransaction();
    status = TXSTATUS.COMMITTING;

    if (OScenarioThreadLocal.INSTANCE.get() != RUN_MODE.RUNNING_DISTRIBUTED
        && !(database.getStorage() instanceof OStorageEmbedded))
      database.getStorage().commit(this, null);
    else {
      final List<String> involvedIndexes = getInvolvedIndexes();

      if (involvedIndexes != null) Collections.sort(involvedIndexes);

      for (int retry = 1; retry <= autoRetries; ++retry) {
        try {

          // LOCK INVOLVED INDEXES
          List<OIndexAbstract<?>> lockedIndexes = null;
          try {
            if (involvedIndexes != null)
              for (String indexName : involvedIndexes) {
                final OIndexAbstract<?> index =
                    (OIndexAbstract<?>)
                        database.getMetadata().getIndexManager().getIndexInternal(indexName);
                if (lockedIndexes == null) lockedIndexes = new ArrayList<OIndexAbstract<?>>();

                index.acquireModificationLock();
                lockedIndexes.add(index);
              }

            if (!useSBTree) {
              // SEARCH FOR INDEX BASED ON DOCUMENT TOUCHED
              final Collection<? extends OIndex<?>> indexes =
                  database.getMetadata().getIndexManager().getIndexes();
              List<? extends OIndex<?>> indexesToLock = null;
              if (indexes != null) {
                indexesToLock = new ArrayList<OIndex<?>>(indexes);
                Collections.sort(
                    indexesToLock,
                    new Comparator<OIndex<?>>() {
                      public int compare(final OIndex<?> indexOne, final OIndex<?> indexTwo) {
                        return indexOne.getName().compareTo(indexTwo.getName());
                      }
                    });
              }

              if (indexesToLock != null && !indexesToLock.isEmpty()) {
                if (lockedIndexes == null) lockedIndexes = new ArrayList<OIndexAbstract<?>>();

                for (OIndex<?> index : indexesToLock) {
                  for (Entry<ORID, ORecordOperation> entry : recordEntries.entrySet()) {
                    final ORecord<?> record = entry.getValue().record.getRecord();
                    if (record instanceof ODocument) {
                      ODocument doc = (ODocument) record;
                      if (!lockedIndexes.contains(index.getInternal())
                          && doc.getSchemaClass() != null
                          && index.getDefinition() != null
                          && doc.getSchemaClass()
                              .isSubClassOf(index.getDefinition().getClassName())) {
                        index.getInternal().acquireModificationLock();
                        lockedIndexes.add((OIndexAbstract<?>) index.getInternal());
                      }
                    }
                  }
                }

                for (OIndexAbstract<?> index : lockedIndexes) index.acquireExclusiveLock();
              }
            }

            final Map<String, OIndex> indexes = new HashMap<String, OIndex>();
            for (OIndex index : database.getMetadata().getIndexManager().getIndexes())
              indexes.put(index.getName(), index);

            final Runnable callback =
                new Runnable() {
                  @Override
                  public void run() {
                    final ODocument indexEntries = getIndexChanges();
                    if (indexEntries != null) {
                      final Map<String, OIndexInternal<?>> indexesToCommit =
                          new HashMap<String, OIndexInternal<?>>();

                      for (Entry<String, Object> indexEntry : indexEntries) {
                        final OIndexInternal<?> index =
                            indexes.get(indexEntry.getKey()).getInternal();
                        indexesToCommit.put(index.getName(), index.getInternal());
                      }

                      for (OIndexInternal<?> indexInternal : indexesToCommit.values())
                        indexInternal.preCommit();

                      for (Entry<String, Object> indexEntry : indexEntries) {
                        final OIndexInternal<?> index =
                            indexesToCommit.get(indexEntry.getKey()).getInternal();

                        if (index == null) {
                          OLogManager.instance()
                              .error(
                                  this,
                                  "Index with name " + indexEntry.getKey() + " was not found.");
                          throw new OIndexException(
                              "Index with name " + indexEntry.getKey() + " was not found.");
                        } else index.addTxOperation((ODocument) indexEntry.getValue());
                      }

                      try {
                        for (OIndexInternal<?> indexInternal : indexesToCommit.values())
                          indexInternal.commit();
                      } finally {
                        for (OIndexInternal<?> indexInternal : indexesToCommit.values())
                          indexInternal.postCommit();
                      }
                    }
                  }
                };

            final String storageType = database.getStorage().getType();

            if (storageType.equals(OEngineLocal.NAME)
                || storageType.equals(OEngineLocalPaginated.NAME))
              database.getStorage().commit(OTransactionOptimistic.this, callback);
            else {
              database
                  .getStorage()
                  .callInLock(
                      new Callable<Object>() {
                        @Override
                        public Object call() throws Exception {
                          database.getStorage().commit(OTransactionOptimistic.this, null);
                          callback.run();
                          return null;
                        }
                      },
                      true);
            }
            // OK
            break;

          } finally {
            // RELEASE INDEX LOCKS IF ANY
            if (lockedIndexes != null) {
              if (!useSBTree) {
                for (OIndexAbstract<?> index : lockedIndexes) index.releaseExclusiveLock();
              }

              for (OIndexAbstract<?> index : lockedIndexes) index.releaseModificationLock();
            }
          }
        } catch (OTimeoutException e) {
          if (autoRetries == 0) {
            OLogManager.instance()
                .debug(
                    this,
                    "Caught timeout exception during commit, but no automatic retry has been set",
                    e);
            throw e;
          } else if (retry == autoRetries) {
            OLogManager.instance()
                .debug(
                    this,
                    "Caught timeout exception during %d/%d. Retry limit is exceeded.",
                    retry,
                    autoRetries);
            throw e;
          } else {
            OLogManager.instance()
                .debug(
                    this,
                    "Caught timeout exception during commit retrying %d/%d...",
                    retry,
                    autoRetries);
          }
        }
      }
    }

    status = TXSTATUS.COMPLETED;
  }