/*
* Copyright (C) 2013 lichtflut Forschungs- und Entwicklungsgesellschaft mbH
*
* 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 org.arastreju.sge.spi.impl;
import org.apache.lucene.analysis.standard.StandardAnalyzer;
import org.apache.lucene.document.Document;
import org.apache.lucene.document.Field;
import org.apache.lucene.document.NumericField;
import org.apache.lucene.index.IndexReader;
import org.apache.lucene.index.IndexWriter;
import org.apache.lucene.index.IndexWriterConfig;
import org.apache.lucene.index.Term;
import org.apache.lucene.search.IndexSearcher;
import org.apache.lucene.search.TermQuery;
import org.apache.lucene.search.TopDocs;
import org.apache.lucene.store.Directory;
import org.apache.lucene.store.FSDirectory;
import org.apache.lucene.util.Version;
import org.arastreju.sge.naming.QualifiedName;
import org.arastreju.sge.persistence.NodeKeyTable;
import org.arastreju.sge.spi.PhysicalNodeID;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.File;
import java.io.IOException;
/**
* <p>
* Node key table based on a lucene index.
* </p>
*
* <p>
* Created Mar 22, 2013
* </p>
*
* @author Oliver Tigges
*/
public abstract class LuceneBasedNodeKeyTable<T extends PhysicalNodeID> implements NodeKeyTable<T> {
private static final Logger LOGGER = LoggerFactory.getLogger(LuceneBasedNodeKeyTable.class);
public static final String PID = "pid";
public static final String QN = "qn";
// ----------------------------------------------------
private final Directory dir;
private final IndexWriter writer;
// ----------------------------------------------------
public static LuceneBasedNodeKeyTable<NumericPhysicalNodeID> forNumericIDs(String baseDir) throws IOException {
return new LuceneBasedNodeKeyTable<NumericPhysicalNodeID>(baseDir) {
@Override
protected NumericPhysicalNodeID createID(Document doc) {
NumericField field = (NumericField) doc.getFieldable(PID);
return new NumericPhysicalNodeID(field.getNumericValue());
}
@Override
protected void setID(Document doc, NumericPhysicalNodeID id) {
NumericField field = new NumericField(PID, Field.Store.YES, false);
field.setLongValue(id.asLong());
doc.add(field);
}
};
}
// ----------------------------------------------------
protected LuceneBasedNodeKeyTable(String baseDir) throws IOException {
final File indexDir = new File(baseDir, "__qn_index");
LOGGER.info("Creating node key table index in {}.", indexDir);
boolean created = indexDir.mkdir();
IndexWriterConfig config = new IndexWriterConfig(Version.LUCENE_35, new StandardAnalyzer(Version.LUCENE_35));
this.dir = FSDirectory.open(indexDir);
this.writer = new IndexWriter(dir, config);
if (created) {
LOGGER.info("Index directory newly created. Starting initialization.");
writer().commit();
}
}
// ----------------------------------------------------
@Override
public T lookup(QualifiedName qualifiedName) {
TermQuery query = new TermQuery(new Term(QN, qualifiedName.toURI()));
IndexReader reader = reader();
try {
IndexSearcher searcher = new IndexSearcher(reader);
TopDocs result = searcher.search(query, 2);
searcher.close();
if (result.scoreDocs.length == 1) {
Document document = reader.document(result.scoreDocs[0].doc);
return createID(document);
} else if (result.scoreDocs.length == 0) {
return null;
} else {
throw new IllegalStateException("Found more than one document for qualified name: "
+ qualifiedName);
}
} catch (IOException e) {
throw new RuntimeException("Could not query by qualified name.", e);
} finally {
try {
reader.close();
} catch (IOException e) {
throw new RuntimeException("Could not close index reader.", e);
}
}
}
@Override
public synchronized void put(QualifiedName qn, T physicalID) {
Document doc = new Document();
doc.add(new Field(QN, qn.toURI(), Field.Store.YES, Field.Index.NOT_ANALYZED));
setID(doc, physicalID);
try {
final IndexWriter writer = writer();
writer.addDocument(doc);
writer.commit();
} catch (IOException e) {
throw new RuntimeException("Could not map phyiscal ID to qualified name in index.", e);
}
}
@Override
public void remove(QualifiedName qn) {
try {
final IndexWriter writer = writer();
writer.deleteDocuments(new Term(QN, qn.toURI()));
writer.commit();
} catch (IOException e) {
throw new RuntimeException("Could not remove qualified name from index.", e);
}
}
@Override
public void shutdown() throws IOException {
writer.close();
dir.close();
}
// ----------------------------------------------------
protected abstract T createID(Document doc);
protected abstract void setID(Document doc, T id);
// ----------------------------------------------------
private IndexReader reader() {
try {
return IndexReader.open(dir, true);
} catch (IOException e) {
throw new RuntimeException("Unable to obtain an index reader.", e);
}
}
private IndexWriter writer() throws IOException {
return writer;
}
}