DATAGRAPH-253 Better abstraction layer for interfacing with Remote Neo4j

This commit is contained in:
Michael Hunger
2014-09-22 18:14:17 +02:00
parent 70e20a846c
commit c2f1230ed4
105 changed files with 11079 additions and 50 deletions

View File

@@ -65,7 +65,6 @@
<groupId>org.neo4j</groupId>
<artifactId>neo4j-lucene-index</artifactId>
<version>${neo4j.version}</version>
<scope>test</scope>
</dependency>
<dependency>
@@ -117,26 +116,6 @@
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.neo4j</groupId>
<artifactId>neo4j-rest-graphdb</artifactId>
<version>${neo4j-rest-graphdb.version}</version>
<exclusions>
<exclusion>
<groupId>org.neo4j</groupId>
<artifactId>neo4j-kernel</artifactId>
</exclusion>
<exclusion>
<groupId>org.neo4j</groupId>
<artifactId>neo4j-lucene-index</artifactId>
</exclusion>
<exclusion>
<groupId>org.neo4j</groupId>
<artifactId>server-api</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.neo4j.app</groupId>
<artifactId>neo4j-server</artifactId>

View File

@@ -0,0 +1,73 @@
/**
* Copyright (c) 2002-2013 "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 <http://www.gnu.org/licenses/>.
*/
package org.neo4j.rest.graphdb;
import org.neo4j.graphdb.*;
import org.neo4j.graphdb.event.KernelEventHandler;
import org.neo4j.graphdb.event.TransactionEventHandler;
import org.neo4j.kernel.GraphDatabaseAPI;
import org.neo4j.kernel.IdGeneratorFactory;
import org.neo4j.kernel.KernelData;
import org.neo4j.kernel.TransactionBuilder;
import org.neo4j.kernel.guard.Guard;
import org.neo4j.kernel.impl.core.KernelPanicEventGenerator;
import org.neo4j.kernel.impl.core.NodeManager;
import org.neo4j.kernel.impl.nioneo.store.StoreId;
import org.neo4j.kernel.impl.persistence.PersistenceSource;
import org.neo4j.kernel.impl.transaction.LockManager;
import org.neo4j.kernel.impl.transaction.XaDataSourceManager;
import org.neo4j.kernel.impl.transaction.xaframework.TxIdGenerator;
import org.neo4j.kernel.impl.util.StringLogger;
import org.neo4j.kernel.info.DiagnosticsManager;
import org.neo4j.rest.graphdb.transaction.NullTransaction;
import javax.transaction.TransactionManager;
import java.util.Collection;
abstract class AbstractRemoteDatabase implements GraphDatabaseAPI {
public Transaction beginTx() {
return new NullTransaction();
}
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();
}
@Override
public TransactionBuilder tx() {
throw new UnsupportedOperationException();
}
@Override
public void shutdown() {
}
}

View File

@@ -0,0 +1,55 @@
/**
* Copyright (c) 2002-2013 "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 <http://www.gnu.org/licenses/>.
*/
package org.neo4j.rest.graphdb;
import org.neo4j.rest.graphdb.query.CypherRestResult;
import org.neo4j.rest.graphdb.query.CypherResult;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.List;
public class CypherRestShell {
public static void main(String[] args) throws IOException {
String uri = (args.length>0) ? args[0] : "http://localhost:7474/db/data";
RestAPIImpl restAPIFacade = args.length>1 ? new RestAPIImpl(uri,args[1],args[2]) : new RestAPIImpl(uri);
System.out.println("Connected to "+uri);
try {
BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
String query;
System.out.print("Query: ");
while ((query=reader.readLine())!=null && !query.isEmpty()) {
long time=System.currentTimeMillis();
CypherResult result = restAPIFacade.query(query, null);
time=System.currentTimeMillis()-time;
System.out.println(result.getColumns());
List<List<Object>> rows = (List<List<Object>>) result.getData();
for (List<Object> row : rows) {
System.out.println(row);
}
System.out.println(rows.size()+" row(s), roundtrip time "+time+" ms.");
System.out.print("Query: ");
}
} finally {
restAPIFacade.close();
}
}
}

View File

@@ -0,0 +1,184 @@
/**
* Copyright (c) 2002-2013 "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 <http://www.gnu.org/licenses/>.
*/
package org.neo4j.rest.graphdb;
import com.sun.jersey.api.client.Client;
import com.sun.jersey.api.client.ClientResponse;
import com.sun.jersey.api.client.WebResource;
import com.sun.jersey.api.client.WebResource.Builder;
import com.sun.jersey.api.client.filter.HTTPBasicAuthFilter;
import com.sun.jersey.api.client.filter.LoggingFilter;
import org.neo4j.helpers.collection.MapUtil;
import org.neo4j.rest.graphdb.util.Config;
import org.neo4j.rest.graphdb.util.JsonHelper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.ws.rs.core.MediaType;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URLEncoder;
import java.util.Map;
import static javax.ws.rs.core.MediaType.APPLICATION_JSON_TYPE;
public class ExecutingRestRequest implements RestRequest {
private static final Logger log = LoggerFactory.getLogger(RestRequest.class);
public static final MediaType STREAMING_JSON_TYPE = new MediaType(APPLICATION_JSON_TYPE.getType(),APPLICATION_JSON_TYPE.getSubtype(), MapUtil.stringMap("stream","true"));
private final String baseUri;
private final UserAgent userAgent = new UserAgent();
private final Client client;
public ExecutingRestRequest( String baseUri ) {
this( baseUri, null, null );
}
public ExecutingRestRequest( String baseUri, String username, String password ) {
this.baseUri = uriWithoutSlash( baseUri );
client = createClient();
addAuthFilter(username, password);
}
protected void addAuthFilter(String username, String password) {
if (username == null) return;
client.addFilter( new HTTPBasicAuthFilter( username, password ) );
}
protected Client createClient() {
Client client = Client.create();
client.setConnectTimeout(Config.getConnectTimeout());
client.setReadTimeout(Config.getReadTimeout());
client.setChunkedEncodingSize(8*1024);
userAgent.install(client);
if (Config.useLoggingFilter()) {
client.addFilter(new LoggingFilter());
}
return client;
}
private ExecutingRestRequest( String uri, Client client ) {
this.baseUri = uriWithoutSlash( uri );
this.client = client;
}
protected String uriWithoutSlash( String uri ) {
return (uri.endsWith("/") ? uri.substring(0, uri.length() - 1) : uri);
}
public static String encode( Object value ) {
if ( value == null ) return "";
try {
return URLEncoder.encode( value.toString(), "utf-8" ).replaceAll( "\\+", "%20" );
} catch ( UnsupportedEncodingException e ) {
throw new RuntimeException( e );
}
}
private Builder builder( String path ) {
WebResource resource = client.resource( uri( pathOrAbsolute( path ) ) );
if (Config.streamingIsEnabled()) return resource.accept(STREAMING_JSON_TYPE).header("X-Stream","true");
return resource.accept(APPLICATION_JSON_TYPE);
}
private String pathOrAbsolute( String path ) {
if ( path.startsWith( "http://" ) ) return path;
return baseUri + "/" + path;
}
@Override
public RequestResult get( String path ) {
if (log.isDebugEnabled()) log.debug("GET "+path);
return RequestResult.extractFrom(builder(path).get(ClientResponse.class));
}
@Override
public RequestResult get( String path, Object data ) {
Builder builder = builder(path);
if ( data != null ) {
builder = builder.entity( JsonHelper.createJsonFrom( data ), APPLICATION_JSON_TYPE );
}
if (log.isDebugEnabled()) log.debug("GET "+path+" "+data);
return RequestResult.extractFrom(builder.get(ClientResponse.class));
}
@Override
public RequestResult delete(String path) {
if (log.isDebugEnabled()) log.debug("DELETE "+path);
return RequestResult.extractFrom(builder(path).delete(ClientResponse.class));
}
@Override
public RequestResult post( String path, Object data ) {
Builder builder = builder( path );
if ( data != null ) {
Object payload = data instanceof InputStream ? data : JsonHelper.createJsonFrom(data);
builder = builder.entity( payload , APPLICATION_JSON_TYPE );
}
if (log.isDebugEnabled()) log.debug("POST "+path+" "+data);
return RequestResult.extractFrom(builder.post(ClientResponse.class));
}
@Override
public RequestResult put( String path, Object data ) {
Builder builder = builder( path );
if ( data != null ) {
builder = builder.entity( JsonHelper.createJsonFrom( data ), APPLICATION_JSON_TYPE );
}
if (log.isDebugEnabled()) log.debug("PUT "+path+" "+data);
return RequestResult.extractFrom(builder.put(ClientResponse.class));
}
@Override
public RestRequest with( String uri ) {
return new ExecutingRestRequest(uri, client);
}
private URI uri( String uri ) {
try {
return new URI( uri );
} catch ( URISyntaxException e ) {
throw new RuntimeException( e );
}
}
@Override
public String getUri() {
return baseUri;
}
@Override
public Map<?, ?> toMap(RequestResult requestResult) {
return requestResult.toMap();
}
public static void shutdown() {
}
}

View File

@@ -0,0 +1,159 @@
/**
* Copyright (c) 2002-2013 "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 <http://www.gnu.org/licenses/>.
*/
package org.neo4j.rest.graphdb;
/**
* @author mh
* @since 13.12.10
*/
import org.neo4j.graphdb.PropertyContainer;
import java.lang.reflect.Array;
import java.util.*;
public class PropertiesMap {
private final Map<String, Object> values = new HashMap<String, Object>();
public PropertiesMap( PropertyContainer container ) {
for ( String key : container.getPropertyKeys() ) {
values.put( key, container.getProperty( key ) );
}
}
public PropertiesMap( Map<String, Object> map ) {
for ( Map.Entry<String, Object> entry : map.entrySet() ) {
values.put( entry.getKey(), toInternalType( entry.getValue() ) );
}
}
public Object getValue( String key ) {
return values.get( key );
}
public Map<String, Object> serialize() {
// TODO Nice with sorted, but TreeMap the best?
Map<String, Object> result = new TreeMap<String, Object>();
for ( Map.Entry<String, Object> entry : values.entrySet() ) {
result.put( entry.getKey(), toSerializedType( entry.getValue() ) );
}
return result;
}
void storeTo( PropertyContainer container ) {
for ( Map.Entry<String, Object> entry : values.entrySet() ) {
container.setProperty( entry.getKey(), entry.getValue() );
}
}
@SuppressWarnings("unchecked")
private static Object toInternalType( Object value ) {
if ( value instanceof List ) {
List list = (List) value;
if ( list.isEmpty() ) {
return new byte[0];
} else {
Object first = list.get( 0 );
if ( first instanceof String ) {
return stringArray( list );
} else if ( first instanceof Number ) {
return numberArray( list );
} else if ( first instanceof Boolean ) {
return booleanArray( list );
} else {
throw new RuntimeException( "Unsupported array type " + first.getClass() +
". Supported array types are arrays of all java primitives (" +
"byte[], char[], short[], int[], long[], float[], double[]) " +
"and String[]" );
}
}
} else {
return assertSupportedPropertyValue( value );
}
}
public static Object assertSupportedPropertyValue( Object value ) {
if ( value == null ) {
throw new RuntimeException( "null value not supported" );
}
final Class<?> type = value.getClass();
if (isSupportedType(type) || type.isArray() && isSupportedType(type.getComponentType())) {
return value;
}
throw new RuntimeException( "Unsupported value type " + type + "." +
" Supported value types are all java primitives (byte, char, short, int, " +
"long, float, double) and String, as well as arrays of all those types" );
}
private static boolean isSupportedType(Class<?> type) {
return type.isPrimitive() || String.class.isAssignableFrom(type) || Number.class.isAssignableFrom(type) || Boolean.class.isAssignableFrom(type);
}
private static Boolean[] booleanArray( List<Boolean> list ) {
return list.toArray( new Boolean[list.size()] );
}
private static Number[] numberArray( List<Number> numbers ) {
Number[] internal = new Number[numbers.size()];
for ( int i = 0; i < internal.length; i++ ) {
Number number = numbers.get( i );
if ( number instanceof Float || number instanceof Double ) {
number = number.doubleValue();
} else {
number = number.longValue();
}
internal[i] = number;
}
final Number[] result;
if ( internal[0] instanceof Double ) {
result = new Double[internal.length];
} else {
result = new Long[internal.length];
}
System.arraycopy( internal, 0, result, 0, internal.length );
return result;
}
private static String[] stringArray( List<String> strings ) {
return strings.toArray( new String[strings.size()] );
}
private Object toSerializedType( Object value ) {
if ( value.getClass().isArray() ) {
if ( value.getClass().getComponentType().isPrimitive() ) {
int size = Array.getLength( value );
List<Object> result = new ArrayList<Object>();
for ( int i = 0; i < size; i++ ) {
result.add( Array.get( value, i ) );
}
return result;
} else {
return Arrays.asList( (Object[]) value );
}
} else {
return value;
}
}
public boolean isEmpty() {
return values.isEmpty();
}
}

View File

@@ -0,0 +1,148 @@
/**
* Copyright (c) 2002-2013 "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 <http://www.gnu.org/licenses/>.
*/
package org.neo4j.rest.graphdb;
import java.io.IOException;
import java.io.InputStream;
import java.net.URI;
import java.util.Map;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.Response.StatusType;
import org.neo4j.rest.graphdb.util.JsonHelper;
import com.sun.jersey.api.client.ClientResponse;
import org.neo4j.rest.graphdb.util.StreamJsonHelper;
/**
* @author Klemens Burchardi
* @since 03.08.11
*/
public class RequestResult {
private final int status;
private final String location;
private ClientResponse response;
private String string;
private Object entity;
private InputStream stream;
RequestResult(int status, String location, String string) {
this.status = status;
this.location = location;
this.string = string;
this.stream = null;
}
RequestResult(int status, String location, InputStream stream, ClientResponse response) {
this.status = status;
this.location = location;
this.response = response;
this.entity = null;
this.stream = stream;
}
public static RequestResult extractFrom(ClientResponse clientResponse) {
final int status = clientResponse.getStatus();
final URI location = clientResponse.getLocation();
// final InputStream data;
if (status == Response.Status.NO_CONTENT.getStatusCode()) {
// data = null;
clientResponse.close();
return new RequestResult(status, uriString(location), null,clientResponse);
} else {
// data = clientResponse.getEntityInputStream();
RequestResult result = new RequestResult(status, uriString(location), clientResponse.getEntity(String.class));
clientResponse.close();
return result;
}
//return new RequestResult(status, uriString(location), data,clientResponse);
}
public static RequestResult extractFrom(Map<String, Object> batchResult) {
return new RequestResult(200, (String) batchResult.get("location"),JsonHelper.createJsonFrom(batchResult.get("body")));
}
private static String uriString(URI location) {
return location==null ? null : location.toString();
}
public int getStatus() {
return status;
}
public String getLocation() {
return location;
}
public Object toEntity() {
if (entity!=null) return entity;
if (stream != null) {
entity = StreamJsonHelper.jsonToSingleValue(stream);
closeStream();
}
else {
entity = JsonHelper.jsonToSingleValue(string);
}
return entity;
}
public boolean isMap() {
return toEntity() instanceof Map;
}
public Map<?, ?> toMap() {
return (Map<?, ?>) toEntity();
}
public boolean statusIs( StatusType status ) {
return getStatus() == status.getStatusCode();
}
public boolean statusOtherThan( StatusType status ) {
return !statusIs(status );
}
public String getText() {
if (string==null && stream!=null) {
string = JsonHelper.readString(stream);
closeStream();
}
return string;
}
private void closeStream() {
if (stream!=null) readFully(stream);
stream = null;
if (response!=null) {
response.close();
response = null;
}
}
private void readFully(InputStream stream) {
try {
while (stream.read()!=-1);
} catch (IOException e) {
// ignore
}
}
}

View File

@@ -0,0 +1,92 @@
/**
* Copyright (c) 2002-2013 "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 <http://www.gnu.org/licenses/>.
*/
package org.neo4j.rest.graphdb;
import org.neo4j.graphdb.*;
import org.neo4j.graphdb.traversal.TraversalDescription;
import org.neo4j.rest.graphdb.query.CypherRestResult;
import org.neo4j.rest.graphdb.entity.RestEntity;
import org.neo4j.rest.graphdb.entity.RestNode;
import org.neo4j.rest.graphdb.entity.RestRelationship;
import org.neo4j.rest.graphdb.traversal.RestTraverser;
import org.neo4j.rest.graphdb.util.QueryResult;
import org.neo4j.rest.graphdb.util.ResultConverter;
import java.util.Collection;
import java.util.Map;
/**
* @author mh
* @since 02.05.12
*/
public interface RestAPI extends RestAPIIndex, RestAPIInternal {
void deleteEntity(RestEntity entity);
void setPropertyOnEntity(RestEntity entity, String key, Object value);
void setPropertiesOnEntity(RestEntity restEntity, Map<String, Object> propertyData);
// Map<?,?> getData(RestEntity uri);
void removeProperty(RestEntity entity, String key);
RestNode getNodeById(long id);
RestNode createNode(Map<String, Object> props);
RestNode createNode(Map<String, Object> props,Collection<String> labels);
RestRelationship getRelationshipById(long id);
RestRelationship createRelationship(Node startNode, Node endNode, RelationshipType type, Map<String, Object> props);
Iterable<RelationshipType> getRelationshipTypes(RestNode node);
int getDegree(RestNode restNode, RelationshipType type, Direction direction);
void addLabels(RestNode node, Collection<String> labels);
void removeLabel(RestNode node, String label);
Iterable<RestNode> getNodesByLabel(String label);
Iterable<RestNode> getNodesByLabelAndProperty(String label, String property, Object value);
org.neo4j.rest.graphdb.query.CypherResult query(String statement, Map<String, Object> params);
QueryResult<Map<String, Object>> query(String statement, Map<String, Object> params, ResultConverter resultConverter);
Transaction beginTx();
Collection<String> getAllLabelNames();
Iterable<RelationshipType> getRelationshipTypes();
TraversalDescription createTraversalDescription();
Iterable<Relationship> getRelationships(RestNode restNode, Direction direction, RelationshipType... types);
RestTraverser traverse(RestNode restNode, Map<String, Object> description);
RestNode merge(String labelName, String key, Object value, Map<String, Object> properties, Collection<String> labels);
RequestResult batch(Collection<Map<String,Object>> batchRequestData);
// internal
RestRequest getRestRequest();
RestNode addToCache(RestNode restNode);
RestNode getFromCache(long id);
void close();
}

View File

@@ -0,0 +1,553 @@
/**
* Copyright (c) 2002-2013 "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 <http://www.gnu.org/licenses/>.
*/
package org.neo4j.rest.graphdb;
import org.neo4j.graphdb.*;
import org.neo4j.graphdb.index.IndexHits;
import org.neo4j.graphdb.traversal.TraversalDescription;
import org.neo4j.helpers.collection.IterableWrapper;
import org.neo4j.helpers.collection.IteratorUtil;
import org.neo4j.helpers.collection.MapUtil;
import org.neo4j.index.impl.lucene.AbstractIndexHits;
import org.neo4j.rest.graphdb.converter.RestEntityExtractor;
import org.neo4j.rest.graphdb.entity.RestEntity;
import org.neo4j.rest.graphdb.entity.RestNode;
import org.neo4j.rest.graphdb.entity.RestRelationship;
import org.neo4j.rest.graphdb.index.IndexInfo;
import org.neo4j.rest.graphdb.index.RestIndex;
import org.neo4j.rest.graphdb.index.RestIndexManager;
import org.neo4j.rest.graphdb.query.CypherResult;
import org.neo4j.rest.graphdb.query.CypherTransaction;
import org.neo4j.rest.graphdb.query.CypherTxResult;
import org.neo4j.rest.graphdb.query.RestQueryResult;
import org.neo4j.rest.graphdb.transaction.RemoteCypherTransaction;
import org.neo4j.rest.graphdb.traversal.RestTraverser;
import org.neo4j.rest.graphdb.util.QueryResult;
import org.neo4j.rest.graphdb.util.ResultConverter;
import javax.ws.rs.core.Response.Status;
import java.util.*;
import static org.neo4j.helpers.collection.MapUtil.map;
public class RestAPICypherImpl implements RestAPI {
public static final String _QUERY_RETURN_NODE = " RETURN id(n) as id, labels(n) as labels, n as data";
public static final String _QUERY_RETURN_REL = " RETURN id(r) as id, type(r) as type, r as data, id(startNode(r)) as start, id(endNode(r)) as end";
public static String MATCH_NODE_QUERY(String name) { return " MATCH ("+name+") WHERE id("+name+") = {id_"+name+"} "; }
public static final String _MATCH_NODE_QUERY = " MATCH (n) WHERE id(n) = {id} ";
public static final String GET_NODE_QUERY = _MATCH_NODE_QUERY + _QUERY_RETURN_NODE;
public static final String _MATCH_REL_QUERY = " START r=rel({id}) ";
public static final String GET_REL_QUERY = _MATCH_REL_QUERY + _QUERY_RETURN_REL;
public static final String GET_REL_TYPES_QUERY = _MATCH_NODE_QUERY + " MATCH (n)-[r]-() RETURN distinct type(r) as relType";
private String createNodeQuery(Collection<String> labels) {
String labelString = toLabelString(labels);
return "CREATE (n" + labelString + " {props}) " + _QUERY_RETURN_NODE;
}
private String mergeQuery(String labelName, String key, Collection<String> labels) {
StringBuilder setLabels = new StringBuilder();
if (labels!=null) {
for (String label : labels) {
if (label.equals(labelName)) continue;
setLabels.append("SET n:").append(label).append(" ");
}
}
return "MERGE (n:`"+labelName+"` {`"+key+"`: {value}}) ON CREATE SET n={props} "+setLabels+ _QUERY_RETURN_NODE;
}
private String toLabelString(Collection<String> labels) {
if (labels==null || labels.size() == 0) return "";
StringBuilder sb = new StringBuilder();
for (String label : labels) {
sb.append(":").append(label);
}
return sb.toString();
}
private final RestAPI restAPI;
private RestRequest restRequest;
private static final ThreadLocal<CypherTransaction> cypherTransaction = new ThreadLocal<>();
protected RestAPICypherImpl(RestAPI restAPI) {
this.restAPI = restAPI;
this.restRequest = restAPI.getRestRequest();
}
@Override
public RestNode getNodeById(long id, Load force) {
if (force != Load.ForceFromServer) {
RestNode restNode = getFromCache(id);
if (restNode != null) return restNode;
}
if (force == Load.FromCache) return new RestNode(RestNode.nodeUri(this, id),this);
Iterator<List<Object>> result = query(GET_NODE_QUERY, map("id", id)).getData().iterator();
if (!result.hasNext()) {
throw new NotFoundException("Node not found " + id);
}
List<Object> row = result.next();
return addToCache(toNode(row));
}
public RestNode getFromCache(long id) {
return restAPI.getFromCache(id);
}
@Override
public RestNode getNodeById(long id) {
return getNodeById(id, Load.FromServer);
}
private RestNode toNode(List<Object> row) {
long id = ((Number) row.get(0)).longValue();
List<String> labels = (List<String>) row.get(1);
Map<String,Object> props = (Map<String, Object>) row.get(2);
return RestNode.fromCypher(id, labels, props, this);
}
private RestRelationship toRel(List<Object> row) {
long id = ((Number) row.get(0)).longValue();
String type = (String)row.get(1);
Map<String,Object> props = (Map<String, Object>) row.get(2);
long start = ((Number) row.get(3)).longValue();
long end = ((Number) row.get(4)).longValue();
return RestRelationship.fromCypher(id, type, props, start,end,this);
}
@Override
public RestRelationship getRelationshipById(long id) {
Iterator<List<Object>> result = query(GET_REL_QUERY, map("id", id)).getData().iterator();
if (!result.hasNext()) {
throw new NotFoundException("Relationship not found " + id);
}
List<Object> row = result.next();
return toRel(row);
}
@Override
public RestNode createNode(Map<String, Object> props) {
return createNode(props,Collections.<String>emptyList());
}
@Override
public RestNode createNode(Map<String, Object> props, Collection<String> labels) {
Map<?, Object> data = props == null ? Collections.emptyMap() : props;
Iterator<List<Object>> result = query(createNodeQuery(labels), map("props", data)).getData().iterator();
if (result.hasNext()) {
return addToCache(toNode(result.next()));
}
throw new RuntimeException("Error creating node with labels: " + labels + " and props: " + props + " no data returned");
}
@Override
public RestNode merge(String labelName, String key, Object value, final Map<String, Object> nodeProperties, Collection<String> labels) {
if (labelName ==null || key == null || value==null) throw new IllegalArgumentException("Label "+ labelName +" key "+key+" and value must not be null");
Map props = nodeProperties.containsKey(key) ? nodeProperties : MapUtil.copyAndPut(nodeProperties, key, value);
Map<String, Object> params = map("props", props, "value", value);
Iterator<List<Object>> result = query(mergeQuery(labelName, key, labels), params).getData().iterator();
if (!result.hasNext())
throw new RuntimeException("Error merging node with labels: " + labelName + " key " + key + " value " + value + " labels " + labels+ " and props: " + props + " no data returned");
return addToCache(toNode(result.next()));
}
public RestNode addToCache(RestNode restNode) {
return restAPI.addToCache(restNode);
}
@Override
public RestRelationship createRelationship(Node startNode, Node endNode, RelationshipType type, Map<String, Object> props) {
String statement = MATCH_NODE_QUERY("n") + MATCH_NODE_QUERY("m") + " CREATE (n)-[r:`"+type.name()+"`]->(m) SET r={props} " + _QUERY_RETURN_REL;
Map<String, Object> params = map("id_n", startNode.getId(), "id_m", endNode.getId(), "props", props);
CypherTransaction.Result result = runQuery(statement, params);
if (!result.hasData()) throw new RuntimeException("Error creating relationship from "+startNode+" to "+endNode+" type "+type.name());
Iterator<List<Object>> it = result.getRows().iterator();
return toRel(it.next());
}
@Override
public void removeLabel(RestNode node, String label) {
CypherTransaction.Result result = runQuery(_MATCH_NODE_QUERY + (" REMOVE n:`" + label + "` ") + _QUERY_RETURN_NODE, map("id", node.getId()));
if (!result.hasData()) {
throw new RuntimeException("Error removing label "+label+" from node "+node);
}
}
@Override
public Iterable<RestNode> getNodesByLabel(String label) {
String statement = "MATCH (n:`" + label + "`) " + _QUERY_RETURN_NODE;
return queryForNodes(statement, null);
}
private Iterable<RestNode> queryForNodes(String statement, Map<String, Object> params) {
Iterable<List<Object>> result = runQuery(statement, params).getRows();
return new IterableWrapper<RestNode,List<Object>>(result) {
protected RestNode underlyingObjectToObject(List<Object> row) {
return addToCache(toNode(row));
}
};
}
@Override
public Iterable<RestNode> getNodesByLabelAndProperty(String label, String property, Object value) {
String statement = "MATCH (n:`" + label + "`) WHERE n.`"+property+"` = {value} " + _QUERY_RETURN_NODE;
return queryForNodes(statement, map("value", value));
}
@Override
public Iterable<RelationshipType> getRelationshipTypes(RestNode node) {
Iterable<List<Object>> result = runQuery(GET_REL_TYPES_QUERY, map("id", node.getId())).getRows();
return new IterableWrapper<RelationshipType, List<Object>>(result) {
protected RelationshipType underlyingObjectToObject(List<Object> row) {
return DynamicRelationshipType.withName(row.get(0).toString());
}
};
}
@Override
public int getDegree(RestNode restNode, RelationshipType type, Direction direction) {
String nodeDegreeQuery = "MATCH (n)" + relPattern(direction,type) + "() WHERE id(n) = {id} RETURN count(*) as degree";
Iterator<List<Object>> degree = runQuery(nodeDegreeQuery, map("id", restNode.getId())).getRows().iterator();
if (!degree.hasNext()) return 0;
return ((Number)degree.next().get(0)).intValue();
}
private String relPattern(Direction direction, RelationshipType... types) {
String typeString = toTypeString(types);
String relPattern = "--";
if (!typeString.isEmpty()) relPattern = "-[r "+typeString+"]-";
if (direction == Direction.OUTGOING) {
relPattern += ">";
} else if (direction == Direction.INCOMING) {
relPattern = "<" + relPattern;
}
return relPattern;
}
private String toTypeString(RelationshipType... types) {
if (types==null || types.length == 0) return "";
StringBuilder typeString = new StringBuilder();
for (RelationshipType type : types) {
if (typeString.length() > 0 ) typeString.append("|");
typeString.append(':').append('`').append(type.name()).append("`");
}
return typeString.toString();
}
@Override
public Iterable<Relationship> getRelationships(RestNode restNode, Direction direction, RelationshipType... types) {
String statement = _MATCH_NODE_QUERY + " MATCH (n)"+relPattern(direction,types)+"() "+_QUERY_RETURN_REL;
CypherTransaction.Result result = runQuery(statement, map("id", restNode.getId()));
return new IterableWrapper<Relationship,List<Object>>(result.getRows()) {
protected Relationship underlyingObjectToObject(List<Object> row) {
return toRel(row);
}
};
}
@Override
public void addLabels(RestNode node, Collection<String> labels) {
String statement = _MATCH_NODE_QUERY + " SET n"+toLabelString(labels) + _QUERY_RETURN_NODE;
runQuery(statement,map("id",node.getId()));
RequestResult response = getRestRequest().with(node.getUri()).post("labels", labels);
if (response.statusOtherThan(Status.NO_CONTENT)) {
throw new IllegalStateException("error adding labels, received " + response);
}
}
public RestRequest getRestRequest() {
return restRequest;
}
@Override
public Transaction beginTx() {
CypherTransaction tx = cypherTransaction.get();
if (tx != null ) {
throw new IllegalStateException("Transaction already running "+tx);
} else {
cypherTransaction.set(newCypherTransaction());
return new RemoteCypherTransaction(cypherTransaction);
}
}
@Override
public <S extends PropertyContainer> IndexHits<S> getIndex(Class<S> entityType, String indexName, String key, Object value) {
String index = key == null ? ":`" + indexName + "`({query})" : ":`" + indexName + "`(`" + key + "`={query})";
if (Node.class.isAssignableFrom(entityType)) {
String statement = "start n=node"+index+ _QUERY_RETURN_NODE;
CypherTransaction.Result result = runQuery(statement, map("query", value));
return toIndexHits(result,true);
}
if (Relationship.class.isAssignableFrom(entityType)) {
String statement = "start r=rel"+index+ _QUERY_RETURN_REL;
CypherTransaction.Result result = runQuery(statement, map("query", value));
return toIndexHits(result,false);
}
throw new IllegalStateException("Unknown index entity type "+entityType);
}
@Override
public <S extends PropertyContainer> IndexHits<S> queryIndex(Class<S> entityType, String indexName, String key, Object value) {
String index = ":`" + indexName + "`({query})";
if (Node.class.isAssignableFrom(entityType)) {
String statement = "start n=node"+index+ _QUERY_RETURN_NODE;
CypherTransaction.Result result = runQuery(statement, map("query", value));
return toIndexHits(result,true);
}
if (Relationship.class.isAssignableFrom(entityType)) {
String statement = "start r=rel"+index+ _QUERY_RETURN_REL;
CypherTransaction.Result result = runQuery(statement, map("query", value));
return toIndexHits(result,false);
}
throw new IllegalStateException("Unknown index entity type "+entityType);
}
private <S extends PropertyContainer> IndexHits<S> toIndexHits(CypherTransaction.Result result, final boolean isNode) {
final int size = IteratorUtil.count(result.getRows());
final Iterator<List<Object>> it = result.getRows().iterator();
return new AbstractIndexHits<S>() {
@Override
public int size() {
return size;
}
@Override
public float currentScore() {
return 0;
}
@Override
protected S fetchNextOrNull() {
if (!it.hasNext()) return null;
return (S)(isNode ? addToCache(toNode(it.next())) : toRel(it.next()));
}
};
}
@Override
public RestIndexManager index() {
return restAPI.index();
}
@Override
public void deleteEntity(RestEntity entity) {
if (entity instanceof Node) {
runQuery(_MATCH_NODE_QUERY + " DELETE n", map("id", entity.getId()));
} else if (entity instanceof Relationship) {
runQuery(_MATCH_REL_QUERY + " DELETE r", map("id", entity.getId()));
}
}
@Override
public void setPropertyOnEntity(RestEntity entity, String key, Object value) {
if (entity instanceof Node) {
runQuery(_MATCH_NODE_QUERY + " SET n.`"+key+"` = {value} ", map("id", entity.getId(), "value", value));
} else if (entity instanceof Relationship) {
runQuery(_MATCH_REL_QUERY + " SET r.`"+key+"` = {value} ", map("id", entity.getId(), "value", value));
}
}
// TODO return entity ???
@Override
public void setPropertiesOnEntity(RestEntity entity, Map<String, Object> properties) {
if (entity instanceof Node) {
runQuery(_MATCH_NODE_QUERY + " SET n = {props} ", map("id", entity.getId(), "props", properties));
} else if (entity instanceof Relationship) {
runQuery(_MATCH_REL_QUERY + " SET r = {props} ", map("id", entity.getId(), "props", properties));
}
}
@Override
public void removeProperty(RestEntity entity, String key) {
if (entity instanceof Node) {
runQuery(_MATCH_NODE_QUERY + " REMOVE n.`"+key+"`", map("id", entity.getId()));
} else if (entity instanceof Relationship) {
runQuery(_MATCH_REL_QUERY + " REMOVE r.`"+key+"`", map("id", entity.getId()));
}
}
// todo handle within cypher tx
@Override
public RestNode getOrCreateNode(RestIndex<Node> index, String key, Object value, final Map<String, Object> properties, Collection<String> labels) {
return restAPI.getOrCreateNode(index,key,value,properties,labels);
}
// todo handle within cypher tx
@Override
public RestRelationship getOrCreateRelationship(RestIndex<Relationship> index, String key, Object value, final RestNode start, final RestNode end, final String type, final Map<String, Object> properties) {
return restAPI.getOrCreateRelationship(index,key,value,start,end,type,properties);
}
public CypherResult query(String statement, Map<String, Object> params) {
return new CypherTxResult(runQuery(statement, params));
}
private CypherTransaction.Result runQuery(String statement, Map<String, Object> params) {
if (cypherTransaction.get() == null) {
return newCypherTransaction().commit(statement,params);
}
return cypherTransaction.get().send(statement,params);
}
private CypherTransaction newCypherTransaction() {
return new CypherTransaction(this, CypherTransaction.ResultType.row);
}
public QueryResult<Map<String, Object>> query(String statement, Map<String, Object> params, ResultConverter resultConverter) {
final CypherResult result = query(statement, params);
if (RestResultException.isExceptionResult(result.asMap())) throw new RestResultException(result.asMap());
return RestQueryResult.toQueryResult(result, this, resultConverter);
}
@Override
public RestTraverser traverse(RestNode restNode, Map<String, Object> description) {
return restAPI.traverse(restNode, description);
}
public RequestResult batch(Collection<Map<String, Object>> batchRequestData) {
return restAPI.batch(batchRequestData);
}
@Override
@SuppressWarnings("unchecked")
public <T extends PropertyContainer> RestIndex<T> getIndex(String indexName) {
return restAPI.getIndex(indexName);
}
@Override
@SuppressWarnings("unchecked")
public void createIndex(String type, String indexName, Map<String, String> config) {
restAPI.createIndex(type, indexName, config);
}
@Override
@SuppressWarnings("unchecked")
public <T extends PropertyContainer> RestIndex<T> createIndex(Class<T> type, String indexName, Map<String, String> config) {
return restAPI.createIndex(type,indexName,config);
}
@Override
public void close() {
restAPI.close();
}
@Override
public boolean isAutoIndexingEnabled(Class<? extends PropertyContainer> clazz) {
return restAPI.isAutoIndexingEnabled(clazz);
}
@Override
public void setAutoIndexingEnabled(Class<? extends PropertyContainer> clazz, boolean enabled) {
restAPI.setAutoIndexingEnabled(clazz, enabled);
}
@Override
public Set<String> getAutoIndexedProperties(Class forClass) {
return restAPI.getAutoIndexedProperties(forClass);
}
@Override
public void startAutoIndexingProperty(Class forClass, String s) {
restAPI.startAutoIndexingProperty(forClass,s);
}
@Override
public void stopAutoIndexingProperty(Class forClass, String s) {
restAPI.stopAutoIndexingProperty(forClass, s);
}
@Override
public void delete(RestIndex index) {
restAPI.delete(index);
}
@Override
public <T extends PropertyContainer> void removeFromIndex(RestIndex index, T entity, String key, Object value) {
restAPI.removeFromIndex(index, entity, key, value);
}
@Override
public <T extends PropertyContainer> void removeFromIndex(RestIndex index, T entity, String key) {
restAPI.removeFromIndex(index,entity,key);
}
@Override
public <T extends PropertyContainer> void removeFromIndex(RestIndex index, T entity) {
restAPI.removeFromIndex(index,entity);
}
@Override
public <T extends PropertyContainer> void addToIndex(T entity, RestIndex index, String key, Object value) {
restAPI.addToIndex(entity,index,key,value);
}
@Override
@SuppressWarnings("unchecked")
public <T extends PropertyContainer> T putIfAbsent(T entity, RestIndex index, String key, Object value) {
return restAPI.putIfAbsent(entity, index, key, value);
}
@Override
public boolean hasToUpdate(long lastUpdate) {
return restAPI.hasToUpdate(lastUpdate);
}
@Override
public IndexInfo indexInfo(final String indexType) {
return restAPI.indexInfo(indexType);
}
@Override
public Collection<String> getAllLabelNames() {
return restAPI.getAllLabelNames();
}
@Override
public Iterable<RelationshipType> getRelationshipTypes() {
return restAPI.getRelationshipTypes();
}
@Override
public TraversalDescription createTraversalDescription() {
return restAPI.createTraversalDescription();
}
public String getBaseUri() {
return restRequest.getUri();
}
@Override
public RestEntityExtractor getEntityExtractor() {
return restAPI.getEntityExtractor();
}
@Override
public RestEntity createRestEntity(Map data) {
return restAPI.createRestEntity(data);
}
}

View File

@@ -0,0 +1,717 @@
/**
* Copyright (c) 2002-2013 "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 <http://www.gnu.org/licenses/>.
*/
package org.neo4j.rest.graphdb;
import org.neo4j.graphdb.*;
import org.neo4j.graphdb.index.IndexHits;
import org.neo4j.graphdb.traversal.TraversalDescription;
import org.neo4j.helpers.collection.IterableWrapper;
import org.neo4j.helpers.collection.MapUtil;
import org.neo4j.index.lucene.ValueContext;
import org.neo4j.rest.graphdb.batch.BatchRestAPI;
import org.neo4j.rest.graphdb.entity.RestEntityCache;
import org.neo4j.rest.graphdb.query.CypherRestResult;
import org.neo4j.rest.graphdb.converter.RelationshipIterableConverter;
import org.neo4j.rest.graphdb.converter.RestEntityExtractor;
import org.neo4j.rest.graphdb.converter.RestIndexHitsConverter;
import org.neo4j.rest.graphdb.entity.RestEntity;
import org.neo4j.rest.graphdb.entity.RestNode;
import org.neo4j.rest.graphdb.entity.RestRelationship;
import org.neo4j.rest.graphdb.index.IndexInfo;
import org.neo4j.rest.graphdb.index.RestIndex;
import org.neo4j.rest.graphdb.index.RestIndexManager;
import org.neo4j.rest.graphdb.index.RetrievedIndexInfo;
import org.neo4j.rest.graphdb.index.SimpleIndexHits;
import org.neo4j.rest.graphdb.query.CypherResult;
import org.neo4j.rest.graphdb.query.RestQueryResult;
import org.neo4j.rest.graphdb.transaction.NullTransaction;
import org.neo4j.rest.graphdb.traversal.RestDirection;
import org.neo4j.rest.graphdb.traversal.RestTraversal;
import org.neo4j.rest.graphdb.traversal.RestTraverser;
import org.neo4j.rest.graphdb.util.JsonHelper;
import org.neo4j.rest.graphdb.util.QueryResult;
import org.neo4j.rest.graphdb.util.ResultConverter;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.Response.Status;
import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.util.*;
import java.util.concurrent.TimeUnit;
import static javax.ws.rs.core.Response.Status.CREATED;
import static org.neo4j.helpers.collection.MapUtil.map;
import static org.neo4j.rest.graphdb.ExecutingRestRequest.encode;
public class RestAPIImpl implements RestAPI {
public static final String _QUERY_RETURN_NODE = " RETURN id(n) as id, labels(n) as labels, n as data";
public static final String GET_REL_TYPES_QUERY = "MATCH (n)-[r]-() WHERE id(n) = {id} RETURN distinct type(r) as relType";
private static final String[] NO_LABELS = new String[0];
protected RestRequest restRequest;
private long entityRefetchTimeInMillis = TimeUnit.SECONDS.toMillis(1000); //TODO move to cache
private final RestEntityCache entityCache = new RestEntityCache(this);
private RestEntityExtractor restEntityExtractor = new RestEntityExtractor(this);
public RestAPIImpl(String uri) {
this.restRequest = createRestRequest(uri, null, null);
}
public RestAPIImpl(String uri, String user, String password) {
this.restRequest = createRestRequest(uri, user, password);
}
protected RestRequest createRestRequest(String uri, String user, String password) {
return new ExecutingRestRequest(uri, user, password);
}
@Override
public RestIndexManager index() {
return new RestIndexManager(this);
}
@Override
public RestNode getNodeById(long id, Load force) {
if (force != Load.ForceFromServer) {
RestNode restNode = entityCache.getNode(id);
if (restNode != null) return restNode;
}
if (force == Load.FromCache) return new RestNode(RestNode.nodeUri(this, id),this);
BatchRestAPI batchRestAPI = new BatchRestAPI(this);
RestNode node = batchRestAPI.getNodeById(id);
// RequestResult response = restRequest.get("node/" + id);
// if (response.statusIs(Status.NOT_FOUND)) {
// throw new NotFoundException("" + id);
// }
// Collection<String> labels = getNodeLabels(id);
// RestNode node = new RestNode(id, labels, (Map<String, Object>) response.toMap(), this);
return entityCache.addToCache(node);
}
@Override
public RestNode addToCache(RestNode restNode) {
return entityCache.addToCache(restNode);
}
@Override
public RestNode getFromCache(long id) {
return entityCache.getNode(id);
}
@Override
public RestNode getNodeById(long id) {
return getNodeById(id, Load.FromServer);
}
@Override
public RestRelationship getRelationshipById(long id) {
RequestResult requestResult = restRequest.get("relationship/" + id);
if (requestResult.statusIs(Status.NOT_FOUND)) {
throw new NotFoundException("" + id);
}
return new RestRelationship(requestResult.toMap(), this);
}
@Override
public RestNode createNode(Map<String, Object> props) {
return createNode(props,Collections.<String>emptyList());
}
@Override
public RestNode createNode(Map<String, Object> props, Collection<String> labels) {
RequestResult result = restRequest.post("node", props);
RestNode node = createRestNode(result);
if (node==null) {
throw RestResultException.create(result);
}
addLabels(node,labels);
node.setLabels(labels);
return entityCache.addToCache(node);
}
@Override
public RestNode getOrCreateNode(RestIndex<Node> index, String key, Object value, final Map<String, Object> properties, Collection<String> labels) {
if (index==null || key == null || value==null) throw new IllegalArgumentException("Unique index "+index+" key "+key+" value must not be null");
final Map<String, Object> data = map("key", key, "value", value, "properties", properties);
final RequestResult result = getRestRequest().post(uniqueIndexPath(index), data);
if (result.statusIs(Response.Status.CREATED) || result.statusIs(Response.Status.OK)) {
RestNode node = (RestNode) getEntityExtractor().convertFromRepresentation(result);
addLabels(node, labels);
node.setLabels(labels);
return entityCache.addToCache(node);
}
throw new RuntimeException(String.format("Error retrieving or creating node for key %s and value %s with index %s", key, value, index.getIndexName()));
}
private String toLabelString(String[] labels) {
if (labels==null || labels.length == 0) return "";
StringBuilder sb = new StringBuilder();
for (String label : labels) {
sb.append(":").append(label);
}
return sb.toString();
}
private RestNode createRestNode(RequestResult result) {
if (result.statusIs(Status.NOT_FOUND)) {
throw new NotFoundException("Node not found");
}
RestNode node = null;
if (result.statusIs(CREATED)) {
node = result.isMap() ? new RestNode(result.toMap(), this) : new RestNode(result.getLocation(), this);
}
if (node == null && result.statusIs(Status.OK)) {
node = new RestNode(result.toMap(), this);
}
return entityCache.addToCache(node);
}
@Override
public RestRelationship createRelationship(Node startNode, Node endNode, RelationshipType type, Map<String, Object> props) {
// final RestRequest restRequest = ((RestNode) startNode).getRestRequest();
final RestNode end = (RestNode) endNode;
Map<String, Object> data = map("to", end.getUri(), "type", type.name());
if (props != null && props.size() > 0) {
data.put("data", props);
}
final RestNode start = (RestNode) startNode;
RequestResult requestResult = getRestRequest().with(start.getUri()).post("relationships", data);
return createRestRelationship(requestResult, startNode);
}
private RestRelationship createRestRelationship(RequestResult requestResult, PropertyContainer element) {
if (requestResult.statusOtherThan(CREATED)) {
final int status = requestResult.getStatus();
throw new RuntimeException("Error creating relationship " + status+" "+requestResult.getText());
}
final String location = requestResult.getLocation();
if (requestResult.isMap()) return new RestRelationship(requestResult.toMap(), this);
return new RestRelationship(location, this);
}
@Override
@SuppressWarnings("unchecked")
public <T extends PropertyContainer> RestIndex<T> getIndex(String indexName) {
final RestIndexManager index = this.index();
if (index.existsForNodes(indexName)) return (RestIndex<T>) index.forNodes(indexName);
if (index.existsForRelationships(indexName)) return (RestIndex<T>) index.forRelationships(indexName);
throw new IllegalArgumentException("Index " + indexName + " does not yet exist");
}
@Override
@SuppressWarnings("unchecked")
public void createIndex(String type, String indexName, Map<String, String> config) {
Map<String,Object> data=new HashMap<String, Object>();
data.put("name",indexName);
data.put("config",config);
restRequest.post("index/" + type, data);
}
@Override
@SuppressWarnings("unchecked")
public <T extends PropertyContainer> RestIndex<T> createIndex(Class<T> type, String indexName, Map<String, String> config) {
if (Node.class.isAssignableFrom(type)) {
return (RestIndex<T>) index().forNodes( indexName, config);
}
if (Relationship.class.isAssignableFrom(type)) {
return (RestIndex<T>) index().forRelationships(indexName, config);
}
throw new IllegalArgumentException("Required Node or Relationship types to create index, got " + type);
}
@Override
public void close() {
ExecutingRestRequest.shutdown();
}
@Override
public RestNode merge(String labelName, String key, Object value, final Map<String, Object> nodeProperties, Collection<String> labels) {
if (labelName ==null || key == null || value==null) throw new IllegalArgumentException("Label "+ labelName +" key "+key+" and value must not be null");
Map props = nodeProperties.containsKey(key) ? nodeProperties : MapUtil.copyAndPut(nodeProperties, key, value);
Map<String, Object> params = map("props", props, "value", value);
Iterator<List<Object>> result = query(mergeQuery(labelName, key, labels), params).getData().iterator();
if (!result.hasNext())
throw new RuntimeException("Error merging node with labels: " + labelName + " key " + key + " value " + value + " labels " + labels+ " and props: " + props + " no data returned");
return entityCache.addToCache(toNode(result.next()));
}
private String mergeQuery(String labelName, String key, Collection<String> labels) {
StringBuilder setLabels = new StringBuilder();
if (labels!=null) {
for (String label : labels) {
if (label.equals(labelName)) continue;
setLabels.append("SET n:").append(label).append(" ");
}
}
return "MERGE (n:`"+labelName+"` {`"+key+"`: {value}}) ON CREATE SET n={props} "+setLabels+ _QUERY_RETURN_NODE;
}
@Override
public boolean isAutoIndexingEnabled(Class<? extends PropertyContainer> clazz) {
RequestResult response = getRestRequest().get(buildPathAutoIndexerStatus(clazz));
if (response.statusIs(Response.Status.OK)) {
return Boolean.parseBoolean(response.getText());
} else {
throw new IllegalStateException("received " + response);
}
}
@Override
public void setAutoIndexingEnabled(Class<? extends PropertyContainer> clazz, boolean enabled) {
RequestResult response = getRestRequest().put(buildPathAutoIndexerStatus(clazz), enabled);
if (response.statusOtherThan(Status.NO_CONTENT)) {
throw new IllegalStateException("received " + response);
}
}
@Override
public Set<String> getAutoIndexedProperties(Class forClass) {
RequestResult response = getRestRequest().get(buildPathAutoIndexerProperties(forClass).toString());
Collection<String> autoIndexedProperties = (Collection<String>) JsonHelper.readJson(response.getText());
return new HashSet<String>(autoIndexedProperties);
}
@Override
public void startAutoIndexingProperty(Class forClass, String s) {
try {
// we need to use a inputstream instead of the string directly. Otherwise "post" implicitly uses
// StreamJsonHelper.writeJsonTo which quotes a given string
InputStream stream = new ByteArrayInputStream(s.getBytes("UTF-8"));
RequestResult response = getRestRequest().post(buildPathAutoIndexerProperties(forClass).toString(), stream);
if (response.statusOtherThan(Status.NO_CONTENT)) {
throw new IllegalStateException("received " + response);
}
} catch (UnsupportedEncodingException e) {
throw new IllegalStateException(e);
}
}
@Override
public void stopAutoIndexingProperty(Class forClass, String s) {
RequestResult response = getRestRequest().delete(buildPathAutoIndexerProperties(forClass).append("/").append(s).toString());
if (response.statusOtherThan(Status.NO_CONTENT)) {
throw new IllegalStateException("received " + response);
}
}
@Override
public void removeLabel(RestNode node, String label) {
RequestResult response = getRestRequest().with(node.getUri()).delete("labels/" + encode(label));
if (response.statusOtherThan(Status.NO_CONTENT)) {
throw new IllegalStateException("received " + response);
}
}
public Collection<String> getNodeLabels(long id) {
RequestResult response = restRequest.get(RestNode.nodeUri(this,id)+"/labels");
if (response.statusOtherThan(Status.OK)) {
throw new IllegalStateException("received " + response);
}
return (Collection<String>) response.toEntity();
}
@Override
public Collection<String> getAllLabelNames() {
RequestResult response = restRequest.get("labels");
if (response.statusOtherThan(Status.OK)) {
throw new IllegalStateException("received " + response);
}
return (Collection<String>) response.toEntity();
}
@Override
public Iterable<RestNode> getNodesByLabel(String label) {
RequestResult response = getRestRequest().get("label/" + encode(label) + "/nodes");
if (response.statusOtherThan(Status.OK)) {
throw new IllegalStateException("received " + response);
}
return (Iterable<RestNode>) getEntityExtractor().convertFromRepresentation(response);
}
private RestNode toNode(List<Object> row) {
long id = ((Number) row.get(0)).longValue();
List<String> labels = (List<String>) row.get(1);
Map<String,Object> restData = (Map<String, Object>) row.get(2);
return new RestNode(id, labels, restData, this);
}
private Iterable<RestNode> queryForNodes(String statement, Map<String, Object> params) {
Iterable<List<Object>> result = query(statement, params).getData();
return new IterableWrapper<RestNode,List<Object>>(result) {
protected RestNode underlyingObjectToObject(List<Object> row) {
return entityCache.addToCache(toNode(row));
}
};
}
@Override
public Iterable<RestNode> getNodesByLabelAndProperty(String label, String property, Object value) {
String statement = "MATCH (n:`" + label + "`) WHERE n.`"+property+"` = {value} " + _QUERY_RETURN_NODE;
return queryForNodes(statement, map("value", value));
}
@Override
public Iterable<RelationshipType> getRelationshipTypes(RestNode node) {
Iterable<List<Object>> result = query(GET_REL_TYPES_QUERY, map("id", node.getId())).getData();
return new IterableWrapper<RelationshipType, List<Object>>(result) {
protected RelationshipType underlyingObjectToObject(List<Object> row) {
return DynamicRelationshipType.withName(row.get(0).toString());
}
};
}
@Override
public int getDegree(RestNode restNode, RelationshipType type, Direction direction) {
String relPattern = "--";
if (type != null) relPattern = "-[:`"+type+"`]-";
if (direction == Direction.OUTGOING) {
relPattern += ">";
} else if (direction == Direction.INCOMING) {
relPattern = "<" + relPattern;
}
String nodeDegreeQuery = "MATCH (n)" + relPattern + "() WHERE id(n) = {id} RETURN count(*) as degree";
Iterator<List<Object>> degree = query(nodeDegreeQuery, map("id", restNode.getId())).getData().iterator();
if (!degree.hasNext()) return 0;
return ((Number)degree.next().get(0)).intValue();
}
@Override
public Iterable<RelationshipType> getRelationshipTypes() {
Object result = restRequest.get("relationship/types").toEntity();
if (!(result instanceof Iterable)) throw new RuntimeException("Error loading relationship types");
return new IterableWrapper<RelationshipType, Object>((Iterable<Object>) result) {
protected RelationshipType underlyingObjectToObject(Object type) {
return DynamicRelationshipType.withName(type.toString());
}
};
}
@Override
public void addLabels(RestNode node, Collection<String> labels) {
if (labels == null || labels.isEmpty()) return;
RequestResult response = getRestRequest().with(node.getUri()).post("labels", labels);
if (response.statusOtherThan(Status.NO_CONTENT)) {
throw new IllegalStateException("error adding labels, received " + response);
}
}
private String buildPathAutoIndexerStatus(Class<? extends PropertyContainer> clazz) {
return buildPathAutoIndexerBase(clazz).append("/status").toString();
}
private StringBuilder buildPathAutoIndexerProperties(Class<? extends PropertyContainer> clazz) {
return buildPathAutoIndexerBase(clazz).append("/properties");
}
private StringBuilder buildPathAutoIndexerBase(Class<? extends PropertyContainer> clazz) {
return new StringBuilder().append("index/auto/").append(indexTypeName(clazz));
}
public RestRequest getRestRequest() {
return restRequest;
}
@Override
public TraversalDescription createTraversalDescription() {
return new RestTraversal();
}
@Override
public Transaction beginTx() {
return new NullTransaction();
}
public long getEntityRefetchTimeInMillis() {
return entityRefetchTimeInMillis;
}
public String getBaseUri() {
return restRequest.getUri();
}
public void setEntityRefetchTimeInMillis(long entityRefetchTimeInMillis) {
this.entityRefetchTimeInMillis = entityRefetchTimeInMillis;
}
@SuppressWarnings("unchecked")
public Iterable<Relationship> wrapRelationships(RequestResult requestResult) {
return (Iterable<Relationship>) new RelationshipIterableConverter(this).convertFromRepresentation(requestResult);
}
public RestEntityExtractor getEntityExtractor() {
return restEntityExtractor;
}
public String indexPath( Class entityType, String indexName, String key, Object value ) {
String typeName = indexTypeName(entityType);
return "index/" + typeName + "/" + encode(indexName) + (key!=null? "/" + encode(key) :"") + (value!=null ? "/" + encode(value):"");
}
private String indexTypeName(Class entityType) {
return entityType.getSimpleName().toLowerCase();
}
public String indexPath( Class entityType, String indexName ) {
return "index/" + indexTypeName(entityType) + "/" + encode(indexName);
}
private String queryPath( Class entityType, String indexName, String key, Object value ) {
return indexPath( entityType, indexName, key,null) + "?query="+ encode(value);
}
@Override
@SuppressWarnings("unchecked")
public <S extends PropertyContainer> IndexHits<S> getIndex(Class<S> entityType, String indexName, String key, Object value) {
String indexPath = indexPath(entityType, indexName, key, value);
RequestResult response = restRequest.get(indexPath);
if (response.statusIs(Response.Status.OK)) {
return new RestIndexHitsConverter(this, entityType).convertFromRepresentation(response);
} else {
return new SimpleIndexHits<S>(Collections.emptyList(), 0, entityType, this);
}
}
@Override
@SuppressWarnings("unchecked")
public <S extends PropertyContainer> IndexHits<S> queryIndex(Class<S> entityType, String indexName, String key, Object value) {
String indexPath = queryPath(entityType, indexName, key, value);
RequestResult response = restRequest.get(indexPath);
if (response.statusIs(Response.Status.OK)) {
return new RestIndexHitsConverter(this, entityType).convertFromRepresentation(response);
} else {
return new SimpleIndexHits<S>(Collections.emptyList(), 0, entityType, this);
}
}
@Override
public void deleteEntity(RestEntity entity) {
getRestRequest().with(entity.getUri()).delete( "" );
entityCache.remove(entity.getId());
}
@Override
public IndexInfo indexInfo(final String indexType) {
RequestResult response = restRequest.get("index/" + encode(indexType));
return new RetrievedIndexInfo(response);
}
@Override
public void setPropertyOnEntity(RestEntity entity, String key, Object value) {
RequestResult result = getRestRequest().with(entity.getUri()).put("properties/" + encode(key), value);
if (result.statusOtherThan(Status.NO_CONTENT))
throw new RuntimeException("Error setting properties on entity "+entity+" properties "+key+" : "+value);
}
@Override
public void setPropertiesOnEntity(RestEntity entity, Map<String,Object> properties) {
RequestResult result = getRestRequest().with(entity.getUri()).put("properties", properties);
if (result.statusOtherThan(Status.NO_CONTENT))
throw new RuntimeException("Error setting properties on entity "+entity+" properties "+properties);
}
@SuppressWarnings("unchecked")
private Map<String, Object> getPropertiesFromEntity(RestEntity entity){
RequestResult response = getRestRequest().with(entity.getUri()).get("properties");
Map<String, Object> properties;
boolean ok = response.statusIs( Status.OK );
if ( ok ) {
properties = (Map<String, Object>) response.toMap( );
} else {
properties = Collections.emptyMap();
}
return properties;
}
private void deleteIndex(String indexPath) {
getRestRequest().delete(indexPath);
}
@Override
public void delete(RestIndex index) {
deleteIndex(indexPath(index, null, null));
}
@Override
public <T extends PropertyContainer> void removeFromIndex(RestIndex index, T entity, String key, Object value) {
String indexPath = indexPath(index, key, value);
deleteIndex(indexPath(indexPath, entity));
}
protected <T extends PropertyContainer> String indexPath(String indexPath, T restEntity) {
return indexPath + "/" + ((RestEntity)restEntity).getId();
}
@Override
public <T extends PropertyContainer> void removeFromIndex(RestIndex index, T entity, String key) {
String indexPath = indexPath(index, key, null);
deleteIndex(indexPath(indexPath, entity));
}
private String indexPath(RestIndex index, String key, Object value) {
return indexPath(index.getEntityType(), index.getIndexName(), key, value);
}
@Override
public <T extends PropertyContainer> void removeFromIndex(RestIndex index, T entity) {
deleteIndex(indexPath(indexPath(index, null, null), entity));
}
public String uniqueIndexPath(RestIndex index) {
return indexPath(index,null,null) + "?uniqueness=get_or_create";
}
@Override
public <T extends PropertyContainer> void addToIndex(T entity, RestIndex index, String key, Object value) {
final RestEntity restEntity = (RestEntity) entity;
String uri = restEntity.getUri();
if (value instanceof ValueContext) {
value = ((ValueContext)value).getCorrectValue();
}
final Map<String, Object> data = map("key", key, "value", value, "uri", uri);
final RequestResult result = getRestRequest().post(indexPath(index, null, null), data);
if (result.statusOtherThan(Status.CREATED)) throw new RuntimeException(String.format("Error adding element %d %s %s to index %s", restEntity.getId(), key, value, index.getIndexName()));
}
@Override
@SuppressWarnings("unchecked")
public <T extends PropertyContainer> T putIfAbsent(T entity, RestIndex index, String key, Object value) {
final RestEntity restEntity = (RestEntity) entity;
restEntity.flush();
String uri = restEntity.getUri();
if (value instanceof ValueContext) {
value = ((ValueContext)value).getCorrectValue();
}
final Map<String, Object> data = map("key", key, "value", value, "uri", uri);
final RequestResult result = getRestRequest().post(uniqueIndexPath(index), data);
if (result.statusIs(Response.Status.CREATED)) {
if (index.getEntityType().equals(Node.class)) return (T)createRestNode(result);
if (index.getEntityType().equals(Relationship.class)) return (T)createRestRelationship(result,restEntity);
}
if (result.statusIs(Response.Status.OK)) {
return (T) getEntityExtractor().convertFromRepresentation(result);
}
throw new RuntimeException(String.format("Error adding element %d %s %s to index %s", restEntity.getId(), key, value, index.getIndexName()));
}
@Override
public boolean hasToUpdate(long lastUpdate) {
return timeElapsed(lastUpdate, getEntityRefetchTimeInMillis());
}
@Override
public void removeProperty(RestEntity entity, String key) {
restRequest.with(entity.getUri()).delete("properties/" + encode(key));
}
private boolean timeElapsed( long since, long isItGreaterThanThis ) {
return System.currentTimeMillis() - since > isItGreaterThanThis;
}
@Override
public RestRelationship getOrCreateRelationship(RestIndex<Relationship> index, String key, Object value, final RestNode start, final RestNode end, final String type, final Map<String, Object> properties) {
if (index==null || key == null || value==null) throw new IllegalArgumentException("Unique index "+index+" key "+key+" value must not be null");
if (start == null || end == null || type == null) throw new IllegalArgumentException("Neither start, end nore type must be null");
final Map<String, Object> data = map("key", key, "value", value, "properties", properties, "start", start.getUri(), "end", end.getUri(), "type", type);
final RequestResult result = getRestRequest().post(uniqueIndexPath(index), data);
if (result.statusIs(Response.Status.CREATED) || result.statusIs(Response.Status.OK)) {
return (RestRelationship) getEntityExtractor().convertFromRepresentation(result);
}
throw new RuntimeException(String.format("Error retrieving or creating relationship for key %s and value %s with index %s", key, value, index.getIndexName()));
}
public org.neo4j.rest.graphdb.query.CypherResult query(String statement, Map<String, Object> params) {
params = (params==null) ? Collections.<String,Object>emptyMap() : params;
final RequestResult requestResult = getRestRequest().post("cypher", map("query", statement, "params", params));
return new CypherRestResult(requestResult);
}
@Override
public Iterable<Relationship> getRelationships(RestNode restNode, Direction direction, RelationshipType... types) {
String path = (types.length > 1) ? relPath(types) : relPath(direction,types.length == 0 ? null : types[0]);
return wrapRelationships(getRestRequest().with(restNode.getUri()).get(path));
}
private String relPath(RelationshipType... types) {
String path = "relationships/all/";
int counter = 0;
for ( RelationshipType type : types ) {
if ( counter++ > 0 ) {
path += "&";
}
path += encode(type.name());
}
return path;
}
private String relPath(Direction direction,RelationshipType type) {
String path = "relationships/" + RestDirection.from(direction).shortName;
return type == null ? path : path + "/" + encode(type.name());
}
private static final String FULLPATH = "fullpath";
@Override
public RestTraverser traverse(RestNode restNode, Map<String, Object> description) {
final RequestResult result = getRestRequest().with(restNode.getUri()).post("traverse/" + FULLPATH, description);
if (result.statusOtherThan(Response.Status.OK)) throw new RuntimeException(String.format("Error executing traversal: %d %s",result.getStatus(), description));
final Object col = result.toEntity();
if (!(col instanceof Collection)) throw new RuntimeException(String.format("Unexpected traversal result, %s instead of collection", col != null ? col.getClass() : null));
return new RestTraverser((Collection) col,restNode.getRestApi());
}
public QueryResult<Map<String, Object>> query(String statement, Map<String, Object> params, ResultConverter resultConverter) {
final CypherResult result = query(statement, params);
if (RestResultException.isExceptionResult(result.asMap())) throw new RestResultException(result.asMap());
return RestQueryResult.toQueryResult(result, this, resultConverter);
}
@Override
public RestEntity createRestEntity(Map data) {
final String uri = (String) data.get("self");
if (uri == null || uri.isEmpty()) return null;
if (uri.contains("/node/")) {
return entityCache.addToCache(new RestNode(data, this));
}
if (uri.contains("/relationship/")) {
return new RestRelationship(data, this);
}
return null;
}
public RequestResult batch(Collection<Map<String, Object>> batchRequestData) {
return restRequest.post("batch",batchRequestData);
}
}

View File

@@ -0,0 +1,65 @@
package org.neo4j.rest.graphdb;
import org.neo4j.graphdb.Node;
import org.neo4j.graphdb.PropertyContainer;
import org.neo4j.graphdb.Relationship;
import org.neo4j.graphdb.index.IndexHits;
import org.neo4j.rest.graphdb.entity.RestNode;
import org.neo4j.rest.graphdb.entity.RestRelationship;
import org.neo4j.rest.graphdb.index.IndexInfo;
import org.neo4j.rest.graphdb.index.RestIndex;
import org.neo4j.rest.graphdb.index.RestIndexManager;
import java.util.Collection;
import java.util.Map;
import java.util.Set;
/**
* @author mh
* @since 21.09.14
*/
public interface RestAPIIndex {
RestIndexManager index();
@SuppressWarnings("unchecked")
<T extends PropertyContainer> RestIndex<T> getIndex(String indexName);
@SuppressWarnings("unchecked")
void createIndex(String type, String indexName, Map<String, String> config);
@SuppressWarnings("unchecked")
<S extends PropertyContainer> IndexHits<S> getIndex(Class<S> entityType, String indexName, String key, Object value);
<S extends PropertyContainer> IndexHits<S> queryIndex(Class<S> entityType, String indexName, String key, Object value);
IndexInfo indexInfo(String indexType);
<T extends PropertyContainer> void removeFromIndex(RestIndex index, T entity, String key, Object value);
<T extends PropertyContainer> void removeFromIndex(RestIndex index, T entity, String key);
<T extends PropertyContainer> void removeFromIndex(RestIndex index, T entity);
<T extends PropertyContainer> void addToIndex(T entity, RestIndex index, String key, Object value);
@SuppressWarnings("unchecked")
<T extends PropertyContainer> T putIfAbsent(T entity, RestIndex index, String key, Object value);
RestNode getOrCreateNode(RestIndex<Node> index, String key, Object value, Map<String, Object> properties, Collection<String> labels);
RestRelationship getOrCreateRelationship(RestIndex<Relationship> index, String key, Object value, RestNode start, RestNode end, String type, Map<String, Object> properties);
@SuppressWarnings("unchecked")
<T extends PropertyContainer> RestIndex<T> createIndex(Class<T> type, String indexName, Map<String, String> config);
boolean isAutoIndexingEnabled(Class<? extends PropertyContainer> clazz);
void setAutoIndexingEnabled(Class<? extends PropertyContainer> clazz, boolean enabled);
Set<String> getAutoIndexedProperties(Class forClass);
void startAutoIndexingProperty(Class forClass, String s);
void stopAutoIndexingProperty(Class forClass, String s);
void delete(RestIndex index);
}

View File

@@ -0,0 +1,30 @@
package org.neo4j.rest.graphdb;
import org.neo4j.rest.graphdb.converter.RestEntityExtractor;
import org.neo4j.rest.graphdb.entity.RestEntity;
import org.neo4j.rest.graphdb.entity.RestNode;
import java.util.Map;
/**
* @author mh
* @since 21.09.14
*/
public interface RestAPIInternal {
RestNode getNodeById(long id, RestAPI.Load force);
boolean hasToUpdate(long lastUpdate);
String getBaseUri();
RestEntityExtractor getEntityExtractor();
// todo add to cache or update data in cache
RestEntity createRestEntity(Map data);
public enum Load {
FromCache,
FromServer,
ForceFromServer
}
}

View File

@@ -0,0 +1,163 @@
/**
* Copyright (c) 2002-2013 "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 <http://www.gnu.org/licenses/>.
*/
package org.neo4j.rest.graphdb;
import org.neo4j.graphdb.*;
import org.neo4j.graphdb.schema.Schema;
import org.neo4j.graphdb.traversal.BidirectionalTraversalDescription;
import org.neo4j.kernel.impl.nioneo.store.StoreId;
import org.neo4j.rest.graphdb.entity.RestNode;
import org.neo4j.rest.graphdb.index.RestIndexManager;
import org.neo4j.rest.graphdb.query.RestCypherQueryEngine;
import org.neo4j.rest.graphdb.transaction.NullTransactionManager;
import org.neo4j.rest.graphdb.traversal.RestTraversal;
import org.neo4j.rest.graphdb.traversal.RestTraversalDescription;
import org.neo4j.rest.graphdb.util.ResourceIterableWrapper;
import javax.transaction.TransactionManager;
import java.util.Collection;
import java.util.LinkedHashSet;
public class RestGraphDatabase extends AbstractRemoteDatabase {
private RestAPI restAPI;
private final RestCypherQueryEngine cypherQueryEngine;
public RestGraphDatabase( RestAPI api){
this.restAPI = api;
cypherQueryEngine = new RestCypherQueryEngine(restAPI);
}
public RestGraphDatabase( String uri ) {
this( new RestAPIImpl( uri ));
}
public RestGraphDatabase( String uri, String user, String password ) {
this(new RestAPIImpl( uri, user, password ));
}
public RestAPI getRestAPI(){
return this.restAPI;
}
public RestIndexManager index() {
return this.restAPI.index();
}
public Node createNode() {
return this.restAPI.createNode(null);
}
public Node getNodeById( long id ) {
return this.restAPI.getNodeById(id);
}
@Override
public Iterable<Node> getAllNodes() {
return cypherQueryEngine.query("match (n) return n", null).to(Node.class);
}
@Override
public Iterable<RelationshipType> getRelationshipTypes() {
return this.restAPI.getRelationshipTypes();
}
public Relationship getRelationshipById( long id ) {
return this.restAPI.getRelationshipById(id);
}
@Override
public String getStoreDir() {
return restAPI.getBaseUri();
}
@Override
public StoreId storeId() {
return null;
}
@Override
public boolean isAvailable(long timeout) {
return restAPI!=null;
}
public TransactionManager getTxManager() {
return new NullTransactionManager();
}
@Override
public DependencyResolver getDependencyResolver() {
return new DependencyResolver.Adapter() {
@Override
public <T> T resolveDependency(Class<T> type, SelectionStrategy selector) throws IllegalArgumentException {
if (TransactionManager.class.isAssignableFrom(type)) return (T)getTxManager();
return null;
}
};
}
@Override
public Transaction beginTx() {
return restAPI.beginTx();
}
@Override
public void shutdown() {
restAPI.close();
}
@Override
public Node createNode(Label... labels) {
LinkedHashSet<String> labelNames = new LinkedHashSet<>(labels.length);
for (int i = 0; i < labels.length; i++) labelNames.add(labels[i].name());
return restAPI.createNode(null,labelNames);
}
@Override
public ResourceIterable<Node> findNodesByLabelAndProperty(Label label, String property, Object value) {
Iterable<RestNode> nodes = restAPI.getNodesByLabelAndProperty(label.name(), property, value);
return new ResourceIterableWrapper<Node,RestNode>(nodes) {
protected Node underlyingObjectToObject(RestNode node) {
return node;
}
};
}
@Override
public Schema schema() {
throw new UnsupportedOperationException();
}
@Override
public RestTraversalDescription traversalDescription() {
return RestTraversal.description();
}
@Override
public BidirectionalTraversalDescription bidirectionalTraversalDescription() {
throw new UnsupportedOperationException();
}
public Collection<String> getAllLabelNames() {
return restAPI.getAllLabelNames();
}
}

View File

@@ -0,0 +1,42 @@
/**
* Copyright (c) 2002-2013 "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 <http://www.gnu.org/licenses/>.
*/
package org.neo4j.rest.graphdb;
import java.util.Map;
public interface RestRequest {
RequestResult get(String path);
RequestResult get(String path, Object data);
RequestResult delete(String path);
RequestResult post(String path, Object data);
RequestResult put(String path, Object data);
RestRequest with(String uri);
String getUri();
Map<?, ?> toMap( RequestResult requestResult);
}

View File

@@ -0,0 +1,80 @@
/**
* Copyright (c) 2002-2013 "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 <http://www.gnu.org/licenses/>.
*/
package org.neo4j.rest.graphdb;
import java.util.List;
import java.util.Map;
public class RestResultException extends RuntimeException {
public RestResultException(Object result, String...messages) {
super(format(toMap(result),messages));
}
public RestResultException(String...messages) {
super(format(messages));
}
private static String format(String[] messages) {
StringBuilder sb = new StringBuilder();
if (messages==null || messages.length==0) return "";
for (String s : messages) {
sb.append(s).append("\n");
}
return sb.toString();
}
private static String format(Map<?, ?> result, String...messages) {
if (result==null && (messages==null || messages.length==0)) return "Unknown Exception";
StringBuilder sb = new StringBuilder();
sb.append(format(messages));
sb.append(result.get("message")).append(" at\n");
sb.append(result.get("exception")).append("\n");
List<String> stacktrace = (List<String>) result.get("stacktrace");
if (stacktrace != null) {
for (String line : stacktrace) {
sb.append(" ").append(line).append("\n");
}
}
if (result.containsKey("body")) sb.append(format(toMap(result.get("body"))));
return sb.toString();
}
public static boolean isExceptionResult(Object result) {
final Map<String, Object> map = toMap(result);
return map!=null && (hasErrorStatus(map) || map.containsKey("exception") || map.containsKey("message") || isExceptionResult(map.get("body")));
}
private static boolean hasErrorStatus(Map<String, Object> map) {
Object status = map.get("status");
return status != null && !status.toString().startsWith("2");
}
private static Map<String, Object> toMap(Object result) {
if (!(result instanceof Map)) return null;
return (Map<String, Object>) result;
}
static RestResultException create(RequestResult result, String...messages) {
if (result.isMap()) return new RestResultException(result.toMap(),messages);
if (messages.length==0) return new RestResultException("Error executing request,status " + result.getStatus()+" message "+result.getText());
return new RestResultException(messages);
}
}

View File

@@ -0,0 +1,28 @@
/**
* Copyright (c) 2002-2013 "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 <http://www.gnu.org/licenses/>.
*/
package org.neo4j.rest.graphdb;
/**
* @author mh
* @since 21.09.11
*/
public interface UpdatableRestResult<T> {
void updateFrom(T newValue, RestAPI restApi);
}

View File

@@ -0,0 +1,72 @@
/**
* Copyright (c) 2002-2013 "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 <http://www.gnu.org/licenses/>.
*/
package org.neo4j.rest.graphdb;
import java.io.IOException;
import java.io.InputStream;
import java.util.Properties;
import com.sun.jersey.api.client.Client;
import com.sun.jersey.api.client.ClientHandlerException;
import com.sun.jersey.api.client.ClientRequest;
import com.sun.jersey.api.client.ClientResponse;
import com.sun.jersey.api.client.filter.ClientFilter;
/**
* @author mh
* @since 10.08.12
*/
public class UserAgent {
public static final String NEO4J_DRIVER_PROPERTY = "org.neo4j.rest.driver";
private final String userAgent = determineUserAgent();
private String determineUserAgent() {
String property = System.getProperty(NEO4J_DRIVER_PROPERTY);
if (property == null || property.trim().isEmpty()) {
final Properties props = loadPomProperties();
return String.format("%s/%s", props.getProperty("artifactId", "neo4j-rest-graphdb"), props.getProperty("version", "0"));
}
return property;
}
private Properties loadPomProperties() {
final Properties props = new Properties();
try {
final InputStream is = getClass().getClassLoader().getResourceAsStream("/META-INF/maven/org.neo4j/neo4j-rest-graphdb/pom.properties");
if (is!=null) {
props.load(is);
is.close();
}
} catch (Exception e) {
// ignore
}
return props;
}
public void install(Client client) {
client.addFilter(new ClientFilter() {
@Override
public ClientResponse handle(ClientRequest cr) throws ClientHandlerException {
cr.getHeaders().add("User-Agent", userAgent);
return getNext().handle(cr);
}
});
}
}

View File

@@ -0,0 +1,174 @@
/**
* Copyright (c) 2002-2013 "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 <http://www.gnu.org/licenses/>.
*/
package org.neo4j.rest.graphdb.batch;
import org.neo4j.graphdb.NotFoundException;
import org.neo4j.graphdb.PropertyContainer;
import org.neo4j.helpers.collection.MapUtil;
import org.neo4j.index.lucene.ValueContext;
import org.neo4j.rest.graphdb.RequestResult;
import org.neo4j.rest.graphdb.RestAPI;
import org.neo4j.rest.graphdb.RestAPIImpl;
import org.neo4j.rest.graphdb.RestResultException;
import org.neo4j.rest.graphdb.converter.RestEntityExtractor;
import org.neo4j.rest.graphdb.converter.RestEntityPropertyRefresher;
import org.neo4j.rest.graphdb.entity.RestEntity;
import org.neo4j.rest.graphdb.entity.RestNode;
import org.neo4j.rest.graphdb.index.IndexInfo;
import org.neo4j.rest.graphdb.index.RestIndex;
import org.neo4j.rest.graphdb.util.DefaultConverter;
import javax.ws.rs.core.Response;
import java.util.*;
import static java.util.Arrays.asList;
public class BatchRestAPI {
private final RecordingRestRequest restRequest;
private final String baseUri;
private final RestAPI restApi;
public BatchRestAPI(RestAPI restApi) {
this.baseUri = restApi.getBaseUri();
this.restApi = restApi;
RestEntityExtractor converter = new RestEntityExtractor(restApi);
this.restRequest = new RecordingRestRequest(new RestOperations(converter), restApi.getBaseUri());
}
public void stop() {
restRequest.stop();
}
public RestNode getNodeById(long id) {
RestOperations.RestOperation nodeRequest = restRequest.get("node/" + id);
RestOperations.RestOperation labelsRequest = restRequest.get("node/" + id+"/labels");
try {
Map<Long, Object> results = executeBatchRequest();
RestNode node = (RestNode) results.get(nodeRequest.getBatchId());
List<String> labels = (List<String>) results.get(labelsRequest.getBatchId());
node.setLabels(labels);
return node;
} catch(RestResultException rre) {
if (rre.getMessage().contains("NodeNotFoundException")) throw new NotFoundException("Node not found: "+id);
else throw rre;
}
}
// public <T extends PropertyContainer> void addToIndex( T entity, RestIndex index, String key, Object value ) {
// final RestEntity restEntity = (RestEntity) entity;
// String uri = restEntity.getUri();
// if (value instanceof ValueContext) {
// value = ((ValueContext)value).getCorrectValue();
// }
// final Map<String, Object> data = MapUtil.map("key", key, "value", value, "uri", uri);
// restRequest.post(index.indexPath(), data);
// }
public Map<Long, Object> executeBatchRequest() {
stop();
RestOperations operations = restRequest.getOperations();
RequestResult response = restApi.batch(createBatchRequestData(operations));
return convertRequestResultToEntities(operations, response);
}
@SuppressWarnings("unchecked")
protected Map<Long, Object> convertRequestResultToEntities(RestOperations operations, RequestResult response) {
Object result = response.toEntity();
if (RestResultException.isExceptionResult(result)) {
throw new RestResultException(result);
}
Collection<Map<String, Object>> responseCollection = (Collection<Map<String, Object>>) result;
Map<Long, Object> mappedObjects = new HashMap<Long, Object>(responseCollection.size());
for (Map<String, Object> entry : responseCollection) {
if (RestResultException.isExceptionResult(entry)) {
throw new RestResultException(entry);
}
final Long batchId = getBatchId(entry);
final RequestResult subResult = RequestResult.extractFrom(entry);
RestOperations.RestOperation restOperation = operations.getOperation(batchId);
Object entity = restOperation.getResultConverter().convertFromRepresentation(subResult);
mappedObjects.put(batchId, entity);
}
return mappedObjects;
}
private Long getBatchId(Map<String, Object> entry) {
return ((Number) entry.get("id")).longValue();
}
protected Collection<Map<String, Object>> createBatchRequestData(RestOperations operations) {
Collection<Map<String, Object>> batch = new ArrayList<Map<String, Object>>();
for (RestOperations.RestOperation operation : operations.getRecordedRequests().values()) {
Map<String, Object> params = new HashMap<String, Object>();
params.put("method", operation.getMethod());
if (operation.isSameUri(baseUri)) {
params.put("to", operation.getUri());
} else {
params.put("to",createOperationUri(operation));
}
if (operation.getData() != null) {
params.put("body", operation.getData());
}
params.put("id", operation.getBatchId());
batch.add(params);
}
return batch;
}
private String createOperationUri(RestOperations.RestOperation operation){
String uri = operation.getBaseUri();
String suffix = operation.getUri();
if (suffix.startsWith("/")){
return uri + suffix;
}
return uri + "/" + suffix;
}
private static class BatchIndexInfo implements IndexInfo {
@Override
public boolean checkConfig(String indexName, Map<String, String> config) {
return true;
}
@Override
public String[] indexNames() {
return new String[0];
}
@Override
public boolean exists(String indexName) {
return true;
}
@Override
public Map<String, String> getConfig(String name) {
return null;
}
}
}

View File

@@ -0,0 +1,124 @@
/**
* Copyright (c) 2002-2013 "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 <http://www.gnu.org/licenses/>.
*/
package org.neo4j.rest.graphdb.batch;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.util.Map;
import javax.ws.rs.core.MediaType;
import org.neo4j.rest.graphdb.RequestResult;
import org.neo4j.rest.graphdb.RestRequest;
import org.neo4j.rest.graphdb.batch.RestOperations.RestOperation;
import org.neo4j.rest.graphdb.batch.RestOperations.RestOperation.Methods;
public class RecordingRestRequest {
private final String baseUri;
private MediaType contentType;
private MediaType acceptHeader;
private RestOperations operations;
private boolean stop;
public RestOperations getOperations() {
return operations;
}
public RecordingRestRequest(final String baseUri) {
this(baseUri, MediaType.APPLICATION_JSON_TYPE, MediaType.APPLICATION_JSON_TYPE );
}
public RecordingRestRequest(RestOperations operations, final String baseUri) {
this(baseUri);
this.operations = operations;
}
public RecordingRestRequest(String baseUri, MediaType contentType, MediaType acceptHeader) {
this.baseUri = uriWithoutSlash( baseUri );
this.contentType = contentType;
this.acceptHeader = acceptHeader;
}
public RestOperation get(String path, Object data) {
return this.record(Methods.GET, path, data, getBaseUri());
}
public RestOperation delete(String path) {
return this.record(Methods.DELETE, path, null, getBaseUri());
}
public RestOperation post(String path, Object data) {
return this.record(Methods.POST, path, data, getBaseUri());
}
public RestOperation put(String path, Object data) {
return this.record(Methods.PUT, path, data, getBaseUri());
}
public RecordingRestRequest with(String uri) {
return new RecordingRestRequest(this.operations, uri);
}
public String getUri() {
return getBaseUri();
}
public RestOperation get(String path) {
return this.record(Methods.GET, path, null, getBaseUri());
}
public RestOperation record(Methods method, String path, Object data, String baseUri){
if (stop) throw new IllegalStateException("BatchRequest already executed");
return this.operations.record(method, path, data,baseUri);
}
private String uriWithoutSlash( String uri ) {
return uri.endsWith("/") ? uri.substring(0, uri.length() - 1) : uri;
}
public static String encode( Object value ) {
if ( value == null ) return "";
try {
return URLEncoder.encode( value.toString(), "utf-8" ).replaceAll( "\\+", "%20" );
} catch ( UnsupportedEncodingException e ) {
throw new RuntimeException( e );
}
}
public Map<Long,RestOperation> getRecordedRequests(){
return this.operations.getRecordedRequests();
}
public void stop() {
this.stop = true;
}
public String getBaseUri() {
return baseUri;
}
}

View File

@@ -0,0 +1,139 @@
/**
* Copyright (c) 2002-2013 "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 <http://www.gnu.org/licenses/>.
*/
package org.neo4j.rest.graphdb.batch;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.concurrent.atomic.AtomicLong;
import javax.ws.rs.core.MediaType;
import org.neo4j.rest.graphdb.RequestResult;
import org.neo4j.rest.graphdb.RestAPI;
import org.neo4j.rest.graphdb.UpdatableRestResult;
import org.neo4j.rest.graphdb.batch.RestOperations.RestOperation.Methods;
import org.neo4j.rest.graphdb.converter.RestEntityExtractor;
import org.neo4j.rest.graphdb.converter.RestResultConverter;
import org.neo4j.rest.graphdb.util.DefaultConverter;
import org.neo4j.rest.graphdb.util.ResultConverter;
public class RestOperations {
private RestResultConverter resultConverter;
private AtomicLong currentBatchId = new AtomicLong(0);
private Map<Long, RestOperation> operations = new LinkedHashMap<Long, RestOperation>();
private MediaType contentType;
private MediaType acceptHeader;
public RestOperations(){
this.contentType = MediaType.APPLICATION_JSON_TYPE;
this.acceptHeader = MediaType.APPLICATION_JSON_TYPE;
}
public RestOperations(RestResultConverter converter) {
this.resultConverter = converter;
}
public RestOperation getOperation(Long batchId) {
return operations.get(batchId);
}
public static class RestOperation {
public void setConverter(RestResultConverter converter) {
this.resultConverter = converter;
}
public enum Methods{
POST,
PUT,
GET,
DELETE
}
private Methods method;
private Object data;
private final String baseUri;
private long batchId;
private String uri;
private MediaType contentType;
private MediaType acceptHeader;
private RestResultConverter resultConverter;
public RestOperation(long batchId, Methods method, String uri, MediaType contentType, MediaType acceptHeader, Object data, String baseUri){
this.batchId = batchId;
this.method = method;
this.uri = uri;
this.contentType = contentType;
this.acceptHeader = acceptHeader;
this.data = data;
this.baseUri = baseUri;
}
public RestResultConverter getResultConverter() {
return resultConverter;
}
public Methods getMethod() {
return method;
}
public Object getData() {
return data;
}
public long getBatchId() {
return batchId;
}
public String getUri() {
return uri;
}
public MediaType getContentType() {
return contentType;
}
public MediaType getAcceptHeader() {
return acceptHeader;
}
public String getBaseUri() {
return baseUri;
}
public boolean isSameUri(String baseUri) {
return this.baseUri.equals(baseUri);
}
}
public Map<Long,RestOperation> getRecordedRequests(){
return this.operations;
}
public RestOperation record(Methods method, String path, Object data, String baseUri){
long batchId = this.currentBatchId.incrementAndGet();
RestOperation r = new RestOperation(batchId,method,path,this.contentType,this.acceptHeader,data,baseUri);
r.setConverter(resultConverter);
operations.put(batchId,r);
return r;
}
}

View File

@@ -0,0 +1,44 @@
/**
* Copyright (c) 2002-2013 "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 <http://www.gnu.org/licenses/>.
*/
package org.neo4j.rest.graphdb.converter;
/**
* User: KBurchardi
* Date: 19.10.11
* Time: 10:37
*/
public class ConversionInfo {
private Object conversionData;
private boolean successfulConversion;
public ConversionInfo(Object conversionData, boolean successfulConversion) {
this.conversionData = conversionData;
this.successfulConversion = successfulConversion;
}
public boolean isSuccessfulConversion() {
return successfulConversion;
}
public Object getConversionData() {
return conversionData;
}
}

View File

@@ -0,0 +1,52 @@
/**
* Copyright (c) 2002-2013 "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 <http://www.gnu.org/licenses/>.
*/
package org.neo4j.rest.graphdb.converter;
import java.util.Collection;
import java.util.Map;
import org.neo4j.graphdb.Relationship;
import org.neo4j.helpers.collection.IterableWrapper;
import org.neo4j.rest.graphdb.RequestResult;
import org.neo4j.rest.graphdb.RestAPI;
import org.neo4j.rest.graphdb.entity.RestRelationship;
/**
* @author mh
* @since 22.09.11
*/
public class RelationshipIterableConverter implements RestResultConverter {
private final RestAPI restAPI;
public RelationshipIterableConverter(RestAPI restAPI) {
this.restAPI = restAPI;
}
@Override
public Object convertFromRepresentation(RequestResult requestResult) {
return new IterableWrapper<Relationship, Object>((Collection<Object>) requestResult.toEntity()) {
@Override
protected Relationship underlyingObjectToObject(Object data) {
return new RestRelationship((Map<?, ?>) data, restAPI);
}
};
}
}

View File

@@ -0,0 +1,84 @@
/**
* Copyright (c) 2002-2013 "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 <http://www.gnu.org/licenses/>.
*/
package org.neo4j.rest.graphdb.converter;
import java.util.*;
import org.neo4j.helpers.collection.IterableWrapper;
import org.neo4j.rest.graphdb.RequestResult;
import org.neo4j.rest.graphdb.RestAPI;
import org.neo4j.rest.graphdb.entity.RestEntity;
import org.neo4j.rest.graphdb.entity.RestNode;
import org.neo4j.rest.graphdb.entity.RestRelationship;
public class RestEntityExtractor implements RestResultConverter {
private final RestAPI restApi;
public RestEntityExtractor(RestAPI restApi) {
this.restApi = restApi;
}
public Object convertFromRepresentation(RequestResult requestResult) {
return convertFromRepresentation(requestResult.toEntity());
}
@SuppressWarnings("unchecked")
public Object convertFromRepresentation(Object value) {
if (value instanceof Map) {
if (canHandle(value)) {
value = convertToEntityIfPossible(value);
} else {
final Map<String,Object> source = (Map<String,Object>) value;
Map<String,Object> result=new HashMap<>(source.size());
for (Map.Entry<String,Object> entry : source.entrySet()) {
result.put(entry.getKey(),convertFromRepresentation(entry.getValue()));
}
return result;
}
}
if (value instanceof Iterable) {
Collection<Object> result = value instanceof Set ? new HashSet<>() : new ArrayList<>();
for (Object o : (Iterable) value) {
result.add(convertFromRepresentation(o));
}
return result;
}
return value;
}
private Object convertToEntityIfPossible(Object value) {
if (value instanceof Map) {
RestEntity restEntity = restApi.createRestEntity((Map) value);
if (restEntity != null) return restEntity;
}
return value;
}
public boolean canHandle(Object value) {
if (value instanceof Map) {
final String uri = (String) ((Map)value).get("self");
if (uri != null && (uri.contains("/node/") || uri.contains("/relationship/"))){
return true;
}
}
return false;
}
}

View File

@@ -0,0 +1,38 @@
/**
* Copyright (c) 2002-2013 "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 <http://www.gnu.org/licenses/>.
*/
package org.neo4j.rest.graphdb.converter;
import org.neo4j.rest.graphdb.RequestResult;
import org.neo4j.rest.graphdb.entity.RestEntity;
public class RestEntityPropertyRefresher implements RestResultConverter {
private final RestEntity entity;
public RestEntityPropertyRefresher(RestEntity entity){
this.entity = entity;
}
@Override
public Object convertFromRepresentation(RequestResult value) {
return this.entity;
}
}

View File

@@ -0,0 +1,49 @@
/**
* Copyright (c) 2002-2013 "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 <http://www.gnu.org/licenses/>.
*/
package org.neo4j.rest.graphdb.converter;
import java.util.Collection;
import org.neo4j.graphdb.PropertyContainer;
import org.neo4j.graphdb.index.IndexHits;
import org.neo4j.rest.graphdb.RequestResult;
import org.neo4j.rest.graphdb.RestAPI;
import org.neo4j.rest.graphdb.index.SimpleIndexHits;
/**
* @author mh
* @since 22.09.11
*/
public class RestIndexHitsConverter<S extends PropertyContainer> implements RestResultConverter {
private final RestAPI restAPI;
private final Class<S> entityType;
public RestIndexHitsConverter(RestAPI restAPI,Class<S> entityType) {
this.restAPI = restAPI;
this.entityType = entityType;
}
public IndexHits<S> convertFromRepresentation(RequestResult response) {
Collection hits = (Collection) response.toEntity();
return new SimpleIndexHits<S>(hits, hits.size(), entityType, restAPI);
}
}

View File

@@ -0,0 +1,27 @@
/**
* Copyright (c) 2002-2013 "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 <http://www.gnu.org/licenses/>.
*/
package org.neo4j.rest.graphdb.converter;
import org.neo4j.rest.graphdb.RequestResult;
public interface RestResultConverter {
public Object convertFromRepresentation(RequestResult value);
}

View File

@@ -0,0 +1,75 @@
/**
* Copyright (c) 2002-2013 "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 <http://www.gnu.org/licenses/>.
*/
package org.neo4j.rest.graphdb.converter;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.neo4j.rest.graphdb.RequestResult;
import org.neo4j.rest.graphdb.util.JsonHelper;
public class RestTableResultExtractor implements RestResultConverter{
private final RestEntityExtractor restEntityExtractor;
public RestTableResultExtractor(RestEntityExtractor restEntityExtractor) {
this.restEntityExtractor = restEntityExtractor;
}
public List<Map<String, Object>> extract(Map<?, ?> restResult) {
List<String> columns = (List<String>) restResult.get("columns");
return extractData(restResult, columns);
}
private List<Map<String, Object>> extractData(Map<?, ?> restResult, List<String> columns) {
List<List<?>> rows = (List<List<?>>) restResult.get("data");
List<Map<String, Object>> result = new ArrayList<Map<String, Object>>(rows.size());
for (List<?> row : rows) {
result.add(mapRow(columns, row));
}
return result;
}
private Map<String, Object> mapRow(List<String> columns, List<?> row) {
int columnCount = columns.size();
Map<String, Object> newRow = new HashMap<String, Object>(columnCount);
for (int i = 0; i < columnCount; i++) {
final Object value = row.get(i);
newRow.put(columns.get(i), restEntityExtractor.convertFromRepresentation(value));
}
return newRow;
}
@Override
public Object convertFromRepresentation(RequestResult value) {
return extract(toMap(value));
}
public boolean canHandle(Object restResult){
return restResult instanceof Map && ((Map)restResult).containsKey("columns") && ((Map)restResult).containsKey("data");
}
public Map<?, ?> toMap(RequestResult requestResult) {
return requestResult.toMap();
}
}

View File

@@ -0,0 +1,155 @@
/**
* Copyright (c) 2002-2013 "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 <http://www.gnu.org/licenses/>.
*/
package org.neo4j.rest.graphdb.converter;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import org.neo4j.rest.graphdb.RestAPI;
import org.neo4j.rest.graphdb.RestResultException;
import org.neo4j.rest.graphdb.traversal.RestPathParser;
/**
* User: KBurchardi
* Date: 18.10.11
* Time: 17:55
*/
public class ResultTypeConverter {
private RestAPI restAPI;
private RestEntityExtractor restEntityExtractor;
public ResultTypeConverter(RestAPI restAPI) {
this.restAPI = restAPI;
this.restEntityExtractor = new RestEntityExtractor(this.restAPI);
}
public Object convertToResultType(Object resultObject, TypeInformation typeInformation){
if (typeInformation.isSingleType() || typeInformation.isPath(typeInformation.type)) {
return convertSingleTypeToResultType(resultObject, typeInformation, typeInformation.type);
}
if (typeInformation.isCollection()){
ArrayList<Object> result = new ArrayList<Object>();
for (Object innerObject : toIterable(resultObject)) {
result.add(convertSingleTypeToResultType(innerObject, typeInformation, typeInformation.genericArguments[0]));
}
return result;
}if (typeInformation.isMap()){
Map<?,?> originalMap = toMap(resultObject);
HashMap<Object,Object> result = new HashMap<Object, Object>(((Map)resultObject).size());
for (Map.Entry<?,?> entry : originalMap.entrySet()) {
Object resultKey = convertSingleTypeToResultType(entry.getKey(), typeInformation, typeInformation.genericArguments[0]);
Object resultValue = convertSingleTypeToResultType(entry.getValue(), typeInformation, typeInformation.genericArguments[1]);
result.put(resultKey, resultValue);
}
return result;
}
throw new RestResultException("could not convert Type "+ resultObject.getClass().getName()+" to Type "+typeInformation.type.getName());
}
private Iterable<?> toIterable(Object resultObject){
if (Iterable.class.isAssignableFrom(resultObject.getClass())){
return (Iterable<?>)resultObject;
}else{
final RestTableResultExtractor extractor = new RestTableResultExtractor(new RestEntityExtractor(this.restAPI));
if (extractor.canHandle(resultObject)){
final List<Map<String, Object>> data = extractor.extract((Map)resultObject);
return (Iterable<?>)data;
}
}
throw new RestResultException("could not convert Type "+ resultObject.getClass().getName()+" to Iterable");
}
private Map<?,?> toMap (Object resultObject){
if (Map.class.isAssignableFrom(resultObject.getClass())){
return (Map<?,?>)resultObject;
}
throw new RestResultException("could not convert Type "+ resultObject.getClass().getName()+" to Map");
}
private Object convertSingleTypeToResultType(Object resultObject, TypeInformation typeInformation, Class singleObjectType){
if (typeInformation.isInstance(resultObject, singleObjectType)){
return resultObject;
}else{
if (typeInformation.isGraphEntity(singleObjectType)){
if (restEntityExtractor.canHandle(resultObject)){
return restEntityExtractor.convertFromRepresentation(resultObject);
}
}
if (typeInformation.isPath(singleObjectType) && resultObject instanceof Map){
return RestPathParser.parse((Map)resultObject, restAPI);
}
ConversionInfo resultInfo = convertFromCollectionType(resultObject, singleObjectType);
if (resultInfo.isSuccessfulConversion()){
return resultInfo.getConversionData();
}
}
throw new RestResultException("could not convert Type "+ resultObject.getClass().getName()+" to Type "+typeInformation.type.getName());
}
private ConversionInfo convertFromCollectionType(Object resultObject, Class singleObjectType){
TypeInformation resultTypeInfo = new TypeInformation(resultObject);
if (resultTypeInfo.isCollectionType()){
if (resultTypeInfo.isCollection()){
Iterable<?> resultIterable = (Iterable) resultObject;
if(!resultIterable.iterator().hasNext()){
return new ConversionInfo(null,true);
}else{
if (iterableHasSingleElement(resultIterable) && resultTypeInfo.getGenericArguments()[0].equals(singleObjectType)){
return new ConversionInfo(resultIterable.iterator().next(), true);
}
}
}else{
Map<?,?> resultMap = (Map)resultObject;
if (resultMap.isEmpty()){
return new ConversionInfo(null,true);
}else{
if (resultMap.size() == 1 && resultTypeInfo.getGenericArguments()[1].equals(singleObjectType) ){
return new ConversionInfo(resultMap.values().iterator().next(), true);
}
}
}
}
return new ConversionInfo(null,false);
}
public boolean iterableHasSingleElement(Iterable<?> object){
Iterator<?> it = object.iterator();
if (!it.hasNext()){
return false;
}
it.next();
if (it.hasNext()){
return false;
}
return true;
}
}

View File

@@ -0,0 +1,125 @@
/**
* Copyright (c) 2002-2013 "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 <http://www.gnu.org/licenses/>.
*/
package org.neo4j.rest.graphdb.converter;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.Map;
import org.neo4j.graphdb.Node;
import org.neo4j.graphdb.Path;
import org.neo4j.graphdb.Relationship;
/**
* User: KBurchardi
* Date: 18.10.11
* Time: 17:16
*/
public class TypeInformation {
Class type;
Class[] genericArguments;
public TypeInformation(Type type) {
this.type = convertToClass(type);
this.genericArguments = extractGenericArguments(type);
}
public TypeInformation(Object object){
this.type = object.getClass();
this.genericArguments = extractGenericArgumentsFromObject(object);
}
private Class convertToClass(Type type) {
if (type instanceof Class) {
return (Class) type;
}else{
return(Class)((ParameterizedType)type).getRawType();
}
}
public boolean isSingleType(){
return !isCollectionType();
}
public boolean isCollectionType() {
return (isCollection() || isMap());
}
public boolean isMap() {
return Map.class.isAssignableFrom(this.type);
}
public boolean isCollection() {
return Iterable.class.isAssignableFrom(this.type);
}
private Class[] extractGenericArgumentsFromObject(Object object){
if (isCollectionType()){
if(isCollection()){
if(((Iterable)object).iterator().hasNext()){
return new Class[]{((Iterable)object).iterator().next().getClass()};
}
}else{
if (!((Map)object).isEmpty()){
return new Class[]{((Map)object).keySet().iterator().next().getClass(), ((Map)object).values().iterator().next().getClass()};
}
}
}
return null;
}
private Class[] extractGenericArguments(Type type) {
if (!(type instanceof ParameterizedType)) {
return null;
}
ParameterizedType parameterizedType = (ParameterizedType) type;
Type[] actualTypeArguments = parameterizedType.getActualTypeArguments();
Class[]result = new Class[actualTypeArguments.length];
for (int i = 0; i < actualTypeArguments.length; i++) {
result[i] = convertToClass(actualTypeArguments[i]);
}
return result;
}
public boolean isInstance(Object resultObject, Class type) {
return type.isInstance(resultObject);
}
public boolean isGraphEntity(Class classType) {
return Node.class.isAssignableFrom(classType)|| Relationship.class.isAssignableFrom(classType);
}
public boolean isPath(Class classType){
return Path.class.isAssignableFrom(classType);
}
public Class getType() {
return type;
}
public Class[] getGenericArguments() {
return genericArguments;
}
}

View File

@@ -0,0 +1,254 @@
/**
* Copyright (c) 2002-2013 "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 <http://www.gnu.org/licenses/>.
*/
package org.neo4j.rest.graphdb.entity;
import java.net.URI;
import java.util.Collection;
import java.util.LinkedHashMap;
import java.util.Map;
import org.neo4j.graphdb.NotFoundException;
import org.neo4j.graphdb.PropertyContainer;
import org.neo4j.helpers.collection.IterableWrapper;
import org.neo4j.rest.graphdb.*;
import org.neo4j.rest.graphdb.util.ArrayConverter;
import org.springframework.data.neo4j.core.UpdateableState;
import static org.neo4j.helpers.collection.MapUtil.map;
public abstract class RestEntity implements PropertyContainer, UpdatableRestResult<RestEntity>, UpdateableState {
private Map<?, ?> structuralData;
protected Map<String, Object> propertyData;
private long lastTimeFetchedPropertyData;
protected RestAPI restApi;
private Long id;
private final ArrayConverter arrayConverter=new ArrayConverter();
private String uri;
public RestEntity( URI uri, RestAPI restApi ) {
this( uri.toString(), restApi );
}
public RestEntity( String uri, RestAPI restApi ) {
this.uri = uri;
this.id = getEntityId(uri);
this.restApi = restApi;
}
public RestEntity( Map<?, ?> data, RestAPI restApi ) {
this.restApi = restApi;
this.structuralData = data;
this.uri = (String) data.get( "self" );
this.id = getEntityId(uri);
setProperties((Map<String, Object>) data.get("data"));
}
public RestEntity(long id, Map<String, Object> restData, RestAPI facade) {
this.restApi = facade;
this.structuralData = restData;
this.uri = nodeUri(facade,id);
this.id = id;
setProperties((Map<String, Object>) structuralData.get("data"));
}
public static String nodeUri(RestAPI facade, long id) {
return facade.getBaseUri()+"/node/" + id;
}
public String getUri() {
return uri;
}
public void updateFrom(RestEntity updateEntity, RestAPI restApi){
// if (this == updateEntity){
// this.lastTimeFetchedPropertyData = 0;
// }
this.uri = updateEntity.getUri();
this.id = getEntityId(uri);
this.structuralData = updateEntity.getStructuralData();
if (updateEntity.lastTimeFetchedPropertyData > 0 && updateEntity.propertyData != null) {
setProperties(updateEntity.propertyData);
}
}
Map<?, ?> getStructuralData() {
// if ( this.structuralData == null ) {
// this.structuralData = restApi.getData(this);
// }
return this.structuralData;
}
Map<String, Object> getPropertyData() {
if (hasToUpdateProperties()) {
doUpdate();
}
return this.propertyData;
}
protected abstract void doUpdate();
protected void setProperties(Map<String, Object> properties) {
tracking = false;
this.propertyData = properties;
this.lastTimeFetchedPropertyData = System.currentTimeMillis();
}
protected boolean hasToUpdateProperties() {
if (tracking) return false;
if (this.propertyData == null) return true;
return restApi.hasToUpdate(this.lastTimeFetchedPropertyData);
}
public Object getProperty( String key ) {
Object value = getPropertyValue(key);
if ( value == null ) {
throw new NotFoundException( "'" + key + "' on " + this );
}
return value;
}
private Object getPropertyValue( String key ) {
Map<String, Object> properties = getPropertyData();
Object value = properties.get( key );
if ( value == null) return null;
if ( value instanceof Collection ) {
Collection col= (Collection) value;
if (col.isEmpty()) return new String[0]; // todo concrete value type ?
Object result = arrayConverter.toArray( col );
if (result == null) throw new IllegalStateException( "Could not determine type of property "+key );
properties.put(key,result);
return result;
}
return PropertiesMap.assertSupportedPropertyValue( value );
}
public Object getProperty( String key, Object defaultValue ) {
Object value = getPropertyValue( key );
return value != null ? value : defaultValue;
}
@SuppressWarnings("unchecked")
public Iterable<String> getPropertyKeys() {
return new IterableWrapper( getPropertyData().keySet() ) {
@Override
protected String underlyingObjectToObject( Object key ) {
return key.toString();
}
};
}
@SuppressWarnings("unchecked")
public Iterable<Object> getPropertyValues() {
return (Iterable<Object>) getPropertyData().values();
}
public boolean hasProperty( String key ) {
return getPropertyData().containsKey( key );
}
public Object removeProperty( String key ) {
Object value = getProperty( key, null );
if (!tracking) restApi.removeProperty(this, key);
if (this.propertyData != null ) this.propertyData.remove(key);
return value;
}
public void setProperty( String key, Object value ) {
if (!tracking) this.restApi.setPropertyOnEntity(this, key, value);
if (this.propertyData == null) this.propertyData = new LinkedHashMap<>();
this.propertyData.put(key,value);
}
private boolean tracking;
@Override
public void flush() {
if (!tracking) return;
if (propertyData != null && !propertyData.isEmpty()) {
this.restApi.setPropertiesOnEntity(this,propertyData);
}
tracking = false;
}
@Override
public void track() {
this.tracking = true;
}
public static long getEntityId( String uri ) {
if (uri.startsWith("{")) return -1;
return Long.parseLong(uri.substring(uri.lastIndexOf('/') + 1));
}
public long getId() {
return id;
}
public void delete() {
this.restApi.deleteEntity(this);
}
@Override
public int hashCode() {
return (int) getId();
}
@Override
public boolean equals( Object o ) {
if (o == null) return false;
return getClass().equals( o.getClass() ) && getId() == ( (RestEntity) o ).getId();
}
public RestGraphDatabase getGraphDatabase() {
return new RestGraphDatabase(restApi);
}
@Override
public String toString() {
return getUri();
}
public RestAPI getRestApi() {
return restApi;
}
public void setLastTimeFetchedPropertyData(long lastTimeFetchedPropertyData) {
this.lastTimeFetchedPropertyData = lastTimeFetchedPropertyData;
}
@Override
public void refresh() {
doUpdate();
}
@Override
public void addPropertiesBatch(Map<String, Object> properties) {
setProperties(properties);
restApi.setPropertiesOnEntity(this, propertyData);
}
@Override
public void addAllLabelsBatch(Collection<String> labels) { }
}

View File

@@ -0,0 +1,42 @@
package org.neo4j.rest.graphdb.entity;
import org.neo4j.kernel.impl.cache.LruCache;
import org.neo4j.rest.graphdb.RestAPI;
/**
* @author mh
* @since 21.09.14
*/
public class RestEntityCache {
private LruCache<Long,RestNode> lruCache = new LruCache<>("RestNode",10000);
private final RestAPI restAPI;
public RestEntityCache(RestAPI restAPI) {
this.restAPI = restAPI;
}
public RestNode addToCache(RestNode node) {
if (node == null) return null;
long id = node.getId();
if (id != -1) {
RestNode existing = lruCache.get(id);
if (existing !=null) {
if (existing != node) existing.updateFrom(node, restAPI);
return existing;
} else {
lruCache.put(id, node);
}
}
return node;
}
public RestNode getNode(long id) {
return lruCache.get(id);
}
public void remove(long id) {
lruCache.remove(id);
}
}

View File

@@ -0,0 +1,237 @@
/**
* Copyright (c) 2002-2013 "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 <http://www.gnu.org/licenses/>.
*/
package org.neo4j.rest.graphdb.entity;
import static java.util.Arrays.asList;
import static org.neo4j.helpers.collection.MapUtil.map;
import static org.neo4j.rest.graphdb.ExecutingRestRequest.encode;
import java.net.URI;
import java.util.*;
import org.neo4j.graphdb.*;
import org.neo4j.graphdb.Traverser.Order;
import org.neo4j.helpers.collection.CombiningIterable;
import org.neo4j.helpers.collection.IterableWrapper;
import org.neo4j.helpers.collection.IteratorUtil;
import org.neo4j.rest.graphdb.RestAPI;
import org.neo4j.rest.graphdb.RestAPIInternal;
import org.neo4j.rest.graphdb.traversal.RestDirection;
import org.neo4j.rest.graphdb.util.ResourceIterableWrapper;
public class RestNode extends RestEntity implements Node {
public RestNode( URI uri, RestAPI restApi ) {
super( uri, restApi );
}
public RestNode( String uri, RestAPI restApi ) {
super( uri, restApi );
}
@SuppressWarnings("unchecked")
public RestNode( Map<?, ?> data, RestAPI restApi ) {
super(data, restApi);
if (data.containsKey("metadata")) {
setLabels((Collection<String>)((Map)data.get("metadata")).get("labels"));
}
}
public RestNode(long id, Collection<String> labels, Map<String, Object> restData, RestAPI facade) {
super(id,restData,facade);
setLabels(labels);
}
public static RestNode fromCypher(long id, Collection<String> labels, Map<String, Object> props, RestAPI facade) {
Map<String, Object> restData = map("data", props, "self", RestNode.nodeUri(facade, id));//,"metadata",map("id",String.valueOf("id"),"labels",labels)
return new RestNode(id, labels, restData, facade);
}
@Override
protected void doUpdate() {
updateFrom(restApi.getNodeById(getId(), RestAPIInternal.Load.ForceFromServer), restApi);
}
@Override
public void updateFrom(RestEntity entity, RestAPI restApi) {
super.updateFrom(entity, restApi);
RestNode node = (RestNode) entity;
if (node.lastLabelFetchTime > 0 && node.labels != null) {
setLabels(node.labels);
}
}
public Relationship createRelationshipTo( Node toNode, RelationshipType type ) {
return this.restApi.createRelationship(this, toNode, type, null);
}
@Override
public Iterable<RelationshipType> getRelationshipTypes() {
return this.restApi.getRelationshipTypes(this);
}
@Override
public int getDegree() {
return this.restApi.getDegree(this, null, Direction.BOTH);
}
@Override
public int getDegree(RelationshipType type) {
return this.restApi.getDegree(this,type,Direction.BOTH);
}
@Override
public int getDegree(Direction direction) {
return this.restApi.getDegree(this,null,direction);
}
@Override
public int getDegree(RelationshipType type, Direction direction) {
return this.restApi.getDegree(this,type,direction);
}
public Iterable<Relationship> getRelationships() {
return restApi.getRelationships(this, Direction.BOTH );
}
public Iterable<Relationship> getRelationships( RelationshipType... types ) {
return restApi.getRelationships(this, Direction.BOTH, types);
}
public Iterable<Relationship> getRelationships( Direction direction ) {
return restApi.getRelationships(this, direction);
}
public Iterable<Relationship> getRelationships( RelationshipType type,
Direction direction ) {
return restApi.getRelationships(this, direction, type);
}
public Relationship getSingleRelationship( RelationshipType type,
Direction direction ) {
return IteratorUtil.singleOrNull( getRelationships( type, direction ) );
}
public boolean hasRelationship() {
return getRelationships().iterator().hasNext();
}
public boolean hasRelationship( RelationshipType... types ) {
return getRelationships( types ).iterator().hasNext();
}
public boolean hasRelationship( Direction direction ) {
return getRelationships( direction ).iterator().hasNext();
}
public boolean hasRelationship( RelationshipType type, Direction direction ) {
return getRelationships( type, direction ).iterator().hasNext();
}
public Traverser traverse( Order order, StopEvaluator stopEvaluator,
ReturnableEvaluator returnableEvaluator, Object... rels ) {
throw new UnsupportedOperationException();
}
public Traverser traverse( Order order, StopEvaluator stopEvaluator,
ReturnableEvaluator returnableEvaluator, RelationshipType type, Direction direction ) {
throw new UnsupportedOperationException();
}
public Traverser traverse( Order order, StopEvaluator stopEvaluator,
ReturnableEvaluator returnableEvaluator, RelationshipType type, Direction direction,
RelationshipType secondType, Direction secondDirection ) {
throw new UnsupportedOperationException();
}
@Override
public Iterable<Relationship> getRelationships(final Direction direction, RelationshipType... types) {
return new CombiningIterable<Relationship>(new IterableWrapper<Iterable<Relationship>, RelationshipType>(asList(types)) {
@Override
protected Iterable<Relationship> underlyingObjectToObject(RelationshipType relationshipType) {
return getRelationships(relationshipType,direction);
}
});
}
@Override
public boolean hasRelationship(Direction direction, RelationshipType... types) {
for (RelationshipType relationshipType : types) {
if (hasRelationship(relationshipType,direction)) return true;
}
return false;
}
private Set<String> labels=null;
private long lastLabelFetchTime = 0;
@Override
public void addLabel(Label label) {
restApi.addLabels(this, Collections.singleton(label.name()));
if (this.labels!=null) this.labels.add(label.name());
else updateLabels();
}
@Override
public void removeLabel(Label label) {
restApi.removeLabel(this,label.name());
if (this.labels!=null) this.labels.remove(label.name());
else updateLabels();
}
@Override
public boolean hasLabel(Label label) {
updateLabels();
return this.labels.contains(label.name());
}
private void updateLabels() {
if (hasToUpdateLabels()) {
doUpdate();
}
}
public void setLabels(Collection<String> labels) {
this.labels = (labels == null) ? new LinkedHashSet<String>() : new LinkedHashSet<>(labels);
this.lastLabelFetchTime = System.currentTimeMillis();
}
private boolean hasToUpdateLabels() {
return labels == null || restApi.hasToUpdate(this.lastLabelFetchTime);
}
@Override
public ResourceIterable<Label> getLabels() {
updateLabels();
return new ResourceIterableWrapper<Label,String>(labels) {
@Override
protected Label underlyingObjectToObject(String s) {
return DynamicLabel.label(s);
}
};
}
@Override
public void addAllLabelsBatch(Collection<String> labels) {
setLabels(labels);
restApi.addLabels(this, labels);
}
}

