
eu.interedition.text.neo4j.Neo4jTextRepository Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of text-core Show documentation
Show all versions of text-core Show documentation
Stand-off Markup/Annotation Text Model
The newest version!
package eu.interedition.text.neo4j;
import com.google.common.collect.AbstractIterator;
import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
import com.google.common.io.CharStreams;
import eu.interedition.text.Anchor;
import eu.interedition.text.Layer;
import eu.interedition.text.Name;
import eu.interedition.text.Query;
import eu.interedition.text.QueryResult;
import eu.interedition.text.TextRange;
import eu.interedition.text.TextRepository;
import eu.interedition.text.simple.SimpleLayer;
import eu.interedition.text.util.BatchLayerAdditionSupport;
import eu.interedition.text.util.UpdateSupport;
import org.apache.lucene.search.Sort;
import org.apache.lucene.search.SortField;
import org.neo4j.graphdb.Direction;
import org.neo4j.graphdb.GraphDatabaseService;
import org.neo4j.graphdb.Node;
import org.neo4j.graphdb.NotFoundException;
import org.neo4j.graphdb.Relationship;
import org.neo4j.graphdb.Transaction;
import org.neo4j.graphdb.index.RelationshipIndex;
import org.neo4j.graphdb.traversal.TraversalDescription;
import org.neo4j.index.lucene.QueryContext;
import org.neo4j.index.lucene.ValueContext;
import org.neo4j.kernel.Traversal;
import org.neo4j.kernel.Uniqueness;
import java.io.IOException;
import java.io.Reader;
import java.net.URI;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;
import static eu.interedition.text.neo4j.LayerNode.Relationships.ANCHORS;
import static eu.interedition.text.neo4j.LayerNode.Relationships.HAS_TEXT;
/**
* @author Gregor Middell
*/
public class Neo4jTextRepository implements TextRepository, UpdateSupport, BatchLayerAdditionSupport {
private final Logger LOG = Logger.getLogger(getClass().getName());
private final Neo4jIndexQuery indexQuery = new Neo4jIndexQuery();
private final Class dataType;
private final DataNodeMapper dataNodeMapper;
private final GraphDatabaseService db;
private final boolean transactional;
private Node base;
public Neo4jTextRepository(Class dataType, DataNodeMapper dataNodeMapper, GraphDatabaseService db, boolean transactional) {
this.dataType = dataType;
this.dataNodeMapper = dataNodeMapper;
this.db = db;
this.transactional = transactional;
}
@Override
public Layer findByIdentifier(long id) {
try {
return new LayerNode(this, db.getNodeById(id));
} catch (NotFoundException e) {
return null;
}
}
@Override
public QueryResult query(final Query query) {
final Transaction tx = begin();
return new QueryResult() {
@Override
public void close() throws IOException {
commit(tx);
}
@Override
public Iterator> iterator() {
final org.apache.lucene.search.Query luceneQuery = indexQuery.build(query);
if (LOG.isLoggable(Level.FINE)) {
LOG.log(Level.FINE, "Lucene Query: {0}", luceneQuery);
}
final Iterator relationships = index().query(
new QueryContext(luceneQuery).sort(new Sort(new SortField("id", SortField.LONG)))
).iterator();
return new AbstractIterator>() {
long last = 0;
@Override
protected Layer computeNext() {
while (relationships.hasNext()) {
final Node layerNode = relationships.next().getStartNode();
if (layerNode.getId() != last) {
last = layerNode.getId();
return new LayerNode(Neo4jTextRepository.this, layerNode);
}
}
return endOfData();
}
};
}
};
}
@Override
public Layer add(Name name, Reader text, T data, Set> anchors) throws IOException {
return Iterables.getOnlyElement(add(Collections.>singleton(new SimpleLayer(name, CharStreams.toString(text), data, anchors, null))));
}
@Override
public Iterable> add(Iterable> batch) throws IOException {
final Transaction tx = begin();
try {
final Node baseNode = baseNode();
final RelationshipIndex index = index();
final List> created = Lists.newLinkedList();
for (Layer layer : batch) {
final Node node = db.createNode();
final Name name = layer.getName();
final URI namespace = name.getNamespace();
final String localName = name.getLocalName();
final String ns = (namespace == null ? "" : namespace.toString());
if (!ns.isEmpty()) {
node.setProperty(LayerNode.NAME_NS, ns);
}
node.setProperty(LayerNode.NAME_LN, localName);
node.setProperty(LayerNode.TEXT, layer.read());
dataNodeMapper.write(layer.data(), node);
for (Anchor anchor : layer.getAnchors()) {
final Layer anchorText = anchor.getText();
if (anchorText instanceof LayerNode) {
final Node anchorTextNode = ((LayerNode) anchorText).node;
final Relationship anchorRel = node.createRelationshipTo(anchorTextNode, ANCHORS);
final TextRange anchorRange = anchor.getRange();
final long rangeStart = anchorRange.getStart();
final long rangeEnd = anchorRange.getEnd();
anchorRel.setProperty(LayerNode.RANGE_START, rangeStart);
anchorRel.setProperty(LayerNode.RANGE_END, rangeEnd);
index.add(anchorRel, "id", ValueContext.numeric(node.getId()));
index.add(anchorRel, "text", ValueContext.numeric(anchorTextNode.getId()));
index.add(anchorRel, "rs", ValueContext.numeric(rangeStart));
index.add(anchorRel, "re", ValueContext.numeric(rangeEnd));
index.add(anchorRel, "len", ValueContext.numeric(anchorRange.length()));
index.add(anchorRel, "ns", ns);
index.add(anchorRel, "ln", localName);
}
}
baseNode.createRelationshipTo(node, HAS_TEXT);
created.add(new LayerNode(this, node));
}
return created;
} finally {
commit(tx);
}
}
protected Node baseNode() {
if (base == null) {
synchronized (this) {
final Node referenceNode = db.getReferenceNode();
Relationship baseRel = referenceNode.getSingleRelationship(HAS_TEXT, Direction.OUTGOING);
if (baseRel == null) {
baseRel = referenceNode.createRelationshipTo(db.createNode(), HAS_TEXT);
}
base = baseRel.getEndNode();
}
}
return base;
}
protected RelationshipIndex index() {
return db.index().forRelationships("anchors");
}
@Override
public Layer add(Name name, Reader text, T data, Anchor anchor) throws IOException {
return add(name, text, data, Collections.singleton(anchor));
}
@Override
public void delete(Iterable> layers) {
final Transaction tx = begin();
final RelationshipIndex index = index();
try {
final Set nodes = Sets.newHashSet();
for (LayerNode> toDelete : Iterables.filter(layers, LayerNode.class)) {
nodes.add(toDelete.node);
for (Relationship anchorRel : TRANSITIVE_ANCHORING.traverse(toDelete.node).relationships()) {
nodes.add(anchorRel.getStartNode());
index.remove(anchorRel);
anchorRel.delete();
}
}
for (Node node : nodes) {
for (Relationship anchorRel : node.getRelationships(ANCHORS, Direction.OUTGOING)) {
index.remove(anchorRel);
anchorRel.delete();
}
for (Relationship primeRel : node.getRelationships(HAS_TEXT)) {
primeRel.delete();
}
node.delete();
}
} finally {
commit(tx);
}
}
private void commit(Transaction tx) {
if (tx != null) {
try {
tx.success();
} finally {
tx.finish();
}
}
}
private Transaction begin() {
return (transactional ? db.beginTx() : null);
}
@Override
public void updateText(Layer target, Reader text) throws IOException {
if (target instanceof LayerNode) {
final Transaction tx = begin();
try {
((LayerNode) target).node.setProperty(LayerNode.TEXT, CharStreams.toString(text));
} finally {
commit(tx);
}
}
}
public T data(Node source) throws IOException {
return dataNodeMapper.read(source, dataType);
}
private static final TraversalDescription TRANSITIVE_ANCHORING = Traversal.description()
.uniqueness(Uniqueness.RELATIONSHIP_GLOBAL)
.relationships(ANCHORS, Direction.INCOMING);
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy