/*
* 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.engine;
import com.orientechnologies.common.exception.OException;
import com.orientechnologies.common.io.OIOException;
import com.orientechnologies.common.log.OLogManager;
import com.orientechnologies.lucene.OLuceneIndexType;
import com.orientechnologies.lucene.builder.OLuceneDocumentBuilder;
import com.orientechnologies.lucene.builder.OLuceneQueryBuilder;
import com.orientechnologies.lucene.collections.OLuceneCompositeKey;
import com.orientechnologies.lucene.collections.OLuceneResultSet;
import com.orientechnologies.lucene.collections.OLuceneResultSetFactory;
import com.orientechnologies.lucene.query.OLuceneQueryContext;
import com.orientechnologies.lucene.tx.OLuceneTxChanges;
import com.orientechnologies.orient.core.command.OCommandContext;
import com.orientechnologies.orient.core.db.record.OIdentifiable;
import com.orientechnologies.orient.core.id.OContextualRecordId;
import com.orientechnologies.orient.core.index.*;
import com.orientechnologies.orient.core.record.impl.ODocument;
import com.orientechnologies.orient.core.sql.parser.ParseException;
import com.orientechnologies.orient.core.storage.OStorage;
import org.apache.lucene.document.Document;
import org.apache.lucene.document.Field;
import org.apache.lucene.index.IndexWriter;
import org.apache.lucene.search.IndexSearcher;
import org.apache.lucene.search.Query;
import org.apache.lucene.search.ScoreDoc;
import org.apache.lucene.store.Directory;
import java.io.IOException;
import java.util.*;
public class OLuceneFullTextIndexEngine extends OLuceneIndexEngineAbstract {
protected OLuceneFacetManager facetManager;
private OLuceneDocumentBuilder builder;
private OLuceneQueryBuilder queryBuilder;
public OLuceneFullTextIndexEngine(OStorage storage, String idxName) {
super(storage, idxName);
}
@Override
public IndexWriter openIndexWriter(Directory directory) throws IOException {
return createIndexWriter(directory);
}
@Override
public void init(String indexName, String indexType, OIndexDefinition indexDefinition, boolean isAutomatic, ODocument metadata) {
super.init(indexName, indexType, indexDefinition, isAutomatic, metadata);
queryBuilder = new OLuceneQueryBuilder(metadata);
builder = new OLuceneDocumentBuilder();
}
@Override
public IndexWriter createIndexWriter(Directory directory) throws IOException {
OLuceneIndexWriterFactory fc = new OLuceneIndexWriterFactory();
facetManager = new OLuceneFacetManager(storage, this, metadata);
OLogManager.instance().debug(this, "Creating Lucene index in '%s'...", directory);
return fc.createIndexWriter(directory, metadata, indexAnalyzer());
}
@Override
public void delete() {
super.delete();
if (facetManager != null) {
facetManager.delete();
}
}
@Override
public int getVersion() {
return 0;
}
@Override
public void onRecordAddedToResultSet(OLuceneQueryContext queryContext, OContextualRecordId recordId, Document ret,
final ScoreDoc score) {
recordId.setContext(new HashMap<String, Object>() {
{
put("score", score.score);
}
});
}
@Override
public boolean contains(Object key) {
return false;
}
@Override
public boolean remove(Object key) {
return false;
}
@Override
public Object get(Object key) {
return getInTx(key, null);
}
@Override
public void put(Object key, Object value) {
Collection<OIdentifiable> container = (Collection<OIdentifiable>) value;
for (OIdentifiable oIdentifiable : container) {
Document doc;
if (index.isAutomatic()) {
doc = buildDocument(key, oIdentifiable);
} else {
doc = putInManualindex(key, oIdentifiable);
}
if (facetManager.supportsFacets()) {
try {
addDocument(facetManager.buildDocument(doc));
// FIXME: writer is commited by timer task, why commit this separately?
facetManager.commit();
} catch (IOException e) {
OLogManager.instance().error(this, "Error while updating facets", e);
}
} else {
addDocument(doc);
}
if (!index.isAutomatic()) {
commit();
}
}
}
@Override
public boolean validatedPut(Object key, OIdentifiable value, Validator<Object, OIdentifiable> validator) {
throw new UnsupportedOperationException("Validated put is not supported by OLuceneFullTextIndexEngine");
}
@Override
public Object getFirstKey() {
return null;
}
@Override
public Object getLastKey() {
return null;
}
@Override
public OIndexCursor iterateEntriesBetween(Object rangeFrom, boolean fromInclusive, Object rangeTo, boolean toInclusive,
boolean ascSortOrder, ValuesTransformer transformer) {
return new LuceneIndexCursor((OLuceneResultSet) get(rangeFrom), rangeFrom);
}
private Set<OIdentifiable> getResults(Query query, OCommandContext context, Object key, OLuceneTxChanges changes) {
try {
IndexSearcher searcher = searcher();
OLuceneQueryContext queryContext = new OLuceneQueryContext(context, searcher, query).setChanges(changes);
if (facetManager.supportsFacets()) {
facetManager.addFacetContext(queryContext, key);
}
return OLuceneResultSetFactory.INSTANCE.create(this, queryContext);
} catch (IOException e) {
throw OIOException.wrapException(new OIndexException("Error reading from Lucene index"), e);
}
}
@Override
public OIndexCursor iterateEntriesMajor(Object fromKey, boolean isInclusive, boolean ascSortOrder,
ValuesTransformer transformer) {
return null;
}
@Override
public OIndexCursor iterateEntriesMinor(Object toKey, boolean isInclusive, boolean ascSortOrder, ValuesTransformer transformer) {
return null;
}
@Override
public boolean hasRangeQuerySupport() {
return false;
}
@Override
public Document buildDocument(Object key, OIdentifiable value) {
if (index.isAutomatic()) {
return builder.build(index, key, value, collectionFields, metadata);
} else {
return putInManualindex(key, value);
}
}
private Document putInManualindex(Object key, OIdentifiable oIdentifiable) {
Document doc = new Document();
doc.add(OLuceneIndexType.createField(RID, oIdentifiable.getIdentity().toString(), Field.Store.YES));
if (key instanceof OCompositeKey) {
List<Object> keys = ((OCompositeKey) key).getKeys();
int k = 0;
for (Object o : keys) {
doc.add(OLuceneIndexType.createField("k" + k, o, Field.Store.NO));
k++;
}
} else if (key instanceof Collection) {
Collection<Object> keys = (Collection<Object>) key;
int k = 0;
for (Object o : keys) {
doc.add(OLuceneIndexType.createField("k" + k, o, Field.Store.NO));
k++;
}
} else {
doc.add(OLuceneIndexType.createField("k0", key, Field.Store.NO));
}
return doc;
}
@Override
public Query buildQuery(Object query) {
try {
return queryBuilder.query(index, query, queryAnalyzer());
} catch (ParseException e) {
throw OException.wrapException(new OIndexEngineException("Error parsing query"), e);
}
}
@Override
public Object getInTx(Object key, OLuceneTxChanges changes) {
try {
Query q = queryBuilder.query(index, key, queryAnalyzer());
OCommandContext context = null;
if (key instanceof OLuceneCompositeKey) {
context = ((OLuceneCompositeKey) key).getContext();
}
return getResults(q, context, key, changes);
} catch (ParseException e) {
throw OException.wrapException(new OIndexEngineException("Error parsing lucene query"), e);
}
}
public class LuceneIndexCursor implements OIndexCursor {
private final Object key;
private OLuceneResultSet resultSet;
private Iterator<OIdentifiable> iterator;
public LuceneIndexCursor(OLuceneResultSet resultSet, Object key) {
this.resultSet = resultSet;
this.iterator = resultSet.iterator();
this.key = key;
}
@Override
public Map.Entry<Object, OIdentifiable> nextEntry() {
if (iterator.hasNext()) {
final OIdentifiable next = iterator.next();
return new Map.Entry<Object, OIdentifiable>() {
@Override
public Object getKey() {
return key;
}
@Override
public OIdentifiable getValue() {
return next;
}
@Override
public OIdentifiable setValue(OIdentifiable value) {
return null;
}
};
}
return null;
}
@Override
public Set<OIdentifiable> toValues() {
return null;
}
@Override
public Set<Map.Entry<Object, OIdentifiable>> toEntries() {
return null;
}
@Override
public Set<Object> toKeys() {
return null;
}
@Override
public void setPrefetchSize(int prefetchSize) {
}
@Override
public boolean hasNext() {
return false;
}
@Override
public OIdentifiable next() {
return null;
}
@Override
public void remove() {
}
}
}