View File

@@ -0,0 +1,111 @@
/**
* Copyright (c) 2002-2013 "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 <http://www.gnu.org/licenses/>.
*/
package org.neo4j.rest.graphdb.entity;
import java.net.URI;
import java.util.Collection;
import java.util.Map;
import org.neo4j.graphdb.DynamicRelationshipType;
import org.neo4j.graphdb.Node;
import org.neo4j.graphdb.NotFoundException;
import org.neo4j.graphdb.Relationship;
import org.neo4j.graphdb.RelationshipType;
import org.neo4j.rest.graphdb.RestAPI;
import org.neo4j.rest.graphdb.RestAPIInternal;
import static org.neo4j.helpers.collection.MapUtil.map;
public class RestRelationship extends RestEntity implements Relationship {
RestRelationship( URI uri, RestAPI restApi ) {
super( uri, restApi );
}
public RestRelationship( String uri, RestAPI restApi ) {
super( uri, restApi );
}
public RestRelationship( Map<?, ?> data, RestAPI restApi ) {
super( data, restApi );
}
@Override
protected void doUpdate() {
updateFrom(restApi.getRelationshipById(getId()), restApi);
}
public Node getEndNode() {
return node( (String) getStructuralData().get( "end" ) );
}
public Node[] getNodes() {
return new Node[]{
node( (String) getStructuralData().get( "start" ) ),
node( (String) getStructuralData().get( "end" ) )
};
}
public static RestRelationship fromCypher(long id, String type, Map<String, Object> props, long start, long end, RestAPI facade) {
Map<String, Object> restData = map("data", props, "self", relUri(facade, id), "start", RestNode.nodeUri(facade, start), "end", RestNode.nodeUri(facade,end), "type",type);
return new RestRelationship(restData, facade);
}
public Node getOtherNode( Node node ) {
long nodeId = node.getId();
String startNodeUri = (String) getStructuralData().get( "start" );
String endNodeUri = (String) getStructuralData().get( "end" );
if ( getEntityId( startNodeUri ) == nodeId ) {
return node( endNodeUri );
} else if ( getEntityId( endNodeUri ) == nodeId ) {
return node( startNodeUri );
} else {
throw new NotFoundException( node + " isn't one of start/end for " + this );
}
}
private RestNode node( String uri ) {
return getRestApi().getNodeById(getEntityId(uri), RestAPIInternal.Load.FromCache);
}
public static String relUri(RestAPI facade, long id) {
return facade.getBaseUri()+"/relationship/" + id;
}
public Node getStartNode() {
return node( (String) getStructuralData().get( "start" ) );
}
public RelationshipType getType() {
return DynamicRelationshipType.withName( (String) getStructuralData().get( "type" ) );
}
public boolean isType( RelationshipType type ) {
return type.name().equals( getStructuralData().get( "type" ) );
}
public RestRelationship create(RestNode startNode, RestNode endNode, RelationshipType type, Map<String, Object> props) {
return this.restApi.createRelationship(startNode, endNode, type, props);
}
}

View File

@@ -0,0 +1,36 @@
/**
* Copyright (c) 2002-2013 "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 <http://www.gnu.org/licenses/>.
*/
package org.neo4j.rest.graphdb.index;
import java.util.Map;
/**
* @author mh
* @since 22.09.11
*/
public interface IndexInfo {
boolean checkConfig(String indexName, Map<String, String> config);
String[] indexNames();
boolean exists(String indexName);
Map<String, String> getConfig(String name);
}

View File

@@ -0,0 +1,73 @@
/**
* Copyright (c) 2002-2013 "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 <http://www.gnu.org/licenses/>.
*/
package org.neo4j.rest.graphdb.index;
import org.neo4j.graphdb.Node;
import org.neo4j.graphdb.PropertyContainer;
import org.neo4j.graphdb.index.AutoIndexer;
import org.neo4j.graphdb.index.IndexManager;
import org.neo4j.graphdb.index.ReadableIndex;
import org.neo4j.rest.graphdb.RestAPI;
import java.util.Set;
public class RestAutoIndexer<T extends PropertyContainer> implements AutoIndexer<T> {
protected final RestAPI restApi;
protected final Class forClass;
protected final ReadableIndex<T> autoIndex;
public RestAutoIndexer(RestAPI restApi, Class forClass, ReadableIndex<T> autoIndex) {
this.restApi = restApi;
this.forClass = forClass;
this.autoIndex = autoIndex;
}
@Override
public void setEnabled(boolean b) {
restApi.setAutoIndexingEnabled(forClass, b);
}
@Override
public boolean isEnabled() {
return restApi.isAutoIndexingEnabled(forClass);
}
@Override
public ReadableIndex<T> getAutoIndex() {
return autoIndex;
}
@Override
public void startAutoIndexingProperty(String s) {
restApi.startAutoIndexingProperty(forClass, s);
}
@Override
public void stopAutoIndexingProperty(String s) {
restApi.stopAutoIndexingProperty(forClass, s);
}
@Override
public Set<String> getAutoIndexedProperties() {
return restApi.getAutoIndexedProperties(forClass);
}
}

View File

@@ -0,0 +1,108 @@
/**
* Copyright (c) 2002-2013 "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 <http://www.gnu.org/licenses/>.
*/
package org.neo4j.rest.graphdb.index;
import org.neo4j.graphdb.GraphDatabaseService;
import org.neo4j.graphdb.PropertyContainer;
import org.neo4j.graphdb.index.Index;
import org.neo4j.graphdb.index.IndexHits;
import org.neo4j.index.lucene.QueryContext;
import org.neo4j.rest.graphdb.RestAPI;
import org.neo4j.rest.graphdb.RestGraphDatabase;
/**
* @author mh
* @since 24.01.11
*/
public abstract class RestIndex<T extends PropertyContainer> implements Index<T> {
private final String indexName;
public String getIndexName() {
return indexName;
}
protected final RestAPI restApi;
RestIndex(String indexName, RestAPI restApi) {
this.indexName = indexName;
this.restApi = restApi;
}
@Override
public GraphDatabaseService getGraphDatabase() {
return new RestGraphDatabase(restApi);
}
private String getTypeName() {
return getEntityType().getSimpleName().toLowerCase();
}
public void add( T entity, String key, Object value ) {
restApi.addToIndex(entity, this, key, value);
}
public T putIfAbsent( T entity, String key, Object value ) {
return restApi.putIfAbsent(entity, this, key, value);
}
public void remove( T entity, String key, Object value ) {
restApi.removeFromIndex(this, entity, key, value);
}
public void remove(T entity, String key) {
restApi.removeFromIndex(this, entity, key);
}
public void remove(T entity) {
restApi.removeFromIndex(this, entity);
}
public void delete() {
restApi.delete(this);
}
public org.neo4j.graphdb.index.IndexHits<T> get( String key, Object value ) {
return restApi.getIndex(getEntityType(), indexName, key, value);
}
public IndexHits<T> query( String key, Object value ) {
return restApi.queryIndex(getEntityType(), indexName, key, value);
}
public org.neo4j.graphdb.index.IndexHits<T> query( Object value ) {
if (value instanceof QueryContext) {
value = ((QueryContext)value).getQueryOrQueryObject();
}
return query("null",value);
}
public String getName() {
return indexName;
}
/*
public RestRequest getRestRequest() {
return restRequest;
}
private Long getBatchId(Map<String, Object> entry) {
return ((Number) entry.get("id")).longValue();
}
*/
}

View File

@@ -0,0 +1,160 @@
/**
* Copyright (c) 2002-2013 "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 <http://www.gnu.org/licenses/>.
*/
package org.neo4j.rest.graphdb.index;
import java.util.Map;
import org.neo4j.graphdb.Node;
import org.neo4j.graphdb.PropertyContainer;
import org.neo4j.graphdb.Relationship;
import org.neo4j.graphdb.index.*;
import org.neo4j.index.impl.lucene.LuceneIndexImplementation;
import org.neo4j.rest.graphdb.RestAPI;
public class RestIndexManager implements IndexManager {
public static final String RELATIONSHIP = "relationship";
public static final String NODE = "node";
public static final String NODE_AUTO_INDEX_NAME = "node_auto_index";
public static final String RELATIONSHIP_AUTO_INDEX_NAME = "relationship_auto_index";
private final RestAPI restApi;
private ReadableIndex<Node> nodeAutoIndex;
private ReadableRelationshipIndex relationshipAutoIndex;
public RestIndexManager(RestAPI restApi) {
this.restApi = restApi;
}
public boolean existsForNodes( String indexName ) {
return indexInfo(NODE).exists(indexName);
}
@SuppressWarnings({"unchecked"})
private IndexInfo indexInfo(final String indexType) {
return restApi.indexInfo(indexType);
}
@SuppressWarnings("unchecked")
private boolean checkIndex( final String indexType, final String indexName, Map<String, String> config ){
final IndexInfo indexInfo = indexInfo(indexType);
return indexInfo.checkConfig(indexName, config);
}
public boolean noConfigProvided(Map<String,String> config) {
return config == null || config.isEmpty();
}
public RestIndex<Node> forNodes( String indexName ) {
if (!checkIndex(NODE, indexName, null)){
createIndex(NODE, indexName, LuceneIndexImplementation.EXACT_CONFIG);
}
return new RestNodeIndex(indexName, restApi );
}
public RestIndex<Node> forNodes( String indexName, Map<String, String> config ) {
if (noConfigProvided(config)){
throw new IllegalArgumentException("No index configuration was provided!");
}
if (!checkIndex(NODE, indexName, config)){
createIndex(NODE, indexName, config);
}
return new RestNodeIndex(indexName, restApi );
}
public String[] nodeIndexNames() {
final IndexInfo indexInfo = indexInfo(NODE);
return indexInfo.indexNames();
}
public boolean existsForRelationships( String indexName ) {
return indexInfo(RELATIONSHIP).exists(indexName);
}
public RelationshipIndex forRelationships( String indexName ) {
if (!checkIndex(RELATIONSHIP, indexName, null)){
createIndex(RELATIONSHIP, indexName, LuceneIndexImplementation.EXACT_CONFIG);
}
return new RestRelationshipIndex(indexName, restApi );
}
public RelationshipIndex forRelationships( String indexName, Map<String, String> config ) {
if (noConfigProvided(config)){
throw new IllegalArgumentException("No index configuration was provided!");
}
if (!checkIndex(RELATIONSHIP, indexName, config)){
createIndex(RELATIONSHIP, indexName, config);
}
return new RestRelationshipIndex(indexName, restApi );
}
private void createIndex(String type, String indexName, Map<String, String> config) {
restApi.createIndex(type,indexName,config);
}
public String[] relationshipIndexNames() {
return indexInfo(RELATIONSHIP).indexNames();
}
@SuppressWarnings({"unchecked"})
public Map<String, String> getConfiguration( Index<? extends PropertyContainer> index ) {
String typeName = typeName(index.getEntityType());
return indexInfo(typeName).getConfig(index.getName());
}
private String typeName(Class<? extends PropertyContainer> type) {
if (Node.class.isAssignableFrom(type)) return NODE;
if (Relationship.class.isAssignableFrom(type)) return RELATIONSHIP;
throw new IllegalArgumentException("Invalid index type "+type);
}
public String setConfiguration( Index<? extends PropertyContainer> index, String s, String s1 ) {
throw new UnsupportedOperationException();
}
public String removeConfiguration( Index<? extends PropertyContainer> index, String s ) {
throw new UnsupportedOperationException();
}
@Override
public AutoIndexer<Node> getNodeAutoIndexer() {
return new RestAutoIndexer<Node>(restApi, Node.class, getNodeAutoIndex());
}
@Override
public RelationshipAutoIndexer getRelationshipAutoIndexer() {
return new RestRelationshipAutoIndexer(restApi, getRelationshipAutoIndex());
}
private ReadableIndex<Node> getNodeAutoIndex() {
if (nodeAutoIndex==null) {
nodeAutoIndex = forNodes(NODE_AUTO_INDEX_NAME);
}
return nodeAutoIndex;
}
private ReadableRelationshipIndex getRelationshipAutoIndex() {
if (relationshipAutoIndex==null) {
relationshipAutoIndex = forRelationships(RELATIONSHIP_AUTO_INDEX_NAME);
}
return relationshipAutoIndex;
}
}

View File

@@ -0,0 +1,45 @@
/**
* Copyright (c) 2002-2013 "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 <http://www.gnu.org/licenses/>.
*/
package org.neo4j.rest.graphdb.index;
import org.neo4j.graphdb.Node;
import org.neo4j.rest.graphdb.RestAPI;
import org.neo4j.rest.graphdb.RestRequest;
/**
* @author mh
* @since 24.01.11
*/
public class RestNodeIndex extends RestIndex<Node> {
public RestNodeIndex(String indexName, RestAPI restApi) {
super(indexName, restApi );
}
public Class<Node> getEntityType() {
return Node.class;
}
@Override
public boolean isWriteable() {
return true;
}
}

View File

@@ -0,0 +1,38 @@
/**
* Copyright (c) 2002-2013 "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 <http://www.gnu.org/licenses/>.
*/
package org.neo4j.rest.graphdb.index;
import org.neo4j.graphdb.Relationship;
import org.neo4j.graphdb.index.IndexManager;
import org.neo4j.graphdb.index.ReadableIndex;
import org.neo4j.graphdb.index.ReadableRelationshipIndex;
import org.neo4j.graphdb.index.RelationshipAutoIndexer;
import org.neo4j.rest.graphdb.RestAPI;
public class RestRelationshipAutoIndexer extends RestAutoIndexer<Relationship> implements RelationshipAutoIndexer {
public RestRelationshipAutoIndexer(RestAPI restApi, ReadableRelationshipIndex autoIndex) {
super(restApi, Relationship.class, autoIndex);
}
public ReadableRelationshipIndex getAutoIndex() {
return (ReadableRelationshipIndex)super.getAutoIndex();
}
}

View File

@@ -0,0 +1,58 @@
/**
* Copyright (c) 2002-2013 "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 <http://www.gnu.org/licenses/>.
*/
package org.neo4j.rest.graphdb.index;
import org.neo4j.graphdb.Node;
import org.neo4j.graphdb.Relationship;
import org.neo4j.graphdb.index.RelationshipIndex;
import org.neo4j.rest.graphdb.RestAPI;
import org.neo4j.rest.graphdb.RestRequest;
/**
* @author mh
* @since 24.01.11
*/
public class RestRelationshipIndex extends RestIndex<Relationship> implements RelationshipIndex {
public RestRelationshipIndex(String indexName, RestAPI restApi) {
super(indexName, restApi );
}
public Class<Relationship> getEntityType() {
return Relationship.class;
}
public org.neo4j.graphdb.index.IndexHits<Relationship> get( String s, Object o, Node node, Node node1 ) {
throw new UnsupportedOperationException();
}
public org.neo4j.graphdb.index.IndexHits<Relationship> query( String s, Object o, Node node, Node node1 ) {
throw new UnsupportedOperationException();
}
public org.neo4j.graphdb.index.IndexHits<Relationship> query( Object o, Node node, Node node1 ) {
throw new UnsupportedOperationException();
}
@Override
public boolean isWriteable() {
return true;
}
}

View File

@@ -0,0 +1,75 @@
/**
* Copyright (c) 2002-2013 "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 <http://www.gnu.org/licenses/>.
*/
package org.neo4j.rest.graphdb.index;
import java.util.Collections;
import java.util.Map;
import java.util.Set;
import org.neo4j.rest.graphdb.RequestResult;
import com.sun.jersey.api.client.ClientResponse;
/**
* @author mh
* @since 22.09.11
*/
public class RetrievedIndexInfo implements IndexInfo {
private Map<String, ?> indexInfo;
public RetrievedIndexInfo(RequestResult response) {
if (response.statusIs(ClientResponse.Status.NO_CONTENT)) this.indexInfo = Collections.emptyMap();
else this.indexInfo = (Map<String, ?>) response.toMap();
}
@Override
public boolean checkConfig(String indexName, Map<String, String> config) {
Map<String, String> existingConfig = (Map<String, String>) indexInfo.get(indexName);
if (config == null) {
return existingConfig != null;
} else {
if (existingConfig == null) {
return false;
} else {
if (existingConfig.entrySet().containsAll(config.entrySet())) {
return true;
} else {
throw new IllegalArgumentException("Index with the same name but different config exists!");
}
}
}
}
@Override
public String[] indexNames() {
Set<String> keys = indexInfo.keySet();
return keys.toArray(new String[keys.size()]);
}
@Override
public boolean exists(String indexName) {
return indexInfo.containsKey(indexName);
}
@Override
public Map<String, String> getConfig(String name) {
return (Map<String, String>) indexInfo.get(name);
}
}

View File

@@ -0,0 +1,102 @@
/**
* Copyright (c) 2002-2013 "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 <http://www.gnu.org/licenses/>.
*/
package org.neo4j.rest.graphdb.index;
import java.util.Collection;
import java.util.Iterator;
import org.neo4j.graphdb.PropertyContainer;
import org.neo4j.graphdb.ResourceIterator;
import org.neo4j.graphdb.index.IndexHits;
import org.neo4j.rest.graphdb.RestAPI;
import org.neo4j.rest.graphdb.UpdatableRestResult;
import org.neo4j.rest.graphdb.converter.RestEntityExtractor;
/**
* @author mh
* @since 22.09.11
*/
public class SimpleIndexHits<T extends PropertyContainer> implements IndexHits<T>, UpdatableRestResult<SimpleIndexHits<T>> {
private Collection<Object> hits;
private Class<T> entityType;
private int size;
private Iterator<Object> iterator;
private RestEntityExtractor entityExtractor;
public SimpleIndexHits(long batchId, Class<T> entityType, final RestAPI restApi) {
this.entityType = entityType;
this.entityExtractor = restApi.getEntityExtractor();
}
public SimpleIndexHits(Collection<Object> hits, int size, Class<T> entityType, final RestAPI restApi) {
this.hits = hits;
this.entityType = entityType;
this.iterator = this.hits.iterator();
this.size = size;
this.entityExtractor = restApi.getEntityExtractor();
}
public int size() {
return size;
}
public void close() {
}
public T getSingle() {
Iterator<Object> it = hits.iterator();
return it.hasNext() ? transform(it.next()) : null;
}
public float currentScore() {
return 0;
}
public ResourceIterator<T> iterator() {
return this;
}
public boolean hasNext() {
return iterator.hasNext();
}
public T next() {
Object value = iterator.next();
return transform(value);
}
private T transform(Object value) {
return (T) entityExtractor.convertFromRepresentation(value);
}
public void remove() {
}
@Override
public void updateFrom(SimpleIndexHits<T> newValue, RestAPI restApi) {
this.hits= newValue.hits;
this.iterator = this.hits.iterator();
this.size = newValue.size;
}
}

View File

@@ -0,0 +1,53 @@
/**
* Copyright (c) 2002-2013 "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 <http://www.gnu.org/licenses/>.
*/
package org.neo4j.rest.graphdb.query;
import org.neo4j.rest.graphdb.RequestResult;
import java.util.Collection;
import java.util.List;
import java.util.Map;
/**
* @author mh
* @since 21.09.11
*/
public class CypherRestResult implements CypherResult {
private Map<?,?> map;
public CypherRestResult(RequestResult requestResult) {
map = requestResult.toMap();
}
@Override
public Collection<String> getColumns() {
return (Collection<String>) map.get("columns");
}
@Override
public Iterable<List<Object>> getData() {
return (Iterable<List<Object>>) map.get("data");
}
@Override
public Map asMap() {
return map;
}
}

View File

@@ -0,0 +1,17 @@
package org.neo4j.rest.graphdb.query;
import java.util.Collection;
import java.util.List;
import java.util.Map;
/**
* @author mh
* @since 25.09.14
*/
public interface CypherResult {
Collection<String> getColumns();
Iterable<List<Object>> getData();
Map asMap();
}

View File

@@ -0,0 +1,213 @@
package org.neo4j.rest.graphdb.query;
import com.sun.jersey.api.client.ClientResponse;
import org.neo4j.helpers.collection.IterableWrapper;
import org.neo4j.helpers.collection.IteratorWrapper;
import org.neo4j.rest.graphdb.*;
import org.springframework.data.neo4j.mapping.RelationshipResult;
import javax.ws.rs.core.Response;
import java.util.*;
import static org.neo4j.helpers.collection.MapUtil.map;
/**
* @author mh
* @since 24.09.14
*/
public class CypherTransaction {
@SuppressWarnings("unchecked")
public enum ResultType {
/** all nodes and rels collated in one bag **/
graph() {
@Override
public List<Object> get(Map data) {
Map graph = (Map) data.get(name());
List result = new ArrayList((List)graph.get("nodes"));
result.addAll((List) graph.get("relationships"));
return result;
}
}, row, rest;
public List<Object> get(Map data) {
return (List<Object>) data.get(name());
}
}
public CypherTransaction(String baseUri, ResultType type) {
this.type = type;
this.request = new ExecutingRestRequest(baseUri);
}
public CypherTransaction(RestAPICypherImpl restAPI, ResultType type) {
this.type = type;
this.request = restAPI.getRestRequest();
}
public static class Result implements Iterable<Map<String,Object>> {
private final List<String> columns;
private final Iterable<List<Object>> rows;
private final Statement statement;
Result(List<String> columns, Iterable<List<Object>> rows, Statement statement) {
this.columns = columns;
this.rows = rows;
this.statement = statement;
}
private static List<Result> toResults(List<Map> resultsData, List<Statement> statements, ResultType type) {
List<Result> results=new ArrayList<>();
for (int i = 0; i < resultsData.size(); i++) {
results.add(toResult(resultsData.get(i), statements.get(i), type));
}
return results;
}
@SuppressWarnings("unchecked")
private static Result toResult(Map resultData, Statement statement, final ResultType type) {
List<String> columns = (List<String>) resultData.get("columns");
List<Map> rowsData = (List<Map>) resultData.get("data");
Iterable<List<Object>> rows = new IterableWrapper<List<Object>,Map>(rowsData) {
protected List<Object> underlyingObjectToObject(Map map) {
return type.get(map);
}
};
return new Result(columns, rows, statement);
}
public List<String> getColumns() {
return columns;
}
public Iterable<List<Object>> getRows() {
return rows;
}
public Statement getStatement() {
return statement;
}
@Override
public Iterator<Map<String, Object>> iterator() {
return new IteratorWrapper<Map<String, Object>,List<Object>>(rows.iterator()) {
protected Map<String, Object> underlyingObjectToObject(List<Object> objects) {
Map<String, Object> row = new LinkedHashMap<>(columns.size());
for (int i = 0; i < columns.size(); i++) {
row.put(columns.get(i), objects.get(i));
}
return row;
}
};
}
public boolean hasData() {
return rows.iterator().hasNext();
}
}
public static class Statement {
private final String statement;
private final ResultType type;
private final Map<String, Object> parameters;
public Statement(String query, Map<String, Object> parameters, ResultType type) {
this.statement = query;
this.type = type;
this.parameters = parameters == null ? Collections.<String,Object>emptyMap() : parameters;
}
public String getStatement() {
return statement;
}
public Map<String, Object> getParameters() {
return new LinkedHashMap<>(parameters);
}
public List<String> getResultDataContents() {
return Collections.singletonList(type.name());
}
}
private final ResultType type;
private String transactionUrl = null;
private String commitUrl = null;
private final RestRequest request;
private final List<Statement> statements = new ArrayList<>(10);
public void add(String statement, Map<String,Object> params) {
statements.add(new Statement(statement,params,type));
}
public Result send(String statement, Map<String,Object> params) {
add(statement,params);
List<Result> results = send(transactionUrl());
if (results.size() > 0) return results.get(results.size() - 1);
else throw new RuntimeException("No Results after single send");
}
public Result commit(String statement, Map<String,Object> params) {
add(statement,params);
List<Result> results = commit();
if (results.size() > 0) return results.get(results.size() - 1);
else throw new RuntimeException("No Results after single commit");
}
public List<Result> send() {
return send(transactionUrl());
}
public List<Result> commit() {
try {
return send(commitUrl());
} finally {
commitUrl = null;
}
}
private List<Result> send(String url) {
try {
RequestResult result = request.post(url, map("statements", statements));
if (result.statusIs(Response.Status.OK) || result.statusIs(Response.Status.CREATED)) {
return Result.toResults(handleResult(result), new ArrayList<>(statements), type);
} else {
throw new RuntimeException("Error executing statements: " + result.getStatus() +
" " + result.getText());
}
} finally {
statements.clear();
}
}
public void rollback() {
if (transactionUrl != null) {
request.delete(transactionUrl);
}
transactionUrl = null;
commitUrl = null;
}
private String commitUrl() {
return (commitUrl == null) ? "transaction/commit" : commitUrl;
}
private String transactionUrl() {
return (transactionUrl==null) ? "transaction" : transactionUrl;
}
@SuppressWarnings("unchecked")
private List<Map> handleResult(RequestResult result) {
Map<?, ?> resultData = result.toMap();
Object errors = resultData.get("errors");
if (errors != null && !((Collection)errors).isEmpty()) throw new RuntimeException("Error executing cypher statements "+errors);
if (result.statusIs(ClientResponse.Status.CREATED)) transactionUrl = result.getLocation();
commitUrl = (String) resultData.get("commit");
return (List<Map>) resultData.get("results");
}
@Override
public String toString() {
return "Transaction: "+transactionUrl;
}
}

View File

@@ -0,0 +1,54 @@
/**
* Copyright (c) 2002-2013 "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 <http://www.gnu.org/licenses/>.
*/
package org.neo4j.rest.graphdb.query;
import org.neo4j.rest.graphdb.RequestResult;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import static org.neo4j.helpers.collection.MapUtil.map;
/**
* @author mh
* @since 21.09.11
*/
public class CypherTxResult implements CypherResult {
private final CypherTransaction.Result result;
public CypherTxResult(CypherTransaction.Result result) {
this.result = result;
}
public Collection<String> getColumns() {
return result.getColumns();
}
public Iterable<List<Object>> getData() {
return result.getRows();
}
@Override
public Map asMap() {
return map("columns",getColumns(),"data",getData());
}
}

View File

@@ -0,0 +1,29 @@
/**
* Copyright (c) 2002-2013 "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 <http://www.gnu.org/licenses/>.
*/
package org.neo4j.rest.graphdb.query;
import java.util.Map;
import org.neo4j.rest.graphdb.util.QueryResult;
public interface QueryEngine<T> {
QueryResult<T> query(String statement, Map<String, Object> params);
}

View File

@@ -0,0 +1,52 @@
/**
* Copyright (c) 2002-2013 "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 <http://www.gnu.org/licenses/>.
*/
package org.neo4j.rest.graphdb.query;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import org.neo4j.rest.graphdb.*;
import org.neo4j.rest.graphdb.converter.RestEntityExtractor;
import org.neo4j.rest.graphdb.converter.RestTableResultExtractor;
import org.neo4j.rest.graphdb.util.ConvertedResult;
import org.neo4j.rest.graphdb.util.DefaultConverter;
import org.neo4j.rest.graphdb.util.Handler;
import org.neo4j.rest.graphdb.util.QueryResult;
import org.neo4j.rest.graphdb.util.QueryResultBuilder;
import org.neo4j.rest.graphdb.util.ResultConverter;
public class RestCypherQueryEngine implements QueryEngine<Map<String,Object>> {
private final RestAPI restApi;
private final ResultConverter resultConverter;
public RestCypherQueryEngine(RestAPI restApi) {
this(restApi,null);
}
public RestCypherQueryEngine(RestAPI restApi, ResultConverter resultConverter) {
this.restApi = restApi;
this.resultConverter = resultConverter!=null ? resultConverter : new DefaultConverter();
}
@Override
public QueryResult<Map<String, Object>> query(String statement, Map<String, Object> params) {
return restApi.query(statement, params, this.resultConverter);
}
}

View File

@@ -0,0 +1,72 @@
/**
* Copyright (c) 2002-2013 "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 <http://www.gnu.org/licenses/>.
*/
package org.neo4j.rest.graphdb.query;
import org.neo4j.rest.graphdb.RestAPI;
import org.neo4j.rest.graphdb.UpdatableRestResult;
import org.neo4j.rest.graphdb.converter.RestTableResultExtractor;
import org.neo4j.rest.graphdb.util.*;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
/**
* @author mh
* @since 03.05.12
*/
public class RestQueryResult implements QueryResult<Map<String, Object>> {
private QueryResultBuilder<Map<String, Object>> result;
private ResultConverter resultConverter;
@Override
public <R> ConvertedResult<R> to(Class<R> type) {
checkResult();
return result.to(type);
}
@Override
public <R> ConvertedResult<R> to(Class<R> type, ResultConverter<Map<String, Object>, R> converter) {
checkResult();
return result.to(type, converter);
}
@Override
public void handle(Handler<Map<String, Object>> handler) {
checkResult();
result.handle(handler);
}
@Override
public Iterator<Map<String, Object>> iterator() {
checkResult();
return result.iterator();
}
private void checkResult() {
if (result==null) throw new IllegalStateException("Result not yet available, please finish the transaction first.");
}
public static QueryResultBuilder<Map<String, Object>> toQueryResult(CypherResult responseData, RestAPI restApi, ResultConverter resultConverter) {
final RestTableResultExtractor extractor = new RestTableResultExtractor(restApi.getEntityExtractor());
final List<Map<String, Object>> data = extractor.extract(responseData.asMap());
return new QueryResultBuilder<>(data, resultConverter);
}
}

View File

@@ -0,0 +1,51 @@
/**
* Copyright (c) 2002-2013 "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 <http://www.gnu.org/licenses/>.
*/
package org.neo4j.rest.graphdb.transaction;
import org.neo4j.graphdb.Lock;
import org.neo4j.graphdb.PropertyContainer;
import org.neo4j.graphdb.Transaction;
public class NullTransaction implements Transaction {
public void success() {
}
public void finish() {
}
@Override
public void close() {
}
public void failure() {
}
@Override
public Lock acquireWriteLock(PropertyContainer propertyContainer) {
return null;
}
@Override
public Lock acquireReadLock(PropertyContainer propertyContainer) {
return null;
}
}

View File

@@ -0,0 +1,107 @@
/**
* Copyright (c) 2002-2013 "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 <http://www.gnu.org/licenses/>.
*/
package org.neo4j.rest.graphdb.transaction;
import javax.transaction.*;
import javax.transaction.xa.XAResource;
public class NullTransactionManager implements TransactionManager {
private static final Transaction TRANSACTION = new Transaction() {
@Override
public void commit() throws HeuristicMixedException, HeuristicRollbackException, RollbackException, SecurityException, SystemException {
}
@Override
public boolean delistResource(XAResource xaResource, int i) throws IllegalStateException, SystemException {
return false;
}
@Override
public boolean enlistResource(XAResource xaResource) throws IllegalStateException, RollbackException, SystemException {
return false;
}
@Override
public int getStatus() throws SystemException {
return Status.STATUS_NO_TRANSACTION;
}
@Override
public void registerSynchronization(Synchronization synchronization) throws IllegalStateException, RollbackException, SystemException {
}
@Override
public void rollback() throws IllegalStateException, SystemException {
}
@Override
public void setRollbackOnly() throws IllegalStateException, SystemException {
}
};
@Override
public void begin() throws NotSupportedException, SystemException {
}
@Override
public void commit() throws HeuristicMixedException, HeuristicRollbackException, IllegalStateException, RollbackException, SecurityException, SystemException {
}
@Override
public int getStatus() throws SystemException {
return 0;
}
@Override
public Transaction getTransaction() throws SystemException {
return TRANSACTION;
}
@Override
public void resume(Transaction transaction) throws IllegalStateException, InvalidTransactionException, SystemException {
}
@Override
public void rollback() throws IllegalStateException, SecurityException, SystemException {
}
@Override
public void setRollbackOnly() throws IllegalStateException, SystemException {
}
@Override
public void setTransactionTimeout(int i) throws SystemException {
}
@Override
public Transaction suspend() throws SystemException {
return TRANSACTION;
}
}

View File

@@ -0,0 +1,81 @@
/**
* Copyright (c) 2002-2013 "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 <http://www.gnu.org/licenses/>.
*/
package org.neo4j.rest.graphdb.transaction;
import org.neo4j.graphdb.*;
import org.neo4j.rest.graphdb.query.CypherTransaction;
import static org.neo4j.helpers.collection.MapUtil.map;
public class RemoteCypherTransaction implements Transaction {
boolean success, failure;
ThreadLocal<CypherTransaction> tx;
public RemoteCypherTransaction(ThreadLocal<CypherTransaction> tx) {
this.tx = tx;
}
public void success() {
this.success = true;
}
public void finish() {
close();
}
@Override
public void close() {
try {
if (success && !failure)
tx().commit();
else
tx().rollback();
} finally {
tx.set(null);
}
}
private CypherTransaction tx() {
CypherTransaction cypherTransaction = tx.get();
if (cypherTransaction == null) throw new IllegalStateException("No transaction active");
return cypherTransaction;
}
public void failure() {
this.failure = true;
}
@Override
public Lock acquireWriteLock(PropertyContainer pc) {
if (pc instanceof Node) {
tx().send("MATCH (n) WHERE id(n) = {id} REMOVE n.` lock property `", map("id", ((Node) pc).getId()));
}
if (pc instanceof Relationship) {
tx().send("START r=rel({id}) REMOVE r.` lock property `", map("id", ((Relationship) pc).getId()));
}
return new Lock() { public void release() { } }; // release at commit
}
@Override
public Lock acquireReadLock(PropertyContainer propertyContainer) {
return null;
}
}

View File

@@ -0,0 +1,82 @@
/**
* Copyright (c) 2002-2013 "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 <http://www.gnu.org/licenses/>.
*/
package org.neo4j.rest.graphdb.traversal;
import static java.util.Arrays.asList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Iterator;
import org.neo4j.graphdb.Node;
import org.neo4j.graphdb.Path;
import org.neo4j.graphdb.PropertyContainer;
import org.neo4j.graphdb.Relationship;
public class NodePath implements Path {
private final Node node;
public NodePath(Node node) {
this.node = node;
}
@Override
public Node startNode() {
return node;
}
@Override
public Node endNode() {
return node;
}
@Override
public Relationship lastRelationship() {
return null;
}
@Override
public Iterable<Relationship> relationships() {
return Collections.emptyList();
}
@Override
public Iterable<Node> nodes() {
return asList(node);
}
@Override
public int length() {
return 0;
}
@Override
public Iterator<PropertyContainer> iterator() {
return Arrays.<PropertyContainer>asList(node).iterator();
}
public Iterable<Relationship> reverseRelationships() {
return relationships();
}
public Iterable<Node> reverseNodes() {
return nodes();
}
}

View File

@@ -0,0 +1,81 @@
/**
* Copyright (c) 2002-2013 "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 <http://www.gnu.org/licenses/>.
*/
package org.neo4j.rest.graphdb.traversal;
import static java.util.Arrays.asList;
import java.util.Arrays;
import java.util.Iterator;
import org.neo4j.graphdb.Node;
import org.neo4j.graphdb.Path;
import org.neo4j.graphdb.PropertyContainer;
import org.neo4j.graphdb.Relationship;
public class RelationshipPath implements Path {
private final Relationship relationship;
public RelationshipPath(Relationship relationship) {
this.relationship = relationship;
}
@Override
public Node startNode() {
return relationship.getStartNode();
}
@Override
public Node endNode() {
return relationship.getEndNode();
}
@Override
public Relationship lastRelationship() {
return relationship;
}
@Override
public Iterable<Relationship> relationships() {
return asList(relationship);
}
@Override
public Iterable<Node> nodes() {
return asList(startNode(),endNode());
}
@Override
public int length() {
return 1;
}
@Override
public Iterator<PropertyContainer> iterator() {
return Arrays.<PropertyContainer>asList(startNode(), lastRelationship(), endNode()).iterator();
}
public Iterable<Relationship> reverseRelationships() {
return relationships();
}
public Iterable<Node> reverseNodes() {
return asList(endNode(),startNode());
}
}

View File

@@ -0,0 +1,45 @@
/**
* Copyright (c) 2002-2013 "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 <http://www.gnu.org/licenses/>.
*/
package org.neo4j.rest.graphdb.traversal;
import org.neo4j.graphdb.Direction;
public enum RestDirection {
INCOMING( Direction.INCOMING, "incoming", "in" ),
OUTGOING( Direction.OUTGOING, "outgoing", "out" ),
BOTH( Direction.BOTH, "all", "all" );
public final Direction direction;
public final String longName;
public final String shortName;
RestDirection( Direction direction, String longName, String shortName ) {
this.direction = direction;
this.longName = longName;
this.shortName = shortName;
}
public static RestDirection from( Direction direction ) {
for ( RestDirection restDirection : values() ) {
if ( restDirection.direction == direction ) return restDirection;
}
throw new RuntimeException( "No Rest-Direction for " + direction );
}
}

View File

