Merge branch 'graphdatabase'
This commit is contained in:
@@ -42,7 +42,6 @@
|
||||
<groupId>org.springframework.data</groupId>
|
||||
<artifactId>spring-data-neo4j</artifactId>
|
||||
<version>1.0.0.BUILD-SNAPSHOT</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.hibernate</groupId>
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
package org.neo4j.kernel;
|
||||
|
||||
import org.neo4j.graphdb.GraphDatabaseService;
|
||||
import org.neo4j.kernel.Config;
|
||||
import org.neo4j.kernel.IdGeneratorFactory;
|
||||
import org.neo4j.kernel.impl.core.*;
|
||||
import org.neo4j.kernel.impl.nioneo.store.FileSystemAbstraction;
|
||||
import org.neo4j.kernel.impl.nioneo.store.StoreId;
|
||||
@@ -48,7 +46,7 @@ public class RestConfig extends Config {
|
||||
}
|
||||
|
||||
private static class NullTransactionManager implements TransactionManager {
|
||||
private static final Transaction TRANSACTION = new Transaction() {
|
||||
private static final Transaction NULL_JAVA_TRANSACTION = new Transaction() {
|
||||
@Override
|
||||
public void commit() throws HeuristicMixedException, HeuristicRollbackException, RollbackException, SecurityException, SystemException {
|
||||
|
||||
@@ -102,7 +100,7 @@ public class RestConfig extends Config {
|
||||
|
||||
@Override
|
||||
public Transaction getTransaction() throws SystemException {
|
||||
return TRANSACTION;
|
||||
return NULL_JAVA_TRANSACTION;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -127,7 +125,7 @@ public class RestConfig extends Config {
|
||||
|
||||
@Override
|
||||
public Transaction suspend() throws SystemException {
|
||||
return TRANSACTION;
|
||||
return NULL_JAVA_TRANSACTION;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,39 +0,0 @@
|
||||
package org.neo4j.rest.graphdb;
|
||||
|
||||
import org.neo4j.graphdb.GraphDatabaseService;
|
||||
import org.neo4j.kernel.EmbeddedGraphDatabase;
|
||||
|
||||
import java.io.File;
|
||||
import java.net.URI;
|
||||
import java.net.URISyntaxException;
|
||||
|
||||
/**
|
||||
* @author mh
|
||||
* @since 25.01.11
|
||||
*/
|
||||
public class GraphDatabaseFactory {
|
||||
public static GraphDatabaseService databaseFor(String url) {
|
||||
return databaseFor( url, null,null );
|
||||
}
|
||||
|
||||
public static GraphDatabaseService databaseFor(String url, String username, String password) {
|
||||
if (url.startsWith( "http://" ) || url.startsWith( "https://" )) {
|
||||
return new RestGraphDatabase( toURI( url ), username,password );
|
||||
}
|
||||
String path=url;
|
||||
if (url.startsWith( "file:" )) {
|
||||
path = toURI( url ).getPath();
|
||||
}
|
||||
File file = new File( path );
|
||||
if (!file.isDirectory()) file=file.getParentFile();
|
||||
return new EmbeddedGraphDatabase( file.getAbsolutePath() );
|
||||
}
|
||||
|
||||
private static URI toURI( String uri ) {
|
||||
try {
|
||||
return new URI(uri);
|
||||
} catch ( URISyntaxException e ) {
|
||||
throw new RuntimeException( "Error using URI "+uri, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
package org.neo4j.rest.graphdb;
|
||||
|
||||
import com.sun.jersey.api.client.ClientResponse;
|
||||
import org.neo4j.graphdb.GraphDatabaseService;
|
||||
import org.neo4j.graphdb.NotFoundException;
|
||||
import org.neo4j.graphdb.PropertyContainer;
|
||||
import org.neo4j.helpers.collection.IterableWrapper;
|
||||
@@ -154,7 +155,11 @@ public class RestEntity implements PropertyContainer {
|
||||
return getClass().equals( o.getClass() ) && getId() == ( (RestEntity) o ).getId();
|
||||
}
|
||||
|
||||
public RestGraphDatabase getGraphDatabase() {
|
||||
public GraphDatabaseService getGraphDatabase() {
|
||||
throw new UnsupportedOperationException("No GraphDatabaseService semantics for the REST-API");
|
||||
}
|
||||
|
||||
public RestGraphDatabase getRestGraphDatabase() {
|
||||
return graphDatabase;
|
||||
}
|
||||
|
||||
|
||||
@@ -4,18 +4,22 @@ import com.sun.jersey.api.client.ClientResponse;
|
||||
import org.neo4j.graphdb.*;
|
||||
import org.neo4j.graphdb.event.KernelEventHandler;
|
||||
import org.neo4j.graphdb.event.TransactionEventHandler;
|
||||
import org.neo4j.graphdb.index.IndexManager;
|
||||
import org.neo4j.graphdb.index.Index;
|
||||
import org.neo4j.graphdb.traversal.TraversalDescription;
|
||||
import org.neo4j.kernel.AbstractGraphDatabase;
|
||||
import org.neo4j.kernel.Config;
|
||||
import org.neo4j.kernel.RestConfig;
|
||||
import org.neo4j.rest.graphdb.index.RestIndexManager;
|
||||
import org.springframework.data.graph.core.GraphDatabase;
|
||||
import org.springframework.data.graph.core.Property;
|
||||
|
||||
import javax.ws.rs.core.Response.Status;
|
||||
import java.io.Serializable;
|
||||
import java.net.URI;
|
||||
import java.util.Map;
|
||||
|
||||
public class RestGraphDatabase extends AbstractGraphDatabase {
|
||||
public class RestGraphDatabase extends AbstractGraphDatabase implements GraphDatabase {
|
||||
|
||||
private RestRequest restRequest;
|
||||
private long propertyRefetchTimeInMillis = 1000;
|
||||
|
||||
@@ -28,86 +32,62 @@ public class RestGraphDatabase extends AbstractGraphDatabase {
|
||||
restRequest = new RestRequest( uri, user, password );
|
||||
}
|
||||
|
||||
public Transaction beginTx() {
|
||||
return new Transaction() {
|
||||
public void success() {
|
||||
}
|
||||
|
||||
public void finish() {
|
||||
|
||||
}
|
||||
|
||||
public void failure() {
|
||||
}
|
||||
};
|
||||
@Override
|
||||
public Node getNodeById(long id) {
|
||||
ClientResponse response = restRequest.get("node/" + id);
|
||||
if ( restRequest.statusIs(response, Status.NOT_FOUND) ) {
|
||||
throw new NotFoundException( "" + id );
|
||||
}
|
||||
return new RestNode( restRequest.toMap(response), this );
|
||||
}
|
||||
|
||||
public <T> TransactionEventHandler<T> registerTransactionEventHandler( TransactionEventHandler<T> tTransactionEventHandler ) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
public <T> TransactionEventHandler<T> unregisterTransactionEventHandler( TransactionEventHandler<T> tTransactionEventHandler ) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
public KernelEventHandler registerKernelEventHandler( KernelEventHandler kernelEventHandler ) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
public KernelEventHandler unregisterKernelEventHandler( KernelEventHandler kernelEventHandler ) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
public IndexManager index() {
|
||||
return new RestIndexManager( restRequest, this );
|
||||
}
|
||||
|
||||
public Node createNode() {
|
||||
ClientResponse response = restRequest.post( "node", null );
|
||||
if ( restRequest.statusOtherThan( response, Status.CREATED ) ) {
|
||||
@Override
|
||||
public Node createNode(Property... props) {
|
||||
ClientResponse response = restRequest.post("node", null);
|
||||
if ( restRequest.statusOtherThan(response, Status.CREATED) ) {
|
||||
throw new RuntimeException( "" + response.getStatus() );
|
||||
}
|
||||
return new RestNode( response.getLocation(), this );
|
||||
}
|
||||
|
||||
public boolean enableRemoteShell() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
public boolean enableRemoteShell( Map<String, Serializable> config ) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
public Iterable<Node> getAllNodes() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
public Node getNodeById( long id ) {
|
||||
ClientResponse response = restRequest.get( "node/" + id );
|
||||
if ( restRequest.statusIs( response, Status.NOT_FOUND ) ) {
|
||||
throw new NotFoundException( "" + id );
|
||||
}
|
||||
return new RestNode( restRequest.toMap( response ), this );
|
||||
}
|
||||
|
||||
public Node getReferenceNode() {
|
||||
Map<?, ?> map = restRequest.toMap( restRequest.get( "" ) );
|
||||
return new RestNode( (String) map.get( "reference_node" ), this );
|
||||
}
|
||||
|
||||
public Relationship getRelationshipById( long id ) {
|
||||
ClientResponse response = restRequest.get( "relationship/" + id );
|
||||
@Override
|
||||
public Relationship getRelationshipById(long id) {
|
||||
ClientResponse response = restRequest.get("relationship/" + id);
|
||||
if ( restRequest.statusIs( response, Status.NOT_FOUND ) ) {
|
||||
throw new NotFoundException( "" + id );
|
||||
}
|
||||
return new RestRelationship( restRequest.toMap( response ), this );
|
||||
}
|
||||
|
||||
public Iterable<RelationshipType> getRelationshipTypes() {
|
||||
throw new UnsupportedOperationException();
|
||||
@Override
|
||||
public Relationship createRelationship(Node startNode, Node endNode, RelationshipType type, Property... props) {
|
||||
Relationship relationship = startNode.createRelationshipTo(endNode, type);
|
||||
return relationship;
|
||||
}
|
||||
|
||||
public void shutdown() {
|
||||
@Override
|
||||
public <T extends PropertyContainer> Index<T> getIndex(String indexName) {
|
||||
return (Index<T>) index().forNodes(indexName); // todo
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T extends PropertyContainer> Index<T> createIndex(Class<T> type, String indexName, boolean fullText) {
|
||||
return (Index<T>) index().forNodes(indexName); // todo
|
||||
}
|
||||
|
||||
@Override
|
||||
public TraversalDescription createTraversalDescription() {
|
||||
return new RestTraversal();
|
||||
}
|
||||
|
||||
public RestIndexManager index() {
|
||||
return new RestIndexManager( restRequest, this );
|
||||
}
|
||||
|
||||
@Override
|
||||
public Node getReferenceNode() {
|
||||
Map<?, ?> map = restRequest.toMap( restRequest.get( "" ) );
|
||||
return new RestNode( (String) map.get( "reference_node" ), this );
|
||||
}
|
||||
|
||||
public RestRequest getRestRequest() {
|
||||
@@ -117,9 +97,77 @@ public class RestGraphDatabase extends AbstractGraphDatabase {
|
||||
public long getPropertyRefetchTimeInMillis() {
|
||||
return propertyRefetchTimeInMillis;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Node createNode() {
|
||||
return createNode((Property[])null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterable<Node> getAllNodes() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterable<RelationshipType> getRelationshipTypes() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void shutdown() {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean enableRemoteShell() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean enableRemoteShell(Map<String, Serializable> initialProperties) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Transaction beginTx() {
|
||||
return new Transaction() {
|
||||
@Override
|
||||
public void failure() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void success() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void finish() {
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> TransactionEventHandler<T> registerTransactionEventHandler(TransactionEventHandler<T> handler) {
|
||||
return handler;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> TransactionEventHandler<T> unregisterTransactionEventHandler(TransactionEventHandler<T> handler) {
|
||||
return handler;
|
||||
}
|
||||
|
||||
@Override
|
||||
public KernelEventHandler registerKernelEventHandler(KernelEventHandler handler) {
|
||||
return handler;
|
||||
}
|
||||
|
||||
@Override
|
||||
public KernelEventHandler unregisterKernelEventHandler(KernelEventHandler handler) {
|
||||
return handler;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getStoreDir() {
|
||||
return restRequest.getUri().toString();
|
||||
return getRestRequest().getUri().toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -35,7 +35,7 @@ public class RestNode extends RestEntity implements Node {
|
||||
if ( restRequest.statusOtherThan( response, Status.CREATED ) ) {
|
||||
throw new RuntimeException( "" + response.getStatus() );
|
||||
}
|
||||
return new RestRelationship( response.getLocation(), getGraphDatabase() );
|
||||
return new RestRelationship( response.getLocation(), getRestGraphDatabase() );
|
||||
}
|
||||
|
||||
public Iterable<Relationship> getRelationships() {
|
||||
@@ -48,7 +48,7 @@ public class RestNode extends RestEntity implements Node {
|
||||
(Collection<Object>) restRequest.toEntity( response ) ) {
|
||||
@Override
|
||||
protected Relationship underlyingObjectToObject( Object data ) {
|
||||
return new RestRelationship( (Map<?, ?>) data, getGraphDatabase() );
|
||||
return new RestRelationship( (Map<?, ?>) data, getRestGraphDatabase() );
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -44,7 +44,7 @@ public class RestRelationship extends RestEntity implements Relationship {
|
||||
}
|
||||
|
||||
private RestNode node( String uri ) {
|
||||
return new RestNode( uri, getGraphDatabase() );
|
||||
return new RestNode( uri, getRestGraphDatabase() );
|
||||
}
|
||||
|
||||
public Node getStartNode() {
|
||||
|
||||
@@ -207,7 +207,7 @@ public class RestTraversal implements RestTraversalDescription
|
||||
{
|
||||
throw new RuntimeException( String.format( "Unexpected traversal result, %s instead of collection", col != null ? col.getClass() : null ) );
|
||||
}
|
||||
return new RestTraverser( (Collection)col, restNode.getGraphDatabase() );
|
||||
return new RestTraverser( (Collection)col, restNode.getRestGraphDatabase() );
|
||||
}
|
||||
|
||||
public static RestTraversalDescription description()
|
||||
|
||||
@@ -1,23 +1,13 @@
|
||||
package org.neo4j.rest.graphdb;
|
||||
|
||||
|
||||
import com.sun.jersey.api.client.Client;
|
||||
import com.sun.jersey.api.client.ClientResponse;
|
||||
import org.apache.log4j.BasicConfigurator;
|
||||
import org.junit.After;
|
||||
import org.junit.AfterClass;
|
||||
import org.junit.Before;
|
||||
import org.junit.BeforeClass;
|
||||
import org.neo4j.graphdb.Direction;
|
||||
import org.neo4j.graphdb.GraphDatabaseService;
|
||||
import org.neo4j.graphdb.Node;
|
||||
import org.neo4j.graphdb.Relationship;
|
||||
import org.neo4j.server.AddressResolver;
|
||||
import org.neo4j.server.NeoServerWithEmbeddedWebServer;
|
||||
import org.neo4j.server.modules.RESTApiModule;
|
||||
import org.neo4j.server.modules.ThirdPartyJAXRSModule;
|
||||
import org.neo4j.server.startup.healthcheck.StartupHealthCheck;
|
||||
import org.neo4j.server.web.Jetty6WebServer;
|
||||
|
||||
import java.net.URI;
|
||||
import java.net.URISyntaxException;
|
||||
@@ -25,7 +15,7 @@ import java.util.Iterator;
|
||||
|
||||
public class RestTestBase {
|
||||
|
||||
protected GraphDatabaseService graphDb;
|
||||
protected RestGraphDatabase graphDb;
|
||||
private static final String HOSTNAME = "localhost";
|
||||
private static final int PORT = 7473;
|
||||
private static LocalTestServer neoServer = new LocalTestServer(HOSTNAME,PORT).withPropertiesFile("test-db.properties");
|
||||
@@ -47,11 +37,6 @@ public class RestTestBase {
|
||||
neoServer.cleanDb();
|
||||
}
|
||||
|
||||
@After
|
||||
public void tearDown() throws Exception {
|
||||
graphDb.shutdown();
|
||||
}
|
||||
|
||||
@AfterClass
|
||||
public static void shutdownDb() {
|
||||
neoServer.stop();
|
||||
@@ -61,7 +46,7 @@ public class RestTestBase {
|
||||
protected Relationship relationship() {
|
||||
Iterator<Relationship> it = node().getRelationships(Direction.OUTGOING).iterator();
|
||||
if (it.hasNext()) return it.next();
|
||||
return node().createRelationshipTo(graphDb.createNode(), Type.TEST);
|
||||
return node().createRelationshipTo(graphDb.createNode(null), Type.TEST);
|
||||
}
|
||||
|
||||
protected Node node() {
|
||||
|
||||
@@ -0,0 +1,82 @@
|
||||
package org.springframework.data.graph.core;
|
||||
|
||||
import org.neo4j.graphdb.Node;
|
||||
import org.neo4j.graphdb.PropertyContainer;
|
||||
import org.neo4j.graphdb.Relationship;
|
||||
import org.neo4j.graphdb.RelationshipType;
|
||||
import org.neo4j.graphdb.index.Index;
|
||||
import org.neo4j.graphdb.traversal.TraversalDescription;
|
||||
|
||||
/**
|
||||
* @author mh
|
||||
* @since 29.03.11
|
||||
*/
|
||||
public interface GraphDatabase {
|
||||
/**
|
||||
* @return the reference node of the underlying graph database
|
||||
*/
|
||||
Node getReferenceNode();
|
||||
|
||||
/**
|
||||
* @param id node id
|
||||
* @return the requested node of the underlying graph database
|
||||
* @throws org.neo4j.graphdb.NotFoundException
|
||||
*/
|
||||
Node getNodeById(long id);
|
||||
|
||||
/**
|
||||
* Transactionally creates the node, sets the properties (if any).
|
||||
* Two shortcut means of providing the properties (very short with static imports)
|
||||
* <code>graphDatabase.createNode(PropertyMap._("name","value"));</code>
|
||||
* <code>graphDatabase.createNode(PropertyMap.props().set("name","value").set("prop","anotherValue").toMap(), "name", "prop");</code>
|
||||
*
|
||||
* @param props properties to be set at node creation might be null
|
||||
* @return the newly created node
|
||||
*/
|
||||
Node createNode(Property... props);
|
||||
|
||||
/**
|
||||
* @param id relationship id
|
||||
* @return the requested relationship of the underlying graph database
|
||||
* @throws org.neo4j.graphdb.NotFoundException
|
||||
*/
|
||||
Relationship getRelationshipById(long id);
|
||||
|
||||
/**
|
||||
* Transactionally creates the relationship, sets the properties (if any) and indexes the given fielss (if any)
|
||||
* Two shortcut means of providing the properties (very short with static imports)
|
||||
* <code>graphDatabase.createRelationship(from,to,TYPE, PropertyMap._("name","value"));</code>
|
||||
* <code>graphDatabase.createRelationship(from,to,TYPE, PropertyMap.props().set("name","value").set("prop","anotherValue").toMap(), "name", "prop");</code>
|
||||
*
|
||||
*
|
||||
* @param startNode start-node of relationship
|
||||
* @param endNode end-node of relationship
|
||||
* @param type relationship type, might by an enum implementing RelationshipType or a DynamicRelationshipType.withName("name")
|
||||
* @param props optional initial properties
|
||||
* @return the newly created relationship
|
||||
*/
|
||||
Relationship createRelationship(Node startNode, Node endNode, RelationshipType type, Property... props);
|
||||
|
||||
/**
|
||||
* @param indexName existing index name, not null
|
||||
* @return existing index {@link Index}
|
||||
* @throws IllegalArgumentException if the index doesn't exist
|
||||
*/
|
||||
<T extends PropertyContainer> Index<T> getIndex(String indexName);
|
||||
|
||||
/**
|
||||
* creates a index
|
||||
* @param type type of index requested - either Node.class or Relationship.class
|
||||
* @param indexName, not null
|
||||
* @param fullText true if a fulltext queryable index is needed, false for exact match
|
||||
* @return node index {@link Index}
|
||||
*/
|
||||
<T extends PropertyContainer> Index<T> createIndex(Class<T> type, String indexName, boolean fullText);
|
||||
|
||||
|
||||
/**
|
||||
* @return a TraversalDescription as starting point for defining a traversal
|
||||
*/
|
||||
TraversalDescription createTraversalDescription();
|
||||
|
||||
}
|
||||
@@ -0,0 +1,89 @@
|
||||
package org.springframework.data.graph.core;
|
||||
|
||||
import org.neo4j.graphdb.GraphDatabaseService;
|
||||
import org.neo4j.kernel.EmbeddedGraphDatabase;
|
||||
import org.springframework.beans.factory.FactoryBean;
|
||||
import org.springframework.data.graph.neo4j.support.DelegatingGraphDatabase;
|
||||
|
||||
import javax.annotation.PreDestroy;
|
||||
import java.io.File;
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.net.URI;
|
||||
|
||||
/**
|
||||
* @author mh
|
||||
* @since 25.01.11
|
||||
*/
|
||||
public class GraphDatabaseFactory implements FactoryBean<GraphDatabase> {
|
||||
|
||||
private String storeLocation;
|
||||
private String userName;
|
||||
private String password;
|
||||
protected GraphDatabase graphDatabase;
|
||||
|
||||
public String getStoreLocation() {
|
||||
return storeLocation;
|
||||
}
|
||||
|
||||
public void setStoreLocation(String storeLocation) {
|
||||
this.storeLocation = storeLocation;
|
||||
}
|
||||
|
||||
public String getUserName() {
|
||||
return userName;
|
||||
}
|
||||
|
||||
public void setUserName(String userName) {
|
||||
this.userName = userName;
|
||||
}
|
||||
|
||||
public String getPassword() {
|
||||
return password;
|
||||
}
|
||||
|
||||
public void setPassword(String password) {
|
||||
this.password = password;
|
||||
}
|
||||
|
||||
private GraphDatabase databaseFor(String url, String username, String password) throws Exception {
|
||||
if (url.startsWith( "http://" ) || url.startsWith( "https://" )) {
|
||||
return createRestGraphDatabase(url, username, password);
|
||||
}
|
||||
String path=url;
|
||||
if (url.startsWith( "file:" )) {
|
||||
path = new URI(url).getPath();
|
||||
}
|
||||
File file = new File( path );
|
||||
// if (!file.isDirectory()) file=file.getParentFile();
|
||||
return new DelegatingGraphDatabase(new EmbeddedGraphDatabase(file.getAbsolutePath()));
|
||||
}
|
||||
|
||||
private GraphDatabase createRestGraphDatabase(String url, String username, String password) throws Exception {
|
||||
Class<?> restGraphDatabaseClass = Class.forName("org.neo4j.rest.graphdb.RestGraphDatabase");
|
||||
Constructor<?> constructor = restGraphDatabaseClass.getConstructor(URI.class, String.class, String.class);
|
||||
return (GraphDatabase) constructor.newInstance(new URI(url), username,password );
|
||||
}
|
||||
|
||||
@Override
|
||||
public GraphDatabase getObject() throws Exception {
|
||||
if (graphDatabase==null) graphDatabase = databaseFor(storeLocation, userName, password);
|
||||
return graphDatabase;
|
||||
}
|
||||
|
||||
@PreDestroy
|
||||
public void shutdown() {
|
||||
if (graphDatabase instanceof DelegatingGraphDatabase) {
|
||||
((DelegatingGraphDatabase)graphDatabase).shutdown();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<?> getObjectType() {
|
||||
return GraphDatabaseService.class;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSingleton() {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
package org.springframework.data.graph.core;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* @author mh
|
||||
* @since 31.03.11
|
||||
*/
|
||||
public class Property {
|
||||
public final String name;
|
||||
public final Object value;
|
||||
|
||||
public Property(String name, Object value) {
|
||||
this.name = name;
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
public static Property _(String name, Object value) {
|
||||
return new Property(name, value);
|
||||
}
|
||||
|
||||
public static Property[] _(String name1, Object value1, String name2, Object value2) {
|
||||
return new Property[]{new Property(name1, value1), new Property(name2, value2)};
|
||||
}
|
||||
|
||||
public static Property[] _(String name1, Object value1, String name2, Object value2, String name3, Object value3) {
|
||||
return new Property[]{new Property(name1, value1), new Property(name2, value2), new Property(name3, value3)};
|
||||
}
|
||||
|
||||
public static Property[] _(Object... nameValuePairs) {
|
||||
if (nameValuePairs.length % 2 != 0)
|
||||
throw new IllegalArgumentException("there must be an even number of name value pairs");
|
||||
Property[] result = new Property[nameValuePairs.length / 2];
|
||||
for (int i = 0; i < result.length; i++) {
|
||||
result[i] = new Property(nameValuePairs[i * 2].toString(), nameValuePairs[i * 2 + 1]);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public static Property[] _(Map<String, Object> properties) {
|
||||
Property[] result = new Property[properties.size()];
|
||||
int i = 0;
|
||||
for (Map.Entry<String, Object> entry : properties.entrySet()) {
|
||||
result[i++] = new Property(entry.getKey(), entry.getValue());
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,115 @@
|
||||
package org.springframework.data.graph.neo4j.support;
|
||||
|
||||
import org.neo4j.graphdb.*;
|
||||
import org.neo4j.graphdb.index.Index;
|
||||
import org.neo4j.graphdb.index.IndexManager;
|
||||
import org.neo4j.graphdb.traversal.TraversalDescription;
|
||||
import org.neo4j.index.impl.lucene.LuceneIndexImplementation;
|
||||
import org.neo4j.kernel.AbstractGraphDatabase;
|
||||
import org.neo4j.kernel.Traversal;
|
||||
import org.springframework.data.graph.core.GraphDatabase;
|
||||
import org.springframework.data.graph.core.NodeBacked;
|
||||
import org.springframework.data.graph.core.Property;
|
||||
import org.springframework.data.graph.core.RelationshipBacked;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* @author mh
|
||||
* @since 29.03.11
|
||||
*/
|
||||
public class DelegatingGraphDatabase implements GraphDatabase {
|
||||
|
||||
protected AbstractGraphDatabase delegate;
|
||||
|
||||
public DelegatingGraphDatabase(final AbstractGraphDatabase delegate) {
|
||||
this.delegate = delegate;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Node getReferenceNode() {
|
||||
return delegate.getReferenceNode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Node getNodeById(long id) {
|
||||
return delegate.getNodeById(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Node createNode(Property... props) {
|
||||
return setProperties(delegate.createNode(), props);
|
||||
}
|
||||
|
||||
private <T extends PropertyContainer> T setProperties(T primitive, Property... properties) {
|
||||
assert primitive != null;
|
||||
if (properties==null || properties.length==0) return primitive;
|
||||
for (Property prop : properties) {
|
||||
if (prop.value==null) {
|
||||
primitive.removeProperty(prop.name);
|
||||
} else {
|
||||
primitive.setProperty(prop.name, prop.value);
|
||||
}
|
||||
}
|
||||
return primitive;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Relationship getRelationshipById(long id) {
|
||||
return delegate.getRelationshipById(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Relationship createRelationship(Node startNode, Node endNode, RelationshipType type, Property... props) {
|
||||
return setProperties(startNode.createRelationshipTo(endNode,type),props);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T extends PropertyContainer> Index<T> getIndex(String indexName) {
|
||||
IndexManager indexManager = delegate.index();
|
||||
if (indexManager.existsForNodes(indexName)) return (Index<T>) indexManager.forNodes(indexName);
|
||||
if (indexManager.existsForRelationships(indexName)) return (Index<T>) indexManager.forRelationships(indexName);
|
||||
throw new IllegalArgumentException("Index "+indexName+" does not exist.");
|
||||
}
|
||||
|
||||
// TODO handle existing indexes
|
||||
@Override
|
||||
public <T extends PropertyContainer> Index<T> createIndex(Class<T> type, String indexName, boolean fullText) {
|
||||
IndexManager indexManager = delegate.index();
|
||||
if (isNode(type)) {
|
||||
if (indexManager.existsForNodes(indexName))
|
||||
return (Index<T>) checkAndGetExistingIndex(indexName, fullText, indexManager.forNodes(indexName));
|
||||
return (Index<T>) indexManager.forNodes(indexName, indexConfigFor(fullText));
|
||||
} else {
|
||||
if (indexManager.existsForRelationships(indexName))
|
||||
return (Index<T>) checkAndGetExistingIndex(indexName, fullText, indexManager.forRelationships(indexName));
|
||||
return (Index<T>) indexManager.forRelationships(indexName, indexConfigFor(fullText));
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isNode(Class<? extends PropertyContainer> type) {
|
||||
if (type.equals(Node.class)) return true;
|
||||
if (type.equals(Relationship.class)) return false;
|
||||
throw new IllegalArgumentException("Unknown Graph Primitive, neither Node nor Relationship"+type);
|
||||
}
|
||||
|
||||
private <T extends PropertyContainer> Index<T> checkAndGetExistingIndex(final String indexName, boolean fullText, final Index<T> index) {
|
||||
Map<String, String> existingConfig = delegate.index().getConfiguration(index);
|
||||
Map<String, String> config = indexConfigFor(fullText);
|
||||
if (config.equals(existingConfig)) return index;
|
||||
throw new IllegalArgumentException("Setup for index "+indexName+" does not match "+(fullText ? "fulltext":"exact"));
|
||||
}
|
||||
|
||||
private Map<String, String> indexConfigFor(boolean fullText) {
|
||||
return fullText ? LuceneIndexImplementation.FULLTEXT_CONFIG : LuceneIndexImplementation.EXACT_CONFIG;
|
||||
}
|
||||
|
||||
@Override
|
||||
public TraversalDescription createTraversalDescription() {
|
||||
return Traversal.description();
|
||||
}
|
||||
|
||||
public void shutdown() {
|
||||
delegate.shutdown();
|
||||
}
|
||||
}
|
||||
@@ -2,6 +2,7 @@ package org.springframework.data.graph.neo4j.template;
|
||||
|
||||
import org.neo4j.graphdb.*;
|
||||
import org.neo4j.graphdb.traversal.TraversalDescription;
|
||||
import org.springframework.data.graph.core.Property;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
@@ -40,11 +41,12 @@ public interface Neo4jOperations {
|
||||
* Two shortcut means of providing the properties (very short with static imports)
|
||||
* <code>template.createNode(PropertyMap._("name","value"));</code>
|
||||
* <code>template.createNode(PropertyMap.props().set("name","value").set("prop","anotherValue").toMap(), "name", "prop");</code>
|
||||
*
|
||||
*
|
||||
* @param props properties to be set at node creation might be null
|
||||
* @param indexFields fields that are automatically indexed from the given properties for the newly created ndoe
|
||||
* @return the newly created node
|
||||
*/
|
||||
Node createNode(Map<String, Object> props, String... indexFields);
|
||||
Node createNode(Property... props);
|
||||
|
||||
/**
|
||||
* Delegates to the GraphDatabaseService
|
||||
@@ -59,14 +61,15 @@ public interface Neo4jOperations {
|
||||
* Two shortcut means of providing the properties (very short with static imports)
|
||||
* <code>template.createRelationship(from,to,TYPE, PropertyMap._("name","value"));</code>
|
||||
* <code>template.createRelationship(from,to,TYPE, PropertyMap.props().set("name","value").set("prop","anotherValue").toMap(), "name", "prop");</code>
|
||||
*
|
||||
*
|
||||
* @param startNode start-node of relationship
|
||||
* @param endNode end-node of relationship
|
||||
* @param type relationship type, might by an enum implementing RelationshipType or a DynamicRelationshipType.withName("name")
|
||||
* @param props optional initial properties
|
||||
* @param indexFields optional indexed fields
|
||||
* @return the newly created relationship
|
||||
*/
|
||||
Relationship createRelationship(Node startNode, Node endNode, RelationshipType type, Map<String, Object> props, String... indexFields);
|
||||
Relationship createRelationship(Node startNode, Node endNode, RelationshipType type, Property... props);
|
||||
|
||||
/**
|
||||
* Queries the supplied index with a lucene query string or query object (if the neo4j-index provider is lucene)
|
||||
@@ -144,14 +147,4 @@ public interface Neo4jOperations {
|
||||
* @return the provided element for convenience
|
||||
*/
|
||||
<T extends PropertyContainer> T index(String indexName, T element, String field, Object value);
|
||||
/**
|
||||
* Auto-indexes all indexFields for the given element's properties if they exist
|
||||
* @param indexName Name of the index, will be checked against existing indexes according to the given element
|
||||
* assumes a "node" node index or "relationship" relationship index for a null value
|
||||
* @param element node or relationship to auto-index
|
||||
* @param indexProperties property names to index
|
||||
* @param <T> the provided element type
|
||||
* @return the provided element for convenience
|
||||
*/
|
||||
<T extends PropertyContainer> T autoIndex(String indexName, T element, String... indexProperties);
|
||||
}
|
||||
|
||||
@@ -25,8 +25,7 @@ import org.neo4j.helpers.collection.IterableWrapper;
|
||||
import org.springframework.dao.DataAccessException;
|
||||
import org.springframework.dao.InvalidDataAccessApiUsageException;
|
||||
import org.springframework.data.graph.UncategorizedGraphStoreException;
|
||||
|
||||
import java.util.Map;
|
||||
import org.springframework.data.graph.core.Property;
|
||||
|
||||
public class Neo4jTemplate implements Neo4jOperations {
|
||||
|
||||
@@ -117,13 +116,13 @@ public class Neo4jTemplate implements Neo4jOperations {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Node createNode(final Map<String, Object> properties, final String... indexFields) {
|
||||
public Node createNode(final Property... properties) {
|
||||
return exec(new GraphCallback<Node>() {
|
||||
@Override
|
||||
public Node doWithGraph(GraphDatabaseService graph) throws Exception {
|
||||
Node node = graphDatabaseService.createNode();
|
||||
if (properties == null) return node;
|
||||
return autoIndex(null, setProperties(node, properties), indexFields);
|
||||
return setProperties(node, properties);
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -148,15 +147,6 @@ public class Neo4jTemplate implements Neo4jOperations {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T extends PropertyContainer> T autoIndex(String indexName, T element, String... indexFields) {
|
||||
for (String indexField : indexFields) {
|
||||
if (!element.hasProperty(indexField)) continue;
|
||||
index(indexName,element, indexField,element.getProperty(indexField));
|
||||
}
|
||||
return element;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T extends PropertyContainer> T index(final String indexName, final T element, final String field, final Object value) {
|
||||
notNull(element, "element", field, "field", value, "value");
|
||||
@@ -290,26 +280,26 @@ public class Neo4jTemplate implements Neo4jOperations {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Relationship createRelationship(final Node startNode, final Node endNode, final RelationshipType relationshipType, final Map<String, Object> properties, final String... indexFields) {
|
||||
public Relationship createRelationship(final Node startNode, final Node endNode, final RelationshipType relationshipType, final Property... properties) {
|
||||
notNull(startNode, "startNode", endNode, "endNode", relationshipType, "relationshipType", properties, "properties");
|
||||
return exec(new GraphCallback<Relationship>() {
|
||||
@Override
|
||||
public Relationship doWithGraph(GraphDatabaseService graph) throws Exception {
|
||||
Relationship relationship = startNode.createRelationshipTo(endNode, relationshipType);
|
||||
if (properties == null) return relationship;
|
||||
return autoIndex("relationship", setProperties(relationship, properties), indexFields);
|
||||
return setProperties(relationship, properties);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private <T extends PropertyContainer> T setProperties(T primitive, Map<String, Object> properties) {
|
||||
private <T extends PropertyContainer> T setProperties(T primitive, Property... properties) {
|
||||
assert primitive != null;
|
||||
if (properties==null) return primitive;
|
||||
for (Map.Entry<String, Object> prop : properties.entrySet()) {
|
||||
if (prop.getValue()==null) {
|
||||
primitive.removeProperty(prop.getKey());
|
||||
for (Property prop : properties) {
|
||||
if (prop.value==null) {
|
||||
primitive.removeProperty(prop.name);
|
||||
} else {
|
||||
primitive.setProperty(prop.getKey(), prop.getValue());
|
||||
primitive.setProperty(prop.name, prop.value);
|
||||
}
|
||||
}
|
||||
return primitive;
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
package org.springframework.data.graph.core;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.springframework.context.support.ClassPathXmlApplicationContext;
|
||||
import org.springframework.data.graph.neo4j.support.DelegatingGraphDatabase;
|
||||
|
||||
import static org.hamcrest.core.Is.is;
|
||||
import static org.hamcrest.core.IsInstanceOf.instanceOf;
|
||||
import static org.hamcrest.core.IsNot.not;
|
||||
import static org.hamcrest.core.IsNull.nullValue;
|
||||
import static org.junit.Assert.assertThat;
|
||||
|
||||
/**
|
||||
* @author mh
|
||||
* @since 29.03.11
|
||||
*/
|
||||
public class GraphDatabaseFactoryTest {
|
||||
|
||||
@Test
|
||||
public void shouldCreateLocalDatabaseFromContext() throws Exception {
|
||||
ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("GraphDatabaseFactory-context.xml");
|
||||
try {
|
||||
GraphDatabase graphDatabase = ctx.getBean("graphDatabase", GraphDatabase.class);
|
||||
assertThat(graphDatabase, is(not(nullValue())));
|
||||
assertThat(graphDatabase, is(instanceOf(DelegatingGraphDatabase.class)));
|
||||
} finally {
|
||||
ctx.close();
|
||||
}
|
||||
|
||||
}
|
||||
@Test
|
||||
public void shouldCreateLocalDatabase() throws Exception {
|
||||
GraphDatabaseFactory factory = new GraphDatabaseFactory();
|
||||
try {
|
||||
factory.setStoreLocation("target/test-db");
|
||||
GraphDatabase graphDatabase = factory.getObject();
|
||||
assertThat(graphDatabase, is(not(nullValue())));
|
||||
assertThat(graphDatabase,is(instanceOf(DelegatingGraphDatabase.class)));
|
||||
} finally {
|
||||
factory.shutdown();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -19,8 +19,7 @@ import java.util.Iterator;
|
||||
import static org.hamcrest.CoreMatchers.is;
|
||||
import static org.hamcrest.CoreMatchers.not;
|
||||
import static org.junit.Assert.*;
|
||||
import static org.springframework.data.graph.neo4j.template.PropertyMap._;
|
||||
import static org.springframework.data.graph.neo4j.template.PropertyMap.props;
|
||||
import static org.springframework.data.graph.core.Property._;
|
||||
|
||||
/**
|
||||
* @author mh
|
||||
@@ -189,7 +188,7 @@ public class Neo4jTemplateApiTest {
|
||||
|
||||
@Test
|
||||
public void testCreateNodeWithProperties() throws Exception {
|
||||
Node node=template.createNode(props().set("test", "testCreateNodeWithProperties").toMap());
|
||||
Node node=template.createNode(_("test", "testCreateNodeWithProperties"));
|
||||
assertTestPropertySet(node, "testCreateNodeWithProperties");
|
||||
}
|
||||
|
||||
|
||||
@@ -13,8 +13,8 @@ import java.util.Set;
|
||||
import static java.util.Arrays.asList;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.neo4j.kernel.Traversal.returnAllButStartNode;
|
||||
import static org.springframework.data.graph.core.Property._;
|
||||
import static org.springframework.data.graph.neo4j.template.NeoTraversalTest.Type.HAS;
|
||||
import static org.springframework.data.graph.neo4j.template.PropertyMap._;
|
||||
|
||||
public class NeoTraversalTest extends NeoApiTest {
|
||||
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<beans xmlns="http://www.springframework.org/schema/beans"
|
||||
xmlns:aop="http://www.springframework.org/schema/aop"
|
||||
xmlns:context="http://www.springframework.org/schema/context"
|
||||
xmlns:jee="http://www.springframework.org/schema/jee"
|
||||
xmlns:tx="http://www.springframework.org/schema/tx"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns:task="http://www.springframework.org/schema/task"
|
||||
xmlns:p="http://www.springframework.org/schema/p"
|
||||
xsi:schemaLocation="http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee-3.0.xsd
|
||||
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd
|
||||
http://www.springframework.org/schema/task http://www.springframework.org/schema/task/spring-task-3.0.xsd
|
||||
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
|
||||
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.0.xsd
|
||||
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd">
|
||||
|
||||
<context:annotation-config />
|
||||
|
||||
|
||||
<bean name="graphDatabase" class="org.springframework.data.graph.core.GraphDatabaseFactory">
|
||||
<property name="storeLocation" value="target/test-db"/>
|
||||
|
||||
</bean>
|
||||
</beans>
|
||||
Reference in New Issue
Block a user