package org.neo4j.rdf.store; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Iterator; import java.util.LinkedList; import org.neo4j.graphdb.Direction; import org.neo4j.graphdb.GraphDatabaseService; import org.neo4j.graphdb.Node; import org.neo4j.graphdb.Relationship; import org.neo4j.graphdb.Transaction; import org.neo4j.helpers.Predicate; import org.neo4j.helpers.collection.FilteringIterator; import org.neo4j.helpers.collection.IteratorWrapper; import org.neo4j.helpers.collection.NestingIterator; import org.neo4j.helpers.collection.PrefetchingIterator; import org.neo4j.index.IndexService; import org.neo4j.meta.model.MetaModel; import org.neo4j.rdf.fulltext.FulltextIndex; import org.neo4j.rdf.model.CompleteStatement; import org.neo4j.rdf.model.Context; import org.neo4j.rdf.model.Literal; import org.neo4j.rdf.model.Resource; import org.neo4j.rdf.model.Statement; import org.neo4j.rdf.model.Uri; import org.neo4j.rdf.model.Value; import org.neo4j.rdf.model.WildcardStatement; import org.neo4j.rdf.store.representation.standard.AbstractUriBasedExecutor; import org.neo4j.rdf.store.representation.standard.DenseTripleStrategy; import org.neo4j.rdf.store.representation.standard.UriBasedExecutor; import org.neo4j.util.GraphDatabaseUtil; public class DenseTripleStore extends RdfStoreImpl { private final MetaModel model; private final GraphDatabaseUtil graphDbUtil; public DenseTripleStore( GraphDatabaseService graphDb, IndexService indexer ) { this( graphDb, indexer, null, null ); } public DenseTripleStore( GraphDatabaseService graphDb, IndexService indexer, MetaModel model, FulltextIndex fulltextIndex ) { super( graphDb, new DenseTripleStrategy( new UriBasedExecutor( graphDb, indexer, model, fulltextIndex ), model ) ); this.model = model; this.graphDbUtil = new GraphDatabaseUtil( graphDb ); } @Override public DenseTripleStrategy getRepresentationStrategy() { return ( DenseTripleStrategy ) super.getRepresentationStrategy(); } @Override public Iterable<CompleteStatement> getStatements( WildcardStatement statement, boolean includeInferredStatements ) { Transaction tx = graphDb().beginTx(); try { if ( includeInferredStatements ) { throw new UnsupportedOperationException( "We currently not " + "support getStatements() with reasoning enabled" ); } Iterable<CompleteStatement> result = null; if ( wildcardPattern( statement, false, false, true ) ) { result = handleSubjectPredicateWildcard( statement ); } else if ( wildcardPattern( statement, false, true, true ) ) { result = handleSubjectWildcardWildcard( statement ); } else if ( wildcardPattern( statement, false, true, false ) ) { result = handleSubjectWildcardObject( statement ); } else if ( wildcardPattern( statement, true, true, false ) ) { result = handleWildcardWildcardObject( statement ); } else if ( wildcardPattern( statement, true, false, false ) ) { result = handleWildcardPredicateObject( statement ); } else if ( wildcardPattern( statement, false, false, false ) ) { result = handleSubjectPredicateObject( statement ); } else if ( wildcardPattern( statement, true, false, true ) ) { // result = handleWildcardPredicateWildcard( statement ); } else if ( wildcardPattern( statement, true, true, true ) ) { // result = handleWildcardWildcardWildcard( statement ); } else { result = super.getStatements( statement, includeInferredStatements ); } if ( result == null ) { result = new LinkedList<CompleteStatement>(); } tx.success(); return result; } finally { tx.finish(); } } public boolean verifyFulltextIndex( String queryOrNullForAll ) { throw new UnsupportedOperationException( "Not implemented yet" ); } private Iterator<Node> getNodesForLiteral( Statement statement ) { Literal literal = ( Literal ) statement.getObject(); Iterable<Node> literalNodes = getRepresentationStrategy(). getExecutor().findLiteralNodes( literal.getValue() ); return literalNodes.iterator(); } private Iterable<CompleteStatement> handleSubjectPredicateWildcard( WildcardStatement statement ) { Node subjectNode = lookupNode( statement.getSubject() ); if ( subjectNode == null ) { return null; } Iterator<Object[]> triples = new ResourceToTripleIterator( subjectNode, ( ( Uri ) statement.getPredicate() ).getUriAsString(), Direction.OUTGOING ); return statementIterator( triples ); } private Iterable<CompleteStatement> handleSubjectWildcardWildcard( WildcardStatement statement ) { Node subjectNode = lookupNode( statement.getSubject() ); if ( subjectNode == null ) { return null; } Iterator<Object[]> triples = new ResourceToTripleIterator( subjectNode, null, Direction.OUTGOING ); return statementIterator( triples ); } private Iterable<CompleteStatement> handleSubjectWildcardObject( WildcardStatement statement ) { Node subjectNode = lookupNode( statement.getSubject() ); if ( subjectNode == null ) { return null; } Iterator<Object[]> triples = new ResourceToTripleIterator( subjectNode, null, Direction.OUTGOING ); triples = new ObjectFilteredIterator( triples, statement.getObject() ); return statementIterator( triples ); } private Iterable<CompleteStatement> handleWildcardWildcardObject( WildcardStatement statement ) { Iterator<Object[]> triples = null; if ( statement.getObject() instanceof Literal ) { triples = new LiteralsToTriplesIterator( getNodesForLiteral( statement ), null ); triples = new ObjectFilteredIterator( triples, statement.getObject() ); } else { Node objectNode = lookupNode( statement.getObject() ); if ( objectNode == null ) { return null; } triples = new ResourceToTripleIterator( objectNode, null, Direction.INCOMING ); } return statementIterator( triples ); } private Iterable<CompleteStatement> handleWildcardPredicateObject( WildcardStatement statement ) { Iterator<Object[]> triples = null; if ( statement.getObject() instanceof Literal ) { String predicate = ( ( Uri ) statement.getPredicate() ).getUriAsString(); triples = new LiteralsToTriplesIterator( getNodesForLiteral( statement ), predicate ); triples = new ObjectFilteredIterator( triples, statement.getObject() ); } else { Node objectNode = lookupNode( statement.getObject() ); if ( objectNode == null ) { return null; } triples = new ResourceToTripleIterator( objectNode, ( ( Uri ) statement.getPredicate() ).getUriAsString(), Direction.INCOMING ); } return statementIterator( triples ); } private Iterable<CompleteStatement> handleSubjectPredicateObject( WildcardStatement statement ) { Node subjectNode = lookupNode( statement.getSubject() ); if ( subjectNode == null ) { return null; } Iterator<Object[]> triples = new ResourceToTripleIterator( subjectNode, ( ( Uri ) statement.getPredicate() ).getUriAsString(), Direction.OUTGOING ); triples = new ObjectFilteredIterator( triples, statement.getObject() ); return statementIterator( triples ); } private Iterable<CompleteStatement> statementIterator( final Iterator<Object[]> triples ) { return new Iterable<CompleteStatement>() { public Iterator<CompleteStatement> iterator() { return new TripleToStatementIterator( triples ); } }; } private String getNodeUriOrNull( Node node ) { return ( String ) node.getProperty( AbstractUriBasedExecutor.URI_PROPERTY_KEY, null ); } private Value getValueForObject( Object objectFromTriple ) { return objectFromTriple instanceof Node ? new Uri( getNodeUriOrNull( ( Node ) objectFromTriple ) ) : new Literal( objectFromTriple ); } private class TripleToStatementIterator extends IteratorWrapper<CompleteStatement, Object[]> { private TripleToStatementIterator( Iterator<Object[]> source ) { super( source ); } @Override protected CompleteStatement underlyingObjectToObject( Object[] triple ) { Node subjectNode = ( Node ) triple[ 0 ]; Resource subject = new Uri( getNodeUriOrNull( subjectNode ) ); Uri predicate = new Uri( ( String ) triple[ 1 ] ); Value object = getValueForObject( triple[ 2 ] ); return object instanceof Resource ? new CompleteStatement( subject, predicate, ( Resource ) object, Context.NULL ) : new CompleteStatement( subject, predicate, ( Literal ) object, Context.NULL ); } } // Given a resource as starting point, possibly a predicate and object // read from the statement, this will iterate through the statements // and return triples. private class ResourceToTripleIterator extends NestingIterator<Object[], Object[]> { private Iterator<Iterator<Object[]>> subIterator; private ResourceToTripleIterator( Node resource, String predicateOrNull, Direction direction ) { super( Arrays.asList( new Object[ 0 ], new Object[ 0 ] ).iterator() ); Collection<Iterator<Object[]>> iterators = new ArrayList<Iterator<Object[]>>(); iterators.add( new ResourceToObjectTripleIterator( resource, predicateOrNull, direction ) ); iterators.add( new ResourceToLiteralTripleIterator( resource, predicateOrNull ) ); this.subIterator = iterators.iterator(); } @Override protected Iterator<Object[]> createNestedIterator( Object[] item ) { return subIterator.next(); } } private class ResourceToObjectTripleIterator extends PrefetchingIterator<Object[]> { private Node resource; private Iterator<Relationship> predicates; private Direction direction; private ResourceToObjectTripleIterator( Node resource, String predicateOrNull, Direction direction ) { this.resource = resource; this.direction = direction; if ( predicateOrNull == null ) { predicates = resource.getRelationships( direction ).iterator(); } else { predicates = resource.getRelationships( relType( predicateOrNull ), direction ).iterator(); } } @Override protected Object[] fetchNextOrNull() { if ( !predicates.hasNext() ) { return null; } Relationship relationship = predicates.next(); String predicate = relationship.getType().name(); Node otherNode = relationship.getOtherNode( resource ); Node subjectNode = direction == Direction.OUTGOING ? resource : otherNode; Node objectNode = direction == Direction.OUTGOING ? otherNode : resource; return relationship == null ? null : new Object[] { subjectNode, predicate, objectNode, }; } } private static Predicate<String> NOT_URI_PROPERTY = new Predicate<String>() { public boolean accept( String item ) { return !item.equals( AbstractUriBasedExecutor.URI_PROPERTY_KEY ); } }; private class ResourceToLiteralTripleIterator extends PrefetchingIterator<Object[]> { private Node resource; private Iterator<String> predicates; private String currentPredicate; private Iterator<Object> currentPredicateIterator; private ResourceToLiteralTripleIterator( Node resource, String predicateOrNull ) { this.resource = resource; this.predicates = predicateOrNull == null ? getResourceLiteralPredicates( resource ) : Arrays.asList( predicateOrNull ).iterator(); } private Iterator<String> getResourceLiteralPredicates( Node resource ) { return new FilteringIterator<String>( resource.getPropertyKeys().iterator(), NOT_URI_PROPERTY ); } @Override protected Object[] fetchNextOrNull() { if ( currentPredicateIterator == null || !currentPredicateIterator.hasNext() ) { if ( predicates.hasNext() ) { currentPredicate = predicates.next(); currentPredicateIterator = new LiteralPredicateIterator( resource, currentPredicate ); } } Object value = currentPredicateIterator != null && currentPredicateIterator.hasNext() ? currentPredicateIterator.next() : null; return value == null ? null : newTriple( value ); } private Object[] newTriple( Object value ) { return new Object[] { this.resource, this.currentPredicate, value }; } } private class LiteralPredicateIterator extends PrefetchingIterator<Object> { private Iterator<Object> values; private LiteralPredicateIterator( Node resource, String predicate ) { Object rawValue = resource.getProperty( predicate, null ); this.values = rawValue == null ? null : graphDbUtil.propertyValueAsList( rawValue ).iterator(); } @Override protected Object fetchNextOrNull() { return values != null && values.hasNext() ? values.next() : null; } } private class ObjectFilteredIterator extends FilteringIterator<Object[]> { private ObjectFilteredIterator( Iterator<Object[]> source, final Value object ) { super( source, new Predicate<Object[]>() { public boolean accept( Object[] triple ) { Value tripleObject = getValueForObject( triple[ 2 ] ); return object.equals( tripleObject ); } }); } } private class LiteralsToTriplesIterator extends NestingIterator<Object[], Node> { private String predicateOrNull; private LiteralsToTriplesIterator( Iterator<Node> source, String predicateOrNull ) { super( source ); this.predicateOrNull = predicateOrNull; } @Override protected Iterator<Object[]> createNestedIterator( Node resource ) { return new ResourceToLiteralTripleIterator( resource, predicateOrNull ); } } }