/*
* Copyright 2014 Orient Technologies.
*
* 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.
*/
package com.orientechnologies.lucene.index;
import com.orientechnologies.common.exception.OException;
import com.orientechnologies.common.listener.OProgressListener;
import com.orientechnologies.common.log.OLogManager;
import com.orientechnologies.common.serialization.types.OBinarySerializer;
import com.orientechnologies.lucene.OLuceneIndex;
import com.orientechnologies.lucene.OLuceneTxOperations;
import com.orientechnologies.lucene.engine.OLuceneIndexEngine;
import com.orientechnologies.lucene.tx.OLuceneTxChanges;
import com.orientechnologies.orient.core.db.record.OIdentifiable;
import com.orientechnologies.orient.core.exception.OInvalidIndexEngineIdException;
import com.orientechnologies.orient.core.index.OIndexAbstract;
import com.orientechnologies.orient.core.index.OIndexCursor;
import com.orientechnologies.orient.core.index.OIndexDefinition;
import com.orientechnologies.orient.core.index.OIndexEngine;
import com.orientechnologies.orient.core.index.OIndexException;
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 com.orientechnologies.orient.core.storage.impl.local.OIndexEngineCallback;
import com.orientechnologies.orient.core.tx.OTransaction;
import com.orientechnologies.orient.core.tx.OTransactionIndexChanges;
import com.orientechnologies.orient.core.tx.OTransactionIndexChangesPerKey;
import org.apache.lucene.document.Document;
import org.apache.lucene.search.IndexSearcher;
import java.io.IOException;
import java.util.Arrays;
import java.util.Collection;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.Callable;
public class OLuceneIndexNotUnique extends OIndexAbstract<Set<OIdentifiable>> implements OLuceneIndex {
public OLuceneIndexNotUnique(String name, String typeId, String algorithm, int version, OAbstractPaginatedStorage storage,
String valueContainerAlgorithm, ODocument metadata) {
super(name, typeId, algorithm, valueContainerAlgorithm, metadata, version, storage);
}
@Override
public long rebuild(OProgressListener iProgressListener) {
return super.rebuild(iProgressListener);
}
@Override
public boolean remove(final Object key, final OIdentifiable value) {
if (key != null) {
OTransaction transaction = getDatabase().getTransaction();
if (transaction.isActive()) {
transaction.addIndexEntry(this, super.getName(), OTransactionIndexChanges.OPERATION.REMOVE, encodeKey(key), value);
OLuceneTxChanges transactionChanges = getTransactionChanges(transaction);
try {
transactionChanges.remove(key, value);
} catch (IOException e) {
OLogManager.instance().error(this, "Error while removing", e);
}
return true;
} else {
while (true) {
try {
return storage.callIndexEngine(false, false, indexId, new OIndexEngineCallback<Boolean>() {
@Override
public Boolean callEngine(OIndexEngine engine) {
OLuceneIndexEngine indexEngine = (OLuceneIndexEngine) engine;
return indexEngine.remove(key, value);
}
});
} catch (OInvalidIndexEngineIdException e) {
doReloadIndexEngine();
}
}
}
}
return true;
}
@Override
public boolean remove(Object key) {
return super.remove(key);
}
@Override
protected OBinarySerializer determineValueSerializer() {
return storage.getComponentsFactory().binarySerializerFactory.getObjectSerializer(OStreamSerializerSBTreeIndexRIDContainer.ID);
}
@Override
protected Iterable<OTransactionIndexChangesPerKey.OTransactionIndexEntry> interpretTxKeyChanges(
OTransactionIndexChangesPerKey changes) {
return changes.interpret(OTransactionIndexChangesPerKey.Interpretation.NonUnique);
}
@Override
public Object getCollatingValue(Object key) {
return key;
}
@Override
protected void commitSnapshot(final Map<Object, Object> snapshot) {
while (true)
try {
storage.callIndexEngine(false, false, indexId, new OIndexEngineCallback<Object>() {
@Override
public Boolean callEngine(OIndexEngine engine) {
OLuceneIndexEngine indexEngine = (OLuceneIndexEngine) engine;
for (Map.Entry<Object, Object> snapshotEntry : snapshot.entrySet()) {
Object key = snapshotEntry.getKey();
OLuceneTxOperations operations = (OLuceneTxOperations) snapshotEntry.getValue();
for (OIdentifiable oIdentifiable : operations.removed) {
indexEngine.remove(decodeKey(key), oIdentifiable);
}
}
for (Map.Entry<Object, Object> snapshotEntry : snapshot.entrySet()) {
Object key = snapshotEntry.getKey();
OLuceneTxOperations operations = (OLuceneTxOperations) snapshotEntry.getValue();
indexEngine.put(decodeKey(key), operations.added);
}
OTransaction transaction = getDatabase().getTransaction();
resetTransactionChanges(transaction);
return null;
}
});
break;
} catch (OInvalidIndexEngineIdException e) {
doReloadIndexEngine();
}
}
protected Object decodeKey(Object key) {
return key;
}
private void resetTransactionChanges(OTransaction transaction) {
transaction.setCustomData(getName(), null);
}
@Override
protected void putInSnapshot(Object key, OIdentifiable value, Map<Object, Object> snapshot) {
key = getCollatingValue(key);
OLuceneTxOperations operations = (OLuceneTxOperations) snapshot.get(key);
if (operations == null) {
operations = new OLuceneTxOperations();
snapshot.put(key, operations);
}
operations.added.add(value.getIdentity());
snapshot.put(key, operations);
}
@Override
protected void removeFromSnapshot(Object key, OIdentifiable value, Map<Object, Object> snapshot) {
key = getCollatingValue(key);
OLuceneTxOperations operations = (OLuceneTxOperations) snapshot.get(key);
if (operations == null) {
operations = new OLuceneTxOperations();
snapshot.put(key, operations);
}
operations.removed.add(value.getIdentity());
snapshot.put(key, operations);
}
@Override
protected void clearSnapshot(IndexTxSnapshot indexTxSnapshot) {
indexTxSnapshot.clear = true;
indexTxSnapshot.indexSnapshot.clear();
}
@Override
protected void onIndexEngineChange(int indexId) {
while (true)
try {
storage.callIndexEngine(false, false, indexId, new OIndexEngineCallback<Object>() {
@Override
public Object callEngine(OIndexEngine engine) {
OLuceneIndexEngine oIndexEngine = (OLuceneIndexEngine) engine;
oIndexEngine.init(getName(), getType(), getDefinition(), isAutomatic(), getMetadata());
return null;
}
});
break;
} catch (OInvalidIndexEngineIdException e) {
doReloadIndexEngine();
}
}
protected Object encodeKey(Object key) {
return key;
}
public OLuceneTxChanges getTransactionChanges(OTransaction transaction) {
OLuceneTxChanges changes = (OLuceneTxChanges) transaction.getCustomData(getName());
if (changes == null) {
while (true)
try {
changes = storage.callIndexEngine(false, false, indexId, new OIndexEngineCallback<OLuceneTxChanges>() {
@Override
public OLuceneTxChanges callEngine(OIndexEngine engine) {
OLuceneIndexEngine indexEngine = (OLuceneIndexEngine) engine;
try {
return indexEngine.buildTxChanges();
} catch (IOException e) {
throw OException.wrapException(new OIndexException("Cannot get searcher from index " + getName()), e);
}
}
});
break;
} catch (OInvalidIndexEngineIdException e) {
doReloadIndexEngine();
}
transaction.setCustomData(getName(), changes);
}
return changes;
}
@Override
public OLuceneIndexNotUnique create(String name, OIndexDefinition indexDefinition, String clusterIndexName,
Set<String> clustersToIndex, boolean rebuild, OProgressListener progressListener) {
return (OLuceneIndexNotUnique) super
.create(indexDefinition, clusterIndexName, clustersToIndex, rebuild, progressListener, determineValueSerializer());
}
@Override
public Set<OIdentifiable> get(final Object key) {
final OTransaction transaction = getDatabase().getTransaction();
if (transaction.isActive()) {
while (true) {
try {
return storage.callIndexEngine(false, false, indexId, new OIndexEngineCallback<Set<OIdentifiable>>() {
@Override
public Set<OIdentifiable> callEngine(OIndexEngine engine) {
OLuceneIndexEngine indexEngine = (OLuceneIndexEngine) engine;
return (Set<OIdentifiable>) indexEngine.getInTx(key, getTransactionChanges(transaction));
}
});
} catch (OInvalidIndexEngineIdException e) {
doReloadIndexEngine();
}
}
} else {
while (true) {
try {
return (Set<OIdentifiable>) storage.getIndexValue(indexId, key);
} catch (OInvalidIndexEngineIdException e) {
doReloadIndexEngine();
}
}
}
}
@Override
public OLuceneIndexNotUnique put(final Object key, final OIdentifiable singleValue) {
if (key != null) {
OTransaction transaction = getDatabase().getTransaction();
if (transaction.isActive()) {
OLuceneTxChanges transactionChanges = getTransactionChanges(transaction);
transaction.addIndexEntry(this, super.getName(), OTransactionIndexChanges.OPERATION.PUT, encodeKey(key), singleValue);
Document luceneDoc;
while (true) {
try {
luceneDoc = storage.callIndexEngine(false, false, indexId, new OIndexEngineCallback<Document>() {
@Override
public Document callEngine(OIndexEngine engine) {
OLuceneIndexEngine oIndexEngine = (OLuceneIndexEngine) engine;
return oIndexEngine.buildDocument(key, singleValue);
}
});
break;
} catch (OInvalidIndexEngineIdException e) {
doReloadIndexEngine();
}
}
try {
transactionChanges.put(key, singleValue, luceneDoc);
} catch (IOException e) {
e.printStackTrace();
}
} else {
while (true) {
try {
storage.updateIndexEntry(indexId, key, new Callable<Object>() {
@Override
public Object call() throws Exception {
return Arrays.asList(singleValue);
}
});
break;
} catch (OInvalidIndexEngineIdException e) {
doReloadIndexEngine();
}
}
}
}
return this;
}
@Override
public long getSize() {
while (true) {
try {
return storage.callIndexEngine(false, false, indexId, new OIndexEngineCallback<Long>() {
// TODO apply current TX
@Override
public Long callEngine(OIndexEngine engine) {
OTransaction transaction = getDatabase().getTransaction();
OLuceneIndexEngine indexEngine = (OLuceneIndexEngine) engine;
return indexEngine.sizeInTx(getTransactionChanges(transaction));
}
});
} catch (OInvalidIndexEngineIdException e) {
doReloadIndexEngine();
}
}
}
@Override
public long getKeySize() {
return 0;
}
@Override
public OIndexCursor iterateEntries(Collection<?> keys, boolean ascSortOrder) {
return null;
}
@Override
public OIndexCursor iterateEntriesBetween(Object fromKey, boolean fromInclusive, Object toKey, boolean toInclusive,
boolean ascOrder) {
while (true) {
try {
return storage.iterateIndexEntriesBetween(indexId, fromKey, fromInclusive, toKey, toInclusive, ascOrder, null);
} catch (OInvalidIndexEngineIdException e) {
doReloadIndexEngine();
}
}
}
@Override
public OIndexCursor iterateEntriesMajor(Object fromKey, boolean fromInclusive, boolean ascOrder) {
while (true) {
try {
return storage.iterateIndexEntriesMajor(indexId, fromKey, fromInclusive, ascOrder, null);
} catch (OInvalidIndexEngineIdException e) {
doReloadIndexEngine();
}
}
}
@Override
public OIndexCursor iterateEntriesMinor(Object toKey, boolean toInclusive, boolean ascOrder) {
while (true) {
try {
return storage.iterateIndexEntriesMinor(indexId, toKey, toInclusive, ascOrder, null);
} catch (OInvalidIndexEngineIdException e) {
doReloadIndexEngine();
}
}
}
@Override
public OIndexCursor cursor() {
while (true) {
try {
return storage.getIndexCursor(indexId, null);
} catch (OInvalidIndexEngineIdException e) {
doReloadIndexEngine();
}
}
}
@Override
public OIndexCursor descCursor() {
while (true) {
try {
return storage.getIndexCursor(indexId, null);
} catch (OInvalidIndexEngineIdException e) {
doReloadIndexEngine();
}
}
}
@Override
public boolean supportsOrderedIterations() {
return false;
}
@Override
public IndexSearcher searcher() throws IOException {
while (true) {
try {
return storage.callIndexEngine(false, false, indexId, new OIndexEngineCallback<IndexSearcher>() {
@Override
public IndexSearcher callEngine(OIndexEngine engine) {
OLuceneIndexEngine indexEngine = (OLuceneIndexEngine) engine;
try {
return indexEngine.searcher();
} catch (IOException e) {
throw OException.wrapException(new OIndexException("Cannot get searcher from index " + getName()), e);
}
}
});
} catch (OInvalidIndexEngineIdException e) {
doReloadIndexEngine();
}
}
}
@Override
public boolean canBeUsedInEqualityOperators() {
return false;
}
}