
org.neo4j.server.rest.web.DatabaseActions Maven / Gradle / Ivy
/*
* Copyright (c) 2002-2016 "Neo Technology,"
* Network Engine for Objects in Lund AB [http://neotechnology.com]
*
* This file is part of Neo4j.
*
* Neo4j is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
package org.neo4j.server.rest.web;
import com.sun.jersey.api.core.HttpContext;
import org.apache.lucene.search.Sort;
import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.function.Function;
import java.util.function.Predicate;
import org.neo4j.function.Predicates;
import org.neo4j.graphalgo.CommonEvaluators;
import org.neo4j.graphalgo.CostEvaluator;
import org.neo4j.graphalgo.GraphAlgoFactory;
import org.neo4j.graphalgo.PathFinder;
import org.neo4j.graphalgo.WeightedPath;
import org.neo4j.graphdb.ConstraintViolationException;
import org.neo4j.graphdb.Direction;
import org.neo4j.graphdb.Label;
import org.neo4j.graphdb.Node;
import org.neo4j.graphdb.NotFoundException;
import org.neo4j.graphdb.Path;
import org.neo4j.graphdb.PathExpander;
import org.neo4j.graphdb.PathExpanderBuilder;
import org.neo4j.graphdb.PathExpanders;
import org.neo4j.graphdb.PropertyContainer;
import org.neo4j.graphdb.Relationship;
import org.neo4j.graphdb.RelationshipType;
import org.neo4j.graphdb.ResourceIterable;
import org.neo4j.graphdb.index.AutoIndexer;
import org.neo4j.graphdb.index.Index;
import org.neo4j.graphdb.index.IndexHits;
import org.neo4j.graphdb.index.IndexManager;
import org.neo4j.graphdb.index.ReadableIndex;
import org.neo4j.graphdb.index.UniqueFactory;
import org.neo4j.graphdb.index.UniqueFactory.UniqueEntity;
import org.neo4j.graphdb.schema.ConstraintCreator;
import org.neo4j.graphdb.schema.ConstraintDefinition;
import org.neo4j.graphdb.schema.ConstraintType;
import org.neo4j.graphdb.schema.IndexCreator;
import org.neo4j.graphdb.schema.IndexDefinition;
import org.neo4j.graphdb.traversal.BranchState;
import org.neo4j.graphdb.traversal.Paths;
import org.neo4j.graphdb.traversal.TraversalDescription;
import org.neo4j.helpers.collection.IterableWrapper;
import org.neo4j.helpers.collection.Iterables;
import org.neo4j.helpers.collection.Pair;
import org.neo4j.index.lucene.QueryContext;
import org.neo4j.kernel.internal.GraphDatabaseAPI;
import org.neo4j.server.database.InjectableProvider;
import org.neo4j.server.rest.domain.EndNodeNotFoundException;
import org.neo4j.server.rest.domain.PropertySettingStrategy;
import org.neo4j.server.rest.domain.RelationshipExpanderBuilder;
import org.neo4j.server.rest.domain.StartNodeNotFoundException;
import org.neo4j.server.rest.domain.TraversalDescriptionBuilder;
import org.neo4j.server.rest.domain.TraverserReturnType;
import org.neo4j.server.rest.paging.Lease;
import org.neo4j.server.rest.paging.LeaseManager;
import org.neo4j.server.rest.paging.PagedTraverser;
import org.neo4j.server.rest.repr.BadInputException;
import org.neo4j.server.rest.repr.ConstraintDefinitionRepresentation;
import org.neo4j.server.rest.repr.DatabaseRepresentation;
import org.neo4j.server.rest.repr.IndexDefinitionRepresentation;
import org.neo4j.server.rest.repr.IndexRepresentation;
import org.neo4j.server.rest.repr.IndexedEntityRepresentation;
import org.neo4j.server.rest.repr.InvalidArgumentsException;
import org.neo4j.server.rest.repr.ListRepresentation;
import org.neo4j.server.rest.repr.NodeIndexRepresentation;
import org.neo4j.server.rest.repr.NodeIndexRootRepresentation;
import org.neo4j.server.rest.repr.NodeRepresentation;
import org.neo4j.server.rest.repr.PathRepresentation;
import org.neo4j.server.rest.repr.PropertiesRepresentation;
import org.neo4j.server.rest.repr.RelationshipIndexRepresentation;
import org.neo4j.server.rest.repr.RelationshipIndexRootRepresentation;
import org.neo4j.server.rest.repr.RelationshipRepresentation;
import org.neo4j.server.rest.repr.Representation;
import org.neo4j.server.rest.repr.RepresentationType;
import org.neo4j.server.rest.repr.ScoredNodeRepresentation;
import org.neo4j.server.rest.repr.ScoredRelationshipRepresentation;
import org.neo4j.server.rest.repr.ValueRepresentation;
import org.neo4j.server.rest.repr.WeightedPathRepresentation;
import static org.neo4j.graphdb.Label.label;
import static org.neo4j.helpers.collection.Iterables.filter;
import static org.neo4j.helpers.collection.Iterables.map;
import static org.neo4j.helpers.collection.Iterators.asList;
import static org.neo4j.server.rest.repr.RepresentationType.CONSTRAINT_DEFINITION;
public class DatabaseActions
{
public static final String SCORE_ORDER = "score";
public static final String RELEVANCE_ORDER = "relevance";
public static final String INDEX_ORDER = "index";
private final GraphDatabaseAPI graphDb;
private final LeaseManager leases;
private final TraversalDescriptionBuilder traversalDescriptionBuilder;
private final PropertySettingStrategy propertySetter;
public static class Provider extends InjectableProvider
{
private final DatabaseActions database;
public Provider( DatabaseActions database )
{
super( DatabaseActions.class );
this.database = database;
}
@Override
public DatabaseActions getValue( HttpContext c )
{
return database;
}
}
private final Function CONSTRAINT_DEF_TO_REPRESENTATION =
ConstraintDefinitionRepresentation::new;
public DatabaseActions( LeaseManager leaseManager, boolean enableScriptSandboxing,
GraphDatabaseAPI graphDatabaseAPI )
{
this.leases = leaseManager;
this.graphDb = graphDatabaseAPI;
this.traversalDescriptionBuilder = new TraversalDescriptionBuilder( enableScriptSandboxing );
this.propertySetter = new PropertySettingStrategy( graphDb );
}
public DatabaseActions( LeaseManager leaseManager, GraphDatabaseAPI graphDatabaseAPI )
{
this( leaseManager, true, graphDatabaseAPI );
}
private Node node( long id ) throws NodeNotFoundException
{
try
{
return graphDb.getNodeById( id );
}
catch ( NotFoundException e )
{
throw new NodeNotFoundException( String.format(
"Cannot find node with id [%d] in database.", id ), e );
}
}
private Relationship relationship( long id )
throws RelationshipNotFoundException
{
try
{
return graphDb.getRelationshipById( id );
}
catch ( NotFoundException e )
{
throw new RelationshipNotFoundException( e );
}
}
// API
public DatabaseRepresentation root()
{
return new DatabaseRepresentation();
}
// Nodes
public NodeRepresentation createNode( Map properties, Label... labels )
throws PropertyValueException
{
Node node = graphDb.createNode();
propertySetter.setProperties( node, properties );
if ( labels != null )
{
for ( Label label : labels )
{
node.addLabel( label );
}
}
return new NodeRepresentation( node );
}
public NodeRepresentation getNode( long nodeId )
throws NodeNotFoundException
{
return new NodeRepresentation( node( nodeId ) );
}
public void deleteNode( long nodeId ) throws NodeNotFoundException, ConstraintViolationException
{
Node node = node( nodeId );
if ( node.hasRelationship() )
{
throw new ConstraintViolationException(
String.format(
"The node with id %d cannot be deleted. Check that the node is orphaned before deletion.",
nodeId ) );
}
node.delete();
}
// Property keys
public Representation getAllPropertyKeys()
{
Collection propKeys = Iterables.asSet( map( new Function()
{
@Override
public ValueRepresentation apply( String key )
{
return ValueRepresentation.string( key );
}
}, graphDb.getAllPropertyKeys() ) );
return new ListRepresentation( RepresentationType.STRING, propKeys );
}
// Node properties
public Representation getNodeProperty( long nodeId, String key )
throws NodeNotFoundException, NoSuchPropertyException
{
Node node = node( nodeId );
try
{
return PropertiesRepresentation.value( node.getProperty( key ) );
}
catch ( NotFoundException e )
{
throw new NoSuchPropertyException( node, key );
}
}
public void setNodeProperty( long nodeId, String key, Object value )
throws PropertyValueException, NodeNotFoundException
{
Node node = node( nodeId );
propertySetter.setProperty( node, key, value );
}
public void removeNodeProperty( long nodeId, String key ) throws NodeNotFoundException, NoSuchPropertyException
{
Node node = node( nodeId );
if ( node.removeProperty( key ) == null )
{
throw new NoSuchPropertyException( node, key );
}
}
public PropertiesRepresentation getAllNodeProperties( long nodeId )
throws NodeNotFoundException
{
return new PropertiesRepresentation( node( nodeId ) );
}
public void setAllNodeProperties( long nodeId,
Map properties ) throws PropertyValueException,
NodeNotFoundException
{
propertySetter.setAllProperties( node( nodeId ), properties );
}
public void removeAllNodeProperties( long nodeId )
throws NodeNotFoundException, PropertyValueException
{
propertySetter.setAllProperties( node( nodeId ), null );
}
// Labels
public void addLabelToNode( long nodeId, Collection labelNames ) throws NodeNotFoundException,
BadInputException
{
try
{
Node node = node( nodeId );
for ( String labelName : labelNames )
{
node.addLabel( label( labelName ) );
}
}
catch ( org.neo4j.graphdb.ConstraintViolationException e )
{
throw new BadInputException( "Unable to add label, see nested exception.", e );
}
}
public void setLabelsOnNode( long nodeId, Collection labels ) throws NodeNotFoundException,
BadInputException
{
Node node = node( nodeId );
try
{
// Remove current labels
for ( Label label : node.getLabels() )
{
node.removeLabel( label );
}
// Add new labels
for ( String labelName : labels )
{
node.addLabel( label( labelName ) );
}
}
catch ( org.neo4j.graphdb.ConstraintViolationException e )
{
throw new BadInputException( "Unable to add label, see nested exception.", e );
}
}
public void removeLabelFromNode( long nodeId, String labelName ) throws NodeNotFoundException
{
node( nodeId ).removeLabel( label( labelName ) );
}
public ListRepresentation getNodeLabels( long nodeId ) throws NodeNotFoundException
{
Iterable labels = new IterableWrapper( node( nodeId ).getLabels() )
{
@Override
protected String underlyingObjectToObject( Label object )
{
return object.name();
}
};
return ListRepresentation.string( labels );
}
public String[] getNodeIndexNames()
{
return graphDb.index().nodeIndexNames();
}
public String[] getRelationshipIndexNames()
{
return graphDb.index().relationshipIndexNames();
}
public IndexRepresentation createNodeIndex(
Map indexSpecification )
{
final String indexName = (String) indexSpecification.get( "name" );
assertIsLegalIndexName( indexName );
if ( indexSpecification.containsKey( "config" ) )
{
@SuppressWarnings( "unchecked" )
Map config = (Map) indexSpecification.get( "config" );
graphDb.index().forNodes( indexName, config );
return new NodeIndexRepresentation( indexName, config );
}
graphDb.index().forNodes( indexName );
return new NodeIndexRepresentation( indexName,
Collections.emptyMap() );
}
public IndexRepresentation createRelationshipIndex(
Map indexSpecification )
{
final String indexName = (String) indexSpecification.get( "name" );
assertIsLegalIndexName( indexName );
if ( indexSpecification.containsKey( "config" ) )
{
@SuppressWarnings( "unchecked" )
Map config = (Map) indexSpecification.get( "config" );
graphDb.index().forRelationships( indexName, config );
return new RelationshipIndexRepresentation( indexName, config );
}
graphDb.index().forRelationships( indexName );
return new RelationshipIndexRepresentation( indexName,
Collections.emptyMap() );
}
public void removeNodeIndex( String indexName )
{
if ( !graphDb.index().existsForNodes( indexName ) )
{
throw new NotFoundException( "No node index named '" + indexName + "'." );
}
graphDb.index().forNodes( indexName ).delete();
}
public void removeRelationshipIndex( String indexName )
{
if ( !graphDb.index().existsForRelationships( indexName ) )
{
throw new NotFoundException( "No relationship index named '" + indexName + "'." );
}
graphDb.index().forRelationships( indexName ).delete();
}
public boolean nodeIsIndexed( String indexName, String key, Object value, long nodeId )
{
Index index = graphDb.index().forNodes( indexName );
Node expectedNode = graphDb.getNodeById( nodeId );
IndexHits hits = index.get( key, value );
return iterableContains( hits, expectedNode );
}
public boolean relationshipIsIndexed( String indexName, String key, Object value, long relationshipId )
{
Index index = graphDb.index().forRelationships( indexName );
Relationship expectedNode = graphDb.getRelationshipById( relationshipId );
IndexHits hits = index.get( key, value );
return iterableContains( hits, expectedNode );
}
private boolean iterableContains( Iterable iterable,
T expectedElement )
{
for ( T possibleMatch : iterable )
{
if ( possibleMatch.equals( expectedElement ) )
{
return true;
}
}
return false;
}
public Representation isAutoIndexerEnabled( String type )
{
AutoIndexer extends PropertyContainer> index = getAutoIndexerForType( type );
return ValueRepresentation.bool( index.isEnabled() );
}
public void setAutoIndexerEnabled( String type, boolean enable )
{
AutoIndexer extends PropertyContainer> index = getAutoIndexerForType( type );
index.setEnabled( enable );
}
private AutoIndexer extends PropertyContainer> getAutoIndexerForType( String type )
{
final IndexManager indexManager = graphDb.index();
switch ( type )
{
case "node":
return indexManager.getNodeAutoIndexer();
case "relationship":
return indexManager.getRelationshipAutoIndexer();
default:
throw new IllegalArgumentException( "invalid type " + type );
}
}
public Representation getAutoIndexedProperties( String type )
{
AutoIndexer extends PropertyContainer> indexer = getAutoIndexerForType( type );
return ListRepresentation.string( indexer.getAutoIndexedProperties() );
}
public void startAutoIndexingProperty( String type, String property )
{
AutoIndexer extends PropertyContainer> indexer = getAutoIndexerForType( type );
indexer.startAutoIndexingProperty( property );
}
public void stopAutoIndexingProperty( String type, String property )
{
AutoIndexer extends PropertyContainer> indexer = getAutoIndexerForType( type );
indexer.stopAutoIndexingProperty( property );
}
// Relationships
public enum RelationshipDirection
{
all( Direction.BOTH ),
in( Direction.INCOMING ),
out( Direction.OUTGOING );
final Direction internal;
private RelationshipDirection( Direction internal )
{
this.internal = internal;
}
}
public RelationshipRepresentation createRelationship( long startNodeId,
long endNodeId, String type, Map properties )
throws StartNodeNotFoundException, EndNodeNotFoundException,
PropertyValueException
{
Node start, end;
try
{
start = node( startNodeId );
}
catch ( NodeNotFoundException e )
{
throw new StartNodeNotFoundException( e );
}
try
{
end = node( endNodeId );
}
catch ( NodeNotFoundException e )
{
throw new EndNodeNotFoundException( e );
}
Relationship rel = start.createRelationshipTo( end,
RelationshipType.withName( type ) );
propertySetter.setProperties( rel, properties );
return new RelationshipRepresentation( rel );
}
public RelationshipRepresentation getRelationship( long relationshipId )
throws RelationshipNotFoundException
{
return new RelationshipRepresentation( relationship( relationshipId ) );
}
public void deleteRelationship( long relationshipId ) throws RelationshipNotFoundException
{
relationship( relationshipId ).delete();
}
@SuppressWarnings( "unchecked" )
public ListRepresentation getNodeRelationships( long nodeId,
RelationshipDirection direction, Collection types )
throws NodeNotFoundException
{
Node node = node( nodeId );
PathExpander expander;
if ( types.isEmpty() )
{
expander = PathExpanders.forDirection( direction.internal );
}
else
{
PathExpanderBuilder builder = PathExpanderBuilder.empty();
for ( String type : types )
{
builder = builder.add(
RelationshipType.withName( type ),
direction.internal );
}
expander = builder.build();
}
return RelationshipRepresentation.list( expander.expand( Paths.singleNodePath( node ), BranchState.NO_STATE ) );
}
// Node degrees
@SuppressWarnings( "unchecked" )
public Representation getNodeDegree( long nodeId, RelationshipDirection direction, Collection types )
throws NodeNotFoundException
{
Node node = node( nodeId );
if ( types.isEmpty() )
{
return PropertiesRepresentation.value( node.getDegree( direction.internal ) );
}
else
{
int sum = 0;
for ( String type : types )
{
sum += node.getDegree( RelationshipType.withName( type ), direction.internal );
}
return PropertiesRepresentation.value( sum );
}
}
// Relationship properties
public PropertiesRepresentation getAllRelationshipProperties(
long relationshipId ) throws RelationshipNotFoundException
{
return new PropertiesRepresentation( relationship( relationshipId ) );
}
public Representation getRelationshipProperty( long relationshipId,
String key ) throws NoSuchPropertyException,
RelationshipNotFoundException
{
Relationship relationship = relationship( relationshipId );
try
{
return PropertiesRepresentation.value( relationship.getProperty( key ) );
}
catch ( NotFoundException e )
{
throw new NoSuchPropertyException( relationship, key );
}
}
public void setAllRelationshipProperties( long relationshipId,
Map properties ) throws PropertyValueException,
RelationshipNotFoundException
{
propertySetter.setAllProperties( relationship( relationshipId ), properties );
}
public void setRelationshipProperty( long relationshipId, String key,
Object value ) throws PropertyValueException,
RelationshipNotFoundException
{
Relationship relationship = relationship( relationshipId );
propertySetter.setProperty( relationship, key, value );
}
public void removeAllRelationshipProperties( long relationshipId )
throws RelationshipNotFoundException, PropertyValueException
{
propertySetter.setAllProperties( relationship( relationshipId ), null );
}
public void removeRelationshipProperty( long relationshipId, String key )
throws RelationshipNotFoundException, NoSuchPropertyException
{
Relationship relationship = relationship( relationshipId );
if ( relationship.removeProperty( key ) == null )
{
throw new NoSuchPropertyException( relationship, key );
}
}
public Representation nodeIndexRoot()
{
return new NodeIndexRootRepresentation( graphDb.index() );
}
public Representation relationshipIndexRoot()
{
return new RelationshipIndexRootRepresentation( graphDb.index() );
}
public IndexedEntityRepresentation addToRelationshipIndex( String indexName, String key, String value,
long relationshipId )
{
Relationship relationship = graphDb.getRelationshipById( relationshipId );
Index index = graphDb.index().forRelationships( indexName );
index.add( relationship, key, value );
return new IndexedEntityRepresentation( relationship, key, value,
new RelationshipIndexRepresentation( indexName,
Collections.emptyMap() ) );
}
public IndexedEntityRepresentation addToNodeIndex( String indexName, String key, String value, long nodeId )
{
Node node = graphDb.getNodeById( nodeId );
Index index = graphDb.index().forNodes( indexName );
index.add( node, key, value );
return new IndexedEntityRepresentation( node, key, value,
new NodeIndexRepresentation( indexName,
Collections.emptyMap() ) );
}
public void removeFromNodeIndex( String indexName, String key, String value, long id )
{
graphDb.index().forNodes( indexName ).remove( graphDb.getNodeById( id ), key, value );
}
public void removeFromNodeIndexNoValue( String indexName, String key, long id )
{
graphDb.index().forNodes( indexName ).remove( graphDb.getNodeById( id ), key );
}
public void removeFromNodeIndexNoKeyValue( String indexName, long id )
{
graphDb.index().forNodes( indexName ).remove( graphDb.getNodeById( id ) );
}
public void removeFromRelationshipIndex( String indexName, String key, String value, long id )
{
graphDb.index().forRelationships( indexName ).remove( graphDb.getRelationshipById( id ), key, value );
}
public void removeFromRelationshipIndexNoValue( String indexName, String key, long id )
{
graphDb.index().forRelationships( indexName ).remove( graphDb.getRelationshipById( id ), key );
}
public void removeFromRelationshipIndexNoKeyValue( String indexName, long id )
{
graphDb.index().forRelationships( indexName ).remove( graphDb.getRelationshipById( id ) );
}
public IndexedEntityRepresentation getIndexedNode( String indexName,
String key, String value, long id )
{
if ( !nodeIsIndexed( indexName, key, value, id ) )
{
throw new NotFoundException();
}
Node node = graphDb.getNodeById( id );
return new IndexedEntityRepresentation( node, key, value,
new NodeIndexRepresentation( indexName,
Collections.emptyMap() ) );
}
public IndexedEntityRepresentation getIndexedRelationship(
String indexName, String key, String value, long id )
{
if ( !relationshipIsIndexed( indexName, key, value, id ) )
{
throw new NotFoundException();
}
Relationship node = graphDb.getRelationshipById( id );
return new IndexedEntityRepresentation( node, key, value,
new RelationshipIndexRepresentation( indexName,
Collections.emptyMap() ) );
}
public ListRepresentation getIndexedNodes( String indexName, final String key,
final String value )
{
if ( !graphDb.index().existsForNodes( indexName ) )
{
throw new NotFoundException();
}
Index index = graphDb.index().forNodes( indexName );
final IndexRepresentation indexRepresentation = new NodeIndexRepresentation( indexName );
final IndexHits indexHits = index.get( key, value );
final IterableWrapper results = new IterableWrapper( indexHits )
{
@Override
protected Representation underlyingObjectToObject( Node node )
{
return new IndexedEntityRepresentation( node, key, value, indexRepresentation );
}
};
return new ListRepresentation( RepresentationType.NODE, results );
}
public ListRepresentation getIndexedNodesByQuery( String indexName,
String query, String sort )
{
return getIndexedNodesByQuery( indexName, null, query, sort );
}
public ListRepresentation getIndexedNodesByQuery( String indexName,
String key, String query, String sort )
{
if ( !graphDb.index().existsForNodes( indexName ) )
{
throw new NotFoundException();
}
if ( query == null )
{
return toListNodeRepresentation();
}
Index index = graphDb.index().forNodes( indexName );
IndexResultOrder order = getOrdering( sort );
QueryContext queryCtx = order.updateQueryContext( new QueryContext( query ) );
IndexHits result = index.query( key, queryCtx );
return toListNodeRepresentation( result, order );
}
private ListRepresentation toListNodeRepresentation()
{
return new ListRepresentation( RepresentationType.NODE, Collections.emptyList() );
}
private ListRepresentation toListNodeRepresentation( final IndexHits result, final IndexResultOrder order )
{
if ( result == null )
{
return new ListRepresentation( RepresentationType.NODE, Collections.emptyList() );
}
final IterableWrapper results = new IterableWrapper( result )
{
@Override
protected Representation underlyingObjectToObject( Node node )
{
final NodeRepresentation nodeRepresentation = new NodeRepresentation( node );
if ( order == null )
{
return nodeRepresentation;
}
return order.getRepresentationFor( nodeRepresentation, result.currentScore() );
}
};
return new ListRepresentation( RepresentationType.NODE, results );
}
private ListRepresentation toListRelationshipRepresentation()
{
return new ListRepresentation( RepresentationType.RELATIONSHIP, Collections.emptyList() );
}
private ListRepresentation toListRelationshipRepresentation( final IndexHits result,
final IndexResultOrder order )
{
if ( result == null )
{
return new ListRepresentation( RepresentationType.RELATIONSHIP, Collections.emptyList() );
}
final IterableWrapper results = new IterableWrapper( result )
{
@Override
protected Representation underlyingObjectToObject( Relationship rel )
{
final RelationshipRepresentation relationshipRepresentation = new RelationshipRepresentation( rel );
if ( order != null )
{
return order.getRepresentationFor( relationshipRepresentation, result.currentScore() );
}
return relationshipRepresentation;
}
};
return new ListRepresentation( RepresentationType.RELATIONSHIP, results );
}
public Pair getOrCreateIndexedNode(
String indexName, String key, String value, Long nodeOrNull, Map properties )
throws BadInputException, NodeNotFoundException
{
assertIsLegalIndexName( indexName );
Node result;
boolean created;
if ( nodeOrNull != null )
{
if ( properties != null )
{
throw new InvalidArgumentsException( "Cannot specify properties for a new node, " +
"when a node to index is specified." );
}
Node node = node( nodeOrNull );
result = graphDb.index().forNodes( indexName ).putIfAbsent( node, key, value );
created = result == null;
if ( created )
{
UniqueNodeFactory factory = new UniqueNodeFactory( indexName, properties );
UniqueEntity entity = factory.getOrCreateWithOutcome( key, value );
// when given a node id, return as created if that node was newly added to the index
created = entity.entity().getId() == node.getId() || entity.wasCreated();
result = entity.entity();
}
}
else
{
if ( properties != null )
{
for ( Map.Entry entry : properties.entrySet() )
{
entry.setValue( propertySetter.convert( entry.getValue() ) );
}
}
UniqueNodeFactory factory = new UniqueNodeFactory( indexName, properties );
UniqueEntity entity = factory.getOrCreateWithOutcome( key, value );
result = entity.entity();
created = entity.wasCreated();
}
return Pair.of( new IndexedEntityRepresentation( result, key, value,
new NodeIndexRepresentation( indexName, Collections.emptyMap() ) ), created );
}
public Pair getOrCreateIndexedRelationship(
String indexName, String key, String value,
Long relationshipOrNull, Long startNode, String type, Long endNode,
Map properties )
throws BadInputException, RelationshipNotFoundException, NodeNotFoundException
{
assertIsLegalIndexName( indexName );
Relationship result;
boolean created;
if ( relationshipOrNull != null )
{
if ( startNode != null || type != null || endNode != null || properties != null )
{
throw new InvalidArgumentsException( "Either specify a relationship to index uniquely, " +
"or the means for creating it." );
}
Relationship relationship = relationship( relationshipOrNull );
result = graphDb.index().forRelationships( indexName ).putIfAbsent( relationship, key, value );
if ( created = result == null )
{
UniqueRelationshipFactory factory =
new UniqueRelationshipFactory( indexName, relationship.getStartNode(),
relationship.getEndNode(), relationship.getType().name(), properties );
UniqueEntity entity = factory.getOrCreateWithOutcome( key, value );
// when given a relationship id, return as created if that relationship was newly added to the index
created = entity.entity().getId() == relationship.getId() || entity.wasCreated();
result = entity.entity();
}
}
else if ( startNode == null || type == null || endNode == null )
{
throw new InvalidArgumentsException( "Either specify a relationship to index uniquely, " +
"or the means for creating it." );
}
else
{
UniqueRelationshipFactory factory =
new UniqueRelationshipFactory( indexName, node( startNode ), node( endNode ), type, properties );
UniqueEntity entity = factory.getOrCreateWithOutcome( key, value );
result = entity.entity();
created = entity.wasCreated();
}
return Pair.of( new IndexedEntityRepresentation( result, key, value,
new RelationshipIndexRepresentation( indexName, Collections.emptyMap() ) ),
created );
}
private class UniqueRelationshipFactory extends UniqueFactory.UniqueRelationshipFactory
{
private final Node start, end;
private final RelationshipType type;
private final Map properties;
UniqueRelationshipFactory( String index, Node start, Node end, String type, Map properties )
{
super( graphDb, index );
this.start = start;
this.end = end;
this.type = RelationshipType.withName( type );
this.properties = properties;
}
@Override
protected Relationship create( Map ignored )
{
return start.createRelationshipTo( end, type );
}
@Override
protected void initialize( Relationship relationship, Map indexed )
{
for ( Map.Entry property : (properties == null ? indexed : properties).entrySet() )
{
relationship.setProperty( property.getKey(), property.getValue() );
}
}
}
private class UniqueNodeFactory extends UniqueFactory.UniqueNodeFactory
{
private final Map properties;
UniqueNodeFactory( String index, Map properties )
{
super( graphDb, index );
this.properties = properties;
}
@Override
protected void initialize( Node node, Map indexed )
{
for ( Map.Entry property : (properties == null ? indexed : properties).entrySet() )
{
node.setProperty( property.getKey(), property.getValue() );
}
}
}
public Representation getAutoIndexedNodes( String key, String value )
{
ReadableIndex index = graphDb.index().getNodeAutoIndexer().getAutoIndex();
return toListNodeRepresentation( index.get( key, value ), null );
}
public ListRepresentation getAutoIndexedNodesByQuery( String query )
{
if ( query != null )
{
ReadableIndex index = graphDb.index().getNodeAutoIndexer().getAutoIndex();
return toListNodeRepresentation( index.query( query ), null );
}
return toListNodeRepresentation();
}
public ListRepresentation getIndexedRelationships( String indexName,
final String key, final String value )
{
if ( !graphDb.index().existsForRelationships( indexName ) )
{
throw new NotFoundException();
}
Index index = graphDb.index().forRelationships( indexName );
final IndexRepresentation indexRepresentation = new RelationshipIndexRepresentation( indexName );
IterableWrapper result =
new IterableWrapper( index.get( key, value ) )
{
@Override
protected Representation underlyingObjectToObject( Relationship relationship )
{
return new IndexedEntityRepresentation( relationship,
key, value, indexRepresentation );
}
};
return new ListRepresentation( RepresentationType.RELATIONSHIP, result );
}
public ListRepresentation getIndexedRelationshipsByQuery( String indexName,
String query, String sort )
{
return getIndexedRelationshipsByQuery( indexName, null, query, sort );
}
public ListRepresentation getIndexedRelationshipsByQuery( String indexName,
String key, String query, String sort )
{
if ( !graphDb.index().existsForRelationships( indexName ) )
{
throw new NotFoundException();
}
if ( query == null )
{
return toListRelationshipRepresentation();
}
Index index = graphDb.index().forRelationships( indexName );
IndexResultOrder order = getOrdering( sort );
QueryContext queryCtx = order.updateQueryContext( new QueryContext(
query ) );
return toListRelationshipRepresentation( index.query( key, queryCtx ), order );
}
public Representation getAutoIndexedRelationships( String key, String value )
{
final ReadableIndex index = graphDb.index().getRelationshipAutoIndexer().getAutoIndex();
return toListRelationshipRepresentation( index.get( key, value ), null );
}
public ListRepresentation getAutoIndexedRelationshipsByQuery( String query )
{
final ReadableIndex index = graphDb.index().getRelationshipAutoIndexer().getAutoIndex();
final IndexHits results = query != null ? index.query( query ) : null;
return toListRelationshipRepresentation( results, null );
}
// Traversal
public ListRepresentation traverse( long startNode,
Map description, final TraverserReturnType returnType )
{
Node node = graphDb.getNodeById( startNode );
TraversalDescription traversalDescription = traversalDescriptionBuilder.from( description );
final Iterable paths = traversalDescription.traverse( node );
return toListPathRepresentation( paths, returnType );
}
private ListRepresentation toListPathRepresentation( final Iterable paths,
final TraverserReturnType returnType )
{
final IterableWrapper result = new IterableWrapper( paths )
{
@Override
protected Representation underlyingObjectToObject( Path position )
{
return returnType.toRepresentation( position );
}
};
return new ListRepresentation( returnType.repType, result );
}
public ListRepresentation pagedTraverse( String traverserId,
TraverserReturnType returnType )
{
Lease lease = leases.getLeaseById( traverserId );
if ( lease == null )
{
throw new NotFoundException( String.format(
"The traverser with id [%s] was not found", traverserId ) );
}
PagedTraverser traverser = lease.getLeasedItemAndRenewLease();
List paths = traverser.next();
if ( paths != null )
{
return toListPathRepresentation( paths, returnType );
}
else
{
leases.remove( traverserId );
// Yuck.
throw new NotFoundException(
String.format(
"The results for paged traverser with id [%s] have been fully enumerated",
traverserId ) );
}
}
public String createPagedTraverser( long nodeId,
Map description, int pageSize, int leaseTime )
{
Node node = graphDb.getNodeById( nodeId );
TraversalDescription traversalDescription = traversalDescriptionBuilder.from( description );
PagedTraverser traverser = new PagedTraverser(
traversalDescription.traverse( node ), pageSize );
return leases.createLease( leaseTime, traverser ).getId();
}
public boolean removePagedTraverse( String traverserId )
{
Lease lease = leases.getLeaseById( traverserId );
if ( lease == null )
{
return false;
}
else
{
leases.remove( lease.getId() );
return true;
}
}
// Graph algos
@SuppressWarnings( "rawtypes" )
public PathRepresentation findSinglePath( long startId, long endId,
Map map )
{
FindParams findParams = new FindParams( startId, endId, map ).invoke();
PathFinder finder = findParams.getFinder();
Node startNode = findParams.getStartNode();
Node endNode = findParams.getEndNode();
Path path = finder.findSinglePath( startNode, endNode );
if ( path == null )
{
throw new NotFoundException();
}
return findParams.pathRepresentationOf( path );
}
@SuppressWarnings( {"rawtypes", "unchecked"} )
public ListRepresentation findPaths( long startId, long endId,
Map map )
{
final FindParams findParams = new FindParams( startId, endId, map ).invoke();
PathFinder finder = findParams.getFinder();
Node startNode = findParams.getStartNode();
Node endNode = findParams.getEndNode();
Iterable paths = finder.findAllPaths( startNode, endNode );
IterableWrapper pathRepresentations = new IterableWrapper(
paths )
{
@Override
protected PathRepresentation underlyingObjectToObject( Path path )
{
return findParams.pathRepresentationOf( path );
}
};
return new ListRepresentation( RepresentationType.PATH,
pathRepresentations );
}
private class FindParams
{
private final long startId;
private final long endId;
private final Map map;
private Node startNode;
private Node endNode;
private PathFinder extends Path> finder;
@SuppressWarnings( "rawtypes" )
private PathRepresentationCreator representationCreator = PATH_REPRESENTATION_CREATOR;
public FindParams( final long startId, final long endId,
final Map map )
{
this.startId = startId;
this.endId = endId;
this.map = map;
}
public Node getStartNode()
{
return startNode;
}
public Node getEndNode()
{
return endNode;
}
public PathFinder extends Path> getFinder()
{
return finder;
}
@SuppressWarnings( "unchecked" )
public PathRepresentation extends Path> pathRepresentationOf(
Path path )
{
return representationCreator.from( path );
}
public FindParams invoke()
{
startNode = graphDb.getNodeById( startId );
endNode = graphDb.getNodeById( endId );
Integer maxDepthObj = (Integer) map.get( "max_depth" );
int maxDepth = (maxDepthObj != null) ? maxDepthObj : 1;
PathExpander expander = RelationshipExpanderBuilder.describeRelationships( map );
String algorithm = (String) map.get( "algorithm" );
algorithm = (algorithm != null) ? algorithm : "shortestPath";
finder = getAlgorithm( algorithm, expander, maxDepth );
return this;
}
private PathFinder extends Path> getAlgorithm( String algorithm,
PathExpander expander, int maxDepth )
{
if ( algorithm.equals( "shortestPath" ) )
{
return GraphAlgoFactory.shortestPath( expander, maxDepth );
}
else if ( algorithm.equals( "allSimplePaths" ) )
{
return GraphAlgoFactory.allSimplePaths( expander, maxDepth );
}
else if ( algorithm.equals( "allPaths" ) )
{
return GraphAlgoFactory.allPaths( expander, maxDepth );
}
else if ( algorithm.equals( "dijkstra" ) )
{
String costProperty = (String) map.get( "cost_property" );
Number defaultCost = (Number) map.get( "default_cost" );
CostEvaluator costEvaluator = defaultCost == null ? CommonEvaluators.doubleCostEvaluator(
costProperty )
: CommonEvaluators
.doubleCostEvaluator( costProperty,
defaultCost.doubleValue() );
representationCreator = WEIGHTED_PATH_REPRESENTATION_CREATOR;
return GraphAlgoFactory.dijkstra( expander, costEvaluator );
}
throw new RuntimeException( "Failed to find matching algorithm" );
}
}
/*
* This enum binds the parameter-string-to-result-order mapping and
* the kind of results returned. This is not correct in general but
* at the time of writing it is the way things are done and is
* quite handy. Feel free to rip out if requirements change.
*/
private enum IndexResultOrder
{
INDEX_ORDER
{
@Override
QueryContext updateQueryContext( QueryContext original )
{
return original.sort( Sort.INDEXORDER );
}
},
RELEVANCE_ORDER
{
@Override
QueryContext updateQueryContext( QueryContext original )
{
return original.sort( Sort.RELEVANCE );
}
},
SCORE_ORDER
{
@Override
QueryContext updateQueryContext( QueryContext original )
{
return original.sortByScore();
}
},
NONE
{
@Override
Representation getRepresentationFor( Representation delegate,
float score )
{
return delegate;
}
@Override
QueryContext updateQueryContext( QueryContext original )
{
return original;
}
};
Representation getRepresentationFor( Representation delegate,
float score )
{
if ( delegate instanceof NodeRepresentation )
{
return new ScoredNodeRepresentation(
(NodeRepresentation) delegate, score );
}
if ( delegate instanceof RelationshipRepresentation )
{
return new ScoredRelationshipRepresentation(
(RelationshipRepresentation) delegate, score );
}
return delegate;
}
abstract QueryContext updateQueryContext( QueryContext original );
}
private final IndexResultOrder getOrdering( String order )
{
if ( INDEX_ORDER.equalsIgnoreCase( order ) )
{
return IndexResultOrder.INDEX_ORDER;
}
else if ( RELEVANCE_ORDER.equalsIgnoreCase( order ) )
{
return IndexResultOrder.RELEVANCE_ORDER;
}
else if ( SCORE_ORDER.equalsIgnoreCase( order ) )
{
return IndexResultOrder.SCORE_ORDER;
}
else
{
return IndexResultOrder.NONE;
}
}
private interface PathRepresentationCreator
{
PathRepresentation from( T path );
}
private static final PathRepresentationCreator PATH_REPRESENTATION_CREATOR =
path -> new PathRepresentation<>( path );
private static final PathRepresentationCreator WEIGHTED_PATH_REPRESENTATION_CREATOR =
path -> new WeightedPathRepresentation( path );
private void assertIsLegalIndexName( String indexName )
{
if ( indexName == null || indexName.equals( "" ) )
{
throw new IllegalArgumentException( "Index name must not be empty." );
}
}
public ListRepresentation getNodesWithLabel( String labelName, Map properties )
{
Iterator nodes;
if ( properties.size() == 0 )
{
nodes = graphDb.findNodes( label( labelName ) );
}
else if ( properties.size() == 1 )
{
Map.Entry prop = Iterables.single( properties.entrySet() );
nodes = graphDb.findNodes( label( labelName ), prop.getKey(), prop.getValue() );
}
else
{
throw new IllegalArgumentException( "Too many properties specified. Either specify one property to " +
"filter by, or none at all." );
}
IterableWrapper nodeRepresentations =
new IterableWrapper( asList( nodes ) )
{
@Override
protected NodeRepresentation underlyingObjectToObject( Node node )
{
return new NodeRepresentation( node );
}
};
return new ListRepresentation( RepresentationType.NODE, nodeRepresentations );
}
public ListRepresentation getAllLabels( boolean inUse )
{
ResourceIterable
© 2015 - 2025 Weber Informatics LLC | Privacy Policy