@@ -0,0 +1,148 @@
/**
* Copyright (c) 2002-2013 "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 <http://www.gnu.org/licenses/>.
*/
package org.neo4j.rest.graphdb.traversal;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import org.neo4j.graphdb.Node;
import org.neo4j.graphdb.Path;
import org.neo4j.graphdb.Relationship;
import org.neo4j.helpers.collection.IterableWrapper;
import org.neo4j.helpers.collection.IteratorUtil;
import org.neo4j.rest.graphdb.RequestResult;
import org.neo4j.rest.graphdb.RestAPI;
import org.neo4j.rest.graphdb.converter.RestResultConverter;
import org.neo4j.rest.graphdb.converter.TypeInformation;
import org.neo4j.rest.graphdb.entity.RestNode;
import org.neo4j.rest.graphdb.entity.RestRelationship;
/**
* @author Michael Hunger
* @since 03.02.11
*/
public class RestPathParser implements RestResultConverter {
private RestAPI restAPI;
public RestPathParser(RestAPI restAPI) {
this.restAPI = restAPI;
}
public static Path parse(Map path, final RestAPI restApi) {
TypeInformation typeInfo = new TypeInformation(path.get("nodes"));
RestPathParser restPathParser = new RestPathParser(restApi);
if (restPathParser.isFullPath(typeInfo)){
return restPathParser.parseFullPath(path, restApi);
}
if (restPathParser.isPath(typeInfo)){
return restPathParser.parsePath(path, restApi);
}
throw new IllegalArgumentException("params map contained illegal type "+typeInfo.getGenericArguments()[0]);
}
private boolean isPath(TypeInformation typeInfo) {
return typeInfo.getGenericArguments()[0].equals(String.class);
}
private boolean isFullPath(TypeInformation typeInfo) {
return Map.class.isAssignableFrom(typeInfo.getGenericArguments()[0]);
}
private Path parseFullPath(Map path, final RestAPI restApi) {
final Collection<Map<?, ?>> nodesData = (Collection<Map<?, ?>>) path.get("nodes");
final Collection<Map<?, ?>> relationshipsData = (Collection<Map<?, ?>>) path.get("relationships");
final Map<?, ?> lastRelationshipData = lastElementMap(relationshipsData);
final Map<?, ?> startData = (Map<?, ?>) path.get("start");
final Map<?, ?> endData = (Map<?, ?>) path.get("end");
final Integer length = (Integer) path.get("length");
RestRelationship lastRelationship = lastRelationshipData == null ? null : new RestRelationship(lastRelationshipData, restApi);
return new SimplePath(
new RestNode(startData,restApi),
new RestNode(endData,restApi),
lastRelationship,
length,
new IterableWrapper<Node, Map<?,?>>(nodesData) {
@Override
protected Node underlyingObjectToObject(Map<?, ?> data) {
return new RestNode(data,restApi);
}
},
new IterableWrapper<Relationship, Map<?,?>>(relationshipsData) {
@Override
protected Relationship underlyingObjectToObject(Map<?, ?> data) {
return new RestRelationship(data,restApi);
}
});
}
private Path parsePath(Map path, final RestAPI restApi){
final Collection<String> nodesData = (Collection<String>) path.get("nodes");
final Collection<String> relationshipsData = (Collection<String>) path.get("relationships");
final String lastRelationshipData = lastElement(relationshipsData);
final String startData = (String) path.get("start");
final String endData = (String) path.get("end");
final Integer length = (Integer) path.get("length");
RestRelationship lastRelationship = lastRelationshipData == null ? null : new RestRelationship(lastRelationshipData, restApi);
return new SimplePath(
new RestNode(startData,restApi),
new RestNode(endData,restApi),
lastRelationship,
length,
new IterableWrapper<Node, String>(nodesData) {
@Override
protected Node underlyingObjectToObject(String data) {
return new RestNode(data,restApi);
}
},
new IterableWrapper<Relationship, String>(relationshipsData) {
@Override
protected Relationship underlyingObjectToObject(String data) {
return new RestRelationship(data,restApi);
}
});
}
private String lastElement(Collection<String> collection){
return IteratorUtil.lastOrNull(collection);
}
private Map<?, ?> lastElementMap(Collection<Map<?, ?>> collection) {
if (collection.isEmpty()) return null;
if (collection instanceof List) {
List<Map<?,?>> list = (List<Map<?,?>>) collection;
return list.get(list.size()-1);
}
Map<?, ?> result = null;
for (Map<?, ?> value : collection) {
result=value;
}
return result;
}
@Override
public Object convertFromRepresentation(RequestResult value) {
return parse(value.toMap(), this.restAPI);
}
}

View File

@@ -0,0 +1,213 @@
/**
* Copyright (c) 2002-2013 "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 <http://www.gnu.org/licenses/>.
*/
package org.neo4j.rest.graphdb.traversal;
import org.neo4j.graphdb.*;
import org.neo4j.graphdb.traversal.*;
import org.neo4j.graphdb.traversal.Traverser;
import org.neo4j.helpers.Predicate;
import org.neo4j.kernel.Traversal;
import org.neo4j.kernel.Uniqueness;
import org.neo4j.rest.graphdb.entity.RestNode;
import java.lang.reflect.Field;
import java.util.*;
/**
* @author Michael Hunger
* @since 02.02.11
*/
public class RestTraversal implements RestTraversalDescription {
private final Map<String, Object> description=new HashMap<String, Object>();
public RestTraversal() {
}
@Override
public String toString() {
return description.toString();
}
public RestTraversalDescription uniqueness(UniquenessFactory uniquenessFactory) {
return uniqueness(uniquenessFactory,null);
}
public RestTraversalDescription uniqueness(UniquenessFactory uniquenessFactory, Object value) {
String uniqueness = restify(uniquenessFactory);
add("uniqueness",value==null ? uniqueness : toMap("name",uniqueness, "value", value));
return null;
}
private String restify(UniquenessFactory uniquenessFactory) {
if (uniquenessFactory instanceof Uniqueness) {
return ((Uniqueness)uniquenessFactory).name().toLowerCase().replace("_"," ");
}
throw new UnsupportedOperationException("Only values of "+Uniqueness.class+" are supported");
}
public RestTraversalDescription prune(PruneEvaluator pruneEvaluator) {
if (pruneEvaluator == PruneEvaluator.NONE) {
return add( "prune_evaluator", toMap( "language", "builtin", "name", "none" ) );
}
Integer maxDepth= getMaxDepthValueOrNull(pruneEvaluator);
if (maxDepth!=null) {
return maxDepth(maxDepth);
}
throw new UnsupportedOperationException("Only max depth supported");
}
private Integer getMaxDepthValueOrNull(PruneEvaluator pruneEvaluator) {
try {
final Field depthField = pruneEvaluator.getClass().getDeclaredField("val$depth");
depthField.setAccessible(true);
return (Integer) depthField.get(pruneEvaluator);
} catch (Exception e) {
return null;
}
}
public RestTraversalDescription filter(Predicate<Path> pathPredicate) {
if (pathPredicate == Evaluators.all()) return add("return_filter",toMap("language","builtin", "name","all"));
if (pathPredicate == Evaluators.excludeStartPosition()) return add("return_filter",toMap("language","builtin", "name","all_but_start_node"));
throw new UnsupportedOperationException("Only builtin paths supported");
}
public RestTraversalDescription evaluator(PathEvaluator evaluator) {
if (evaluator == Evaluators.all()) return add("return_filter",toMap("language","builtin", "name","all"));
if (evaluator == Evaluators.excludeStartPosition()) return add("return_filter",toMap("language","builtin", "name","all_but_start_node"));
throw new UnsupportedOperationException("Only builtin paths supported");
}
public RestTraversalDescription evaluator(Evaluator evaluator) {
if (evaluator == Evaluators.all()) return add("return_filter",toMap("language","builtin", "name","all"));
if (evaluator == Evaluators.excludeStartPosition()) return add("return_filter",toMap("language","builtin", "name","all_but_start_node"));
throw new UnsupportedOperationException("Only builtin paths supported");
}
public RestTraversalDescription prune(ScriptLanguage language, String code) {
return add("prune_evaluator",toMap("language",language.name().toLowerCase(),"body",code ));
}
public RestTraversalDescription filter(ScriptLanguage language, String code) {
return add("return_filter",toMap("language",language.name().toLowerCase(),"body",code ));
}
public RestTraversalDescription maxDepth(int depth) {
return add("max_depth", depth);
}
public RestTraversalDescription order(BranchOrderingPolicy branchOrderingPolicy) {
throw new UnsupportedOperationException();
}
public RestTraversalDescription depthFirst() {
return add("order","depth_first");
}
public RestTraversalDescription breadthFirst() {
return add("order", "breadth_first");
}
private RestTraversalDescription add(String key, Object value) {
description.put(key,value);
return this;
}
public RestTraversalDescription relationships(RelationshipType relationshipType) {
return relationships(relationshipType, null);
}
public RestTraversalDescription relationships(RelationshipType relationshipType, Direction direction) {
if (!description.containsKey("relationships")) {
description.put("relationships",new HashSet<Map<String,Object>>());
}
Set<Map<String,Object>> relationships= (Set<Map<String, Object>>) description.get("relationships");
relationships.add(toMap("type", relationshipType.name(), "direction", directionString(direction)));
return this;
}
private Map<String, Object> toMap(Object...params) {
if (params.length % 2 != 0) throw new IllegalArgumentException("toMap needs an even number of arguments, but was "+Arrays.toString(params));
Map<String, Object> result = new HashMap<String, Object>();
for (int i = 0; i < params.length; i+=2) {
if (params[i+1] == null) continue;
result.put(params[i].toString(), params[i + 1].toString());
}
return result;
}
private String directionString(Direction direction) {
return RestDirection.from(direction).shortName;
}
public RestTraversalDescription expand(RelationshipExpander relationshipExpander) {
throw new UnsupportedOperationException();
}
public Traverser traverse(Node node) {
final RestNode restNode = (RestNode) node;
return restNode.getRestApi().traverse(restNode, description);
}
public static RestTraversalDescription description() {
return new RestTraversal();
}
public Map<String,Object> getPostData() {
return description;
}
@Override
public TraversalDescription expand(PathExpander<?> expander) {
throw new UnsupportedOperationException();
}
@Override
public <STATE> TraversalDescription expand(PathExpander<STATE> expander, InitialStateFactory<STATE> initialState) {
throw new UnsupportedOperationException();
}
@Override
public <STATE> TraversalDescription expand(PathExpander<STATE> expander, InitialBranchState<STATE> initialState) {
throw new UnsupportedOperationException();
}
@Override
public TraversalDescription sort(Comparator<? super Path> comparator) {
throw new UnsupportedOperationException();
}
@Override
public TraversalDescription reverse() {
throw new UnsupportedOperationException();
}
@Override
public Traverser traverse(Node... startNode) {
throw new UnsupportedOperationException();
}
@Override
public Traverser traverse(Iterable<Node> nodes) {
throw new UnsupportedOperationException();
}
}

View File

@@ -0,0 +1,52 @@
/**
* Copyright (c) 2002-2013 "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 <http://www.gnu.org/licenses/>.
*/
package org.neo4j.rest.graphdb.traversal;
import org.neo4j.graphdb.Direction;
import org.neo4j.graphdb.Path;
import org.neo4j.graphdb.RelationshipType;
import org.neo4j.graphdb.traversal.PruneEvaluator;
import org.neo4j.graphdb.traversal.TraversalDescription;
import org.neo4j.helpers.Predicate;
/**
* @author Michael Hunger
* @since 03.02.11
*/
public interface RestTraversalDescription extends TraversalDescription {
RestTraversalDescription prune(ScriptLanguage language, String code);
RestTraversalDescription prune(PruneEvaluator pruneEvaluator);
RestTraversalDescription filter(ScriptLanguage language, String code);
RestTraversalDescription maxDepth(int depth);
RestTraversalDescription breadthFirst();
RestTraversalDescription relationships(RelationshipType relationshipType, Direction direction);
RestTraversalDescription filter(Predicate<Path> pathPredicate);
public enum ScriptLanguage {
JAVASCRIPT;
}
}

View File

@@ -0,0 +1,79 @@
/**
* Copyright (c) 2002-2013 "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 <http://www.gnu.org/licenses/>.
*/
package org.neo4j.rest.graphdb.traversal;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Map;
import org.neo4j.graphdb.*;
import org.neo4j.graphdb.traversal.TraversalMetadata;
import org.neo4j.graphdb.traversal.Traverser;
import org.neo4j.rest.graphdb.util.WrappingResourceIterator;
import org.neo4j.rest.graphdb.RestAPI;
import org.neo4j.rest.graphdb.util.ResourceIterableWrapper;
/**
* @author Michael Hunger
* @since 03.02.11
*/
public class RestTraverser implements Traverser {
private final Collection<Path> paths;
public RestTraverser(Collection col, RestAPI restApi) {
this.paths = parseToPaths(col, restApi);
}
private Collection<Path> parseToPaths(Collection col, RestAPI restApi) {
Collection<Path> result=new ArrayList<Path>(col.size());
for (Object path : col) {
if (!(path instanceof Map)) throw new RuntimeException("Expected Map for Path representation but got: "+(path!=null ? path.getClass() : null));
result.add(RestPathParser.parse((Map) path, restApi));
}
return result;
}
public ResourceIterable<Node> nodes() {
return new ResourceIterableWrapper<Node, Path>(paths) {
@Override
protected Node underlyingObjectToObject(Path path) {
return path.endNode();
}
};
}
public ResourceIterable<Relationship> relationships() {
return new ResourceIterableWrapper<Relationship, Path>(paths) {
@Override
protected Relationship underlyingObjectToObject(Path path) {
return path.lastRelationship();
}
};
}
public ResourceIterator<Path> iterator() {
return new WrappingResourceIterator<Path>(paths.iterator());
}
@Override
public TraversalMetadata metadata() {
throw new UnsupportedOperationException();
}
}

View File

@@ -0,0 +1,121 @@
/**
* Copyright (c) 2002-2013 "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 <http://www.gnu.org/licenses/>.
*/
package org.neo4j.rest.graphdb.traversal;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import org.neo4j.graphdb.Node;
import org.neo4j.graphdb.Path;
import org.neo4j.graphdb.PropertyContainer;
import org.neo4j.graphdb.Relationship;
import org.neo4j.helpers.collection.IteratorUtil;
/**
* @author Michael Hunger
* @since 03.02.11
*/
public class SimplePath implements Path {
Iterable<Relationship> relationships;
Iterable<Node> nodes;
private final Relationship lastRelationship;
private int length;
private Node startNode;
private Node endNode;
public SimplePath(Node startNode, Node endNode, Relationship lastRelationship, int length, Iterable<Node> nodes, Iterable<Relationship> relationships) {
this.startNode = startNode;
this.endNode = endNode;
this.lastRelationship = lastRelationship;
this.length = length;
this.nodes = nodes;
this.relationships = relationships;
}
public Node startNode() {
return startNode;
}
public Node endNode() {
return endNode;
}
public Relationship lastRelationship() {
return lastRelationship;
}
public Iterable<Relationship> relationships() {
return relationships;
}
public Iterable<Node> nodes() {
return nodes;
}
public int length() {
return length;
}
public Iterator<PropertyContainer> iterator() {
return new Iterator<PropertyContainer>()
{
Iterator<? extends PropertyContainer> current = nodes().iterator();
Iterator<? extends PropertyContainer> next = relationships().iterator();
public boolean hasNext()
{
return current.hasNext();
}
public PropertyContainer next()
{
try
{
return current.next();
}
finally
{
Iterator<? extends PropertyContainer> temp = current;
current = next;
next = temp;
}
}
public void remove()
{
next.remove();
}
};
}
public Iterable<Relationship> reverseRelationships() {
List<Relationship> reverseRels = IteratorUtil.addToCollection(relationships, new ArrayList<Relationship>());
Collections.reverse(reverseRels);
return reverseRels;
}
public Iterable<Node> reverseNodes() {
List<Node> reverseNodes = IteratorUtil.addToCollection(nodes, new ArrayList<Node>());
Collections.reverse(reverseNodes);
return reverseNodes;
}
}

View File

@@ -0,0 +1,81 @@
/**
* Copyright (c) 2002-2013 "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 <http://www.gnu.org/licenses/>.
*/
package org.neo4j.rest.graphdb.util;
import java.lang.reflect.Array;
import java.util.Collection;
/**
* @author Michael Hunger
* @since 02.02.11
*/
public class ArrayConverter {
public Object toArray(Collection col) {
Object entry = getNonNullEntry(col);
if (entry==null) return null;
Class<? extends Object> elementClass = getArrayElementClass( entry );
Object array = Array.newInstance(elementClass, col.size());
if (Object.class.isAssignableFrom( elementClass)) {
col.toArray( (Object[])array );
} else {
int i=0;
for ( Object value : col ) {
setArrayValue(array,i,value,elementClass);
i+=1;
}
}
return array;
}
private void setArrayValue( Object array, int i, Object value, Class<? extends Object> type ) {
if (value==null) return;
if ( value instanceof Number ) {
Number number = (Number) value;
if (type.equals( int.class )) { Array.setInt( array, i, number.intValue()); return;}
if (type.equals( long.class )) { Array.setLong( array, i, number.longValue()); return;}
if (type.equals( double.class )) { Array.setDouble( array, i, number.doubleValue()); return;}
if (type.equals( float.class )) { Array.setFloat( array, i, number.floatValue()); return;}
if (type.equals( byte.class )) { Array.setByte( array, i, number.byteValue()); return;}
if (type.equals( short.class )) { Array.setShort( array, i, number.shortValue()); return;}
}
if (type.equals( char.class )) { Array.setChar( array, i, (Character)value ); return;}
if (type.equals( boolean.class )) { Array.setBoolean( array, i, (Boolean) value ); return;}
}
private Class<? extends Object> getArrayElementClass( Object entry ) {
Class<? extends Object> type = entry.getClass();
if (type.equals( Integer.class )) return int.class;
if (type.equals( Long.class )) return long.class;
if (type.equals( Double.class )) return double.class;
if (type.equals( Float.class )) return float.class;
if (type.equals( Byte.class )) return byte.class;
if (type.equals( Short.class )) return short.class;
if (type.equals( Character.class )) return char.class;
if (type.equals( Boolean.class )) return boolean.class;
return type;
}
private Object getNonNullEntry(Collection col) {
for ( Object entry : col ) {
if (entry!=null) return entry;
}
return null;
}
}

View File

@@ -0,0 +1,58 @@
/**
* Copyright (c) 2002-2013 "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 <http://www.gnu.org/licenses/>.
*/
package org.neo4j.rest.graphdb.util;
import java.util.concurrent.TimeUnit;
public class Config {
public final static String CONFIG_PREFIX = "org.neo4j.rest.";
public static final String CONFIG_STREAM = CONFIG_PREFIX + "stream";
public static final String CONFIG_BATCH_TRANSACTION = CONFIG_PREFIX+"batch_transaction";
public static final String CONFIG_LOG_REQUESTS = CONFIG_PREFIX+"logging_filter";
public static final String WRITE_THREADS = "write_threads";
public static int getConnectTimeout() {
return getTimeout("connect_timeout", 30);
}
public static int getReadTimeout() {
return getTimeout("read_timeout", 30);
}
public static boolean streamingIsEnabled() {
return Boolean.parseBoolean(System.getProperty(CONFIG_STREAM,"true"));
}
public static boolean useBatchTransactions() {
return System.getProperty(CONFIG_BATCH_TRANSACTION,"false").equalsIgnoreCase("true");
}
public static boolean useLoggingFilter() {
return System.getProperty(CONFIG_LOG_REQUESTS,"false").equalsIgnoreCase("true");
}
private static int getTimeout(final String param, final int defaultValue) {
return (int) TimeUnit.SECONDS.toMillis(Integer.parseInt(System.getProperty(CONFIG_PREFIX + param, "" + defaultValue)));
}
public static int getWriterThreads() {
return Integer.parseInt(System.getProperty(CONFIG_PREFIX + WRITE_THREADS, "" + 10));
}
}

View File

@@ -0,0 +1,27 @@
/**
* Copyright (c) 2002-2013 "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 <http://www.gnu.org/licenses/>.
*/
package org.neo4j.rest.graphdb.util;
public interface ConvertedResult<R> extends Iterable<R> {
R single();
R singleOrNull();
void handle(Handler<R> handler);
}

View File

@@ -0,0 +1,93 @@
/**
* Copyright (c) 2002-2013 "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 <http://www.gnu.org/licenses/>.
*/
package org.neo4j.rest.graphdb.util;
import java.util.Iterator;
import java.util.Map;
import org.neo4j.graphdb.Node;
import org.neo4j.graphdb.Path;
import org.neo4j.graphdb.Relationship;
import org.neo4j.rest.graphdb.traversal.NodePath;
import org.neo4j.rest.graphdb.traversal.RelationshipPath;
public class DefaultConverter<T,R> implements ResultConverter<T,R> {
public R convert(Object value, Class type) {
if (value == null || type.isInstance(value)) return (R) value;
Object singleValue = extractValue(value);
if (singleValue == null || type.isInstance(singleValue)) return (R) singleValue;
final Class<?> sourceType = singleValue.getClass();
Object result = doConvert(singleValue, sourceType, type);
if (result == null)
throw new RuntimeException("Cannot automatically convert " + sourceType + " to " + type + " please use a custom converter");
return (R) result;
}
protected Object extractValue(Object value) {
if (value instanceof Map) return extractSingle(((Map)value).values());
if (value instanceof Iterable) return extractSingle((Iterable)value);
return value;
}
private Object extractSingle(Iterable values) {
final Iterator it = values.iterator();
if (!it.hasNext()) throw new RuntimeException("Cannot extract single value from empty Iterable.");
final Object result = it.next();
if (it.hasNext()) throw new RuntimeException("Cannot extract single value from Iterable with more than one elements.");
return result;
}
protected Object doConvert(Object value, Class<?> sourceType, Class type) {
if (Node.class.isAssignableFrom(type)) {
return toNode(value, sourceType);
}
if (Relationship.class.isAssignableFrom(type)) {
return toRelationship(value, sourceType);
}
if (Path.class.isAssignableFrom(type)) {
return toPath(value, sourceType);
}
if (type.isEnum()) {
return Enum.valueOf(type, value.toString());
}
return null;
}
protected Path toPath(Object value, Class<?> sourceType) {
if (Node.class.isAssignableFrom(sourceType)) return new NodePath((Node) value);
if (Relationship.class.isAssignableFrom(sourceType)) return new RelationshipPath((Relationship) value);
return null;
}
protected Relationship toRelationship(Object value, Class<?> sourceType) {
if (Relationship.class.isAssignableFrom(sourceType)) return ((Relationship) value);
if (Path.class.isAssignableFrom(sourceType)) return ((Path) value).lastRelationship();
if (Node.class.isAssignableFrom(sourceType)) return ((Node) value).getRelationships().iterator().next();
return null;
}
protected Node toNode(Object value, Class<?> sourceType) {
if (Node.class.isAssignableFrom(sourceType)) return (Node)value;
if (Path.class.isAssignableFrom(sourceType)) return ((Path) value).endNode();
if (Relationship.class.isAssignableFrom(sourceType)) return ((Relationship) value).getEndNode();
return null;
}
}

View File

@@ -0,0 +1,24 @@
/**
* Copyright (c) 2002-2013 "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 <http://www.gnu.org/licenses/>.
*/
package org.neo4j.rest.graphdb.util;
public interface Handler<R> {
void handle(R value);
}

View File

@@ -0,0 +1,97 @@
/**
* Copyright (c) 2002-2013 "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 <http://www.gnu.org/licenses/>.
*/
package org.neo4j.rest.graphdb.util;
/**
* @author mh
* @since 13.12.10
*/
import java.io.IOException;
import java.io.InputStream;
import java.io.StringWriter;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Scanner;
import org.codehaus.jackson.JsonGenerator;
import org.codehaus.jackson.map.ObjectMapper;
import org.neo4j.rest.graphdb.PropertiesMap;
public class JsonHelper {
static final ObjectMapper OBJECT_MAPPER = new ObjectMapper();
@SuppressWarnings("unchecked")
public static Map<String, Object> jsonToMap( String json ) {
return (Map<String, Object>) readJson( json );
}
@SuppressWarnings("unchecked")
public static List<Map<String, Object>> jsonToListOfRelationshipRepresentations( String json ) {
return (List<Map<String, Object>>) readJson( json );
}
public static Object readJson( String json ) {
try {
return OBJECT_MAPPER.readValue( json, Object.class );
} catch ( IOException e ) {
throw new RuntimeException( "Error reading as JSON '"+json+"'", e);
}
}
public static Object jsonToSingleValue( String json ) {
Object jsonObject = readJson( json );
return jsonObject;
/*
return jsonObject instanceof Collection<?> ? jsonObject :
PropertiesMap.assertSupportedPropertyValue( jsonObject );
*/
}
public static String createJsonFrom( Object data ) {
try {
StringWriter writer = new StringWriter();
JsonGenerator generator = OBJECT_MAPPER.getJsonFactory()
.createJsonGenerator( writer ).useDefaultPrettyPrinter();
OBJECT_MAPPER.writeValue(generator, data);
writer.close();
return writer.getBuffer().toString();
} catch ( IOException e ) {
throw new RuntimeException( e );
}
}
public static String readString(InputStream stream) {
try {
return new Scanner(stream).useDelimiter("\\Z").next();
} catch(Exception ioe) {
System.err.println("Error reading string from stream "+ioe.getMessage());
return "";
} finally {
try {
stream.close();
} catch (IOException e) {
// ignore
}
}
}
}

View File

@@ -0,0 +1,27 @@
/**
* Copyright (c) 2002-2013 "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 <http://www.gnu.org/licenses/>.
*/
package org.neo4j.rest.graphdb.util;
public interface QueryResult<T> extends Iterable<T> {
<R> ConvertedResult<R> to(Class<R> type);
<R> ConvertedResult<R> to(Class<R> type, ResultConverter<T, R> resultConverter);
void handle(Handler<T> handler);
}

View File

@@ -0,0 +1,132 @@
/**
* Copyright (c) 2002-2013 "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 <http://www.gnu.org/licenses/>.
*/
package org.neo4j.rest.graphdb.util;
import java.util.Iterator;
import java.util.Map;
import org.neo4j.graphdb.index.IndexHits;
import org.neo4j.helpers.collection.ClosableIterable;
import org.neo4j.helpers.collection.IteratorUtil;
import org.neo4j.helpers.collection.IteratorWrapper;
public class QueryResultBuilder<T> implements QueryResult<T> {
private Iterable<T> result;
private final ResultConverter defaultConverter;
private final boolean isClosableIterable;
private boolean isClosed;
public QueryResultBuilder(Iterable<T> result) {
this(result, new DefaultConverter());
}
public QueryResultBuilder(Iterable<T> result, final ResultConverter<T,?> defaultConverter) {
this.result = result;
this.isClosableIterable = result instanceof IndexHits || result instanceof ClosableIterable;
this.defaultConverter = defaultConverter;
}
public static String replaceParams(String statement, Map<String, Object> params) {
if (params==null || params.isEmpty()) return statement;
for (Map.Entry<String, Object> param : params.entrySet()) {
statement = statement.replaceAll("%"+param.getKey()+"\\b",""+param.getValue());
}
return statement;
}
@Override
public <R> ConvertedResult<R> to(Class<R> type) {
return this.to(type, defaultConverter);
}
@Override
public <R> ConvertedResult<R> to(final Class<R> type, final ResultConverter<T, R> resultConverter) {
return new ConvertedResult<R>() {
@Override
public R single() {
try {
final T value = IteratorUtil.single(QueryResultBuilder.this.result);
return resultConverter.convert(value, type);
} finally {
closeIfNeeded();
}
}
@Override
public R singleOrNull() {
try {
final T value = IteratorUtil.singleOrNull(QueryResultBuilder.this.result);
return resultConverter.convert(value, type);
} finally {
closeIfNeeded();
}
}
@Override
public void handle(Handler<R> handler) {
try {
for (T value : result) {
handler.handle(resultConverter.convert(value, type));
}
} finally {
closeIfNeeded();
}
}
@Override
public Iterator<R> iterator() {
return new IteratorWrapper<R, T>(result.iterator()) {
protected R underlyingObjectToObject(T value) {
return resultConverter.convert(value, type);
}
};
}
};
}
@Override
public void handle(Handler<T> handler) {
try {
for (T value : result) {
handler.handle(value);
}
} finally {
closeIfNeeded();
}
}
private void closeIfNeeded() {
if (isClosableIterable && !isClosed) {
if (result instanceof IndexHits) {
((IndexHits) result).close();
} else if (result instanceof ClosableIterable) {
((ClosableIterable) result).close();
}
isClosed=true;
}
}
@Override
public Iterator<T> iterator() {
return result.iterator();
}
}

View File

@@ -0,0 +1,45 @@
/**
* Copyright (c) 2002-2013 "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 <http://www.gnu.org/licenses/>.
*/
package org.neo4j.rest.graphdb.util;
import org.neo4j.graphdb.ResourceIterable;
import org.neo4j.graphdb.ResourceIterator;
import org.neo4j.helpers.collection.IterableWrapper;
import java.util.Iterator;
public abstract class ResourceIterableWrapper<T,U> extends IterableWrapper<T,U> implements ResourceIterable<T> {
public ResourceIterableWrapper(Iterable<U> iterableToWrap) {
super(iterableToWrap);
}
public ResourceIterator<T> iterator() {
final Iterator<T> it = super.iterator();
return new ResourceIterator<T>() {
public void close() { }
public boolean hasNext() { return it.hasNext(); }
public T next() { return it.next(); }
public void remove() { }
};
}
}

View File

@@ -0,0 +1,32 @@
/**
* Copyright (c) 2002-2013 "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 <http://www.gnu.org/licenses/>.
*/
package org.neo4j.rest.graphdb.util;
public interface ResultConverter<T, R> {
R convert(T value, Class<R> type);
ResultConverter NO_OP_RESULT_CONVERTER = new ResultConverter() {
@Override
public Object convert(Object value, Class type) {
return null;
}
};
}

View File

@@ -0,0 +1,92 @@
/**
* Copyright (c) 2002-2013 "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 <http://www.gnu.org/licenses/>.
*/
package org.neo4j.rest.graphdb.util;
/**
* @author mh
* @since 13.12.10
*/
import org.codehaus.jackson.JsonGenerator;
import org.codehaus.jackson.map.ObjectMapper;
import org.neo4j.rest.graphdb.PropertiesMap;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.StringWriter;
import java.util.Collection;
import java.util.List;
import java.util.Map;
public class StreamJsonHelper {
static final ObjectMapper OBJECT_MAPPER = new ObjectMapper();
@SuppressWarnings("unchecked")
public static Map<String, Object> jsonToMap( InputStream stream ) {
return (Map<String, Object>) readJson( stream );
}
@SuppressWarnings("unchecked")
public static List<Map<String, Object>> jsonToListOfRelationshipRepresentations( InputStream stream ) {
return (List<Map<String, Object>>) readJson( stream );
}
public static Object readJson( InputStream stream ) {
try {
return OBJECT_MAPPER.readValue(stream, Object.class);
} catch ( IOException e ) {
throw new RuntimeException( "Error reading input '"+JsonHelper.readString(stream)+"' as JSON ", e);
} finally {
if (stream!=null) {
close(stream);
}
}
}
private static void close(InputStream stream) {
try {
stream.close();
} catch (IOException e) {
// ignore
}
}
public static Object jsonToSingleValue( InputStream stream ) {
Object jsonObject = readJson( stream );
return jsonObject;
/* return jsonObject instanceof Collection<?> ? jsonObject :
PropertiesMap.assertSupportedPropertyValue( jsonObject );
*/
}
// todo boolean close
public static void writeJsonTo( Object data , OutputStream stream) {
try {
JsonGenerator generator = OBJECT_MAPPER.getJsonFactory()
.createJsonGenerator(stream);
OBJECT_MAPPER.writeValue(generator, data);
stream.close();
} catch ( IOException e ) {
throw new RuntimeException( e );
}
}
}

View File

@@ -0,0 +1,34 @@
/**
* Copyright (c) 2002-2013 "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 <http://www.gnu.org/licenses/>.
*/
package org.neo4j.rest.graphdb.util;
import org.neo4j.graphdb.Node;
import org.neo4j.graphdb.Relationship;
public class TestHelper {
public static Relationship firstRelationshipBetween( Iterable<Relationship> relationships, final Node startNode, final Node endNode ) {
for ( Relationship relationship : relationships ) {
if ( relationship.getOtherNode( startNode ).equals( endNode ) ) return relationship;
}
return null;
}
}

View File

@@ -0,0 +1,79 @@
/**
* Copyright (c) 2002-2013 "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 <http://www.gnu.org/licenses/>.
*/
package org.neo4j.rest.graphdb.util;
import org.neo4j.graphdb.ResourceIterator;
import java.util.Iterator;
public class WrappingResourceIterator<T> implements ResourceIterator<T>
{
private final Iterator<T> iterator;
boolean hasNext;
public WrappingResourceIterator(Iterator<T> iterator)
{
this.iterator = iterator;
hasNext = iterator.hasNext();
}
@Override
public void close()
{
hasNext = false;
}
@Override
public boolean hasNext()
{
return hasNext;
}
@Override
public T next()
{
assertHasNext();
T result = iterator.next();
hasNext = iterator.hasNext();
return result;
}
@Override
public void remove()
{
assertHasNext();
try
{
iterator.remove();
}
finally
{
hasNext = iterator.hasNext();
}
}
private void assertHasNext()
{
if ( ! hasNext )
{
throw new IllegalArgumentException( "Iterator already closed" );
}
}
}

View File

