DATAGRAPH-253 Better abstraction layer for interfacing with Remote Neo4j
This commit is contained in:
@@ -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>
|
||||
|
||||
@@ -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() {
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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() {
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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) { }
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
*/
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
@@ -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 );
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -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() { }
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
@@ -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 );
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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" );
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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" );
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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());
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -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 );
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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()));
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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" ) );
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
@@ -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));
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -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()));
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -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());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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 {
|
||||
|
||||
}
|
||||
}
|
||||
@@ -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")));
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -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
Reference in New Issue
Block a user