org.neo4j.rest.graphdb.RestAPIImpl Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of spring-data-neo4j-rest Show documentation
Show all versions of spring-data-neo4j-rest Show documentation
pring Data Neo4j Wrapper for the Neo4j REST API, provides a Graph Database proxy for the remote invocation.
/**
* 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 .
*/
package org.neo4j.rest.graphdb;
import org.neo4j.graphdb.*;
import org.neo4j.graphdb.index.IndexHits;
import org.neo4j.helpers.collection.IterableWrapper;
import org.neo4j.helpers.collection.MapUtil;
import org.neo4j.index.lucene.ValueContext;
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.RestTraversalDescription;
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 java.lang.String.format;
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);
private RestIndexManager restIndexManager = new RestIndexManager(this);
private Map indexInfos = new HashMap<>();
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 restIndexManager;
}
@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);
}
RestNode node;
Map data = (Map) response.toMap();
if (response.isMap() && data.containsKey("metadata")) {
node = new RestNode(data, this);
} else {
Collection labels = getNodeLabels(id);
node = new RestNode(id, labels, data, this);
}
return entityCache.addToCache(node);
}
@Override
public RestRelationship getRelationshipById(long id, Load force) {
if (force != Load.ForceFromServer) {
RestRelationship restRel = entityCache.getRelationship(id);
if (restRel != null) return restRel;
}
if (force == Load.FromCache) return new RestRelationship(RestRelationship.relUri(this, id),this);
// BatchRestAPI batchRestAPI = new BatchRestAPI(this);
// RestNode node = batchRestAPI.getNodeById(id);
RequestResult response = restRequest.get("relationship/" + id);
if (response.statusIs(Status.NOT_FOUND)) {
throw new NotFoundException("" + id);
}
Map data = (Map) response.toMap();
RestRelationship rel = new RestRelationship(data, this);
return entityCache.addToCache(rel);
}
@Override
public RestNode addToCache(RestNode restNode) {
return entityCache.addToCache(restNode);
}
@Override
public RestRelationship addToCache(RestRelationship rel) {
return entityCache.addToCache(rel);
}
@Override
public RestNode getNodeFromCache(long id) {
return entityCache.getNode(id);
}
@Override
public RestRelationship getRelFromCache(long id) {
return entityCache.getRelationship(id);
}
@Override
public void removeNodeFromCache(long id) {
entityCache.removeNode(id);
}
@Override
public void removeRelFromCache(long id) {
entityCache.removeRelationship(id);
}
@Override
public RestNode getNodeById(long id) {
return getNodeById(id, Load.FromServer);
}
@Override
public RestRelationship getRelationshipById(long id) {
return getRelationshipById(id, Load.FromServer);
}
@Override
public RestNode createNode(Map props) {
return createNode(props,Collections.emptyList());
}
@Override
public RestNode createNode(Map props, Collection 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 index, String key, Object value, final Map properties, Collection labels) {
if (index==null || key == null || value==null) throw new IllegalArgumentException("Unique index "+index+" key "+key+" value must not be null");
final Map 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 props) {
// final RestRequest restRequest = ((RestNode) startNode).getRestRequest();
final RestNode end = (RestNode) endNode;
Map 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 RestIndex getIndex(String indexName) {
final RestIndexManager index = this.index();
if (index.existsForNodes(indexName)) return (RestIndex) index.forNodes(indexName);
if (index.existsForRelationships(indexName)) return (RestIndex) index.forRelationships(indexName);
throw new IllegalArgumentException("Index " + indexName + " does not yet exist");
}
@Override
@SuppressWarnings("unchecked")
public void createIndex(String type, String indexName, Map config) {
Map data=new HashMap();
data.put("name",indexName);
data.put("config",config);
restRequest.post("index/" + type, data);
IndexInfo indexInfo = indexInfos.get(type);
if (indexInfo!=null) indexInfo.setExpired();
}
public void resetIndex(Class type) {
if (Node.class.isAssignableFrom(type)) {
indexInfo(RestIndexManager.NODE).setExpired();
}
if (Relationship.class.isAssignableFrom(type)) {
indexInfo(RestIndexManager.RELATIONSHIP).setExpired();
}
}
@Override
@SuppressWarnings("unchecked")
public RestIndex createIndex(Class type, String indexName, Map config) {
resetIndex(type);
if (Node.class.isAssignableFrom(type)) {
return (RestIndex) index().forNodes( indexName, config);
}
if (Relationship.class.isAssignableFrom(type)) {
return (RestIndex) index().forRelationships(indexName, config);
}
throw new IllegalArgumentException("Required Node or Relationship types to create index, got " + type);
}
@Override
public void close() {
ExecutingRestRequest.shutdown();
}
// TODO
@Override
public Relationship getOrCreateRelationship(Node start, Node end, RelationshipType type, Direction direction, Map props) {
final Iterable existingRelationships = start.getRelationships(type, direction);
for (final Relationship existingRelationship : existingRelationships) {
if (existingRelationship != null && existingRelationship.getOtherNode(start).equals(end))
return existingRelationship;
}
if (direction == Direction.INCOMING) {
return createRelationship(end, start, type, props);
} else {
return createRelationship(start,end,type,props);
}
}
protected Collection removeMissingRelationships(Node node, Collection targetNodes,
RelationshipType type, Direction direction, Label targetLabel) {
targetNodes = new ArrayList<>(targetNodes);
for (Relationship relationship : node.getRelationships( type, direction ) ) {
Node otherNode = relationship.getOtherNode(node);
if ( !targetNodes.remove(otherNode) ) {
if (targetLabel != null && !relationship.getOtherNode(node).hasLabel(targetLabel)) continue;
relationship.delete();
}
}
return targetNodes;
}
@Override
public Iterable updateRelationships(final Node start, Collection endNodes, final RelationshipType type, final Direction direction, String targetLabelName) {
Label targetLabel = targetLabelName==null ? null : DynamicLabel.label(targetLabelName);
Collection remainingEndNodes = removeMissingRelationships(start, endNodes, type, direction, targetLabel);
List result=new ArrayList<>(remainingEndNodes.size());
for (Node endNode : remainingEndNodes) {
result.add(getOrCreateRelationship(start, endNode,type,direction,null));
}
return result;
}
@Override
public RestNode merge(String labelName, String key, Object value, final Map nodeProperties, Collection 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 params = map("props", props, "value", value);
Iterator> 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 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 getAutoIndexedProperties(Class forClass) {
RequestResult response = getRestRequest().get(buildPathAutoIndexerProperties(forClass).toString());
Collection autoIndexedProperties = (Collection) JsonHelper.readJson(response.getText());
return new HashSet(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 getNodeLabels(long id) {
RequestResult response = restRequest.get(RestNode.nodeUri(this,id)+"/labels");
if (response.statusOtherThan(Status.OK)) {
throw new IllegalStateException("received " + response);
}
return (Collection) response.toEntity();
}
@Override
public Collection getAllLabelNames() {
RequestResult response = restRequest.get("labels");
if (response.statusOtherThan(Status.OK)) {
throw new IllegalStateException("received " + response);
}
return (Collection) response.toEntity();
}
@Override
public Iterable getNodesByLabel(String label) {
RequestResult response = getRestRequest().get("label/" + encode(label) + "/nodes");
if (response.statusOtherThan(Status.OK)) {
throw new IllegalStateException("received " + response);
}
return (Iterable) getEntityExtractor().convertFromRepresentation(response);
}
private RestNode toNode(List
© 2015 - 2025 Weber Informatics LLC | Privacy Policy