@@ -18,7 +18,7 @@ package org.springframework.data.neo4j.rest;
import org.neo4j.graphdb.*;
import org.neo4j.graphdb.index.Index;
import org.neo4j.rest.graphdb.RestAPI;
import org.neo4j.rest.graphdb.RestAPIFacade;
import org.neo4j.rest.graphdb.RestAPIImpl;
import org.neo4j.rest.graphdb.entity.RestNode;
import org.neo4j.rest.graphdb.index.RestIndex;
import org.neo4j.rest.graphdb.index.RestIndexManager;
@@ -56,21 +56,17 @@ public class SpringRestGraphDatabase extends org.neo4j.rest.graphdb.RestGraphDat
}
public SpringRestGraphDatabase( String uri ) {
this( new RestAPIFacade( uri ) );
this( new RestAPIImpl( uri ) );
}
public SpringRestGraphDatabase( String uri, String user, String password ) {
this(new RestAPIFacade( uri, user, password ));
this(new RestAPIImpl( uri, user, password ));
}
@Override
public Node createNode(Map<String, Object> props, Collection<String> labels) {
RestAPI restAPI = super.getRestAPI();
RestNode node = restAPI.createNode(props);
if (labels!=null && !labels.isEmpty()) {
restAPI.addLabels(node, toLabels(labels));
}
return node;
return restAPI.createNode(props,labels);
}
private String[] toLabels(Collection<String> labels) {
@@ -93,14 +89,13 @@ public class SpringRestGraphDatabase extends org.neo4j.rest.graphdb.RestGraphDat
public Node getOrCreateNode(String indexName, String key, Object value, final Map<String, Object> properties, Collection<String> labels) {
if (indexName ==null || key == null || value==null) throw new IllegalArgumentException("Unique index "+ indexName +" key "+key+" value must not be null");
final RestIndex<Node> nodeIndex = index().forNodes(indexName);
RestNode node = getRestAPI().getOrCreateNode(nodeIndex, key, value, properties);
getRestAPI().addLabels(node,toLabels(labels));
return node;
return getRestAPI().getOrCreateNode(nodeIndex, key, value, properties, labels);
}
@Override
public Node merge(String labelName, String key, Object value, final Map<String, Object> nodeProperties, Collection<String> labels) {
return schemaIndexProvider.merge(labelName,key,value,nodeProperties, labels);
return getRestAPI().merge(labelName,key,value,nodeProperties, labels);
// return schemaIndexProvider.merge(labelName,key,value,nodeProperties, labels);
}

View File

@@ -0,0 +1,44 @@
/**
* Copyright (c) 2002-2013 "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 <http://www.gnu.org/licenses/>.
*/
package org.neo4j.rest.graphdb;
import java.util.Map;
import javax.ws.rs.PathParam;
import org.neo4j.server.plugins.Name;
/**
* User: KBurchardi
* Date: 13.10.11
* Time: 11:41
*/
public interface CypherPlugin {
@Name( "execute_query" )
Iterable<Object> executeScript(
@PathParam("query") final String query,
@PathParam( "params") Map parameters,
@PathParam( "format") final String format);
Iterable<Object> execute_query(
@PathParam("query") final String query,
@PathParam( "params") Map parameters,
@PathParam( "format") final String format);
}

View File

@@ -0,0 +1,40 @@
/**
* Copyright (c) 2002-2013 "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 <http://www.gnu.org/licenses/>.
*/
package org.neo4j.rest.graphdb;
import org.junit.After;
import org.junit.Test;
import org.neo4j.graphdb.NotFoundException;
import org.neo4j.graphdb.Transaction;
public class EmptyGraphTest extends RestTestBase {
@Test(expected = NotFoundException.class)
public void testGetReferenceNodeOnEmptyDbFails() {
try (Transaction tx = getGraphDatabase().beginTx()) {
node().delete();
tx.success();
}
try (Transaction tx = getGraphDatabase().beginTx()) {
getGraphDatabase().getNodeById(node().getId());
tx.success();
}
}
}

View File

@@ -0,0 +1,46 @@
/**
* Copyright (c) 2002-2013 "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 <http://www.gnu.org/licenses/>.
*/
package org.neo4j.rest.graphdb;
import javax.ws.rs.*;
/**
* User: KBurchardi
* Date: 20.10.11
* Time: 13:21
*/
@Path( "/helloworld" )
public interface HelloWorldService {
@GET
@Path( "/{nodeId}" )
public String get(@PathParam("nodeId") long nodeId);
@PUT
@Path( "/{nodeId}" )
public String put(@PathParam("nodeId") long nodeId, String body);
@POST
@Path( "/{nodeId}" )
public String post(@PathParam("nodeId") long nodeId, String body);
@POST
@Path( "/empty/{nodeId}" )
public void postWithoutResult(@PathParam("nodeId") long nodeId);
@DELETE
@Path( "/{nodeId}" )
public String delete(@PathParam("nodeId") long nodeId);
}

View File

@@ -0,0 +1,49 @@
/**
* Copyright (c) 2002-2013 "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 <http://www.gnu.org/licenses/>.
*/
package org.neo4j.rest.graphdb;
import org.hamcrest.Description;
import org.junit.internal.matchers.TypeSafeMatcher;
import org.neo4j.graphdb.Node;
import org.neo4j.graphdb.Relationship;
import org.neo4j.rest.graphdb.util.TestHelper;
/**
* @author mh
* @since 24.01.11
*/
class IsRelationshipToNodeMatcher extends TypeSafeMatcher<Iterable<Relationship>> {
private final Node startNode;
private final Node endNode;
public IsRelationshipToNodeMatcher( Node startNode, Node endNode ) {
this.startNode = startNode;
this.endNode = endNode;
}
@Override
public boolean matchesSafely( Iterable<Relationship> relationships ) {
return TestHelper.firstRelationshipBetween( relationships, startNode, endNode ) != null;
}
public void describeTo( Description description ) {
description.appendValue( startNode ).appendText( " to " ).appendValue( endNode ).appendText( "not contained in relationships" );
}
}

View File

@@ -0,0 +1,230 @@
/**
* Copyright (c) 2002-2013 "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 <http://www.gnu.org/licenses/>.
*/
package org.neo4j.rest.graphdb;
import org.eclipse.jetty.util.component.LifeCycle;
import org.neo4j.graphdb.GraphDatabaseService;
import org.neo4j.kernel.GraphDatabaseAPI;
import org.neo4j.kernel.configuration.Config;
import org.neo4j.kernel.logging.Logging;
import org.neo4j.server.CommunityNeoServer;
import org.neo4j.server.configuration.PropertyFileConfigurator;
import org.neo4j.server.database.Database;
import org.neo4j.server.database.WrappedDatabase;
import org.neo4j.server.modules.RESTApiModule;
import org.neo4j.server.modules.ServerModule;
import org.neo4j.server.modules.ThirdPartyJAXRSModule;
import org.neo4j.server.preflight.PreFlightTasks;
import org.neo4j.server.web.Jetty9WebServer;
import org.neo4j.server.web.WebServer;
import org.neo4j.test.TestGraphDatabaseFactory;
import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import java.io.File;
import java.io.IOException;
import java.net.URI;
import java.net.URL;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import static java.util.Arrays.asList;
/**
* @author mh
* @since 24.03.11
*/
public class LocalTestServer {
private CommunityNeoServer neoServer;
private final int port;
private final String hostname;
protected String propertiesFile = "test-db.properties";
private final GraphDatabaseAPI graphDatabase;
private String userAgent;
public LocalTestServer() {
this("localhost",7473);
}
public LocalTestServer(String hostname, int port) {
this.port = port;
this.hostname = hostname;
graphDatabase = (GraphDatabaseAPI) new TestGraphDatabaseFactory().newImpermanentDatabase();
}
public void start() {
if (neoServer!=null) throw new IllegalStateException("Server already running");
URL url = getClass().getResource("/" + propertiesFile);
if (url==null) throw new IllegalArgumentException("Could not resolve properties file "+propertiesFile);
Logging logging = graphDatabase.getDependencyResolver().resolveDependency(Logging.class);
final Jetty9WebServer jettyWebServer = new Jetty9WebServer(logging); /* {
@Override
protected void startJetty() {
final Server jettyServer = getJetty();
jettyServer.setStopAtShutdown(true);
final JettyStartupListener startupListener = new JettyStartupListener();
jettyServer.getServer().addLifeCycleListener(startupListener);
// System.err.println("jetty is started before notification " + jettyServer.isStarted());
super.startJetty();
startupListener.await();
jettyServer.removeLifeCycleListener(startupListener);
// System.err.println("jetty is started after notification " + jettyServer.isStarted());
}
@Override
public void stop() {
final Server jettyServer = getJetty();
final JettyStartupListener listener = new JettyStartupListener();
jettyServer.getServer().addLifeCycleListener(listener);
super.stop();
listener.await();
jettyServer.removeLifeCycleListener(listener);
}
}; */
jettyWebServer.addFilter(new Filter() {
public void init(FilterConfig filterConfig) throws ServletException { }
public void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain) throws IOException, ServletException {
userAgent = ((HttpServletRequest)request).getHeader("User-Agent");
filterChain.doFilter(request, response);
}
public void destroy() { }
},"/*");
neoServer = new CommunityNeoServer(new PropertyFileConfigurator(new File(url.getPath())), new Database.Factory() {
@Override
public Database newDatabase(Config config, Logging logging) {
return new WrappedDatabase(graphDatabase);
}
},logging) {
@Override
protected int getWebServerPort() {
return port;
}
@Override
protected PreFlightTasks createPreflightTasks() {
return new PreFlightTasks(logging);
}
@Override
protected WebServer createWebServer() {
return jettyWebServer;
}
@Override
protected Iterable<ServerModule> createServerModules() {
return asList(new RESTApiModule(webServer,database,configurator.configuration(),logging),new ThirdPartyJAXRSModule(webServer,configurator,logging,this));
}
};
neoServer.start();
try {
Thread.sleep(500);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
public void stop() {
try {
neoServer.stop();
} catch(Exception e) {
System.err.println("Error stopping server: "+e.getMessage());
}
neoServer=null;
}
public int getPort() {
return port;
}
public String getHostname() {
return hostname;
}
public LocalTestServer withPropertiesFile(String propertiesFile) {
this.propertiesFile = propertiesFile;
return this;
}
public Database getDatabase() {
return neoServer.getDatabase();
}
public URI baseUri() {
return neoServer.baseUri();
}
public void cleanDb() {
Neo4jDatabaseCleaner cleaner = new Neo4jDatabaseCleaner(getGraphDatabase());
cleaner.cleanDb();
}
public GraphDatabaseService getGraphDatabase() {
return getDatabase().getGraph();
}
public String getUserAgent() {
return userAgent;
}
private static class JettyStartupListener implements LifeCycle.Listener {
CountDownLatch latch=new CountDownLatch(1);
public void await() {
try {
latch.await(5, TimeUnit.SECONDS);
} catch(InterruptedException ie) {
Thread.currentThread().interrupt();
throw new RuntimeException(ie);
}
}
@Override
public void lifeCycleStarting(LifeCycle event) {
System.err.println("STARTING");
}
@Override
public void lifeCycleStarted(LifeCycle event) {
System.err.println("STARTED");
latch.countDown();
}
@Override
public void lifeCycleFailure(LifeCycle event, Throwable cause) {
System.err.println("FAILURE "+cause.getMessage());
latch.countDown();
throw new RuntimeException(cause);
}
@Override
public void lifeCycleStopping(LifeCycle event) {
System.err.println("STOPPING");
}
@Override
public void lifeCycleStopped(LifeCycle event) {
System.err.println("STOPPED");
latch.countDown();
}
}
}

View File

@@ -0,0 +1,260 @@
/**
* Copyright (c) 2002-2013 "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 <http://www.gnu.org/licenses/>.
*/
package org.neo4j.rest.graphdb;
import java.util.Map;
import org.neo4j.graphdb.Direction;
import org.neo4j.graphdb.GraphDatabaseService;
import org.neo4j.graphdb.Node;
import org.neo4j.graphdb.Relationship;
import org.neo4j.graphdb.RelationshipType;
import org.neo4j.graphdb.Transaction;
import org.neo4j.graphdb.index.Index;
import org.neo4j.graphdb.index.IndexManager;
import org.neo4j.helpers.collection.MapUtil;
/**
* Creates a database using the matrix example for testing purposes
* @author Klemens Burchardi
* @since 03.08.11
*/
public class MatrixDataGraph {
private Node referenceNode;
long getNeoNodeId() {
Transaction transaction = getGraphDatabase().beginTx();
try {
return getNeoNode().getId();
} finally {
transaction.success();transaction.close();
}
}
public long getReferenceNodeId() {
return referenceNode.getId();
}
/** specify relationship types*/
public enum RelTypes implements RelationshipType{
NEO_NODE,
KNOWS,
FIGHTS,
CODED_BY,
PERSONS_REFERENCE,
HEROES_REFERENCE,
HERO,
VILLAINS_REFERENCE,
VILLAIN
}
private GraphDatabaseService graphDb;
public MatrixDataGraph(GraphDatabaseService graphDb){
this.graphDb = graphDb;
}
public MatrixDataGraph(GraphDatabaseService graphDb, long referenceNode) {
this.graphDb = graphDb;
this.referenceNode = getNodeById(graphDb, referenceNode);
}
private Node getNodeById(GraphDatabaseService graphDb, long id) {
try (Transaction tx = graphDb.beginTx()) {
Node node = graphDb.getNodeById(id);
tx.success();
return node;
}
}
/**
* fills the database with nodes and relationships, using the matrix example
* @return MatrixDataGraph the instance for chaining purposes
*/
public MatrixDataGraph createNodespace() {
try (Transaction tx = this.graphDb.beginTx()) {
if (this.referenceNode == null) {
referenceNode = this.graphDb.createNode();
}
//create the index for all characters that are considered good guys (sorry cypher)
IndexManager index = this.graphDb.index();
Index<Node> goodGuys = index.forNodes("heroes");
//create persons collection node
Node persons = this.graphDb.createNode();
persons.setProperty("type", "Persons Collection");
//create heroes collection node
Node heroes = this.graphDb.createNode();
heroes.setProperty("type", "Heroes Collection");
//create villains collection node
Node villains = this.graphDb.createNode();
villains.setProperty("type", "Villains Collection");
// create neo node
Node neo = this.graphDb.createNode();
addMultiplePropertiesToNode(neo, MapUtil.map("age", 29, "name", "Thomas Anderson", "type", "hero"));
// connect the persons collection node to the reference node
referenceNode.createRelationshipTo(persons, RelTypes.PERSONS_REFERENCE);
// connect the heroes collection node to the persons collection node
persons.createRelationshipTo(heroes, RelTypes.HEROES_REFERENCE);
// connect the villains collection node to the persons collection node
persons.createRelationshipTo(villains, RelTypes.VILLAINS_REFERENCE);
// connect neo to the reference node
referenceNode.createRelationshipTo(neo, RelTypes.NEO_NODE);
// connect neo to the heroes collection node
heroes.createRelationshipTo(neo, RelTypes.HERO);
// create trinity node
Node trinity = this.graphDb.createNode();
addMultiplePropertiesToNode(trinity, MapUtil.map("name", "Trinity", "type", "hero"));
createRelationshipWithProperties(neo, trinity, RelTypes.KNOWS, MapUtil.map("age", "3 days"));
// connect trinity to the heroes collection node
heroes.createRelationshipTo(trinity, RelTypes.HERO);
// create morpheus node
Node morpheus = this.graphDb.createNode();
addMultiplePropertiesToNode(morpheus, MapUtil.map("name", "Morpheus", "occupation", "Total badass", "rank", "Captain", "type", "hero"));
neo.createRelationshipTo(morpheus, RelTypes.KNOWS);
createRelationshipWithProperties(morpheus, trinity, RelTypes.KNOWS, MapUtil.map("age", "12 years"));
// connect morpheus to the heroes collection node
heroes.createRelationshipTo(morpheus, RelTypes.HERO);
//add all good guys to the index
addMultipleNodesToIndex(goodGuys, "name", MapUtil.map("Neo", neo, "Trinity", trinity, "Morpheus", morpheus));
// create cypher node
Node cypher = this.graphDb.createNode();
addMultiplePropertiesToNode(cypher, MapUtil.map("last name", "Reagan", "name", "Cypher", "type", "villain"));
trinity.createRelationshipTo(cypher, RelTypes.KNOWS);
createRelationshipWithProperties(morpheus, cypher, RelTypes.KNOWS, MapUtil.map("disclosure", "public"));
// connect cypher to the villains collection node
villains.createRelationshipTo(cypher, RelTypes.VILLAIN);
// create smith node
Node smith = this.graphDb.createNode();
addMultiplePropertiesToNode(smith, MapUtil.map("language", "C++", "name", "Agent Smith", "version", "1.0b", "type", "villain"));
neo.createRelationshipTo(smith, RelTypes.FIGHTS);
createRelationshipWithProperties(cypher, smith, RelTypes.KNOWS, MapUtil.map("age", "6 months", "disclosure", "secret"));
// connect smith to the villains collection node
villains.createRelationshipTo(smith, RelTypes.VILLAIN);
// create architect node
Node architect = this.graphDb.createNode();
architect.setProperty("name", "The Architect");
smith.createRelationshipTo(architect, RelTypes.CODED_BY);
tx.success();
}
return this;
}
public Node getReferenceNode() {
return referenceNode;
}
public void addMultiplePropertiesToNode(Node node, Map<String,Object> props){
for (Map.Entry<String, Object> entry : props.entrySet()){
node.setProperty(entry.getKey(), entry.getValue());
}
}
public void addMultipleNodesToIndex(Index<Node> indexName, String key, Map<String, Object> namedNodes){
for (Map.Entry<String, Object> entry : namedNodes.entrySet()){
indexName.add((Node)entry.getValue(), key, entry.getKey());
}
}
public void createRelationshipWithProperties(Node startNode, Node endNode, RelationshipType relType, Map<String,Object> props){
Relationship rel = startNode.createRelationshipTo(endNode, relType);
for (Map.Entry<String, Object> entry : props.entrySet()){
rel.setProperty(entry.getKey(), entry.getValue());
}
}
public GraphDatabaseService getGraphDatabase() {
return graphDb;
}
/**
* Get the Neo node. (a.k.a. Thomas Anderson node)
*
* @return the Neo node
*/
public Node getNeoNode() {
try (Transaction tx = graphDb.beginTx()) {
Node neoNode = this.referenceNode.getSingleRelationship(
RelTypes.NEO_NODE, Direction.OUTGOING).getEndNode();
tx.success();
return neoNode;
}
}
/**
* Get the Persons Collection node
*
* @return the Persons Collection node
*/
public Node getPersonsCollectionNode() {
try (Transaction tx = graphDb.beginTx()) {
Node personsCollection = this.referenceNode.getSingleRelationship(
RelTypes.PERSONS_REFERENCE, Direction.OUTGOING).getEndNode();
tx.success();
return personsCollection;
}
}
/**
* Get the Heroes Collection node
*
* @return the Heroes Collection node
*/
public Node getHeroesCollectionNode() {
try (Transaction tx = graphDb.beginTx()) {
Node heroesCollection = this.getPersonsCollectionNode().getSingleRelationship(
RelTypes.HEROES_REFERENCE, Direction.OUTGOING).getEndNode();
tx.success();
return heroesCollection;
}
}
/**
* Get the Villains Collection node
*
* @return the Villains Collection node
*/
public Node getVillainsCollectionNode() {
try (Transaction tx = graphDb.beginTx()) {
Node villainsCollection = this.getPersonsCollectionNode().getSingleRelationship(
RelTypes.VILLAINS_REFERENCE, Direction.OUTGOING).getEndNode();
tx.success();
return villainsCollection;
}
}
}

View File

@@ -0,0 +1,237 @@
/**
* Copyright (c) 2002-2013 "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 <http://www.gnu.org/licenses/>.
*/
package org.neo4j.rest.graphdb;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.neo4j.graphdb.*;
import org.neo4j.graphdb.index.Index;
import org.neo4j.graphdb.index.IndexHits;
import org.neo4j.graphdb.index.IndexManager;
import org.neo4j.graphdb.traversal.*;
import org.neo4j.graphdb.traversal.Traverser;
import org.neo4j.helpers.collection.IteratorUtil;
import org.neo4j.rest.graphdb.MatrixDataGraph.RelTypes;
import org.neo4j.rest.graphdb.traversal.RestTraversal;
import org.neo4j.rest.graphdb.traversal.RestTraversalDescription.ScriptLanguage;
public class MatrixDatabaseRestTest extends RestTestBase{
private MatrixDataGraph embeddedmdg;
private MatrixDataGraph restmdg;
@Before
public void matrixTestSetUp() {
//fill server db with matrix nodes
this.embeddedmdg = new MatrixDataGraph(getGraphDatabase()).createNodespace();
this.restmdg = new MatrixDataGraph(getRestGraphDb(),embeddedmdg.getReferenceNodeId());
}
@Test
public void testSetMaxtrixProperty() {
restmdg.getNeoNode().setProperty( "occupation", "the one" );
try (Transaction tx = getGraphDatabase().beginTx()) {
Node node = embeddedmdg.getNeoNode();
Assert.assertEquals("the one", node.getProperty("occupation"));
tx.success();
}
}
@Test
public void checkForIndex() throws Exception {
IndexManager index = getRestGraphDb().index();
assertTrue(index.existsForNodes("heroes"));
}
@Test
public void useTrinityIndex() throws Exception {
IndexManager index = getRestGraphDb().index();
Index<Node> goodGuys = index.forNodes("heroes");
IndexHits<Node> hits = goodGuys.get( "name", "Trinity" );
Node trinity = hits.getSingle();
assertEquals( "Trinity", trinity.getProperty("name") );
}
@Test
public void useMorpheusQuery() throws Exception {
IndexManager index = getRestGraphDb().index();
Index<Node> goodGuys = index.forNodes("heroes");
for (Node morpheus : goodGuys.query("name", "Morpheus")){
assertEquals( "Morpheus", morpheus.getProperty("name") );
}
}
/**
* get the number of all nodes that know the neo node
* @throws Exception
*/
@Test
public void getNeoFriends() throws Exception {
Node neoNode = restmdg.getNeoNode();
System.out.println(neoNode.getProperty("name"));
Traverser friendsTraverser = getFriends( neoNode );
int numberOfFriends = 0;
for ( Path friendPath : friendsTraverser ) {
numberOfFriends++;
}
assertEquals( 4, numberOfFriends );
}
/**
* get the number of all heroes that are connected to the heroes collection node
* @throws Exception
*/
@Test
public void checkNumberOfHeroes() throws Exception {
Traverser heroesTraverser = getHeroesViaRest();
int numberOfHeroes = 0;
for ( Path heroPath : heroesTraverser ) {
numberOfHeroes++;
}
assertEquals( 3, numberOfHeroes );
}
/**
* check if rest traversal returns the same as embedded traversal
* @throws Exception
*/
@Test
public void checkTraverseByProperties() throws Exception {
try (Transaction tx = getGraphDatabase().beginTx()) {
Node heroesTraverserRest = IteratorUtil.first(getHeroesViaRest().nodes());
Node heroesTraverserByProperties = IteratorUtil.first(getHeroesByNodeProperties().nodes());
assertEquals(heroesTraverserRest.getId(), heroesTraverserByProperties.getId());
tx.success();
}
}
/**
* check if different REST Traversals for all heroes return the same
* @throws Exception
*/
@Test
public void checkTraverseByPropertiesRest() throws Exception {
Traverser heroesTraverserRest = getHeroesViaRest();
Traverser heroesTraverserByPropertiesRest = getHeroesByNodePropertiesViaRest();
assertEquals( heroesTraverserRest.nodes().iterator().next(), heroesTraverserByPropertiesRest.nodes().iterator().next() );
}
/**
* check if rest traversal and traversal via the collection node return the same result
* @throws Exception
*/
@Test
public void checkTraverseByCollectionNode() throws Exception {
Traverser heroesTraverserRest = getHeroesViaRest();
Traverser heroesTraverserByCollection = getHeroesByCollectionNodeViaRest();
assertEquals( heroesTraverserRest.nodes().iterator().next(), heroesTraverserByCollection.nodes().iterator().next() );
}
/**
* returns a traverser for all nodes that have an outgoing relationship of the type KNOWS
* @param person the startnode
* @return the Traverser
*/
private static Traverser getFriends( final Node person ) {
TraversalDescription td = RestTraversal.description()
.maxDepth(10)
.breadthFirst()
.relationships( RelTypes.KNOWS, Direction.OUTGOING )
.evaluator(Evaluators.excludeStartPosition());
return td.traverse( person );
}
/**
* returns a traverser for all nodes that have an outgoing relationship of the type HERO an are 3 positions down in the path
* @return the Traverser
*/
private Traverser getHeroesViaRest() {
TraversalDescription td = RestTraversal.description()
.maxDepth(3)
.breadthFirst()
.relationships( RelTypes.PERSONS_REFERENCE, Direction.OUTGOING )
.relationships( RelTypes.HEROES_REFERENCE, Direction.OUTGOING )
.relationships( RelTypes.HERO, Direction.OUTGOING )
.filter(ScriptLanguage.JAVASCRIPT, "position.length() == 3;");
return td.traverse(this.restmdg.getReferenceNode());
}
/**
* returns a traverser for all nodes that have a hero relationship and are connected to the hero collection node
* @return
*/
private Traverser getHeroesByCollectionNodeViaRest(){
TraversalDescription td = RestTraversal.description()
.maxDepth(10)
.breadthFirst()
.relationships( RelTypes.HERO, Direction.OUTGOING );
return td.traverse( this.restmdg.getHeroesCollectionNode() );
}
/**
* returns a traverser for all nodes that have a property type == hero via the REST API
* @return the Traverser
*/
private Traverser getHeroesByNodePropertiesViaRest() {
TraversalDescription td = RestTraversal.description()
.maxDepth(3)
.breadthFirst()
.relationships( RelTypes.PERSONS_REFERENCE, Direction.OUTGOING )
.relationships( RelTypes.HEROES_REFERENCE, Direction.OUTGOING )
.relationships( RelTypes.HERO, Direction.OUTGOING )
.filter(ScriptLanguage.JAVASCRIPT, "position.endNode().getProperty('type','none') == 'hero';");
return td.traverse(this.restmdg.getReferenceNode());
}
/**
* returns a traverser for all nodes that have a property type == hero in the embedded Database
* @return the Traverser
*/
private Traverser getHeroesByNodeProperties() {
GraphDatabaseService db = this.embeddedmdg.getGraphDatabase();
TraversalDescription td = db.traversalDescription()
.breadthFirst()
.relationships( RelTypes.PERSONS_REFERENCE, Direction.OUTGOING )
.relationships( RelTypes.HEROES_REFERENCE, Direction.OUTGOING )
.relationships( RelTypes.HERO, Direction.OUTGOING )
.evaluator(new Evaluator() {
public Evaluation evaluate(Path path) {
Node node = path.endNode();
Object type = node.getProperty("type", "none");
return type.equals("hero") ? Evaluation.INCLUDE_AND_PRUNE : Evaluation.EXCLUDE_AND_CONTINUE;
}
});
return td.traverse(this.embeddedmdg.getReferenceNode());
}
}

View File

@@ -0,0 +1,263 @@
/**
* Copyright (c) 2002-2013 "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 <http://www.gnu.org/licenses/>.
*/
package org.neo4j.rest.graphdb;
import org.junit.*;
import org.neo4j.graphdb.*;
import org.neo4j.graphdb.index.Index;
import org.neo4j.graphdb.index.IndexHits;
import org.neo4j.graphdb.index.IndexManager;
import org.neo4j.graphdb.traversal.*;
import org.neo4j.graphdb.traversal.Traverser;
import org.neo4j.helpers.Predicate;
import org.neo4j.kernel.Traversal;
import org.neo4j.rest.graphdb.MatrixDataGraph.RelTypes;
import org.neo4j.test.ImpermanentGraphDatabase;
import org.neo4j.test.TestGraphDatabaseFactory;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
/**
* TestClass for the MatrixDatabase
* @author Klemens Burchardi
* @since 03.08.11
*/
public class MatrixDatabaseTest {
private static GraphDatabaseService graphDb;
private static MatrixDataGraph mdg;
private Transaction tx;
@BeforeClass
public static void beforeClass() {
graphDb = new TestGraphDatabaseFactory().newImpermanentDatabase();
mdg = new MatrixDataGraph(graphDb).createNodespace();
}
@AfterClass
public static void afterClass() {
graphDb.shutdown();
}
@Before
public void setUp() throws Exception {
tx = mdg.getGraphDatabase().beginTx();
}
@After
public void tearDown() throws Exception {
tx.success();
tx.close();
}
@Test
public void checkNeoProperties() throws Exception {
Node neoNode = mdg.getNeoNode();
boolean isSetupCorrectly = false;
if (neoNode.getProperty("age").equals(29) &&
neoNode.getProperty("name").equals("Thomas Anderson")){
isSetupCorrectly = true;
}
assertTrue(isSetupCorrectly);
}
/**
* get the number of all nodes that know the neo node
* @throws Exception
*/
@Test
public void getNeoFriends() throws Exception {
Node neoNode = mdg.getNeoNode();
Traverser friendsTraverser = getFriends( neoNode );
int numberOfFriends = 0;
for ( Path friendPath : friendsTraverser ) {
numberOfFriends++;
}
assertEquals( 4, numberOfFriends );
}
/**
* get the number of all heroes that are connected to the heroes collection node
* @throws Exception
*/
@Test
public void checkNumberOfHeroes() throws Exception {
Traverser heroesTraverser = getHeroes();
int numberOfHeroes = 0;
for ( Path heroPath : heroesTraverser ) {
numberOfHeroes++;
}
assertEquals( 3, numberOfHeroes );
}
@Test
public void checkForIndex() throws Exception {
IndexManager index = graphDb.index();
assertTrue(index.existsForNodes("heroes"));
}
@Test
public void checkForHeroesCollection() throws Exception {
Node heroesCollectionNode = mdg.getHeroesCollectionNode();
assertEquals( "Heroes Collection", heroesCollectionNode.getProperty("type") );
}
@Test
public void useMorpheusQuery() throws Exception {
IndexManager index = graphDb.index();
Index<Node> goodGuys = index.forNodes("heroes");
for (Node morpheus : goodGuys.query("name", "Morpheus")){
assertEquals( "Morpheus", morpheus.getProperty("name") );
}
}
@Test
public void useTrinityIndex() throws Exception {
IndexManager index = graphDb.index();
Index<Node> goodGuys = index.forNodes("heroes");
IndexHits<Node> hits = goodGuys.get( "name", "Trinity" );
Node trinity = hits.getSingle();
assertEquals( "Trinity", trinity.getProperty("name") );
}
@Test
public void compareIndexAndTraversal() throws Exception {
IndexManager index = graphDb.index();
Index<Node> goodGuys = index.forNodes("heroes");
IndexHits<Node> hits = goodGuys.query( "name", "*" );
Traverser heroesTraverser = getHeroes();
assertEquals( heroesTraverser.nodes().iterator().next().getId(), hits.iterator().next().getId() );
}
@Test
public void checkTraverseByProperties() throws Exception {
Traverser heroesTraverser = getHeroes();
Traverser heroesTraverserByProperties = getHeroesByNodeProperties();
assertEquals( heroesTraverser.nodes().iterator().next(), heroesTraverserByProperties.nodes().iterator().next() );
}
/**
* returns a traverser for all nodes that have an outgoing relationship of the type KNOWS
* @param person the startnode
* @return the Traverser
*/
private static Traverser getFriends( final Node person ) {
TraversalDescription td = graphDb.traversalDescription()
.breadthFirst()
.relationships( RelTypes.KNOWS, Direction.OUTGOING )
.evaluator( Evaluators.excludeStartPosition() );
return td.traverse( person );
}
/**
* returns a traverser for all nodes that have an outgoing relationship of the type HERO
* @return the Traverser
*/
private static Traverser getHeroes() {
TraversalDescription td = graphDb.traversalDescription()
.breadthFirst()
.relationships( RelTypes.HERO, Direction.OUTGOING )
.evaluator( Evaluators.excludeStartPosition() );
return td.traverse( mdg.getHeroesCollectionNode() );
}
/**
* returns a traverser for all nodes that have a property type == hero in the embedded Database
* @return the Traverser
*/
private Traverser getHeroesByNodeProperties() {
TraversalDescription td = graphDb.traversalDescription()
.breadthFirst()
.relationships( RelTypes.PERSONS_REFERENCE, Direction.OUTGOING )
.relationships( RelTypes.HEROES_REFERENCE, Direction.OUTGOING )
.relationships( RelTypes.HERO, Direction.OUTGOING )
.evaluator(Evaluators.excludeStartPosition())
.evaluator(new Evaluator() {
public Evaluation evaluate(Path path) {
return path.endNode().getProperty("type", "none").equals("hero") ? Evaluation.INCLUDE_AND_PRUNE : Evaluation.EXCLUDE_AND_CONTINUE;
}
});
return td.traverse(mdg.getReferenceNode());
}
/**
* checks if neo has a friend named cypher
* @throws Exception
*/
@Test
public void findCypher() throws Exception{
Node neoNode = mdg.getNeoNode();
Traverser friendsTraverser = getFriends( neoNode );
boolean foundCypher = false;
for ( Path friendPath : friendsTraverser ) {
if (friendPath.endNode().getProperty("name").equals("Cypher")){
foundCypher = true;
break;
}
}
assertTrue(foundCypher);
}
/**
* get all nodes that have an outgoing CODED_BY relationship
* @throws Exception
*/
@Test
public void getMatrixHackers() throws Exception
{
Traverser traverser = findHackers( mdg.getNeoNode() );
int numberOfHackers = 0;
for ( Path hackerPath : traverser ) {
numberOfHackers++;
}
assertEquals( 1, numberOfHackers );
}
/**
* returns a traverser for all nodes that have an outgoing CODED_BY relationship
* based on a startnode
* @param startNode the node to start from
* @return the 'Traverser
*/
private static Traverser findHackers( final Node startNode ) {
TraversalDescription td = graphDb.traversalDescription()
.breadthFirst()
.relationships( RelTypes.CODED_BY, Direction.OUTGOING )
.relationships( RelTypes.KNOWS, Direction.OUTGOING )
.evaluator(
Evaluators.includeWhereLastRelationshipTypeIs(RelTypes.CODED_BY) );
return td.traverse( startNode );
}
}

View File

@@ -0,0 +1,102 @@
/**
* Copyright (c) 2002-2013 "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 <http://www.gnu.org/licenses/>.
*/
package org.neo4j.rest.graphdb;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
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.graphdb.index.Index;
import org.neo4j.graphdb.index.IndexManager;
import org.neo4j.graphdb.index.ReadableIndex;
import org.neo4j.graphdb.index.RelationshipIndex;
import org.neo4j.rest.graphdb.index.RestAutoIndexer;
import org.neo4j.test.ImpermanentGraphDatabase;
import org.neo4j.tooling.GlobalGraphOperations;
/**
* @author mh
* @since 09.05.11
*/
public class Neo4jDatabaseCleaner {
private GraphDatabaseService graph;
public Neo4jDatabaseCleaner(GraphDatabaseService graph) {
this.graph = graph;
}
public Map<String, Object> cleanDb() {
// if (graph instanceof ImpermanentGraphDatabase) {
// ((ImpermanentGraphDatabase)graph).cleanContent();
// return Collections.emptyMap();
// }
Map<String, Object> result = new HashMap<String, Object>();
Transaction tx = graph.beginTx();
try {
removeNodes(result);
clearIndex(result);
tx.success();
} finally {
tx.close();
}
return result;
}
private void removeNodes(Map<String, Object> result) {
int nodes = 0, relationships = 0;
for (Node node : GlobalGraphOperations.at(graph).getAllNodes()) {
for (Relationship rel : node.getRelationships(Direction.OUTGOING)) {
rel.delete();
relationships++;
}
node.delete();
nodes++;
}
result.put("nodes", nodes);
result.put("relationships", relationships);
}
private void clearIndex(Map<String, Object> result) {
IndexManager indexManager = graph.index();
result.put("node-indexes", Arrays.asList(indexManager.nodeIndexNames()));
result.put("relationship-indexes", Arrays.asList(indexManager.relationshipIndexNames()));
for (String ix : indexManager.nodeIndexNames()) {
deleteIndex(indexManager.forNodes(ix));
}
for (String ix : indexManager.relationshipIndexNames()) {
deleteIndex(indexManager.forRelationships(ix));
}
}
private void deleteIndex(Index index) {
try {
index.delete();
} catch (UnsupportedOperationException e) {
// pass
}
}
}

View File

@@ -0,0 +1,90 @@
/**
* Copyright (c) 2002-2013 "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 <http://www.gnu.org/licenses/>.
*/
package org.neo4j.rest.graphdb;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import org.hamcrest.Description;
import org.junit.internal.matchers.TypeSafeMatcher;
import org.neo4j.graphdb.Direction;
import org.neo4j.graphdb.Node;
import org.neo4j.graphdb.Relationship;
import org.neo4j.graphdb.RelationshipType;
public class RelationshipHasMatcher extends TypeSafeMatcher<Iterable<Relationship>>{
private final Node node;
private final Direction direction;
private final List<String> typeNames;
public RelationshipHasMatcher(Node startNode, Direction direction, RelationshipType... types){
this.node = startNode;
this.direction = direction;
this.typeNames = fillTypeNames(Arrays.asList(types));
}
@Override
public void describeTo(Description description) {
description.appendText("Not all relationships matched the constraints. Node: ").appendValue(node).appendText(" direction: ").appendValue(direction).appendText(" relationship type(s): ").appendValue(typeNames);
}
@Override
public boolean matchesSafely(Iterable<Relationship> relationships) {
for (Relationship relationship : relationships) {
boolean isStartnode = this.node.equals(relationship.getStartNode());
boolean isEndnode = this.node.equals(relationship.getEndNode());
if (!isStartnode && !isEndnode){
return false;
}
Direction relationshipDirection = isStartnode ? Direction.OUTGOING : Direction.INCOMING;
if (this.direction != null && this.direction!= relationshipDirection){
return false;
}
if(!this.typeNames.isEmpty() && !this.typeNames.contains(relationship.getType().name())){
return false;
}
}
return true;
}
public static RelationshipHasMatcher match(Node startNode, Direction direction, RelationshipType... types){
return new RelationshipHasMatcher(startNode, direction, types);
}
public static List<String> fillTypeNames(List<RelationshipType> types){
List<String> names = new ArrayList<String>();
for (RelationshipType type : types) {
names.add(type.name());
}
return names;
}
}

View File

@@ -0,0 +1,396 @@
/**
* Copyright (c) 2002-2013 "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 <http://www.gnu.org/licenses/>.
*/
package org.neo4j.rest.graphdb;
import static org.hamcrest.CoreMatchers.hasItem;
import static org.hamcrest.CoreMatchers.hasItems;
import static org.junit.Assert.*;
import static org.neo4j.helpers.collection.MapUtil.map;
import java.util.*;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.neo4j.graphdb.*;
import org.neo4j.graphdb.index.Index;
import org.neo4j.graphdb.index.IndexManager;
import org.neo4j.helpers.collection.IterableWrapper;
import org.neo4j.helpers.collection.IteratorUtil;
import org.neo4j.index.impl.lucene.LuceneIndexImplementation;
import org.neo4j.rest.graphdb.entity.RestNode;
import org.neo4j.rest.graphdb.entity.RestRelationship;
import org.neo4j.rest.graphdb.index.RestIndex;
import org.neo4j.rest.graphdb.index.RestIndexManager;
import org.neo4j.rest.graphdb.util.TestHelper;
public class RestAPITest extends RestTestBase {
public static final List<String> NO_LABELS = Collections.<String>emptyList();
private RestAPI restAPI;
public static final Label LABEL_FOO = DynamicLabel.label("FOO");
public static final Label LABEL_BAR = DynamicLabel.label("BAR");
@Before
public void init(){
this.restAPI = ((RestGraphDatabase)getRestGraphDb()).getRestAPI();
}
@Test
public void testUserAgent() throws Exception {
restAPI.createNode(map());
assertTrue(getUserAgent().matches("neo4j-rest-graphdb/[\\d.]+"));
}
@Test
public void testOverrideUserAgent() throws Exception {
System.setProperty(UserAgent.NEO4J_DRIVER_PROPERTY,"foo/bar");
new RestAPIImpl(restAPI.getBaseUri()).createNode(map());
assertTrue(getUserAgent().matches("foo/bar"));
System.setProperty(UserAgent.NEO4J_DRIVER_PROPERTY,"");
}
@Test
public void testCreateNodeWithParams() {
Map<String, Object> props = new HashMap<String, Object>();
props.put("name", "test");
Node node = this.restAPI.createNode(props);
Assert.assertEquals( node, getRestGraphDb().getNodeById( node.getId() ));
Assert.assertEquals( "test", getRestGraphDb().getNodeById( node.getId()).getProperty("name") );
}
@Test
public void testGetSingleRelationshipShouldReturnNullIfThereIsNone() throws Exception {
assertNull(node().getSingleRelationship(DynamicRelationshipType.withName("foo"),Direction.OUTGOING));
}
@Test
public void testHasSingleRelationshipShouldReturnFalseIfThereIsNone() throws Exception {
assertEquals(false,node().hasRelationship(DynamicRelationshipType.withName("foo"),Direction.OUTGOING));
}
@Test
public void testCreateRelationshipWithParams() {
Node refNode = node();
Node node = getRestGraphDb().createNode();
Map<String, Object> props = new HashMap<String, Object>();
props.put("name", "test");
Relationship rel = this.restAPI.createRelationship(refNode, node, Type.TEST, props );
Relationship foundRelationship = TestHelper.firstRelationshipBetween( refNode.getRelationships( Type.TEST, Direction.OUTGOING ), refNode, node );
Assert.assertNotNull( "found relationship", foundRelationship );
Assert.assertEquals( "same relationship", rel, foundRelationship );
Assert.assertThat( refNode.getRelationships( Type.TEST, Direction.OUTGOING ), new IsRelationshipToNodeMatcher( refNode, node ) );
Assert.assertThat( refNode.getRelationships( Direction.OUTGOING ), new IsRelationshipToNodeMatcher( refNode, node ) );
Assert.assertThat( refNode.getRelationships( Direction.BOTH ), new IsRelationshipToNodeMatcher( refNode, node ) );
Assert.assertThat( refNode.getRelationships( Type.TEST ), new IsRelationshipToNodeMatcher( refNode, node ) );
Assert.assertEquals( "test", rel.getProperty("name") );
}
@Test (expected = IllegalArgumentException.class)
public void testForNotCreatedIndex() {
this.restAPI.getIndex("i do not exist");
}
@Test
public void testIndexForNodes(){
RestIndexManager index = (RestIndexManager) getRestGraphDb().index();
Index<Node> testIndex = index.forNodes("indexName");
assertTrue(index.existsForNodes("indexName"));
}
@Test
public void testGetIndexForNodes(){
RestIndexManager index = (RestIndexManager) getRestGraphDb().index();
Index<Node> testIndex = index.forNodes("indexName");
Assert.assertEquals(testIndex.getName(), this.restAPI.getIndex("indexName").getName());
}
@Test
public void testCreateRestAPIIndexForNodes(){
this.restAPI.createIndex(Node.class, "indexName", LuceneIndexImplementation.FULLTEXT_CONFIG);
RestIndexManager index = (RestIndexManager) getRestGraphDb().index();
assertTrue(index.existsForNodes("indexName"));
}
@Test
public void testForDoubleCreatedIndexForNodesWithSameParams() {
this.restAPI.createIndex(Node.class, "indexName", LuceneIndexImplementation.FULLTEXT_CONFIG);
this.restAPI.createIndex(Node.class, "indexName", LuceneIndexImplementation.FULLTEXT_CONFIG);
}
@Test
public void testForDoubleCreatedIndexForNodesWithSameParamsWithoutFullText() {
this.restAPI.createIndex(Node.class, "indexName", LuceneIndexImplementation.EXACT_CONFIG);
RestIndexManager index = (RestIndexManager) getRestGraphDb().index();
Index<Node> testIndex = index.forNodes("indexName");
}
@Test (expected = IllegalArgumentException.class)
public void testForDoubleCreatedIndexForNodesWithEmptyParams() {
this.restAPI.createIndex(Node.class, "indexName", LuceneIndexImplementation.FULLTEXT_CONFIG);
RestIndexManager index = (RestIndexManager) getRestGraphDb().index();
Index<Node> testIndex = index.forNodes("indexName", new HashMap<String, String>());
}
@Test (expected = IllegalArgumentException.class)
public void testForDoubleCreatedIndexForNodesWithEmptyParamsReversed() {
RestIndexManager index = (RestIndexManager) getRestGraphDb().index();
Index<Node> testIndex = index.forNodes("indexName", new HashMap<String, String>());
this.restAPI.createIndex(Node.class, "indexName", LuceneIndexImplementation.FULLTEXT_CONFIG);
}
@Test (expected = IllegalArgumentException.class)
public void testForDoubleCreatedIndexForNodesWithDifferentParamsViaREST() {
this.restAPI.createIndex(Node.class, "indexName", LuceneIndexImplementation.FULLTEXT_CONFIG);
this.restAPI.createIndex(Node.class, "indexName", LuceneIndexImplementation.EXACT_CONFIG);
}
@Test (expected = IllegalArgumentException.class)
public void testForDoubleCreatedIndexForNodesWithDifferentParams() {
this.restAPI.createIndex(Node.class, "indexName", LuceneIndexImplementation.FULLTEXT_CONFIG);
HashMap<String, String> config = new HashMap<String, String>();
config.put("test", "value");
RestIndexManager index = (RestIndexManager) getRestGraphDb().index();
Index<Node> testIndex = index.forNodes("indexName", config);
}
@Test (expected = IllegalArgumentException.class)
public void testForDoubleCreatedIndexForNodesWithDifferentParamsReversed() {
HashMap<String, String> config = new HashMap<String, String>();
config.put("test", "value");
RestIndexManager index = (RestIndexManager) getRestGraphDb().index();
Index<Node> testIndex = index.forNodes("indexName", config);
this.restAPI.createIndex(Node.class, "indexName", LuceneIndexImplementation.FULLTEXT_CONFIG);
}
@Test
public void testGetIndexByIndexForNodesCreationViaRestAPI(){
IndexManager index = getRestGraphDb().index();
Index<Node> testIndex = index.forNodes("indexName");
Assert.assertEquals(testIndex.getName(), this.restAPI.getIndex("indexName").getName());
}
@Test
public void testCreateRestAPIIndexForRelationship(){
Node refNode = node();
Node node = getRestGraphDb().createNode();
Map<String, Object> props = new HashMap<String, Object>();
props.put("name", "test");
Relationship rel = this.restAPI.createRelationship(refNode, node, Type.TEST, props );
this.restAPI.createIndex(Relationship.class, "indexName", LuceneIndexImplementation.FULLTEXT_CONFIG);
IndexManager index = getRestGraphDb().index();
assertTrue(index.existsForRelationships("indexName"));
}
@Test
public void testIndexForRelationships(){
RestIndexManager index = (RestIndexManager) getRestGraphDb().index();
Index<Relationship> testIndex = index.forRelationships("indexName");
assertTrue(index.existsForRelationships("indexName"));
}
@Test
public void testGetIndexForRelationships(){
RestIndexManager index = (RestIndexManager) getRestGraphDb().index();
Index<Relationship> testIndex = index.forRelationships("indexName");
Assert.assertEquals(testIndex.getName(), this.restAPI.getIndex("indexName").getName());
}
@Test
public void testCreateRestAPIIndexForRelationships(){
this.restAPI.createIndex(Relationship.class, "indexName", LuceneIndexImplementation.FULLTEXT_CONFIG);
RestIndexManager index = (RestIndexManager) getRestGraphDb().index();
assertTrue(index.existsForRelationships("indexName"));
}
@Test
public void testForDoubleCreatedIndexForRelationshipsWithSameParams() {
this.restAPI.createIndex(Relationship.class, "indexName", LuceneIndexImplementation.FULLTEXT_CONFIG);
this.restAPI.createIndex(Relationship.class, "indexName", LuceneIndexImplementation.FULLTEXT_CONFIG);
}
@Test
public void testForDoubleCreatedIndexForRelationshipsWithSameParamsWithoutFullText() {
this.restAPI.createIndex(Relationship.class, "indexName", LuceneIndexImplementation.EXACT_CONFIG);
RestIndexManager index = (RestIndexManager) getRestGraphDb().index();
Index<Relationship> testIndex = index.forRelationships("indexName");
}
@Test (expected = IllegalArgumentException.class)
public void testForDoubleCreatedIndexForRelationshipsWithEmptyParams() {
this.restAPI.createIndex(Relationship.class, "indexName", LuceneIndexImplementation.FULLTEXT_CONFIG);
RestIndexManager index = (RestIndexManager) getRestGraphDb().index();
Index<Relationship> testIndex = index.forRelationships("indexName", new HashMap<String, String>());
}
@Test (expected = IllegalArgumentException.class)
public void testForDoubleCreatedIndexForRelationshipsWithEmptyParamsReversed() {
RestIndexManager index = (RestIndexManager) getRestGraphDb().index();
Index<Relationship> testIndex = index.forRelationships("indexName", new HashMap<String, String>());
this.restAPI.createIndex(Relationship.class, "indexName", LuceneIndexImplementation.FULLTEXT_CONFIG);
}
@Test (expected = IllegalArgumentException.class)
public void testForDoubleCreatedIndexForRelationshipsWithDifferentParamsViaREST() {
this.restAPI.createIndex(Relationship.class, "indexName", LuceneIndexImplementation.FULLTEXT_CONFIG);
this.restAPI.createIndex(Relationship.class, "indexName", LuceneIndexImplementation.EXACT_CONFIG);
}
@Test (expected = IllegalArgumentException.class)
public void testForDoubleCreatedIndexForRelationshipsWithDifferentParams() {
this.restAPI.createIndex(Relationship.class, "indexName", LuceneIndexImplementation.FULLTEXT_CONFIG);
HashMap<String, String> config = new HashMap<String, String>();
config.put("test", "value");
RestIndexManager index = (RestIndexManager) getRestGraphDb().index();
Index<Relationship> testIndex = index.forRelationships("indexName", config);
}
@Test (expected = IllegalArgumentException.class)
public void testForDoubleCreatedIndexForRelationshipsWithDifferentParamsReversed() {
HashMap<String, String> config = new HashMap<String, String>();
config.put("test", "value");
RestIndexManager index = (RestIndexManager) getRestGraphDb().index();
Index<Relationship> testIndex = index.forRelationships("indexName", config);
this.restAPI.createIndex(Relationship.class, "indexName", LuceneIndexImplementation.FULLTEXT_CONFIG);
}
@Test
public void testGetIndexByIndexForRelationshipsCreationViaRestAPI(){
IndexManager index = getRestGraphDb().index();
Index<Relationship> testIndex = index.forRelationships("indexName");
Assert.assertEquals(testIndex.getName(), this.restAPI.getIndex("indexName").getName());
}
@Test
public void testCreateIndexWithSameNameButDifferentType(){
this.restAPI.createIndex(Relationship.class, "indexName", LuceneIndexImplementation.FULLTEXT_CONFIG);
this.restAPI.createIndex(Node.class, "indexName", LuceneIndexImplementation.FULLTEXT_CONFIG);
RestIndexManager index = (RestIndexManager) getRestGraphDb().index();
assertTrue(index.existsForNodes("indexName"));
assertTrue(index.existsForRelationships("indexName"));
}
@Test
public void testCreateNodeUniquely() {
final RestIndex<Node> index = restAPI.createIndex(Node.class, "unique-node", LuceneIndexImplementation.EXACT_CONFIG);
final RestNode node1 = restAPI.getOrCreateNode(index, "uid", "42", map("name", "Michael"), NO_LABELS);
final RestNode node2 = restAPI.getOrCreateNode(index, "uid", "42", map("name", "Michael2"), NO_LABELS);
assertEquals(node1,node2);
assertEquals("Michael",node1.getProperty("name"));
assertEquals("Michael",node2.getProperty("name"));
final RestNode node3 = restAPI.getOrCreateNode(index, "uid", "41", map("name", "Emil"), NO_LABELS);
assertEquals(false, node1.equals(node3));
}
@Test
public void testCreateRelationshipUniquely() {
final RestIndex<Relationship> index = restAPI.createIndex(Relationship.class, "unique-rel", LuceneIndexImplementation.EXACT_CONFIG);
final RestNode michael = restAPI.createNode(map("name", "Michael"));
final RestNode david = restAPI.createNode(map("name","David"));
final RestNode peter = restAPI.createNode(map("name","Peter"));
final RestRelationship rel1 = restAPI.getOrCreateRelationship(index, "uid", "42", michael, david, "KNOWS", map("at", "Neo4j"));
final RestRelationship rel2 = restAPI.getOrCreateRelationship(index, "uid", "42", michael, david, "KNOWS", map("at", "Neo4j"));
assertEquals(rel1,rel2);
assertEquals("Neo4j",rel1.getProperty("at"));
assertEquals("Neo4j",rel2.getProperty("at"));
final RestRelationship rel3 = restAPI.getOrCreateRelationship(index, "uid", "41", michael, david, "KNOWS", map("at", "Neo4j"));
assertEquals(false, rel3.equals(rel1));
}
@Test
public void testGetNodeLabel() {
RestNode node = restAPI.createNode(map());
node.addLabel(LABEL_FOO);
int count=0;
for (Label label1 : node.getLabels()) {
assertEquals(LABEL_FOO.name(),label1.name());
count++;
}
assertEquals("one label",1,count);
}
@Test
public void testRemoveNodeLabel() {
RestNode node = restAPI.createNode(map());
node.addLabel(LABEL_FOO);
node.removeLabel(LABEL_FOO);
assertFalse(node.getLabels().iterator().hasNext());
}
@Test
public void testGetNodeByLabel() throws Exception {
RestNode node = restAPI.createNode(map());
node.addLabel(LABEL_FOO);
int count=0;
for (RestNode restNode : restAPI.getNodesByLabel(LABEL_FOO.name())) {
assertEquals(node,restNode);
count++;
}
assertEquals("one node with label",1,count);
}
@Test
public void testSetNodeLabel() throws Exception {
RestNode n1 = restAPI.createNode(map("name", "node1"));
n1.addLabel(LABEL_FOO);
n1.addLabel(LABEL_BAR);
Collection<String> labels = IteratorUtil.asCollection(new IterableWrapper<String, Label>(n1.getLabels()) {
protected String underlyingObjectToObject(Label label) {
return label.name();
}
});
assertThat(labels, hasItems(LABEL_FOO.name(), LABEL_BAR.name()));
}
@Test
public void testRemoveNodeLabel2() throws Exception {
RestNode n1 = restAPI.createNode(map("name", "node1"));
n1.addLabel(LABEL_FOO);
n1.addLabel(LABEL_BAR);
n1.removeLabel(LABEL_BAR);
Collection<String> labels = IteratorUtil.asCollection(new IterableWrapper<String, Label>(n1.getLabels()) {
protected String underlyingObjectToObject(Label label) {
return label.name();
}
});
assertThat(labels, hasItems(LABEL_FOO.name()));
}
@Test
public void testGetNodeByLabelAndProperty() throws Exception {
RestNode node = restAPI.createNode(map("name","foo bar"));
node.addLabel(LABEL_FOO);
int count=0;
for (RestNode restNode : restAPI.getNodesByLabelAndProperty(LABEL_FOO.name(),"name","foo bar")) {
assertEquals(node,restNode);
count++;
}
assertEquals("one node with label",1,count);
}
@Test
public void testGetAllLabelNames() throws Exception {
RestNode node = restAPI.createNode(map("name","foo bar"));
node.addLabel(LABEL_FOO);
node.addLabel(LABEL_BAR);
assertThat(restAPI.getAllLabelNames(), hasItems(LABEL_FOO.name(),LABEL_BAR.name()));
}
}

View File

@@ -0,0 +1,148 @@
/**
* Copyright (c) 2002-2013 "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 <http://www.gnu.org/licenses/>.
*/
package org.neo4j.rest.graphdb;
import org.junit.Before;
import org.junit.Test;
import org.neo4j.graphdb.DynamicRelationshipType;
import org.neo4j.graphdb.Node;
import org.neo4j.graphdb.PropertyContainer;
import org.neo4j.graphdb.Relationship;
import org.neo4j.graphdb.index.*;
import java.net.URISyntaxException;
import static org.junit.Assert.*;
public class RestAutoIndexTest extends RestTestBase {
@Test
public void testEnableDisableAutoIndexerNode() {
AutoIndexer<Node> indexer = getRestGraphDb().index().getNodeAutoIndexer();
testEnableDisableAutoIndexer(indexer);
}
@Test
public void testEnableDisableAutoIndexerRelationship() {
RelationshipAutoIndexer indexer = getRestGraphDb().index().getRelationshipAutoIndexer();
testEnableDisableAutoIndexer(indexer);
}
@Test
public void testAddRemoveAutoIndexerPropertiesOnNodes() {
AutoIndexer<Node> indexer = getRestGraphDb().index().getNodeAutoIndexer();
testAddRemoveAutoIndexerProperties(indexer);
}
@Test
public void testAddRemoveAutoIndexerPropertiesOnRelationships() {
RelationshipAutoIndexer indexer = getRestGraphDb().index().getRelationshipAutoIndexer();
testAddRemoveAutoIndexerProperties(indexer);
}
@Test
public void testGetAutoIndexOnNodes() {
ReadableIndex<Node> autoIndex = getRestGraphDb().index().getNodeAutoIndexer().getAutoIndex();
assertNotNull(autoIndex);
}
@Test
public void testGetAutoIndexOnRelationships() {
ReadableRelationshipIndex autoIndex = getRestGraphDb().index().getRelationshipAutoIndexer().getAutoIndex();
assertNotNull(autoIndex);
}
@Test
public void testAutoIndexingByCheckingIndexData() {
IndexManager indexManager = getRestGraphDb().index();
AutoIndexer<Node> nodeAutoIndexer = indexManager.getNodeAutoIndexer();
RelationshipAutoIndexer relationshipAutoIndex = indexManager.getRelationshipAutoIndexer();
// setup auto indexing
nodeAutoIndexer.startAutoIndexingProperty("nodeProperty");
nodeAutoIndexer.setEnabled(true);
relationshipAutoIndex.startAutoIndexingProperty("relationshipProperty");
relationshipAutoIndex.setEnabled(true);
// create two connected nodes
Node startNode = getRestGraphDb().createNode();
Node endNode = getRestGraphDb().createNode();
startNode.setProperty("nodeProperty", "startNode");
endNode.setProperty("nodeProperty", "endNode");
Relationship relationship = startNode.createRelationshipTo(endNode, DynamicRelationshipType.withName("sample"));
relationship.setProperty("relationshipProperty", "sample");
// check index data
ReadableIndex<Node> nodeAutoIndex = nodeAutoIndexer.getAutoIndex();
IndexHits<Node> nodeHits = nodeAutoIndex.get("nodeProperty", "startNode");
Node nodeByIndex = nodeHits.getSingle();
assertEquals(startNode, nodeByIndex);
nodeHits = nodeAutoIndex.get("nodeProperty", "endNode");
nodeByIndex = nodeHits.getSingle();
assertEquals(endNode, nodeByIndex);
nodeHits = nodeAutoIndex.get("nodeProperty", "nonExistingValue");
assertEquals(0, nodeHits.size());
IndexHits<Relationship> relationshipHits = relationshipAutoIndex.getAutoIndex().get("relationshipProperty", "sample");
Relationship relationshipByIndex = relationshipHits.getSingle();
assertEquals(relationship, relationshipByIndex);
}
private void testAddRemoveAutoIndexerProperties(AutoIndexer<? extends PropertyContainer> indexer) {
assertTrue(indexer.getAutoIndexedProperties().isEmpty());
indexer.startAutoIndexingProperty("property1");
assertTrue(indexer.getAutoIndexedProperties().size()==1);
assertTrue(indexer.getAutoIndexedProperties().contains("property1"));
indexer.startAutoIndexingProperty("property2");
assertTrue(indexer.getAutoIndexedProperties().size() == 2);
assertTrue(indexer.getAutoIndexedProperties().contains("property2"));
indexer.stopAutoIndexingProperty("property2");
assertTrue(indexer.getAutoIndexedProperties().size() == 1);
assertFalse(indexer.getAutoIndexedProperties().contains("property2"));
indexer.stopAutoIndexingProperty("property1");
assertTrue(indexer.getAutoIndexedProperties().isEmpty());
indexer.stopAutoIndexingProperty("propertyUnknown");
assertTrue(indexer.getAutoIndexedProperties().isEmpty());
}
private void testEnableDisableAutoIndexer(AutoIndexer<? extends PropertyContainer> indexer) {
assertFalse("indexer is not enabled by default", indexer.isEnabled());
indexer.setEnabled(true);
assertTrue("indexer was enabled",indexer.isEnabled());
indexer.setEnabled(false);
assertFalse("indexer was disabled", indexer.isEnabled());
}
@Before
public void setUp() throws Exception {
super.setUp();
getGraphDatabase().index().getNodeAutoIndexer().setEnabled(false);
getGraphDatabase().index().getRelationshipAutoIndexer().setEnabled(false);
}
}

View File

@@ -0,0 +1,170 @@
/**
* Copyright (c) 2002-2013 "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 <http://www.gnu.org/licenses/>.
*/
package org.neo4j.rest.graphdb;
import static java.util.Arrays.asList;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import java.util.Collection;
import java.util.Map;
import org.junit.Before;
import org.junit.Test;
import org.neo4j.graphdb.Node;
import org.neo4j.graphdb.Transaction;
import org.neo4j.helpers.collection.IteratorUtil;
import org.neo4j.helpers.collection.MapUtil;
import org.neo4j.rest.graphdb.query.RestCypherQueryEngine;
public class RestCypherQueryEngineTest extends RestTestBase {
private RestCypherQueryEngine queryEngine;
private RestAPI restAPI;
private MatrixDataGraph embeddedMatrixdata;
private MatrixDataGraph restMatrixData;
@Before
public void init() throws Exception {
embeddedMatrixdata = new MatrixDataGraph(getGraphDatabase(),nodeId()).createNodespace();
restMatrixData = new MatrixDataGraph(getRestGraphDb(),nodeId());
this.restAPI = ((RestGraphDatabase)getRestGraphDb()).getRestAPI();
queryEngine = new RestCypherQueryEngine(restAPI);
}
@Test
public void testGetReferenceNode(){
final String queryString = "start n=node({reference}) return n";
final Node result = (Node) queryEngine.query(queryString, MapUtil.map("reference",node().getId())).to(Node.class).single();
Transaction tx = getGraphDatabase().beginTx();
try {
assertEquals(embeddedMatrixdata.getReferenceNode(), result);
} finally {
tx.success();tx.close();
}
}
@Test
public void testGetNeoNode(){
final String queryString = "start neo=node({neoname}) return neo";
final Node result = (Node) queryEngine.query(queryString, MapUtil.map("neoname",getNeoId())).to(Node.class).single();
assertNeoNodeEquals( result);
}
private void assertNodeEquals(Node node, Node result) {
Transaction tx = getGraphDatabase().beginTx();
try {
assertEquals(node, result);
} finally {
tx.success();tx.close();
}
}
@Test
public void testGetNeoNodeByIndexLookup(){
final String queryString = "start neo=node:heroes(name={neoname}) return neo";
final Node result = (Node) queryEngine.query(queryString, MapUtil.map("neoname","Neo")).to(Node.class).single();
assertNeoNodeEquals(result);
}
@Test
public void testGetNeoNodeByIndexQuery(){
final String queryString = "start neo=node:heroes({neoquery}) return neo";
final Node result = (Node) queryEngine.query(queryString, MapUtil.map("neoquery","name:Neo")).to(Node.class).single();
assertNeoNodeEquals(result);
}
private void assertNeoNodeEquals(Node result) {
Transaction tx = getGraphDatabase().beginTx();
try {
assertEquals(embeddedMatrixdata.getNeoNode(), result);
} finally {
tx.success();tx.close();
}
}
@Test
public void testGetNeoNodeSingleProperty(){
final String queryString = "start n=node({neo}) return n.name";
final String result = (String) queryEngine.query(queryString, MapUtil.map("neo",getNeoId())).to(String.class).single();
assertEquals("Thomas Anderson", result);
}
@Test
public void testGetNeoNodeViaMorpheus(){
final String queryString = "start morpheus=node:heroes(name={morpheusname}) match (morpheus) <-[:KNOWS]- (neo) return neo";
final Node result = (Node) queryEngine.query(queryString, MapUtil.map("morpheusname","Morpheus")).to(Node.class).single();
assertNeoNodeEquals(result);
}
@Test
public void testCollectConverter(){
final String queryString = "start neo=node:heroes(name={neoname}) match (neo) -- (other) return neo, collect(other) as o";
final Map result = (Map) queryEngine.query(queryString, MapUtil.map("neoname","Neo")).to(Map.class).single();
assertNeoNodeEquals((Node) result.get("neo"));
Collection<Node> others = (Collection<Node>) result.get("o");
assertEquals(5,others.size());
for (Node other : others) {
assertTrue(other.getId() >= 0);
}
}
@Test
public void testGetCypherNodeViaMorpheusAndFilter(){
final String queryString = "start morpheus=node:heroes(name={morpheusname}) match (morpheus) -[:KNOWS]-> (person) where person.type = \"villain\" return person";
final Node result = (Node) queryEngine.query(queryString, MapUtil.map("morpheusname","Morpheus")).to(Node.class).single();
assertEquals("Cypher", result.getProperty("name"));
}
@Test
public void testGetArchitectViaMorpheusAndFilter(){
final String queryString = "start morpheus=node:heroes(name={morpheusname}) match (morpheus) -[:KNOWS]-> (person) -[:KNOWS]-> (smith) -[:CODED_BY]-> (architect) where person.type = \"villain\" return architect";
final Node result = (Node) queryEngine.query(queryString, MapUtil.map("morpheusname","Morpheus")).to(Node.class).single();
assertEquals("The Architect", result.getProperty("name"));
}
@Test
public void testGetNeoNodeMultipleProperties(){
final String queryString = "start neo=node({neoId}) return neo.name, neo.type, neo.age";
final Collection<Map<String,Object>> result = IteratorUtil.asCollection(queryEngine.query(queryString, MapUtil.map("neoId",getNeoId())));
assertEquals(asList( MapUtil.map("neo.name", "Thomas Anderson", "neo.type","hero", "neo.age", 29 )),result);
}
@Test
public void testGetRelationshipType(){
final String queryString ="start n=node({reference}) match (n)-[r]->() return type(r)";
final Collection<String> result = IteratorUtil.asCollection(queryEngine.query(queryString, MapUtil.map("reference",node().getId())).to(String.class));
assertTrue(result.contains("NEO_NODE"));
}
public long getNeoId(){
Transaction tx = getGraphDatabase().beginTx();
try {
return embeddedMatrixdata.getNeoNode().getId();
} finally {
tx.success();tx.close();
}
}
}

View File

@@ -0,0 +1,90 @@
/**
* Copyright (c) 2002-2013 "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 <http://www.gnu.org/licenses/>.
*/
package org.neo4j.rest.graphdb;
import java.util.Arrays;
import org.junit.Assert;
import org.junit.Test;
import org.neo4j.graphdb.Direction;
import org.neo4j.graphdb.Node;
import org.neo4j.graphdb.Relationship;
import org.neo4j.rest.graphdb.util.TestHelper;
public class RestEntityTest extends RestTestBase {
@Test
public void testSetProperty() {
node().setProperty( "name", "test" );
Node node = node();
Assert.assertEquals( "test", node.getProperty( "name" ) );
}
@Test
public void testSetStringArrayProperty() {
node().setProperty( "name", new String[]{"test"} );
Node node = node();
Assert.assertArrayEquals( new String[]{"test"}, (String[])node.getProperty( "name" ) );
}
@Test
public void testSetDoubleArrayProperty() {
double[] data = {0, 1, 2};
node().setProperty( "data", data );
Node node = node();
Assert.assertTrue("same double array",Arrays.equals( data, (double[])node.getProperty( "data" ) ));
}
@Test
public void testRemoveProperty() {
Node node = node();
node.setProperty( "name", "test" );
Assert.assertEquals( "test", node.getProperty( "name" ) );
node.removeProperty( "name" );
Assert.assertEquals( false, node.hasProperty( "name" ) );
}
@Test
public void testSetPropertyOnRelationship() {
Node refNode = node();
Node node = getRestGraphDb().createNode();
Relationship rel = refNode.createRelationshipTo( node, Type.TEST );
rel.setProperty( "name", "test" );
Assert.assertEquals( "test", rel.getProperty( "name" ) );
Relationship foundRelationship = TestHelper.firstRelationshipBetween( refNode.getRelationships( Type.TEST, Direction.OUTGOING ), refNode, node );
Assert.assertEquals( "test", foundRelationship.getProperty( "name" ) );
}
@Test
public void testRemovePropertyOnRelationship() {
Node refNode = node();
Node node = getRestGraphDb().createNode();
Relationship rel = refNode.createRelationshipTo( node, Type.TEST );
rel.setProperty( "name", "test" );
Assert.assertEquals( "test", rel.getProperty( "name" ) );
Relationship foundRelationship = TestHelper.firstRelationshipBetween( refNode.getRelationships( Type.TEST, Direction.OUTGOING ), refNode, node );
Assert.assertEquals( "test", foundRelationship.getProperty( "name" ) );
rel.removeProperty( "name" );
Assert.assertEquals( false, rel.hasProperty( "name" ) );
Relationship foundRelationship2 = TestHelper.firstRelationshipBetween( refNode.getRelationships( Type.TEST, Direction.OUTGOING ), refNode, node );
Assert.assertEquals( false, foundRelationship2.hasProperty( "name" ) );
}
}

View File

@@ -0,0 +1,122 @@
/**
* Copyright (c) 2002-2013 "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 <http://www.gnu.org/licenses/>.
*/
package org.neo4j.rest.graphdb;
import java.util.Collection;
import java.util.Date;
import org.junit.Assert;
import org.junit.Test;
import org.neo4j.graphdb.*;
import org.neo4j.helpers.collection.IteratorUtil;
import org.neo4j.rest.graphdb.util.TestHelper;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertTrue;
import static org.junit.matchers.JUnitMatchers.hasItems;
public class RestGraphDbTest extends RestTestBase {
@Test
public void testGetRefNode() {
Node refNode = node();
Node nodeById = getRestGraphDb().getNodeById( refNode.getId() );
Assert.assertEquals( refNode, nodeById );
}
@Test
public void testCreateNode() {
Node node = getRestGraphDb().createNode();
Assert.assertEquals( node, getRestGraphDb().getNodeById( node.getId() ) );
}
@Test
public void testCreateRelationship() {
Node refNode = node();
Node node = getRestGraphDb().createNode();
Relationship rel = refNode.createRelationshipTo( node, Type.TEST );
Relationship foundRelationship = TestHelper.firstRelationshipBetween( refNode.getRelationships( Type.TEST, Direction.OUTGOING ), refNode, node );
Assert.assertNotNull( "found relationship", foundRelationship );
Assert.assertEquals( "same relationship", rel, foundRelationship );
Assert.assertThat( refNode.getRelationships( Type.TEST, Direction.OUTGOING ), new IsRelationshipToNodeMatcher( refNode, node ) );
Assert.assertThat( refNode.getRelationships( Direction.OUTGOING ), new IsRelationshipToNodeMatcher( refNode, node ) );
Assert.assertThat( refNode.getRelationships( Direction.BOTH ), new IsRelationshipToNodeMatcher( refNode, node ) );
Assert.assertThat( refNode.getRelationships( Type.TEST ), new IsRelationshipToNodeMatcher( refNode, node ) );
}
@Test
public void testBasic() {
final GraphDatabaseService gdb = getRestGraphDb();
Node refNode = node();
Node node = gdb.createNode();
final RelationshipType TEST = DynamicRelationshipType.withName("TEST");
Relationship rel = refNode.createRelationshipTo( node,
TEST);
rel.setProperty( "date", new Date().getTime() );
node.setProperty( "name", "Mattias test" );
refNode.createRelationshipTo( node,
TEST);
for ( Relationship relationship : refNode.getRelationships() ) {
System.out.println( "rel prop:" + relationship.getProperty( "date", null ) );
Node endNode = relationship.getEndNode();
System.out.println( "node prop:" + endNode.getProperty( "name", null ) );
}
assertThat(gdb.getAllNodes(),hasItems(refNode, node));
boolean found = false;
for (RelationshipType type : gdb.getRelationshipTypes()) {
found |= TEST.name().equals(type.name());
}
assertEquals("rel-type TEST found",true,found);
}
@Test
public void testCreateNodeWithLabels() {
Label label1 = DynamicLabel.label("FOO");
Label label2 = DynamicLabel.label("BAR");
Node node = getRestGraphDb().createNode(label1, label2);
Collection<Label> labels = IteratorUtil.asCollection(node.getLabels());
assertEquals(2,labels.size());
for (Label label : labels) {
assertTrue(label.name().equals(label1.name()) || label.name().equals(label2.name()));
}
}
@Test
public void testGetNodesByLabelAndProperty() throws Exception {
Label label1 = DynamicLabel.label("FOO");
Label label2 = DynamicLabel.label("BAR");
GraphDatabaseService db = getRestGraphDb();
Node node = db.createNode(label1, label2);
node.setProperty("name","foo bar");
node.setProperty("age",42);
Collection<Node> nodes = IteratorUtil.asCollection(db.findNodesByLabelAndProperty(label1, "name", "foo bar"));
assertEquals(1,nodes.size());
assertEquals(node,nodes.iterator().next());
nodes = IteratorUtil.asCollection(db.findNodesByLabelAndProperty(label2, "age", 42));
assertEquals(1,nodes.size());
assertEquals(node,nodes.iterator().next());
nodes = IteratorUtil.asCollection(db.findNodesByLabelAndProperty(label2, "age", 43));
assertEquals(0,nodes.size());
}
}

View File

@@ -0,0 +1,246 @@
/**
* Copyright (c) 2002-2013 "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 <http://www.gnu.org/licenses/>.
*/
package org.neo4j.rest.graphdb;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.apache.lucene.index.Term;
import org.apache.lucene.search.TermQuery;
import org.junit.Assert;
import org.junit.Test;
import org.neo4j.graphdb.Node;
import org.neo4j.graphdb.Relationship;
import org.neo4j.graphdb.index.Index;
import org.neo4j.graphdb.index.IndexHits;
import org.neo4j.graphdb.index.IndexManager;
import org.neo4j.graphdb.index.RelationshipIndex;
import org.neo4j.index.lucene.QueryContext;
import static org.junit.Assert.assertEquals;
public class RestIndexTest extends RestTestBase {
private static final String NODE_INDEX_NAME = "NODE_INDEX";
private static final String REL_INDEX_NAME = "REL_INDEX";
@Test
public void testAddToNodeIndex() {
nodeIndex().add(node(), "name", "test");
IndexHits<Node> hits = nodeIndex().get("name", "test");
Assert.assertEquals("index results", true, hits.hasNext());
Assert.assertEquals(node(), hits.next());
}
@Test
public void testUseCriticalCharactersInKeyAndValue() {
nodeIndex().add(node(), "na#me", "te?t");
IndexHits<Node> hits = nodeIndex().get("na#me", "te?t");
Assert.assertEquals("index results", true, hits.hasNext());
Assert.assertEquals(node(), hits.next());
}
@Test
public void testPutNodeIfAbsentIndex() {
final Node node = nodeIndex().putIfAbsent(node(), "name", "test");
Assert.assertEquals(node(), node);
IndexHits<Node> hits = nodeIndex().get("name", "test");
Assert.assertEquals("index results", true, hits.hasNext());
Assert.assertEquals(node(), hits.next());
}
@Test
public void testPutNodeIfAbsentWithExistingNodeIndex() {
nodeIndex().add(node(), "name", "test");
final Node node = nodeIndex().putIfAbsent(node(), "name", "test");
Assert.assertEquals(node(), node);
IndexHits<Node> hits = nodeIndex().get("name", "test");
Assert.assertEquals("index results", true, hits.hasNext());
Assert.assertEquals(node(), hits.next());
}
@Test
public void testStarQuery() {
Node node = node();
nodeIndex().add(node, "name", "test");
Node res = nodeIndex().query("*:*").getSingle();
assertEquals(node,res);
}
@Test
public void testNotFoundInNodeIndex() {
IndexHits<Node> hits = nodeIndex().get("foo", "bar");
Assert.assertEquals("no index results", false, hits.hasNext());
}
@Test
public void testAddToRelationshipIndex() {
final long value = System.currentTimeMillis();
relationshipIndex().add(relationship(), "name", value);
IndexHits<Relationship> hits = relationshipIndex().get("name", value);
Assert.assertEquals("index results", true, hits.hasNext());
Assert.assertEquals(relationship(), hits.next());
}
@Test
public void testNotFoundInRelationshipIndex() {
IndexHits<Relationship> hits = relationshipIndex().get("foo", "bar");
Assert.assertEquals("no index results", false, hits.hasNext());
}
@Test
public void testDeleteKeyValueFromNodeIndex() {
String value = String.valueOf(System.currentTimeMillis());
nodeIndex().add(node(), "time", value);
IndexHits<Node> hits = nodeIndex().get("time", value);
Assert.assertEquals("found in index results", true, hits.hasNext());
Assert.assertEquals("found in index results", node(), hits.next());
nodeIndex().remove(node(), "time", value);
IndexHits<Node> hitsAfterRemove = nodeIndex().get("time", value);
Assert.assertEquals("not found in index results", false, hitsAfterRemove.hasNext());
}
@Test
public void testDeleteKeyFromNodeIndex() {
String value = String.valueOf(System.currentTimeMillis());
nodeIndex().add(node(), "time", value);
IndexHits<Node> hits = nodeIndex().get("time", value);
Assert.assertEquals("found in index results", true, hits.hasNext());
Assert.assertEquals("found in index results", node(), hits.next());
nodeIndex().remove(node(), "time");
IndexHits<Node> hitsAfterRemove = nodeIndex().get("time", value);
Assert.assertEquals("not found in index results", false, hitsAfterRemove.hasNext());
}
@Test
public void testDeleteNodeFromNodeIndex() {
String value = String.valueOf(System.currentTimeMillis());
nodeIndex().add(node(), "time", value);
IndexHits<Node> hits = nodeIndex().get("time", value);
Assert.assertEquals("found in index results", true, hits.hasNext());
Assert.assertEquals("found in index results", node(), hits.next());
nodeIndex().remove(node());
IndexHits<Node> hitsAfterRemove = nodeIndex().get("time", value);
Assert.assertEquals("not found in index results", false, hitsAfterRemove.hasNext());
}
@Test
public void testDeleteIndex() {
final String indexName = nodeIndex().getName();
nodeIndex().delete();
final List<String> indexNames = Arrays.asList(getRestGraphDb().index().nodeIndexNames());
Assert.assertEquals("removed index name",false,indexNames.contains(indexName));
}
@Test
public void testCreateFulltextIndex() {
Map<String,String> config=new HashMap<String, String>();
config.put("provider", "lucene");
config.put("type","fulltext");
final IndexManager indexManager = getRestGraphDb().index();
final Index<Node> index = indexManager.forNodes("fulltext", config);
final Map<String, String> config2 = indexManager.getConfiguration(index);
Assert.assertEquals("provider", config.get("provider"), config2.get("provider"));
Assert.assertEquals("type", config.get("type"), config2.get("type"));
}
@Test
public void testQueryFulltextIndexWithKey() {
Map<String,String> config=new HashMap<String, String>();
config.put("provider","lucene");
config.put("type","fulltext");
final Index<Node> index = getRestGraphDb().index().forNodes("text-index", config);
index.add(node(),"text","any text");
final IndexHits<Node> hits = index.query("text", "any t*");
Assert.assertEquals("found in index results", true, hits.hasNext());
Assert.assertEquals("found in index results", node(), hits.next());
}
@Test
public void testQueryFulltextIndexWithOutKey() {
Map<String,String> config=new HashMap<String, String>();
config.put("provider","lucene");
config.put("type","fulltext");
final Index<Node> index = getRestGraphDb().index().forNodes("text-index", config);
index.add(node(),"text","any text");
final IndexHits<Node> hits = index.query("text:any t*");
Assert.assertEquals("found in index results", true, hits.hasNext());
Assert.assertEquals("found in index results", node(), hits.next());
}
@Test
public void testQueryFulltextIndexWithLuceneQueryTerm() {
Map<String, String> config = new HashMap<String, String>();
config.put("provider", "lucene");
config.put("type", "fulltext");
final Index<Node> index = getRestGraphDb().index().forNodes("text-index", config);
index.add(node(), "text", "any text");
TermQuery luceneQuery = new TermQuery(new Term("text", "any t*"));
// TODO this works only because the toString implementation renders a complete query -> dangerous assumption
final IndexHits<Node> hits = index.query(luceneQuery);
Assert.assertEquals("found in index results", true, hits.hasNext());
Assert.assertEquals("found in index results", node(), hits.next());
}
@Test
public void testQueryFulltextIndexWithQueryContext() {
Map<String, String> config = new HashMap<String, String>();
config.put("provider", "lucene");
config.put("type", "fulltext");
final Index<Node> index = getRestGraphDb().index().forNodes("text-index", config);
index.add(node(), "text", "any text");
QueryContext ctx = new QueryContext("text:any t*");
final IndexHits<Node> hits = index.query(ctx);
Assert.assertEquals("found in index results", true, hits.hasNext());
Assert.assertEquals("found in index results", node(), hits.next());
}
@Test
public void testDeleteFromRelationshipIndex() {
String value = String.valueOf(System.currentTimeMillis());
relationshipIndex().add(relationship(), "time", value);
IndexHits<Relationship> hits = relationshipIndex().get("time", value);
Assert.assertEquals("found in index results", true, hits.hasNext());
Assert.assertEquals("found in index results", relationship(), hits.next());
relationshipIndex().remove(relationship(), "time", value);
IndexHits<Relationship> hitsAfterRemove = relationshipIndex().get("time", value);
Assert.assertEquals("not found in index results", false, hitsAfterRemove.hasNext());
}
private Index<Node> nodeIndex() {
return getRestGraphDb().index().forNodes(NODE_INDEX_NAME);
}
private RelationshipIndex relationshipIndex() {
return getRestGraphDb().index().forRelationships(REL_INDEX_NAME);
}
@Test
public void testNodeIndexIsListed() {
nodeIndex().add(node(), "name", "test");
Assert.assertTrue("node index name listed", Arrays.asList(getRestGraphDb().index().nodeIndexNames()).contains(NODE_INDEX_NAME));
}
@Test
public void testRelationshipIndexIsListed() {
relationshipIndex().add(relationship(), "name", "test");
Assert.assertTrue("relationship index name listed", Arrays.asList(getRestGraphDb().index().relationshipIndexNames()).contains(REL_INDEX_NAME));
}
}

View File

@@ -0,0 +1,197 @@
/**
* Copyright (c) 2002-2013 "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 <http://www.gnu.org/licenses/>.
*/
package org.neo4j.rest.graphdb;
import static java.util.Arrays.asList;
import static org.hamcrest.core.Is.is;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertTrue;
import static org.neo4j.graphdb.Direction.INCOMING;
import static org.neo4j.graphdb.Direction.OUTGOING;
import static org.neo4j.helpers.collection.IteratorUtil.count;
import static org.neo4j.rest.graphdb.RelationshipHasMatcher.match;
import org.junit.Before;
import org.junit.Test;
import org.neo4j.graphdb.*;
import org.neo4j.helpers.collection.IteratorUtil;
import org.neo4j.rest.graphdb.MatrixDataGraph.RelTypes;
import java.util.List;
public class RestNodeTest extends RestTestBase {
private MatrixDataGraph embeddedMatrixdata;
private MatrixDataGraph restMatrixData;
private Node neo;
@Before
public void createMatrixdata() {
embeddedMatrixdata = new MatrixDataGraph(getGraphDatabase()).createNodespace();
restMatrixData = new MatrixDataGraph(getRestGraphDb(),embeddedMatrixdata.getReferenceNode().getId());
neo = restMatrixData.getNeoNode();
}
@Test
public void testGetRelationshipsWithoutDirectionWithoutRelationshipType() {
Iterable<Relationship> relationships = neo.getRelationships();
assertThat(relationships, match(neo, null));
assertThat(neo.getDegree(),is(count(relationships)));
}
@Test
public void testGetRelationshipsWithIncomingDirectionWithoutRelationshipType() {
Iterable<Relationship> relationships = neo.getRelationships(INCOMING);
assertThat(relationships, match(neo, INCOMING));
assertThat(neo.getDegree(INCOMING),is(2));
}
@Test
public void testGetRelationshipsWithOutgoingDirectionWithoutRelationshipType() {
Iterable<Relationship> relationships = neo.getRelationships(OUTGOING);
assertThat(relationships, match(neo, OUTGOING));
assertThat(neo.getDegree(OUTGOING),is(count(relationships)));
}
@Test
public void testGetRelationshipsWithoutDirectionWithSingleRelationshipType() {
Iterable<Relationship> relationships = neo.getRelationships(RelTypes.NEO_NODE);
assertThat(relationships, match(neo, null, RelTypes.NEO_NODE));
assertThat(neo.getDegree(RelTypes.NEO_NODE),is(count(relationships)));
}
@Test
public void testGetRelationshipsWithIncomingDirectionWithSingleRelationshipType() {
Iterable<Relationship> relationships = neo.getRelationships(INCOMING, RelTypes.NEO_NODE);
assertThat(relationships, match(neo, INCOMING, RelTypes.NEO_NODE));
assertThat(neo.getDegree(RelTypes.NEO_NODE,INCOMING),is(count(relationships)));
}
@Test
public void testGetRelationshipsWithOutgoingDirectionWithSingleRelationshipType() {
Iterable<Relationship> relationships = neo.getRelationships(OUTGOING, RelTypes.KNOWS);
assertThat(relationships, match(neo, OUTGOING, RelTypes.KNOWS));
assertThat(neo.getDegree(RelTypes.KNOWS,OUTGOING),is(count(relationships)));
}
@Test
public void testGetRelationshipsWithoutDirectionWithMultipleRelationshipTypes() {
Iterable<Relationship> relationships = neo.getRelationships(RelTypes.NEO_NODE, RelTypes.HERO);
assertThat(relationships, match(neo, null, RelTypes.NEO_NODE, RelTypes.HERO));
}
@Test
public void testGetRelationshipsWithIncomingDirectionWithMultipleRelationshipTypes() {
Iterable<Relationship> relationships = neo.getRelationships(INCOMING, RelTypes.NEO_NODE, RelTypes.HERO );
assertThat(relationships, match(neo, INCOMING, RelTypes.NEO_NODE, RelTypes.HERO));
}
@Test
public void testGetRelationshipsWithOutgoingDirectionWithMultipleRelationshipTypes() {
Iterable<Relationship> relationships = neo.getRelationships(OUTGOING, RelTypes.KNOWS, RelTypes.FIGHTS );
assertThat(relationships, match(neo, OUTGOING, RelTypes.KNOWS, RelTypes.FIGHTS));
}
@Test
public void testHasRelationshipsWithoutDirectionWithoutRelationshipType() {
boolean hasRelationship = neo.hasRelationship();
assertTrue(hasRelationship);
assertThat(neo.getDegree(),is(5));
}
@Test
public void testHasRelationshipsWithIncomingDirectionWithoutRelationshipType() {
boolean hasRelationship = neo.hasRelationship(INCOMING);
assertTrue(hasRelationship);
}
@Test
public void testHasRelationshipsWithOutgoingDirectionWithoutRelationshipType() {
boolean hasRelationship = neo.hasRelationship(OUTGOING);
assertTrue(hasRelationship);
}
@Test
public void testHasRelationshipsWithoutDirectionWithSingleRelationshipType() {
boolean hasRelationship = neo.hasRelationship(RelTypes.KNOWS);
assertTrue(hasRelationship);
}
@Test
public void testHasRelationshipsWithIncomingDirectionWithSingleRelationshipType() {
boolean hasRelationship = neo.hasRelationship(INCOMING, RelTypes.HERO);
assertTrue(hasRelationship);
}
@Test
public void testHasRelationshipsWithIncomingDirectionWithSingleRelationshipTypeParamsReversed() {
boolean hasRelationship = neo.hasRelationship(RelTypes.HERO, INCOMING);
assertTrue(hasRelationship);
}
@Test
public void testHasRelationshipsWithOutgoingDirectionWithSingleRelationshipType() {
boolean hasRelationship = neo.hasRelationship(OUTGOING, RelTypes.KNOWS);
assertTrue(hasRelationship);
}
@Test
public void testHasRelationshipsWithOutgoingDirectionWithSingleRelationshipTypeParamsReversed() {
boolean hasRelationship = neo.hasRelationship(RelTypes.KNOWS, OUTGOING);
assertTrue(hasRelationship);
}
@Test
public void testHasRelationshipsWithoutDirectionWithMultipleRelationshipTypes() {
boolean hasRelationship = neo.hasRelationship(RelTypes.KNOWS, RelTypes.HERO);
assertTrue(hasRelationship);
}
@Test
public void testHasRelationshipsWithIncomingDirectionWithMultipleRelationshipTypes() {
boolean hasRelationship = neo.hasRelationship(INCOMING, RelTypes.NEO_NODE, RelTypes.HERO);
assertTrue(hasRelationship);
}
@Test
public void testHasRelationshipsWithOutgoingDirectionWithMultipleRelationshipTypes() {
boolean hasRelationship = neo.hasRelationship(OUTGOING, RelTypes.KNOWS, RelTypes.FIGHTS);
assertTrue(hasRelationship);
}
@Test
public void testGetNodeLabels() throws Exception {
Label label = DynamicLabel.label("TestPerson");
Node node = getRestGraphDb().createNode(label);
assertTrue(node.hasLabel(label));
Node node2 = getRestGraphDb().getNodeById(node.getId());
assertTrue(node2.hasLabel(label));
Iterable<RelationshipType> types = neo.getRelationshipTypes();
List<String> expectedTypes = asList(RelTypes.NEO_NODE.name(), RelTypes.HERO.name(), RelTypes.KNOWS.name(), RelTypes.FIGHTS.name());
int count = 0;
for (RelationshipType type : types) {
if (expectedTypes.contains(type.name())) count++;
}
assertThat(count,is(expectedTypes.size()));
}
}

View File

@@ -0,0 +1,139 @@
/**
* Copyright (c) 2002-2013 "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 <http://www.gnu.org/licenses/>.
*/
package org.neo4j.rest.graphdb;
import org.junit.After;
import org.junit.AfterClass;
import org.junit.Before;
import org.junit.BeforeClass;
import org.neo4j.graphdb.*;
import org.neo4j.helpers.collection.IteratorUtil;
import org.neo4j.rest.graphdb.entity.RestNode;
import org.neo4j.rest.graphdb.util.Config;
import org.neo4j.tooling.GlobalGraphOperations;
import java.net.URISyntaxException;
import java.util.Iterator;
import static org.junit.Assert.assertEquals;
public class RestTestBase {
private GraphDatabaseService restGraphDb;
private static final String HOSTNAME = "localhost";
private static final int PORT = 7473;
private static LocalTestServer neoServer;
public static final String SERVER_ROOT = "http://" + HOSTNAME + ":" + PORT;
protected static final String SERVER_ROOT_URI = SERVER_ROOT + "/db/data/";
private long referenceNodeId;
private Node referenceNode;
static {
initServer();
}
protected static void initServer() {
if (neoServer!=null) {
neoServer.stop();
}
neoServer = new LocalTestServer(HOSTNAME,PORT).withPropertiesFile("server-test-db.properties");
}
@BeforeClass
public static void startDb() throws Exception {
neoServer.start();
tryConnect();
}
private static void tryConnect() throws InterruptedException {
int retryCount = 3;
for (int i = 0; i < retryCount; i++) {
try {
RequestResult result = new ExecutingRestRequest(SERVER_ROOT_URI).get("");
assertEquals(200, result.getStatus());
System.err.println("Successful HTTP connection to "+SERVER_ROOT_URI);
return;
} catch (Exception e) {
System.err.println("Error retrieving ROOT URI " + e.getMessage());
Thread.sleep(500);
}
}
}
@Before
public void setUp() throws Exception {
System.setProperty(Config.CONFIG_BATCH_TRANSACTION,"false");
neoServer.cleanDb();
restGraphDb = new RestGraphDatabase(SERVER_ROOT_URI);
GraphDatabaseService db = getGraphDatabase();
try (Transaction tx = db.beginTx()) {
Node node = db.createNode();
this.referenceNodeId = node.getId();
tx.success();
}
this.referenceNode = restGraphDb.getNodeById(referenceNodeId);
}
@After
public void tearDown() throws Exception {
restGraphDb.shutdown();
}
@AfterClass
public static void shutdownDb() {
neoServer.stop();
}
protected Relationship relationship() {
Iterator<Relationship> it = node().getRelationships(Direction.OUTGOING).iterator();
if (it.hasNext()) return it.next();
return node().createRelationshipTo(restGraphDb.createNode(), Type.TEST);
}
protected Node node() {
return referenceNode;
}
protected long nodeId() {
return referenceNodeId;
}
protected GraphDatabaseService getGraphDatabase() {
return neoServer.getGraphDatabase();
}
protected GraphDatabaseService getRestGraphDb() {
return restGraphDb;
}
protected int countExistingNodes() {
return IteratorUtil.count(GlobalGraphOperations.at(getGraphDatabase()).getAllNodes());
}
protected Node loadRealNode(Node node) {
return getGraphDatabase().getNodeById(node.getId());
}
public String getUserAgent() {
return neoServer.getUserAgent();
}
}

View File

@@ -0,0 +1,41 @@
/**
* Copyright (c) 2002-2013 "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 <http://www.gnu.org/licenses/>.
*/
package org.neo4j.rest.graphdb;
import org.junit.Assert;
import org.junit.Test;
import org.neo4j.graphdb.Node;
import org.neo4j.graphdb.Relationship;
import org.neo4j.graphdb.traversal.TraversalDescription;
import org.neo4j.graphdb.traversal.Traverser;
import org.neo4j.rest.graphdb.traversal.RestTraversal;
public class RestTraversalExecutionTest extends RestTestBase {
@Test
public void testTraverseToNeighbour() {
final Relationship rel = relationship();
final TraversalDescription traversalDescription = RestTraversal.description().maxDepth(1).breadthFirst();
System.out.println("traversalDescription = " + traversalDescription);
final Traverser traverser = traversalDescription.traverse(rel.getStartNode());
final Iterable<Node> nodes = traverser.nodes();
Assert.assertEquals(rel.getEndNode(), nodes.iterator().next());
}
}

View File

@@ -0,0 +1,131 @@
/**
* Copyright (c) 2002-2013 "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 <http://www.gnu.org/licenses/>.
*/
package org.neo4j.rest.graphdb;
import java.util.Map;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.neo4j.kernel.Uniqueness;
import org.neo4j.rest.graphdb.traversal.RestTraversal;
import org.neo4j.rest.graphdb.traversal.RestTraversalDescription;
/**
* @author Michael Hunger
* @since 03.02.11
*/
public class RestTraversalTest {
private RestTraversal traversalDescription;
@Before
public void setUp() throws Exception {
traversalDescription = (RestTraversal) RestTraversal.description();
}
@Test
public void testUniqueness() throws Exception {
traversalDescription.uniqueness(Uniqueness.NODE_PATH);
Assert.assertEquals("node path", getPostData("uniqueness"));
}
@Test
public void testUniquenessWithValue() throws Exception {
traversalDescription.uniqueness(Uniqueness.NODE_PATH,"test");
String param = "uniqueness";
final Map uniquenessMap = (Map) getPostData(param);
Assert.assertEquals("node path", uniquenessMap.get("name"));
Assert.assertEquals("test", uniquenessMap.get("value"));
}
private Object getPostData(String param) {
return traversalDescription.getPostData().get(param);
}
@Test
public void testPruneScript() throws Exception {
traversalDescription.prune(RestTraversalDescription.ScriptLanguage.JAVASCRIPT, "return true;");
Map pruneEvaluator= (Map) getPostData("prune_evaluator");
Assert.assertEquals("javascript", pruneEvaluator.get("language"));
Assert.assertEquals("return true;", pruneEvaluator.get("body"));
}
@Test
public void testFilterScript() throws Exception {
traversalDescription.filter(RestTraversalDescription.ScriptLanguage.JAVASCRIPT, "return true;");
Map pruneEvaluator= (Map) getPostData("return_filter");
Assert.assertEquals("javascript", pruneEvaluator.get("language"));
Assert.assertEquals("return true;", pruneEvaluator.get("body"));
}
@Test
public void testEvaluator() throws Exception {
}
@Test
public void testPrune() throws Exception {
}
@Test
public void testFilter() throws Exception {
}
@Test
public void testMaxDepth() throws Exception {
}
@Test
public void testOrder() throws Exception {
}
@Test
public void testDepthFirst() throws Exception {
}
@Test
public void testBreadthFirst() throws Exception {
}
@Test
public void testRelationships() throws Exception {
}
@Test
public void testRelationshipsAndDirection() throws Exception {
}
@Test
public void testExpand() throws Exception {
}
@Test
public void testComplexTraversal() throws Exception {
}
}

View File

@@ -0,0 +1,235 @@
/**
* Copyright (c) 2002-2013 "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 <http://www.gnu.org/licenses/>.
*/
package org.neo4j.rest.graphdb;
import static java.util.Arrays.asList;
import static junit.framework.Assert.assertEquals;
import static junit.framework.Assert.assertFalse;
import static junit.framework.Assert.assertNull;
import static junit.framework.Assert.assertTrue;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Hashtable;
import java.util.Map;
import org.junit.Before;
import org.junit.Test;
import org.neo4j.graphdb.Node;
import org.neo4j.graphdb.Path;
import org.neo4j.graphdb.Relationship;
import org.neo4j.helpers.collection.MapUtil;
import org.neo4j.rest.graphdb.converter.ResultTypeConverter;
import org.neo4j.rest.graphdb.converter.TypeInformation;
import org.neo4j.rest.graphdb.entity.RestNode;
import org.neo4j.rest.graphdb.entity.RestRelationship;
import org.neo4j.rest.graphdb.traversal.SimplePath;
/**
* User: KBurchardi
* Date: 18.10.11
* Time: 18:13
*/
public class ResultTypeConverterTest extends RestTestBase {
private ResultTypeConverter converter;
private RestAPI restAPI;
@Before
public void init(){
restAPI = ((RestGraphDatabase)getRestGraphDb()).getRestAPI();
converter = new ResultTypeConverter(restAPI);
}
@Test
public void testConvertJSONDataToNode(){
Object result = converter.convertToResultType(MapUtil.map("self","http://localhost:7474/db/data/node/2", "data", MapUtil.map("propname", "testprop")), new TypeInformation(RestNode.class));
assertEquals(RestNode.class, result.getClass());
assertEquals("testprop", ((Node)result).getProperty("propname"));
}
@Test
public void testConvertJSONDataToRelationship(){
Object result = converter.convertToResultType(MapUtil.map("self","http://localhost:7474/db/data/relationship/2", "data", MapUtil.map("propname", "testprop")), new TypeInformation(RestRelationship.class));
assertEquals(RestRelationship.class, result.getClass());
assertEquals("testprop", ((Relationship)result).getProperty("propname"));
}
@Test
public void testConvertJSONDataToPath(){
String node1 = "http://localhost:7474/db/data/node/1";
String node2 = "http://localhost:7474/db/data/node/2";
String relationship1 = "http://localhost:7474/db/data/relationship/1";
Map<String, Object> path = new HashMap<String, Object>();
path.put("start", node1);
path.put("nodes", asList(node1, node2));
path.put("length",1);
path.put("relationships", asList(relationship1));
path.put("end", node2);
Path result = (Path)converter.convertToResultType(path, new TypeInformation(Path.class));
assertEquals(SimplePath.class, result.getClass());
assertEquals(1, result.startNode().getId());
assertEquals(2, result.endNode().getId());
assertEquals(1, result.lastRelationship().getId());
}
@Test
public void testConvertJSONDataToFullPath(){
Map<String, Object> node1 = MapUtil.map("self","http://localhost:7474/db/data/node/1", "data", MapUtil.map("propname", "testprop1"));
Map<String, Object> node2 = MapUtil.map("self","http://localhost:7474/db/data/node/2", "data", MapUtil.map("propname", "testprop2"));
Map<String, Object> relationship1 = MapUtil.map("self","http://localhost:7474/db/data/relationship/1", "data", MapUtil.map("propname", "testproprel1"));
Map<String, Object> path = new HashMap<String, Object>();
path.put("start", node1);
path.put("nodes", asList(node1, node2));
path.put("length",1);
path.put("relationships", asList(relationship1));
path.put("end", node2);
Object result = converter.convertToResultType(path, new TypeInformation(Path.class));
assertEquals(SimplePath.class, result.getClass());
assertEquals("testprop1", ((SimplePath)result).startNode().getProperty("propname"));
assertEquals("testprop2", ((SimplePath)result).endNode().getProperty("propname"));
assertEquals("testproprel1", ((SimplePath)result).lastRelationship().getProperty("propname"));
}
@Test
public void testConvertSimpleObjectToSameClass(){
Object result = converter.convertToResultType("test", new TypeInformation(String.class));
assertEquals(String.class, result.getClass());
assertEquals("test", result);
}
@Test
public void testConvertIterableToIterableWithSameType(){
Object result = converter.convertToResultType(asList("test","test2"), new TypeInformation(asList("test")));
assertEquals(asList("test","test2"), result);
}
@Test (expected = RestResultException.class)
public void testConvertIterableToIterableWithWrongType(){
converter.convertToResultType(asList("test"), new TypeInformation(asList(2)));
}
@Test
public void testConvertMapToMapWithSameType(){
Object result = converter.convertToResultType(MapUtil.map("test",1,"test2",2), new TypeInformation(MapUtil.map("test",0)));
assertEquals(MapUtil.map("test", 1, "test2", 2), result);
}
@Test (expected = RestResultException.class)
public void testConvertMapToMapWithWrongType(){
converter.convertToResultType(MapUtil.map("test",1,"test2",2), new TypeInformation(MapUtil.map("test","0")));
}
@Test (expected = RestResultException.class)
public void testConvertSimpleObjectToWrongClass(){
converter.convertToResultType("test", new TypeInformation(Integer.class));
}
@Test
public void testConvertDifferentIterablesWithSameType(){
HashSet<String> set = new HashSet<String>();
set.add("test");
Object result = converter.convertToResultType(set, new TypeInformation(asList("t")));
assertEquals(asList("test"), result);
}
@Test (expected = RestResultException.class)
public void testConvertDifferentIterablesWithWrongType(){
HashSet<String> set = new HashSet<String>();
set.add("test");
converter.convertToResultType(set, new TypeInformation(asList(2)));
}
@Test
public void testConvertDifferentMapsWithSameType(){
Hashtable<String,String> table = new Hashtable<String,String>();
table.put("testkey", "testvalue");
Object result = converter.convertToResultType(table, new TypeInformation(MapUtil.map("test","test")));
assertEquals(MapUtil.map("testkey","testvalue"), result);
}
@Test (expected = RestResultException.class)
public void testConvertDifferentMapsWithWrongType(){
Hashtable<String,String> table = new Hashtable<String,String>();
table.put("testkey", "testvalue");
converter.convertToResultType(table, new TypeInformation(MapUtil.map("test",2)));
}
@Test
public void testConvertFromIterableWithSameTypeAndSingleElementToObject(){
Object result = converter.convertToResultType(Collections.singletonList("test"), new TypeInformation(String.class));
assertEquals(String.class, result.getClass());
assertEquals("test", result);
}
@Test (expected = RestResultException.class)
public void testConvertFromIterableWithWrongTypeToObject(){
converter.convertToResultType(Collections.singletonList("test"), new TypeInformation(Integer.class));
}
@Test (expected = RestResultException.class)
public void testConvertFromIterableWithSameTypeAndMultipleElementsToObject(){
converter.convertToResultType(asList("test", "test2"), new TypeInformation(String.class));
}
@Test
public void testConvertFromEmptyIterableToObject(){
Object result = converter.convertToResultType(Collections.emptyList(), new TypeInformation(String.class));
assertNull(result);
}
@Test
public void testConvertFromMapWithSameTypeAndSingleElementToObject(){
Object result = converter.convertToResultType(Collections.singletonMap("test", 2), new TypeInformation(Integer.class));
assertEquals(Integer.class, result.getClass());
assertEquals(2, result);
}
@Test
public void testConvertFromEmptyMapToObject(){
Object result = converter.convertToResultType(MapUtil.map(), new TypeInformation(String.class));
assertNull(result);
}
@Test (expected = RestResultException.class)
public void testConvertFromMapWithWrongTypeToObject(){
converter.convertToResultType(MapUtil.map("test",2), new TypeInformation(String.class));
}
@Test (expected = RestResultException.class)
public void testConvertFromMapWithSameTypeAndMultipleElementsToObject(){
converter.convertToResultType(MapUtil.map("test","test","test2","test2"), new TypeInformation(String.class));
}
@Test
public void testIterableHasSingleElement(){
assertTrue(converter.iterableHasSingleElement(asList("test")));
assertFalse(converter.iterableHasSingleElement(new ArrayList<Object>()));
assertFalse(converter.iterableHasSingleElement(asList("test", "test2")));
}
}

View File

@@ -0,0 +1,53 @@
package org.neo4j.rest.graphdb;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.neo4j.graphdb.Node;
import org.neo4j.graphdb.Transaction;
import org.neo4j.rest.graphdb.query.RestCypherQueryEngine;
import org.neo4j.rest.graphdb.util.Config;
import org.neo4j.rest.graphdb.util.QueryResult;
import java.util.Map;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.neo4j.helpers.collection.MapUtil.map;
/**
* @author Michael Hunger @since 27.10.13
*/
public class SimpleTransactionTest extends RestTestBase {
@Test
public void testQueryWithinTransaction() throws Exception {
RestGraphDatabase db = (RestGraphDatabase) getRestGraphDb();
RestCypherQueryEngine cypher = new RestCypherQueryEngine(db.getRestAPI());
Transaction tx = db.beginTx();
QueryResult<Map<String,Object>> result = cypher.query("CREATE (person1 { personId: {id}, started: {started} }) return person1",
map("id", 1, "started", System.currentTimeMillis()));
try {
result.to(Node.class).singleOrNull();
} catch(IllegalStateException ise) { assertEquals(true, ise.getMessage().contains("finish the transaction")); }
tx.success();
tx.close();
Node node = result.to(Node.class).singleOrNull();
assertNotNull(node);
assertEquals(1,node.getProperty("personId"));
}
@Override
@Before
public void setUp() throws Exception {
super.setUp();
System.setProperty(Config.CONFIG_BATCH_TRANSACTION,"true");
}
@Override
@After
public void tearDown() throws Exception {
System.clearProperty(Config.CONFIG_BATCH_TRANSACTION);
super.tearDown();
}
}

View File

@@ -0,0 +1,30 @@
/**
* Copyright (c) 2002-2013 "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 <http://www.gnu.org/licenses/>.
*/
package org.neo4j.rest.graphdb;
import org.neo4j.graphdb.RelationshipType;
/**
* @author mh
* @since 24.01.11
*/
enum Type implements RelationshipType {
TEST
}

View File

@@ -0,0 +1,147 @@
/**
* Copyright (c) 2002-2013 "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 <http://www.gnu.org/licenses/>.
*/
package org.neo4j.rest.graphdb;
import static java.util.Arrays.asList;
import static junit.framework.Assert.assertEquals;
import static junit.framework.Assert.assertTrue;
import java.util.Collection;
import java.util.Map;
import org.junit.Test;
import org.neo4j.graphdb.Node;
import org.neo4j.helpers.collection.MapUtil;
import org.neo4j.rest.graphdb.converter.TypeInformation;
/**
* User: KBurchardi
* Date: 18.10.11
* Time: 16:11
*/
public class TypeInformationTest {
@Test
public void testSingleValueNode(){
TypeInformation typeInfo = createTypeInfo("testSingleValueNode");
assertEquals(Node.class, typeInfo.getType());
}
@Test
public void testSingleValueString(){
TypeInformation typeInfo = createTypeInfo("testSingleValueString");
assertEquals(String.class, typeInfo.getType());
}
@Test
public void testIterableObject(){
TypeInformation typeInfo = createTypeInfo("testIterableObject");
assertEquals(Iterable.class, typeInfo.getType());
assertEquals(1, typeInfo.getGenericArguments().length);
assertEquals(Object.class, typeInfo.getGenericArguments()[0]);
}
@Test
public void testIterableNode(){
TypeInformation typeInfo = createTypeInfo("testIterableNode");
assertEquals(Iterable.class, typeInfo.getType());
assertEquals(1, typeInfo.getGenericArguments().length);
assertEquals(Node.class, typeInfo.getGenericArguments()[0]);
}
@Test
public void testCollectionObject(){
TypeInformation typeInfo = createTypeInfo("testCollectionObject");
assertEquals(Collection.class, typeInfo.getType());
assertEquals(1, typeInfo.getGenericArguments().length);
assertEquals(Object.class, typeInfo.getGenericArguments()[0]);
}
@Test
public void testCollectionNode(){
TypeInformation typeInfo = createTypeInfo("testCollectionNode");
assertEquals(Collection.class, typeInfo.getType());
assertEquals(1, typeInfo.getGenericArguments().length);
assertEquals(Node.class, typeInfo.getGenericArguments()[0]);
}
@Test
public void testMapStringObject(){
TypeInformation typeInfo = createTypeInfo("testMapStringObject");
assertEquals(Map.class, typeInfo.getType());
assertEquals(2, typeInfo.getGenericArguments().length);
assertEquals(String.class, typeInfo.getGenericArguments()[0]);
assertEquals(Object.class, typeInfo.getGenericArguments()[1]);
}
@Test
public void testMapIntegerNode(){
TypeInformation typeInfo = createTypeInfo("testMapIntegerNode");
assertEquals(Map.class, typeInfo.getType());
assertEquals(2, typeInfo.getGenericArguments().length);
assertEquals(Integer.class, typeInfo.getGenericArguments()[0]);
assertEquals(Node.class, typeInfo.getGenericArguments()[1]);
}
@Test
public void testTypeInformationBasicMethods(){
TypeInformation typeInfoBasic = createTypeInfo("testSingleValueNode");
assertTrue(typeInfoBasic.isInstance("test", String.class));
assertTrue(typeInfoBasic.isSingleType());
assertTrue(typeInfoBasic.isGraphEntity(Node.class));
TypeInformation typeInfoCollection = createTypeInfo("testCollectionObject");
assertTrue(typeInfoCollection.isCollectionType());
assertTrue(typeInfoCollection.isCollection());
TypeInformation typeInfoMap = createTypeInfo("testMapStringObject");
assertTrue(typeInfoMap.isCollectionType());
assertTrue(typeInfoMap.isMap());
}
@Test
public void testCreateTypeInformationByIterable(){
TypeInformation typeInfoIterable = new TypeInformation(asList("test","test2"));
assertTrue(typeInfoIterable.isCollectionType());
assertTrue(typeInfoIterable.isCollection());
assertEquals(1, typeInfoIterable.getGenericArguments().length);
assertEquals(String.class, typeInfoIterable.getGenericArguments()[0]);
}
@Test
public void testCreateTypeInformationByMap(){
TypeInformation typeInfoMap = new TypeInformation(MapUtil.map("test",1));
assertTrue(typeInfoMap.isCollectionType());
assertTrue(typeInfoMap.isMap());
assertEquals(2, typeInfoMap.getGenericArguments().length);
assertEquals(String.class, typeInfoMap.getGenericArguments()[0]);
assertEquals(Integer.class, typeInfoMap.getGenericArguments()[1]);
}
public TypeInformation createTypeInfo(String methodName){
try {
return new TypeInformation(TypeInformationTestInterface.class.getMethod(methodName).getGenericReturnType());
} catch (NoSuchMethodException e) {
return null;
}
}
}

View File

@@ -0,0 +1,42 @@
/**
* Copyright (c) 2002-2013 "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 <http://www.gnu.org/licenses/>.
*/
package org.neo4j.rest.graphdb;
import java.util.Collection;
import java.util.Map;
import org.neo4j.graphdb.Node;
/**
* User: KBurchardi
* Date: 18.10.11
* Time: 16:09
*/
public interface TypeInformationTestInterface {
Node testSingleValueNode();
String testSingleValueString();
Iterable<Object> testIterableObject();
Iterable<Node> testIterableNode();
Collection<Object> testCollectionObject();
Collection<Node> testCollectionNode();
Map<String,Object> testMapStringObject();
Map<Integer, Node> testMapIntegerNode();
}

View File

@@ -0,0 +1,60 @@
/**
* Copyright (c) 2002-2013 "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 <http://www.gnu.org/licenses/>.
*/
package org.neo4j.rest.graphdb.extension;
import javax.ws.rs.*;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.Response.Status;
@Path("/helloworld")
public class HelloWorldResource {
@GET
@Path("/{nodeId}")
@Consumes(MediaType.APPLICATION_JSON)
public Response get(@PathParam("nodeId") long nodeId) {
return Response.status(Status.OK).entity(("\"get " + nodeId + "\"").getBytes()).build();
}
@PUT
@Path("/{nodeId}")
@Consumes(MediaType.APPLICATION_JSON)
public Response put(@PathParam("nodeId") long nodeId, String body) {
return Response.status(Status.OK).entity(("\"put " + nodeId +":"+body +"\"").getBytes()).build();
}
@POST
@Path("/{nodeId}")
@Consumes(MediaType.APPLICATION_JSON)
public Response post(@PathParam("nodeId") long nodeId, String body) {
return Response.status(Status.OK).entity(("\"post " + nodeId +":"+body + "\"").getBytes()).build();
}
@POST
@Path("/empty/{nodeId}")
@Consumes(MediaType.APPLICATION_JSON)
public Response postWithoutResult(@PathParam("nodeId") long nodeId) {
return Response.status(Status.NO_CONTENT).build();
}
@DELETE
@Path("/{nodeId}")
@Consumes(MediaType.APPLICATION_JSON)
public Response delete(@PathParam("nodeId") long nodeId) {
return Response.status(Status.OK).entity(("\"delete " + nodeId + "\"").getBytes()).build();
}
}

View File

@@ -0,0 +1,81 @@
package org.neo4j.rest.graphdb.query;
import org.junit.Test;
import org.neo4j.graphdb.Node;
import org.neo4j.graphdb.NotFoundException;
import org.neo4j.rest.graphdb.RestAPIImpl;
import org.neo4j.rest.graphdb.RestAPIInternal;
import org.neo4j.rest.graphdb.RestTestBase;
import org.neo4j.rest.graphdb.entity.RestEntity;
import java.util.*;
import static java.util.Arrays.asList;
import static org.junit.Assert.assertEquals;
public class CypherTransactionTest extends RestTestBase {
@Test
public void testSingleSend() throws Exception {
CypherTransaction transaction = new CypherTransaction(SERVER_ROOT_URI, CypherTransaction.ResultType.row);
CypherTransaction.Result result = transaction.send("RETURN 42", null);
assertEquals(asList("42"), result.getColumns());
Iterator<List<Object>> rows = result.getRows().iterator();
assertEquals(true,rows.hasNext());
assertEquals(Arrays.<Object>asList(42), rows.next());
assertEquals(false,rows.hasNext());
assertEquals("RETURN 42", result.getStatement().getStatement());
assertEquals(Collections.<String,Object>emptyMap(), result.getStatement().getParameters());
}
@Test
public void testGraphResult() throws Exception {
CypherTransaction transaction = new CypherTransaction(SERVER_ROOT_URI, CypherTransaction.ResultType.graph);
transaction.add("CREATE (n:Person {name:'Graph'}) RETURN n", null);
List<CypherTransaction.Result> commit = transaction.commit();
assertEquals(1, commit.size());
CypherTransaction.Result result = commit.get(0);
Map nodeMap = (Map) result.getRows().iterator().next().get(0);
assertEquals("Graph", ((Map)nodeMap.get("properties")).get("name"));
assertEquals(asList("Person"), nodeMap.get("labels"));
assertEquals(true, nodeMap.containsKey("id"));
Node node = getRestGraphDb().getNodeById(Long.parseLong(nodeMap.get("id").toString()));
assertEquals("Graph",node.getProperty("name"));
}
@Test
public void testRestResult() throws Exception {
CypherTransaction transaction = new CypherTransaction(SERVER_ROOT_URI, CypherTransaction.ResultType.rest);
transaction.add("CREATE (n:Person {name:'Rest'}) RETURN n", null);
List<CypherTransaction.Result> commit = transaction.commit();
assertEquals(1, commit.size());
CypherTransaction.Result result = commit.get(0);
Map nodeMap = (Map) result.getRows().iterator().next().get(0);
assertEquals("Rest", ((Map)nodeMap.get("data")).get("name"));
assertEquals(true, nodeMap.containsKey("self"));
Node node = getRestGraphDb().getNodeById(RestEntity.getEntityId(nodeMap.get("self").toString()));
assertEquals("Rest",node.getProperty("name"));
}
@Test
public void testCommit() throws Exception {
CypherTransaction transaction = new CypherTransaction(SERVER_ROOT_URI, CypherTransaction.ResultType.row);
CypherTransaction.Result result = transaction.send("CREATE (n {name:'John'}) RETURN id(n)", null);
List<CypherTransaction.Result> commit = transaction.commit();
assertEquals(0, commit.size());
Node node = getRestGraphDb().getNodeById(((Number) result.getRows().iterator().next().get(0)).longValue());
assertEquals("John",node.getProperty("name"));
}
@Test(expected = NotFoundException.class)
public void testRollback() throws Exception {
CypherTransaction transaction = new CypherTransaction(SERVER_ROOT_URI, CypherTransaction.ResultType.row);
CypherTransaction.Result result = transaction.send("CREATE (n {name:'John'}) RETURN id(n)", null);
transaction.rollback();
RestAPIImpl api = new RestAPIImpl(SERVER_ROOT_URI);
api.getNodeById(((Number) result.getRows().iterator().next().get(0)).longValue(), RestAPIInternal.Load.ForceFromServer);
}
}

Some files were not shown because too many files have changed in this diff Show More