/*
*
* * 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.index;
import com.orientechnologies.common.comparator.ODefaultComparator;
import com.orientechnologies.common.listener.OProgressListener;
import com.orientechnologies.common.serialization.types.OBinarySerializer;
import com.orientechnologies.common.types.OModifiableBoolean;
import com.orientechnologies.orient.core.db.ODatabase;
import com.orientechnologies.orient.core.db.record.OIdentifiable;
import com.orientechnologies.orient.core.db.record.ridbag.sbtree.OIndexRIDContainer;
import com.orientechnologies.orient.core.exception.OInvalidIndexEngineIdException;
import com.orientechnologies.orient.core.id.ORID;
import com.orientechnologies.orient.core.iterator.OEmptyIterator;
import com.orientechnologies.orient.core.record.impl.ODocument;
import com.orientechnologies.orient.core.serialization.serializer.stream.OStreamSerializerSBTreeIndexRIDContainer;
import com.orientechnologies.orient.core.storage.impl.local.OAbstractPaginatedStorage;
import java.util.*;
import java.util.concurrent.Callable;
/**
* Abstract index implementation that supports multi-values for the same key.
*
* @author Luca Garulli
*/
public abstract class OIndexMultiValues extends OIndexAbstract<Set<OIdentifiable>> {
public OIndexMultiValues(String name, final String type, String algorithm, int version, OAbstractPaginatedStorage storage,
String valueContainerAlgorithm, final ODocument metadata) {
super(name, type, algorithm, valueContainerAlgorithm, metadata, version, storage);
}
public Set<OIdentifiable> get(Object key) {
key = getCollatingValue(key);
final ODatabase database = getDatabase();
final boolean txIsActive = database.getTransaction().isActive();
if (!txIsActive)
keyLockManager.acquireSharedLock(key);
try {
acquireSharedLock();
try {
Set<OIdentifiable> values;
while (true) {
try {
values = (Set<OIdentifiable>) storage.getIndexValue(indexId, key);
break;
} catch (OInvalidIndexEngineIdException e) {
doReloadIndexEngine();
}
}
if (values == null)
return Collections.emptySet();
return Collections.unmodifiableSet(values);
} finally {
releaseSharedLock();
}
} finally {
if (!txIsActive)
keyLockManager.releaseSharedLock(key);
}
}
public long count(Object key) {
key = getCollatingValue(key);
final ODatabase database = getDatabase();
final boolean txIsActive = database.getTransaction().isActive();
if (!txIsActive)
keyLockManager.acquireSharedLock(key);
try {
acquireSharedLock();
try {
Set<OIdentifiable> values;
while (true) {
try {
values = (Set<OIdentifiable>) storage.getIndexValue(indexId, key);
break;
} catch (OInvalidIndexEngineIdException e) {
doReloadIndexEngine();
}
}
if (values == null)
return 0;
return values.size();
} finally {
releaseSharedLock();
}
} finally {
if (!txIsActive)
keyLockManager.releaseSharedLock(key);
}
}
public OIndexMultiValues put(Object key, final OIdentifiable singleValue) {
if (singleValue != null && !singleValue.getIdentity().isPersistent())
throw new IllegalArgumentException("Cannot index a non persistent record (" + singleValue.getIdentity() + ")");
key = getCollatingValue(key);
final ODatabase database = getDatabase();
final boolean txIsActive = database.getTransaction().isActive();
if (!txIsActive) {
keyLockManager.acquireExclusiveLock(key);
}
try {
acquireSharedLock();
try {
if (!singleValue.getIdentity().isValid())
(singleValue.getRecord()).save();
final ORID identity = singleValue.getIdentity();
final boolean durable;
if (metadata != null && Boolean.TRUE.equals(metadata.field("durableInNonTxMode")))
durable = true;
else
durable = false;
Set<OIdentifiable> values = null;
while (true) {
try {
values = (Set<OIdentifiable>) storage.getIndexValue(indexId, key);
break;
} catch (OInvalidIndexEngineIdException e) {
doReloadIndexEngine();
}
}
final Set<OIdentifiable> cvalues = values;
final Callable<Object> creator = new Callable<Object>() {
@Override
public Object call() throws Exception {
Set<OIdentifiable> result = cvalues;
if (result == null) {
if (ODefaultIndexFactory.SBTREEBONSAI_VALUE_CONTAINER.equals(valueContainerAlgorithm)) {
result = new OIndexRIDContainer(getName(), durable);
} else {
throw new IllegalStateException("MVRBTree is not supported any more");
}
}
result.add(identity);
return result;
}
};
while (true) {
try {
storage.updateIndexEntry(indexId, key, creator);
return this;
} catch (OInvalidIndexEngineIdException e) {
doReloadIndexEngine();
}
}
} finally {
releaseSharedLock();
}
} finally {
if (!txIsActive)
keyLockManager.releaseExclusiveLock(key);
}
}
@Override
public boolean remove(Object key, final OIdentifiable value) {
key = getCollatingValue(key);
final ODatabase database = getDatabase();
final boolean txIsActive = database.getTransaction().isActive();
if (!txIsActive)
keyLockManager.acquireExclusiveLock(key);
try {
acquireSharedLock();
try {
Set<OIdentifiable> values = null;
while (true) {
try {
values = (Set<OIdentifiable>) storage.getIndexValue(indexId, key);
break;
} catch (OInvalidIndexEngineIdException e) {
doReloadIndexEngine();
}
}
if (values == null) {
return false;
}
final OModifiableBoolean removed = new OModifiableBoolean(false);
final Callable<Object> creator = new EntityRemover(value, removed, values);
while (true)
try {
storage.updateIndexEntry(indexId, key, creator);
break;
} catch (OInvalidIndexEngineIdException e) {
doReloadIndexEngine();
}
return removed.getValue();
} finally {
releaseSharedLock();
}
} finally {
if (!txIsActive)
keyLockManager.releaseExclusiveLock(key);
}
}
public OIndexMultiValues create(final String name, final OIndexDefinition indexDefinition, final String clusterIndexName,
final Set<String> clustersToIndex, boolean rebuild, final OProgressListener progressListener) {
return (OIndexMultiValues) super
.create(indexDefinition, clusterIndexName, clustersToIndex, rebuild, progressListener, determineValueSerializer());
}
protected OBinarySerializer determineValueSerializer() {
return storage.getComponentsFactory().binarySerializerFactory.getObjectSerializer(OStreamSerializerSBTreeIndexRIDContainer.ID);
}
@Override
public OIndexCursor iterateEntriesBetween(Object fromKey, boolean fromInclusive, Object toKey, boolean toInclusive,
boolean ascOrder) {
fromKey = getCollatingValue(fromKey);
toKey = getCollatingValue(toKey);
acquireSharedLock();
try {
while (true)
try {
return storage.iterateIndexEntriesBetween(indexId, fromKey, fromInclusive, toKey, toInclusive, ascOrder,
MultiValuesTransformer.INSTANCE);
} catch (OInvalidIndexEngineIdException e) {
doReloadIndexEngine();
}
} finally {
releaseSharedLock();
}
}
@Override
public OIndexCursor iterateEntriesMajor(Object fromKey, boolean fromInclusive, boolean ascOrder) {
fromKey = getCollatingValue(fromKey);
acquireSharedLock();
try {
while (true) {
try {
return storage.iterateIndexEntriesMajor(indexId, fromKey, fromInclusive, ascOrder, MultiValuesTransformer.INSTANCE);
} catch (OInvalidIndexEngineIdException e) {
doReloadIndexEngine();
}
}
} finally {
releaseSharedLock();
}
}
@Override
public OIndexCursor iterateEntriesMinor(Object toKey, boolean toInclusive, boolean ascOrder) {
toKey = getCollatingValue(toKey);
acquireSharedLock();
try {
while (true) {
try {
return storage.iterateIndexEntriesMinor(indexId, toKey, toInclusive, ascOrder, MultiValuesTransformer.INSTANCE);
} catch (OInvalidIndexEngineIdException e) {
doReloadIndexEngine();
}
}
} finally {
releaseSharedLock();
}
}
@Override
public OIndexCursor iterateEntries(Collection<?> keys, boolean ascSortOrder) {
final List<Object> sortedKeys = new ArrayList<Object>(keys);
final Comparator<Object> comparator;
if (ascSortOrder)
comparator = ODefaultComparator.INSTANCE;
else
comparator = Collections.reverseOrder(ODefaultComparator.INSTANCE);
Collections.sort(sortedKeys, comparator);
return new OIndexAbstractCursor() {
private Iterator<?> keysIterator = sortedKeys.iterator();
private Iterator<OIdentifiable> currentIterator = OEmptyIterator.IDENTIFIABLE_INSTANCE;
private Object currentKey;
@Override
public Map.Entry<Object, OIdentifiable> nextEntry() {
if (currentIterator == null)
return null;
Object key = null;
if (!currentIterator.hasNext()) {
Collection<OIdentifiable> result = null;
while (keysIterator.hasNext() && (result == null || result.isEmpty())) {
key = keysIterator.next();
key = getCollatingValue(key);
acquireSharedLock();
try {
while (true)
try {
result = (Collection<OIdentifiable>) storage.getIndexValue(indexId, key);
break;
} catch (OInvalidIndexEngineIdException e) {
doReloadIndexEngine();
}
} finally {
releaseSharedLock();
}
}
if (result == null) {
currentIterator = null;
return null;
}
currentKey = key;
currentIterator = result.iterator();
}
final OIdentifiable resultValue = currentIterator.next();
return new Map.Entry<Object, OIdentifiable>() {
@Override
public Object getKey() {
return currentKey;
}
@Override
public OIdentifiable getValue() {
return resultValue;
}
@Override
public OIdentifiable setValue(OIdentifiable value) {
throw new UnsupportedOperationException("setValue");
}
};
}
};
}
public long getSize() {
acquireSharedLock();
try {
while (true)
try {
return storage.getIndexSize(indexId, MultiValuesTransformer.INSTANCE);
} catch (OInvalidIndexEngineIdException e) {
doReloadIndexEngine();
}
} finally {
releaseSharedLock();
}
}
public long getKeySize() {
acquireSharedLock();
try {
while (true) {
try {
return storage.getIndexSize(indexId, null);
} catch (OInvalidIndexEngineIdException e) {
doReloadIndexEngine();
}
}
} finally {
releaseSharedLock();
}
}
@Override
public OIndexCursor cursor() {
acquireSharedLock();
try {
while (true) {
try {
return storage.getIndexCursor(indexId, MultiValuesTransformer.INSTANCE);
} catch (OInvalidIndexEngineIdException e) {
doReloadIndexEngine();
}
}
} finally {
releaseSharedLock();
}
}
@Override
public OIndexCursor descCursor() {
acquireSharedLock();
try {
while (true)
try {
return storage.getIndexDescCursor(indexId, MultiValuesTransformer.INSTANCE);
} catch (OInvalidIndexEngineIdException e) {
doReloadIndexEngine();
}
} finally {
releaseSharedLock();
}
}
private static final class MultiValuesTransformer implements OIndexEngine.ValuesTransformer {
private static final MultiValuesTransformer INSTANCE = new MultiValuesTransformer();
@Override
public Collection<OIdentifiable> transformFromValue(Object value) {
return (Collection<OIdentifiable>) value;
}
}
private static class EntityRemover implements Callable<Object> {
private final OIdentifiable value;
private final OModifiableBoolean removed;
private final Set<OIdentifiable> values;
public EntityRemover(OIdentifiable value, OModifiableBoolean removed, Set<OIdentifiable> values) {
this.value = value;
this.removed = removed;
this.values = values;
}
@Override
public Object call() throws Exception {
if (value == null) {
removed.setValue(true);
return null;
} else if (values.remove(value)) {
removed.setValue(true);
if (values.isEmpty())
return null;
else
return values;
}
return values;
}
}
}