DATAGRAPH-699 - Implement missing operations for Cypher based REST-API separately.
This commit is contained in:
@@ -0,0 +1,62 @@
|
||||
package org.springframework.data.neo4j.aspects;
|
||||
|
||||
import org.springframework.data.neo4j.annotation.*;
|
||||
import org.springframework.data.neo4j.support.index.IndexType;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* @author mh
|
||||
* @since 30.08.15
|
||||
*/
|
||||
@RelationshipEntity(type="LEAD")
|
||||
public class LeadRelationship {
|
||||
|
||||
@GraphId
|
||||
private Long id;
|
||||
|
||||
@Indexed(unique = true)
|
||||
private Long uuid;
|
||||
|
||||
@Indexed(indexType = IndexType.SIMPLE, indexName = "date-index")
|
||||
private Date createdDate = new Date();
|
||||
|
||||
@StartNode
|
||||
private Person person;
|
||||
@EndNode
|
||||
private Group group;
|
||||
|
||||
public LeadRelationship() {
|
||||
}
|
||||
|
||||
public LeadRelationship(Person person, Group group) {
|
||||
this.person = person;
|
||||
this.group = group;
|
||||
this.uuid = Math.abs(UUID.randomUUID().getMostSignificantBits());
|
||||
}
|
||||
|
||||
public Long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public Long getUuid() {
|
||||
return uuid;
|
||||
}
|
||||
|
||||
public Date getCreatedDate() {
|
||||
return createdDate;
|
||||
}
|
||||
|
||||
public Person getPerson() {
|
||||
return person;
|
||||
}
|
||||
|
||||
public Group getGroup() {
|
||||
return group;
|
||||
}
|
||||
|
||||
public void setCreatedDate(Date createdDate) {
|
||||
this.createdDate = createdDate;
|
||||
}
|
||||
}
|
||||
@@ -121,6 +121,7 @@
|
||||
<value>org.springframework.data.neo4j.aspects.Developer</value>
|
||||
<value>org.springframework.data.neo4j.aspects.Person</value>
|
||||
<value>org.springframework.data.neo4j.aspects.Group</value>
|
||||
<value>org.springframework.data.neo4j.aspects.LeadRelationship</value>
|
||||
<value>org.springframework.data.neo4j.aspects.SubGroup</value>
|
||||
<value>org.springframework.data.neo4j.aspects.Toyota</value>
|
||||
<value>org.springframework.data.neo4j.aspects.Volvo</value>
|
||||
|
||||
@@ -27,23 +27,30 @@ 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.converter.RestIndexHitsConverter;
|
||||
import org.neo4j.rest.graphdb.entity.RestEntity;
|
||||
import org.neo4j.rest.graphdb.entity.RestEntityCache;
|
||||
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.SimpleIndexHits;
|
||||
import org.neo4j.rest.graphdb.query.*;
|
||||
import org.neo4j.rest.graphdb.transaction.TransactionFinishListener;
|
||||
import org.neo4j.rest.graphdb.traversal.RestTraversalDescription;
|
||||
import org.neo4j.rest.graphdb.traversal.RestTraverser;
|
||||
import org.neo4j.rest.graphdb.util.QueryResult;
|
||||
import org.neo4j.rest.graphdb.util.QueryResultBuilder;
|
||||
import org.neo4j.rest.graphdb.util.ResultConverter;
|
||||
import org.springframework.dao.DataIntegrityViolationException;
|
||||
|
||||
import javax.ws.rs.core.Response;
|
||||
import java.util.*;
|
||||
|
||||
import static java.util.Arrays.asList;
|
||||
import static org.neo4j.helpers.collection.MapUtil.map;
|
||||
import static org.neo4j.rest.graphdb.RestAPIIndexImpl.queryPath;
|
||||
import static org.neo4j.rest.graphdb.query.CypherTransaction.ResultType.row;
|
||||
import static org.neo4j.rest.graphdb.query.CypherTransaction.Statement;
|
||||
|
||||
@@ -64,8 +71,9 @@ public class RestAPICypherImpl implements RestAPI {
|
||||
|
||||
public static final String GET_REL_TYPES_QUERY = _MATCH_NODE_QUERY + " MATCH (n)-[r]-() RETURN distinct type(r) as relType";
|
||||
|
||||
private RestIndexManager restIndex = new RestIndexManager(this);
|
||||
private RestIndexManager restIndexOld;
|
||||
private RestAPIIndex restAPIIndex;
|
||||
private final RestEntityCache entityCache = new RestEntityCache(this);
|
||||
private RestEntityExtractor restEntityExtractor = new RestEntityExtractor(this);
|
||||
|
||||
private String createNodeQuery(Collection<String> labels) {
|
||||
String labelString = toLabelString(labels);
|
||||
@@ -98,7 +106,7 @@ public class RestAPICypherImpl implements RestAPI {
|
||||
|
||||
protected RestAPICypherImpl(RestAPI restAPI) {
|
||||
this.restAPI = restAPI;
|
||||
restIndexOld = new RestIndexManager(restAPI);
|
||||
restAPIIndex = new RestAPIIndexImpl(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -117,8 +125,41 @@ public class RestAPICypherImpl implements RestAPI {
|
||||
}
|
||||
|
||||
@Override
|
||||
public RestRelationship addToCache(RestRelationship restRelationship) {
|
||||
return restAPI.addToCache(restRelationship);
|
||||
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
|
||||
@@ -145,33 +186,6 @@ public class RestAPICypherImpl implements RestAPI {
|
||||
}
|
||||
}
|
||||
|
||||
public RestNode getNodeFromCache(long id) {
|
||||
return restAPI.getNodeFromCache(id);
|
||||
}
|
||||
public RestRelationship getRelFromCache(long id) {
|
||||
return restAPI.getRelFromCache(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeNodeFromCache(long id) {
|
||||
restAPI.removeNodeFromCache(id);
|
||||
}
|
||||
@Override
|
||||
public void removeRelFromCache(long id) {
|
||||
restAPI.removeRelFromCache(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public RestNode getNodeById(long id) {
|
||||
return getNodeById(id, Load.FromServer);
|
||||
}
|
||||
|
||||
@Override
|
||||
public RestRelationship getRelationshipById(long id) {
|
||||
return getRelationshipById(id, Load.FromServer);
|
||||
}
|
||||
|
||||
|
||||
private RestNode toNode(List<Object> row) {
|
||||
long id = ((Number) row.get(0)).longValue();
|
||||
List<String> labels = (List<String>) row.get(1);
|
||||
@@ -216,10 +230,6 @@ public class RestAPICypherImpl implements RestAPI {
|
||||
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;
|
||||
@@ -315,6 +325,7 @@ public class RestAPICypherImpl implements RestAPI {
|
||||
|
||||
@Override
|
||||
public void addLabels(RestNode node, Collection<String> labels) {
|
||||
if (labels==null || labels.isEmpty()) return;
|
||||
String statement = _MATCH_NODE_QUERY + " SET n" + toLabelString(labels) + _QUERY_RETURN_NODE;
|
||||
CypherTransaction.Result result = runQuery(statement, map("id", node.getId()));
|
||||
|
||||
@@ -336,9 +347,31 @@ public class RestAPICypherImpl implements RestAPI {
|
||||
return txManager;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public <S extends PropertyContainer> IndexHits<S> getIndexQuery(Class<S> entityType, String indexName, String key, Object value) {
|
||||
String indexPath = RestAPIIndexImpl.indexPath(entityType, indexName, key, value);
|
||||
RequestResult response = getRestRequest().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);
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public <S extends PropertyContainer> IndexHits<S> queryIndexQuery(Class<S> entityType, String indexName, String key, Object value) {
|
||||
String indexPath = queryPath(entityType, indexName, key, value);
|
||||
RequestResult response = getRestRequest().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 <S extends PropertyContainer> IndexHits<S> getIndex(Class<S> entityType, String indexName, String key, Object value) {
|
||||
if (value instanceof Query) return restAPI.getIndex(entityType, indexName, key, value);
|
||||
if (value instanceof Query) return getIndexQuery(entityType, indexName, key, value);
|
||||
String index = key == null ? ":`" + indexName + "`({query})" : ":`" + indexName + "`(`" + key + "`={query})";
|
||||
if (Node.class.isAssignableFrom(entityType)) {
|
||||
String statement = "start n=node" + index + _QUERY_RETURN_NODE;
|
||||
@@ -355,7 +388,7 @@ public class RestAPICypherImpl implements RestAPI {
|
||||
|
||||
@Override
|
||||
public <S extends PropertyContainer> IndexHits<S> queryIndex(Class<S> entityType, String indexName, String key, Object value) {
|
||||
if (value instanceof Query) return restAPI.queryIndex(entityType,indexName,key,value);
|
||||
if (value instanceof Query) return queryIndexQuery(entityType, indexName, key, value);
|
||||
String index = ":`" + indexName + "`({query})";
|
||||
if (key != null && !key.isEmpty() && !value.toString().contains(":")) value = key + ":"+value;
|
||||
if (Node.class.isAssignableFrom(entityType)) {
|
||||
@@ -395,7 +428,7 @@ public class RestAPICypherImpl implements RestAPI {
|
||||
|
||||
@Override
|
||||
public RestIndexManager index() {
|
||||
return restIndex;
|
||||
return restAPIIndex.index();
|
||||
}
|
||||
|
||||
|
||||
@@ -403,10 +436,10 @@ public class RestAPICypherImpl implements RestAPI {
|
||||
public void deleteEntity(RestEntity entity) {
|
||||
if (entity instanceof Node) {
|
||||
runQuery(_MATCH_NODE_QUERY + " DELETE n", map("id", entity.getId()));
|
||||
restAPI.removeNodeFromCache(entity.getId());
|
||||
removeNodeFromCache(entity.getId());
|
||||
} else if (entity instanceof Relationship) {
|
||||
runQuery(_MATCH_REL_QUERY + " DELETE r", map("id", entity.getId()));
|
||||
restAPI.removeRelFromCache(entity.getId());
|
||||
removeRelFromCache(entity.getId());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -441,13 +474,13 @@ public class RestAPICypherImpl implements RestAPI {
|
||||
// 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);
|
||||
return restAPIIndex.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);
|
||||
return restAPIIndex.getOrCreateRelationship(index, key, value, start, end, type, properties);
|
||||
}
|
||||
|
||||
public CypherResult query(String statement, Map<String, Object> params) {
|
||||
@@ -530,9 +563,15 @@ public class RestAPICypherImpl implements RestAPI {
|
||||
}
|
||||
}
|
||||
|
||||
private static final String FULLPATH = "fullpath";
|
||||
|
||||
@Override
|
||||
public RestTraverser traverse(RestNode restNode, Map<String, Object> description) {
|
||||
return restAPI.traverse(restNode, 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 RequestResult batch(Collection<Map<String, Object>> batchRequestData) {
|
||||
@@ -551,25 +590,13 @@ public class RestAPICypherImpl implements RestAPI {
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public void createIndex(String type, String indexName, Map<String, String> config) {
|
||||
restAPI.createIndex(type, indexName, config);
|
||||
restAPIIndex.createIndex(type, indexName, config);
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public <T extends PropertyContainer> RestIndex<T> createIndex(Class<T> type, String indexName, Map<String, String> config) {
|
||||
resetIndex(type);
|
||||
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 resetIndex(Class type) {
|
||||
restAPI.resetIndex(type);
|
||||
return restAPIIndex.createIndex(type,indexName,config);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -634,59 +661,92 @@ public class RestAPICypherImpl implements RestAPI {
|
||||
|
||||
@Override
|
||||
public boolean isAutoIndexingEnabled(Class<? extends PropertyContainer> clazz) {
|
||||
return restAPI.isAutoIndexingEnabled(clazz);
|
||||
return restAPIIndex.isAutoIndexingEnabled(clazz);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setAutoIndexingEnabled(Class<? extends PropertyContainer> clazz, boolean enabled) {
|
||||
restAPI.setAutoIndexingEnabled(clazz, enabled);
|
||||
restAPIIndex.setAutoIndexingEnabled(clazz, enabled);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<String> getAutoIndexedProperties(Class forClass) {
|
||||
return restAPI.getAutoIndexedProperties(forClass);
|
||||
return restAPIIndex.getAutoIndexedProperties(forClass);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void startAutoIndexingProperty(Class forClass, String s) {
|
||||
restAPI.startAutoIndexingProperty(forClass, s);
|
||||
restAPIIndex.startAutoIndexingProperty(forClass, s);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stopAutoIndexingProperty(Class forClass, String s) {
|
||||
restAPI.stopAutoIndexingProperty(forClass, s);
|
||||
restAPIIndex.stopAutoIndexingProperty(forClass, s);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void delete(RestIndex index) {
|
||||
restAPI.delete(index);
|
||||
restAPIIndex.delete(index);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T extends PropertyContainer> void removeFromIndex(RestIndex index, T entity, String key, Object value) {
|
||||
restAPI.removeFromIndex(index, entity, key, value);
|
||||
restAPIIndex.removeFromIndex(index, entity, key, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T extends PropertyContainer> void removeFromIndex(RestIndex index, T entity, String key) {
|
||||
restAPI.removeFromIndex(index, entity, key);
|
||||
restAPIIndex.removeFromIndex(index, entity, key);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T extends PropertyContainer> void removeFromIndex(RestIndex index, T entity) {
|
||||
restAPI.removeFromIndex(index, entity);
|
||||
restAPIIndex.removeFromIndex(index, entity);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public <T extends PropertyContainer> void addToIndex(T entity, RestIndex index, String key, Object value) {
|
||||
restAPI.addToIndex(entity, index, key, value);
|
||||
public <T extends PropertyContainer> void addToIndex(final T entity, final RestIndex index, final String key, final Object value) {
|
||||
if (!getTxManager().isActive()) {
|
||||
restAPIIndex.addToIndex(entity, index, key, value);
|
||||
return;
|
||||
}
|
||||
getTxManager().getRemoteCypherTransaction().registerListener(new TransactionFinishListener() {
|
||||
@Override
|
||||
public void comitted() {
|
||||
restAPIIndex.addToIndex(entity, index, key, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void rolledBack() {
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public <T extends PropertyContainer> T putIfAbsent(T entity, RestIndex index, String key, Object value) {
|
||||
return restAPI.putIfAbsent(entity, index, key, value);
|
||||
public <T extends PropertyContainer> T putIfAbsent(final T entity, final RestIndex index, final String key, final Object value) {
|
||||
if (!getTxManager().isActive()) {
|
||||
return restAPIIndex.putIfAbsent(entity, index, key, value);
|
||||
}
|
||||
getTxManager().getRemoteCypherTransaction().registerListener(new TransactionFinishListener() {
|
||||
@Override
|
||||
public void comitted() {
|
||||
T result = restAPIIndex.putIfAbsent(entity, index, key, value);
|
||||
if (result == null || result.equals(entity)) return;
|
||||
throw new DataIntegrityViolationException("Unique property "+key+" was to be set to duplicate value "+value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void rolledBack() {
|
||||
}
|
||||
});
|
||||
return entity;
|
||||
}
|
||||
|
||||
@Override
|
||||
public IndexInfo indexInfo(final String indexType) {
|
||||
return restAPIIndex.indexInfo(indexType);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -694,10 +754,6 @@ public class RestAPICypherImpl implements RestAPI {
|
||||
return restAPI.hasToUpdate(lastUpdate);
|
||||
}
|
||||
|
||||
@Override
|
||||
public IndexInfo indexInfo(final String indexType) {
|
||||
return restAPI.indexInfo(indexType);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
@@ -721,14 +777,37 @@ public class RestAPICypherImpl implements RestAPI {
|
||||
|
||||
@Override
|
||||
public RestEntityExtractor getEntityExtractor() {
|
||||
return restAPI.getEntityExtractor();
|
||||
return restEntityExtractor;
|
||||
}
|
||||
|
||||
@Override
|
||||
public RestEntity createRestEntity(Map data) {
|
||||
return restAPI.createRestEntity(data);
|
||||
if (data.containsKey("id") && data.containsKey("properties")) {
|
||||
long id = asLong(data, "id");
|
||||
Map props = (Map) data.get("properties");
|
||||
if (data.containsKey("type")) {
|
||||
return RestRelationship.fromCypher(id,(String)data.get("type"),props,asLong(data, "startNode"),asLong(data, "endNode"),this);
|
||||
}
|
||||
if (data.containsKey("labels")) {
|
||||
List<String> labels = (List<String>) data.get("labels");
|
||||
return entityCache.addToCache(RestNode.fromCypher(id,labels,props, this));
|
||||
}
|
||||
}
|
||||
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;
|
||||
}
|
||||
|
||||
protected long asLong(Map data, String idKey) {
|
||||
Object idValue = data.get(idKey);
|
||||
return idValue instanceof Number ? ((Number)idValue).longValue() : Long.parseLong(idValue.toString());
|
||||
}
|
||||
|
||||
public Iterable<Node> getAllNodes() {
|
||||
String statement = "MATCH (n) " + _QUERY_RETURN_NODE;
|
||||
|
||||
@@ -23,20 +23,16 @@ 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.RestEntityCache;
|
||||
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.CypherRestResult;
|
||||
import org.neo4j.rest.graphdb.query.CypherResult;
|
||||
import org.neo4j.rest.graphdb.query.RestQueryResult;
|
||||
import org.neo4j.rest.graphdb.transaction.NullTransaction;
|
||||
@@ -44,19 +40,14 @@ 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;
|
||||
@@ -72,15 +63,16 @@ public class RestAPIImpl implements RestAPI {
|
||||
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<String,IndexInfo> indexInfos = new HashMap<>();
|
||||
|
||||
private final RestAPIIndexImpl restIndexAPI;
|
||||
public RestAPIImpl(String uri) {
|
||||
this.restRequest = createRestRequest(uri, null, null);
|
||||
restIndexAPI = new RestAPIIndexImpl(this);
|
||||
}
|
||||
|
||||
public RestAPIImpl(String uri, String user, String password) {
|
||||
this.restRequest = createRestRequest(uri, user, password);
|
||||
restIndexAPI = new RestAPIIndexImpl(this);
|
||||
}
|
||||
|
||||
protected RestRequest createRestRequest(String uri, String user, String password) {
|
||||
@@ -89,7 +81,7 @@ public class RestAPIImpl implements RestAPI {
|
||||
|
||||
@Override
|
||||
public RestIndexManager index() {
|
||||
return restIndexManager;
|
||||
return restIndexAPI.index();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -191,20 +183,6 @@ public class RestAPIImpl implements RestAPI {
|
||||
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();
|
||||
@@ -215,7 +193,7 @@ public class RestAPIImpl implements RestAPI {
|
||||
}
|
||||
|
||||
|
||||
private RestNode createRestNode(RequestResult result) {
|
||||
public RestNode createRestNode(RequestResult result) {
|
||||
if (result.statusIs(Status.NOT_FOUND)) {
|
||||
throw new NotFoundException("Node not found");
|
||||
}
|
||||
@@ -229,6 +207,16 @@ public class RestAPIImpl implements RestAPI {
|
||||
return entityCache.addToCache(node);
|
||||
}
|
||||
|
||||
public 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
|
||||
public RestRelationship createRelationship(Node startNode, Node endNode, RelationshipType type, Map<String, Object> props) {
|
||||
// final RestRequest restRequest = ((RestNode) startNode).getRestRequest();
|
||||
@@ -242,58 +230,6 @@ public class RestAPIImpl implements RestAPI {
|
||||
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);
|
||||
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 <T extends PropertyContainer> RestIndex<T> createIndex(Class<T> type, String indexName, Map<String, String> config) {
|
||||
resetIndex(type);
|
||||
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();
|
||||
@@ -360,56 +296,6 @@ public class RestAPIImpl implements RestAPI {
|
||||
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));
|
||||
@@ -513,23 +399,6 @@ public class RestAPIImpl implements RestAPI {
|
||||
}
|
||||
}
|
||||
|
||||
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 RestTraversalDescription createTraversalDescription() {
|
||||
@@ -564,63 +433,12 @@ public class RestAPIImpl implements RestAPI {
|
||||
}
|
||||
|
||||
|
||||
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.removeNode(entity.getId());
|
||||
}
|
||||
@Override
|
||||
public IndexInfo indexInfo(final String indexType) {
|
||||
IndexInfo indexInfo = indexInfos.get(indexType);
|
||||
if (indexInfo != null && !indexInfo.isExpired()) {
|
||||
return indexInfo;
|
||||
}
|
||||
RequestResult response = restRequest.get("index/" + encode(indexType));
|
||||
indexInfo = new RetrievedIndexInfo(response);
|
||||
indexInfos.put(indexType,indexInfo);
|
||||
return indexInfo;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void setPropertyOnEntity(RestEntity entity, String key, Object value) {
|
||||
RequestResult result = getRestRequest().with(entity.getUri()).put("properties/" + encode(key), value);
|
||||
@@ -649,81 +467,6 @@ public class RestAPIImpl implements RestAPI {
|
||||
return properties;
|
||||
}
|
||||
|
||||
private void deleteIndex(String indexPath) {
|
||||
getRestRequest().delete(indexPath);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void delete(RestIndex index) {
|
||||
deleteIndex(indexPath(index, null, null));
|
||||
resetIndex(index.getEntityType());
|
||||
}
|
||||
|
||||
@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));
|
||||
resetIndex(index.getEntityType());
|
||||
}
|
||||
|
||||
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));
|
||||
resetIndex(index.getEntityType());
|
||||
}
|
||||
|
||||
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));
|
||||
resetIndex(index.getEntityType());
|
||||
}
|
||||
|
||||
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());
|
||||
@@ -738,18 +481,6 @@ public class RestAPIImpl implements RestAPI {
|
||||
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));
|
||||
@@ -822,6 +553,11 @@ public class RestAPIImpl implements RestAPI {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public RestRequest getRestRequest() {
|
||||
return restRequest;
|
||||
}
|
||||
|
||||
protected long asLong(Map data, String idKey) {
|
||||
Object idValue = data.get(idKey);
|
||||
return idValue instanceof Number ? ((Number)idValue).longValue() : Long.parseLong(idValue.toString());
|
||||
@@ -830,4 +566,100 @@ public class RestAPIImpl implements RestAPI {
|
||||
public RequestResult batch(Collection<Map<String, Object>> batchRequestData) {
|
||||
return restRequest.post("batch",batchRequestData);
|
||||
}
|
||||
|
||||
@Override
|
||||
public IndexInfo indexInfo(String indexType) {
|
||||
return restIndexAPI.indexInfo(indexType);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stopAutoIndexingProperty(Class forClass, String s) {
|
||||
restIndexAPI.stopAutoIndexingProperty(forClass, s);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void startAutoIndexingProperty(Class forClass, String s) {
|
||||
restIndexAPI.startAutoIndexingProperty(forClass, s);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<String> getAutoIndexedProperties(Class forClass) {
|
||||
return restIndexAPI.getAutoIndexedProperties(forClass);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setAutoIndexingEnabled(Class<? extends PropertyContainer> clazz, boolean enabled) {
|
||||
restIndexAPI.setAutoIndexingEnabled(clazz, enabled);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAutoIndexingEnabled(Class<? extends PropertyContainer> clazz) {
|
||||
return restIndexAPI.isAutoIndexingEnabled(clazz);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T extends PropertyContainer> RestIndex<T> createIndex(Class<T> type, String indexName, Map<String, String> config) {
|
||||
return restIndexAPI.createIndex(type, indexName, config);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void createIndex(String type, String indexName, Map<String, String> config) {
|
||||
restIndexAPI.createIndex(type, indexName, config);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T extends PropertyContainer> RestIndex<T> getIndex(String indexName) {
|
||||
return restIndexAPI.getIndex(indexName);
|
||||
}
|
||||
|
||||
@Override
|
||||
public RestRelationship getOrCreateRelationship(RestIndex<Relationship> index, String key, Object value, RestNode start, RestNode end, String type, Map<String, Object> properties) {
|
||||
return restIndexAPI.getOrCreateRelationship(index, key, value, start, end, type, properties);
|
||||
}
|
||||
|
||||
@Override
|
||||
public RestNode getOrCreateNode(RestIndex<Node> index, String key, Object value, Map<String, Object> properties, Collection<String> labels) {
|
||||
return restIndexAPI.getOrCreateNode(index, key, value, properties, labels);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T extends PropertyContainer> T putIfAbsent(T entity, RestIndex index, String key, Object value) {
|
||||
return restIndexAPI.putIfAbsent(entity, index, key, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T extends PropertyContainer> void addToIndex(T entity, RestIndex index, String key, Object value) {
|
||||
restIndexAPI.addToIndex(entity, index, key, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T extends PropertyContainer> void removeFromIndex(RestIndex index, T entity) {
|
||||
restIndexAPI.removeFromIndex(index, entity);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T extends PropertyContainer> void removeFromIndex(RestIndex index, T entity, String key) {
|
||||
restIndexAPI.removeFromIndex(index, entity, key);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T extends PropertyContainer> void removeFromIndex(RestIndex index, T entity, String key, Object value) {
|
||||
restIndexAPI.removeFromIndex(index, entity, key, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void delete(RestIndex index) {
|
||||
restIndexAPI.delete(index);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <S extends PropertyContainer> IndexHits<S> queryIndex(Class<S> entityType, String indexName, String key, Object value) {
|
||||
return restIndexAPI.queryIndex(entityType, indexName, key, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <S extends PropertyContainer> IndexHits<S> getIndex(Class<S> entityType, String indexName, String key, Object value) {
|
||||
return restIndexAPI.getIndex(entityType, indexName, key, value);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,332 @@
|
||||
package org.neo4j.rest.graphdb;
|
||||
|
||||
import org.neo4j.graphdb.Node;
|
||||
import org.neo4j.graphdb.NotFoundException;
|
||||
import org.neo4j.graphdb.PropertyContainer;
|
||||
import org.neo4j.graphdb.Relationship;
|
||||
import org.neo4j.graphdb.index.IndexHits;
|
||||
import org.neo4j.index.lucene.ValueContext;
|
||||
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.*;
|
||||
import org.neo4j.rest.graphdb.util.JsonHelper;
|
||||
|
||||
import javax.ws.rs.core.Response;
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.InputStream;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.util.*;
|
||||
|
||||
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;
|
||||
|
||||
/**
|
||||
* @author mh
|
||||
* @since 25.08.15
|
||||
*/
|
||||
public class RestAPIIndexImpl implements RestAPIIndex {
|
||||
private final RestRequest restRequest;
|
||||
private RestIndexManager restIndexManager;
|
||||
private Map<String,IndexInfo> indexInfos = new HashMap<>();
|
||||
private final RestAPI restAPI;
|
||||
|
||||
public RestAPIIndexImpl(RestAPI restAPI) {
|
||||
this.restAPI = restAPI;
|
||||
restRequest = restAPI.getRestRequest();
|
||||
restIndexManager = new RestIndexManager(restAPI);
|
||||
}
|
||||
|
||||
@Override
|
||||
public RestIndexManager index() {
|
||||
return restIndexManager;
|
||||
}
|
||||
|
||||
public static String indexPath( Class entityType, String indexName ) {
|
||||
return "index/" + indexTypeName(entityType) + "/" + encode(indexName);
|
||||
}
|
||||
|
||||
public static String queryPath( Class entityType, String indexName, String key, Object value ) {
|
||||
return indexPath( entityType, indexName, key,null) + "?query="+ encode(value);
|
||||
}
|
||||
|
||||
public static 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):"");
|
||||
}
|
||||
|
||||
public static String indexTypeName(Class entityType) {
|
||||
return entityType.getSimpleName().toLowerCase();
|
||||
}
|
||||
|
||||
@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(restAPI, entityType).convertFromRepresentation(response);
|
||||
} else {
|
||||
return new SimpleIndexHits<S>(Collections.emptyList(), 0, entityType, restAPI);
|
||||
}
|
||||
}
|
||||
@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(restAPI, entityType).convertFromRepresentation(response);
|
||||
} else {
|
||||
return new SimpleIndexHits<S>(Collections.emptyList(), 0, entityType, restAPI);
|
||||
}
|
||||
}
|
||||
private void deleteIndex(String indexPath) {
|
||||
getRestRequest().delete(indexPath);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void delete(RestIndex index) {
|
||||
deleteIndex(indexPath(index, null, null));
|
||||
resetIndex(index.getEntityType());
|
||||
}
|
||||
|
||||
@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));
|
||||
resetIndex(index.getEntityType());
|
||||
}
|
||||
|
||||
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));
|
||||
resetIndex(index.getEntityType());
|
||||
}
|
||||
|
||||
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));
|
||||
resetIndex(index.getEntityType());
|
||||
}
|
||||
|
||||
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(Response.Status.CREATED)) {
|
||||
throw new RuntimeException(String.format("Error adding element %d %s %s to index %s status %s\n%s", restEntity.getId(), key, value, index.getIndexName(), result.getStatus(),result.getText()));
|
||||
}
|
||||
}
|
||||
|
||||
@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) restAPI.getEntityExtractor().convertFromRepresentation(result);
|
||||
}
|
||||
throw new RuntimeException(String.format("Error adding element %d %s %s to index %s", restEntity.getId(), key, value, index.getIndexName()));
|
||||
}
|
||||
|
||||
public RestNode createRestNode(RequestResult result) {
|
||||
if (result.statusIs(Response.Status.NOT_FOUND)) {
|
||||
throw new NotFoundException("Node not found");
|
||||
}
|
||||
RestNode node = null;
|
||||
if (result.statusIs(CREATED)) {
|
||||
node = result.isMap() ? new RestNode(result.toMap(), restAPI) : new RestNode(result.getLocation(), restAPI);
|
||||
}
|
||||
if (node == null && result.statusIs(Response.Status.OK)) {
|
||||
node = new RestNode(result.toMap(), restAPI);
|
||||
}
|
||||
return restAPI.addToCache(node);
|
||||
}
|
||||
|
||||
public 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(), restAPI);
|
||||
return new RestRelationship(location, restAPI);
|
||||
}
|
||||
|
||||
@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) restAPI.getEntityExtractor().convertFromRepresentation(result);
|
||||
restAPI.addLabels(node, labels);
|
||||
node.setLabels(labels);
|
||||
return restAPI.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()));
|
||||
}
|
||||
|
||||
@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) restAPI.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()));
|
||||
}
|
||||
|
||||
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
|
||||
@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);
|
||||
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 <T extends PropertyContainer> RestIndex<T> createIndex(Class<T> type, String indexName, Map<String, String> config) {
|
||||
resetIndex(type);
|
||||
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 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(Response.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(Response.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(Response.Status.NO_CONTENT)) {
|
||||
throw new IllegalStateException("received " + response);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public IndexInfo indexInfo(final String indexType) {
|
||||
IndexInfo indexInfo = indexInfos.get(indexType);
|
||||
if (indexInfo != null && !indexInfo.isExpired()) {
|
||||
return indexInfo;
|
||||
}
|
||||
RequestResult response = restRequest.get("index/" + encode(indexType));
|
||||
indexInfo = new RetrievedIndexInfo(response);
|
||||
indexInfos.put(indexType,indexInfo);
|
||||
return indexInfo;
|
||||
}
|
||||
}
|
||||
@@ -25,8 +25,6 @@ public interface RestAPIInternal {
|
||||
// todo add to cache or update data in cache
|
||||
RestEntity createRestEntity(Map data);
|
||||
|
||||
void resetIndex(Class type);
|
||||
|
||||
public enum Load {
|
||||
FromCache,
|
||||
FromServer,
|
||||
|
||||
@@ -69,6 +69,13 @@ public abstract class RestEntity implements PropertyContainer, UpdatableRestResu
|
||||
this.id = id;
|
||||
setProperties((Map<String, Object>) structuralData.get("data"));
|
||||
}
|
||||
public RestEntity(RestEntity entity, RestAPI facade) {
|
||||
this.restApi = facade;
|
||||
this.structuralData = entity.structuralData;
|
||||
this.id = entity.id;
|
||||
this.uri = nodeUri(facade,id);
|
||||
setProperties((Map<String, Object>) structuralData.get("data"));
|
||||
}
|
||||
|
||||
public static String nodeUri(RestAPI facade, long id) {
|
||||
return facade.getBaseUri()+"/node/" + id;
|
||||
|
||||
@@ -58,6 +58,10 @@ public class RestNode extends RestEntity implements Node {
|
||||
super(id,restData,facade);
|
||||
setLabels(labels);
|
||||
}
|
||||
public RestNode(RestNode node, RestAPI facade) {
|
||||
super(node,facade);
|
||||
setLabels(node.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)
|
||||
@@ -73,7 +77,7 @@ public class RestNode extends RestEntity implements Node {
|
||||
public void updateFrom(RestEntity entity, RestAPI restApi) {
|
||||
super.updateFrom(entity, restApi);
|
||||
RestNode node = (RestNode) entity;
|
||||
if (node.lastLabelFetchTime > 0 && node.labels != null) {
|
||||
if (node.lastLabelFetchTime > lastLabelFetchTime && node.labels != null) {
|
||||
setLabels(node.labels);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -47,6 +47,9 @@ public class RestRelationship extends RestEntity implements Relationship {
|
||||
public RestRelationship( Map<?, ?> data, RestAPI restApi ) {
|
||||
super( data, restApi );
|
||||
}
|
||||
public RestRelationship( RestRelationship relationship, RestAPI restApi ) {
|
||||
super( relationship, restApi );
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doUpdate() {
|
||||
|
||||
@@ -28,6 +28,8 @@ import org.neo4j.graphdb.index.*;
|
||||
import org.neo4j.index.impl.lucene.LuceneIndexImplementation;
|
||||
import org.neo4j.rest.graphdb.RestAPI;
|
||||
|
||||
import static org.neo4j.rest.graphdb.ExecutingRestRequest.encode;
|
||||
|
||||
public class RestIndexManager implements IndexManager {
|
||||
public static final String RELATIONSHIP = "relationship";
|
||||
public static final String NODE = "node";
|
||||
|
||||
@@ -24,12 +24,17 @@ import org.neo4j.rest.graphdb.query.CypherTransaction;
|
||||
|
||||
import javax.transaction.Status;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
import static org.neo4j.helpers.collection.MapUtil.map;
|
||||
|
||||
public class RemoteCypherTransaction implements Transaction {
|
||||
|
||||
private final List<TransactionFinishListener> listeners = new ArrayList<>();
|
||||
|
||||
int status = Status.STATUS_NO_TRANSACTION;
|
||||
boolean success, failure;
|
||||
CypherTransaction tx;
|
||||
@@ -46,6 +51,12 @@ public class RemoteCypherTransaction implements Transaction {
|
||||
'}';
|
||||
}
|
||||
|
||||
public void registerListener(TransactionFinishListener listener) {
|
||||
if (!listeners.contains(listener)) {
|
||||
listeners.add(listener);
|
||||
}
|
||||
}
|
||||
|
||||
public RemoteCypherTransaction(CypherTransaction tx) {
|
||||
this.tx = tx;
|
||||
status = Status.STATUS_ACTIVE;
|
||||
@@ -68,10 +79,6 @@ public class RemoteCypherTransaction implements Transaction {
|
||||
close();
|
||||
}
|
||||
|
||||
public void terminate() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
if (tx() != null && innerCounter.decrementAndGet() > 0) {
|
||||
@@ -83,16 +90,25 @@ public class RemoteCypherTransaction implements Transaction {
|
||||
status = Status.STATUS_COMMITTED;
|
||||
}
|
||||
else {
|
||||
tx().rollback();
|
||||
if (tx()!=null) tx().rollback();
|
||||
status = Status.STATUS_ROLLEDBACK;
|
||||
}
|
||||
} finally {
|
||||
tx = null;
|
||||
notifyFinish();
|
||||
}
|
||||
}
|
||||
|
||||
private void notifyFinish() {
|
||||
for (TransactionFinishListener listener : listeners) {
|
||||
if (status == Status.STATUS_COMMITTED) listener.comitted();
|
||||
else listener.rolledBack();
|
||||
}
|
||||
listeners.clear();
|
||||
}
|
||||
|
||||
private CypherTransaction tx() {
|
||||
if (tx == null) throw new IllegalStateException("No transaction active");
|
||||
if (tx == null && !failure) throw new IllegalStateException("No transaction active");
|
||||
return tx;
|
||||
}
|
||||
|
||||
@@ -129,4 +145,9 @@ public class RemoteCypherTransaction implements Transaction {
|
||||
public boolean isActive() {
|
||||
return tx != null;
|
||||
}
|
||||
|
||||
public void terminate() {
|
||||
failure();
|
||||
close();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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.transaction;
|
||||
|
||||
/**
|
||||
* @author mh
|
||||
* @since 20.05.15
|
||||
*/
|
||||
public interface TransactionFinishListener {
|
||||
void comitted();
|
||||
void rolledBack();
|
||||
}
|
||||
@@ -32,7 +32,6 @@ import org.neo4j.rest.graphdb.query.CypherTransaction;
|
||||
|
||||
|
||||
public class QueryResultBuilder<T> implements QueryResult<T> {
|
||||
private CypherTransaction.Result cypherResult;
|
||||
private Iterable<T> result;
|
||||
private final ResultConverter defaultConverter;
|
||||
private final boolean isClosableIterable;
|
||||
@@ -44,18 +43,10 @@ public class QueryResultBuilder<T> implements QueryResult<T> {
|
||||
|
||||
public QueryResultBuilder(Iterable<T> result, final ResultConverter<T,?> defaultConverter) {
|
||||
this.result = result;
|
||||
this.isClosableIterable = result instanceof IndexHits || result instanceof ClosableIterable;
|
||||
this.isClosableIterable = result instanceof IndexHits || result instanceof ClosableIterable || result instanceof AutoCloseable;;
|
||||
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);
|
||||
@@ -121,9 +112,15 @@ public class QueryResultBuilder<T> implements QueryResult<T> {
|
||||
private void closeIfNeeded() {
|
||||
if (isClosableIterable && !isClosed) {
|
||||
if (result instanceof IndexHits) {
|
||||
((IndexHits) result).close();
|
||||
((IndexHits) result).close();
|
||||
} else if (result instanceof ClosableIterable) {
|
||||
((ClosableIterable) result).close();
|
||||
((ClosableIterable) result).close();
|
||||
} else if (result instanceof AutoCloseable) {
|
||||
try {
|
||||
((AutoCloseable)result).close();
|
||||
} catch (Exception e) {
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
isClosed=true;
|
||||
}
|
||||
|
||||
@@ -32,9 +32,12 @@ import org.neo4j.rest.graphdb.MatrixDataGraph.RelTypes;
|
||||
import org.neo4j.test.ImpermanentGraphDatabase;
|
||||
import org.neo4j.test.TestGraphDatabaseFactory;
|
||||
|
||||
import java.util.HashSet;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
import static org.neo4j.helpers.collection.IteratorUtil.addToCollection;
|
||||
import static org.neo4j.helpers.collection.IteratorUtil.asCollection;
|
||||
|
||||
|
||||
/**
|
||||
@@ -149,7 +152,7 @@ public class MatrixDatabaseTest {
|
||||
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() );
|
||||
assertEquals( addToCollection(heroesTraverser.nodes(), new HashSet<Node>()), addToCollection(hits.iterator() , new HashSet<Node>()));
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -28,6 +28,7 @@ import java.util.Map;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.neo4j.graphdb.GraphDatabaseService;
|
||||
import org.neo4j.graphdb.Node;
|
||||
import org.neo4j.graphdb.Transaction;
|
||||
import org.neo4j.helpers.collection.IteratorUtil;
|
||||
@@ -40,7 +41,11 @@ public class RestCypherQueryEngineTest extends RestTestBase {
|
||||
private RestAPI restAPI;
|
||||
private MatrixDataGraph embeddedMatrixdata;
|
||||
private MatrixDataGraph restMatrixData;
|
||||
|
||||
|
||||
protected GraphDatabaseService createRestGraphDatabase() {
|
||||
return new RestGraphDatabase(SERVER_ROOT_URI);
|
||||
}
|
||||
|
||||
@Before
|
||||
public void init() throws Exception {
|
||||
embeddedMatrixdata = new MatrixDataGraph(getGraphDatabase(),nodeId()).createNodespace();
|
||||
|
||||
@@ -28,6 +28,7 @@ import org.apache.lucene.index.Term;
|
||||
import org.apache.lucene.search.TermQuery;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
import org.neo4j.graphdb.GraphDatabaseService;
|
||||
import org.neo4j.graphdb.Node;
|
||||
import org.neo4j.graphdb.Relationship;
|
||||
import org.neo4j.graphdb.index.Index;
|
||||
@@ -43,6 +44,10 @@ public class RestIndexTest extends RestTestBase {
|
||||
private static final String NODE_INDEX_NAME = "NODE_INDEX";
|
||||
private static final String REL_INDEX_NAME = "REL_INDEX";
|
||||
|
||||
protected GraphDatabaseService createRestGraphDatabase() {
|
||||
return new RestGraphDatabase(SERVER_ROOT_URI);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAddToNodeIndex() {
|
||||
nodeIndex().add(node(), "name", "test");
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
package org.neo4j.rest.graphdb;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.neo4j.graphdb.GraphDatabaseService;
|
||||
import org.neo4j.rest.graphdb.query.CypherTransactionExecutionException;
|
||||
|
||||
/**
|
||||
* @author mh
|
||||
@@ -13,4 +15,9 @@ public class UpdateRelationshipRestApiImplTest extends UpdateRelationshipTest {
|
||||
restAPI = new RestAPIImpl(SERVER_ROOT_URI);
|
||||
return new RestGraphDatabase(restAPI);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUpdateRelationshipsRemoveAddNoType() throws Exception {
|
||||
super.testUpdateRelationshipsRemoveAddNoType();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -67,6 +67,13 @@ public class UpdateRelationshipTest extends RestTestBase {
|
||||
updateRelationships();
|
||||
}
|
||||
|
||||
/*
|
||||
given
|
||||
(remove)-[:LIKES]->(node)
|
||||
(node)-[:KNOWS]->(keep)
|
||||
when update to (node)-->(keep)
|
||||
then
|
||||
*/
|
||||
@Test(expected = CypherTransactionExecutionException.class)
|
||||
public void testUpdateRelationshipsRemoveAddNoType() throws Exception {
|
||||
Node node = node();
|
||||
@@ -74,6 +81,7 @@ public class UpdateRelationshipTest extends RestTestBase {
|
||||
node.createRelationshipTo(keep, KNOWS);
|
||||
type = null;
|
||||
updateTo = asList(keep);
|
||||
expected = asList(remove, keep);
|
||||
updateRelationships();
|
||||
}
|
||||
|
||||
|
||||
@@ -16,19 +16,13 @@
|
||||
|
||||
package org.springframework.data.neo4j.rest.integration;
|
||||
|
||||
import org.junit.After;
|
||||
import org.junit.AfterClass;
|
||||
import org.junit.Before;
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.*;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.springframework.data.neo4j.aspects.support.EntityPropertyValidationTests;
|
||||
import org.springframework.data.neo4j.rest.support.RestTestBase;
|
||||
import org.springframework.test.context.CleanContextCacheTestExecutionListener;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
import org.springframework.test.context.TestExecutionListeners;
|
||||
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
import org.springframework.test.context.support.DependencyInjectionTestExecutionListener;
|
||||
import org.springframework.test.context.transaction.TransactionalTestExecutionListener;
|
||||
|
||||
/**
|
||||
* @author mh
|
||||
@@ -37,7 +31,6 @@ import org.springframework.test.context.transaction.TransactionalTestExecutionLi
|
||||
@RunWith(SpringJUnit4ClassRunner.class)
|
||||
@ContextConfiguration(locations = {"classpath:org/springframework/data/neo4j/aspects/support/Neo4jGraphPersistenceTests-context.xml",
|
||||
"classpath:RestTests-context.xml"})
|
||||
@TestExecutionListeners({CleanContextCacheTestExecutionListener.class, DependencyInjectionTestExecutionListener.class, TransactionalTestExecutionListener.class})
|
||||
public class RestEntityPropertyValidationTests extends EntityPropertyValidationTests {
|
||||
|
||||
@BeforeClass
|
||||
|
||||
@@ -19,15 +19,14 @@ package org.springframework.data.neo4j.rest.integration;
|
||||
import org.junit.AfterClass;
|
||||
import org.junit.Before;
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.springframework.data.neo4j.aspects.support.FinderTests;
|
||||
import org.springframework.data.neo4j.rest.support.RestTestBase;
|
||||
import org.springframework.test.context.CleanContextCacheTestExecutionListener;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
import org.springframework.test.context.TestExecutionListeners;
|
||||
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
import org.springframework.test.context.support.DependencyInjectionTestExecutionListener;
|
||||
import org.springframework.test.context.transaction.TransactionalTestExecutionListener;
|
||||
import org.springframework.transaction.annotation.Propagation;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
/**
|
||||
* @author mh
|
||||
@@ -36,7 +35,6 @@ import org.springframework.test.context.transaction.TransactionalTestExecutionLi
|
||||
@RunWith(SpringJUnit4ClassRunner.class)
|
||||
@ContextConfiguration(locations = {"classpath:org/springframework/data/neo4j/aspects/support/Neo4jGraphPersistenceTests-context.xml",
|
||||
"classpath:RestTests-context.xml"})
|
||||
@TestExecutionListeners({CleanContextCacheTestExecutionListener.class, DependencyInjectionTestExecutionListener.class, TransactionalTestExecutionListener.class})
|
||||
public class RestFinderTests extends FinderTests {
|
||||
|
||||
@BeforeClass
|
||||
@@ -54,4 +52,9 @@ public class RestFinderTests extends FinderTests {
|
||||
RestTestBase.shutdownDb();
|
||||
}
|
||||
|
||||
@Test
|
||||
@Transactional(propagation = Propagation.NOT_SUPPORTED)
|
||||
public void testFindRelationshipEntity() {
|
||||
super.testFindRelationshipEntity();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,13 +23,9 @@ import org.junit.runner.RunWith;
|
||||
import org.neo4j.rest.graphdb.query.CypherTransactionExecutionException;
|
||||
import org.springframework.data.neo4j.aspects.support.GraphRepositoryTests;
|
||||
import org.springframework.data.neo4j.rest.support.RestTestBase;
|
||||
import org.springframework.test.context.CleanContextCacheTestExecutionListener;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
import org.springframework.test.context.TestExecutionListeners;
|
||||
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
import org.springframework.test.context.support.DependencyInjectionTestExecutionListener;
|
||||
import org.springframework.test.context.transaction.BeforeTransaction;
|
||||
import org.springframework.test.context.transaction.TransactionalTestExecutionListener;
|
||||
|
||||
/**
|
||||
* @author mh
|
||||
@@ -38,7 +34,6 @@ import org.springframework.test.context.transaction.TransactionalTestExecutionLi
|
||||
@RunWith(SpringJUnit4ClassRunner.class)
|
||||
@ContextConfiguration(locations = {"classpath:org/springframework/data/neo4j/aspects/support/Neo4jGraphPersistenceTests-context.xml",
|
||||
"classpath:RestTests-context.xml"})
|
||||
@TestExecutionListeners({CleanContextCacheTestExecutionListener.class, DependencyInjectionTestExecutionListener.class, TransactionalTestExecutionListener.class})
|
||||
public class RestGraphRepositoryTests extends GraphRepositoryTests {
|
||||
|
||||
@BeforeClass
|
||||
|
||||
@@ -25,12 +25,8 @@ import org.springframework.data.neo4j.aspects.Person;
|
||||
import org.springframework.data.neo4j.aspects.support.IndexTests;
|
||||
import org.springframework.data.neo4j.rest.support.RestTestBase;
|
||||
import org.springframework.test.annotation.DirtiesContext;
|
||||
import org.springframework.test.context.CleanContextCacheTestExecutionListener;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
import org.springframework.test.context.TestExecutionListeners;
|
||||
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
import org.springframework.test.context.support.DependencyInjectionTestExecutionListener;
|
||||
import org.springframework.test.context.transaction.TransactionalTestExecutionListener;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
@@ -43,7 +39,7 @@ import static org.springframework.data.neo4j.aspects.Person.persistedPerson;
|
||||
*/
|
||||
@RunWith(SpringJUnit4ClassRunner.class)
|
||||
@ContextConfiguration(locations = {"classpath:org/springframework/data/neo4j/aspects/support/Neo4jGraphPersistenceTests-context.xml",
|
||||
"classpath:RestTests-context.xml"})
|
||||
"classpath:RestTests-context-index.xml"})
|
||||
@DirtiesContext(classMode = DirtiesContext.ClassMode.AFTER_EACH_TEST_METHOD)
|
||||
public class RestIndexTests extends IndexTests {
|
||||
|
||||
|
||||
@@ -24,12 +24,16 @@ import org.junit.Ignore;
|
||||
import org.junit.Test;
|
||||
import org.neo4j.graphdb.GraphDatabaseService;
|
||||
import org.springframework.dao.DataAccessException;
|
||||
import org.springframework.data.neo4j.config.JtaTransactionManagerFactoryBean;
|
||||
import org.springframework.data.neo4j.config.NullTransactionManager;
|
||||
import org.springframework.data.neo4j.core.GraphDatabase;
|
||||
import org.springframework.data.neo4j.rest.support.RestTestHelper;
|
||||
import org.springframework.data.neo4j.template.Neo4jTemplateApiTests;
|
||||
import org.springframework.transaction.PlatformTransactionManager;
|
||||
import org.springframework.transaction.jta.JtaTransactionManager;
|
||||
import org.springframework.transaction.jta.UserTransactionAdapter;
|
||||
|
||||
import javax.transaction.TransactionManager;
|
||||
|
||||
public class RestNeo4jTemplateApiTests extends Neo4jTemplateApiTests
|
||||
{
|
||||
@@ -64,7 +68,8 @@ public class RestNeo4jTemplateApiTests extends Neo4jTemplateApiTests
|
||||
@Override
|
||||
protected PlatformTransactionManager createTransactionManager()
|
||||
{
|
||||
return new JtaTransactionManager(new NullTransactionManager());
|
||||
TransactionManager txm = graphDatabase.getTransactionManager();
|
||||
return new JtaTransactionManager(new UserTransactionAdapter( txm ), txm);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -31,12 +31,8 @@ import org.springframework.data.neo4j.aspects.Mentorship;
|
||||
import org.springframework.data.neo4j.aspects.Person;
|
||||
import org.springframework.data.neo4j.aspects.support.NodeEntityRelationshipTests;
|
||||
import org.springframework.data.neo4j.rest.support.RestTestBase;
|
||||
import org.springframework.test.context.CleanContextCacheTestExecutionListener;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
import org.springframework.test.context.TestExecutionListeners;
|
||||
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
import org.springframework.test.context.support.DependencyInjectionTestExecutionListener;
|
||||
import org.springframework.test.context.transaction.TransactionalTestExecutionListener;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
@@ -49,7 +45,6 @@ import static org.junit.Assert.assertFalse;
|
||||
@RunWith(SpringJUnit4ClassRunner.class)
|
||||
@ContextConfiguration(locations = {"classpath:org/springframework/data/neo4j/aspects/support/Neo4jGraphPersistenceTests-context.xml",
|
||||
"classpath:RestTests-context.xml"})
|
||||
@TestExecutionListeners({CleanContextCacheTestExecutionListener.class, DependencyInjectionTestExecutionListener.class, TransactionalTestExecutionListener.class})
|
||||
public class RestNodeEntityRelationshipTests extends NodeEntityRelationshipTests {
|
||||
|
||||
@BeforeClass
|
||||
|
||||
@@ -22,12 +22,8 @@ import org.neo4j.graphdb.ConstraintViolationException;
|
||||
import org.neo4j.rest.graphdb.query.CypherTransactionExecutionException;
|
||||
import org.springframework.data.neo4j.aspects.support.NodeEntityTests;
|
||||
import org.springframework.data.neo4j.rest.support.RestTestBase;
|
||||
import org.springframework.test.context.CleanContextCacheTestExecutionListener;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
import org.springframework.test.context.TestExecutionListeners;
|
||||
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
import org.springframework.test.context.support.DependencyInjectionTestExecutionListener;
|
||||
import org.springframework.test.context.transaction.TransactionalTestExecutionListener;
|
||||
|
||||
/**
|
||||
* @author mh
|
||||
@@ -35,8 +31,7 @@ import org.springframework.test.context.transaction.TransactionalTestExecutionLi
|
||||
*/
|
||||
@RunWith(SpringJUnit4ClassRunner.class)
|
||||
@ContextConfiguration(locations = {"classpath:org/springframework/data/neo4j/aspects/support/Neo4jGraphPersistenceTests-context.xml",
|
||||
"classpath:RestTests-context.xml"})
|
||||
@TestExecutionListeners({CleanContextCacheTestExecutionListener.class, DependencyInjectionTestExecutionListener.class, TransactionalTestExecutionListener.class})
|
||||
"classpath:RestTests-context-index.xml"})
|
||||
public class RestNodeEntityTests extends NodeEntityTests {
|
||||
|
||||
@BeforeClass
|
||||
@@ -60,7 +55,7 @@ public class RestNodeEntityTests extends NodeEntityTests {
|
||||
// super.testSetShortProperty();
|
||||
}
|
||||
|
||||
@Test(expected = CypherTransactionExecutionException.class)
|
||||
@Test(expected = IllegalStateException.class)
|
||||
public void testDefaultFailOnDuplicateSetToTrueCausesExceptionWhenAnotherDuplicateEntityCreated() {
|
||||
super.testDefaultFailOnDuplicateSetToTrueCausesExceptionWhenAnotherDuplicateEntityCreated();
|
||||
}
|
||||
|
||||
@@ -22,12 +22,8 @@ import org.junit.BeforeClass;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.springframework.data.neo4j.aspects.support.ProjectionTests;
|
||||
import org.springframework.data.neo4j.rest.support.RestTestBase;
|
||||
import org.springframework.test.context.CleanContextCacheTestExecutionListener;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
import org.springframework.test.context.TestExecutionListeners;
|
||||
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
import org.springframework.test.context.support.DependencyInjectionTestExecutionListener;
|
||||
import org.springframework.test.context.transaction.TransactionalTestExecutionListener;
|
||||
|
||||
/**
|
||||
* @author mh
|
||||
@@ -36,7 +32,6 @@ import org.springframework.test.context.transaction.TransactionalTestExecutionLi
|
||||
@RunWith(SpringJUnit4ClassRunner.class)
|
||||
@ContextConfiguration(locations = {"classpath:org/springframework/data/neo4j/aspects/support/Neo4jGraphPersistenceTests-context.xml",
|
||||
"classpath:RestTests-context.xml"})
|
||||
@TestExecutionListeners({CleanContextCacheTestExecutionListener.class, DependencyInjectionTestExecutionListener.class, TransactionalTestExecutionListener.class})
|
||||
public class RestProjectionTests extends ProjectionTests {
|
||||
|
||||
@BeforeClass
|
||||
|
||||
@@ -22,12 +22,8 @@ import org.junit.BeforeClass;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.springframework.data.neo4j.aspects.support.PropertyTests;
|
||||
import org.springframework.data.neo4j.rest.support.RestTestBase;
|
||||
import org.springframework.test.context.CleanContextCacheTestExecutionListener;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
import org.springframework.test.context.TestExecutionListeners;
|
||||
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
import org.springframework.test.context.support.DependencyInjectionTestExecutionListener;
|
||||
import org.springframework.test.context.transaction.TransactionalTestExecutionListener;
|
||||
|
||||
/**
|
||||
* @author mh
|
||||
@@ -36,7 +32,6 @@ import org.springframework.test.context.transaction.TransactionalTestExecutionLi
|
||||
@RunWith(SpringJUnit4ClassRunner.class)
|
||||
@ContextConfiguration(locations = {"classpath:org/springframework/data/neo4j/aspects/support/Neo4jGraphPersistenceTests-context.xml",
|
||||
"classpath:RestTests-context.xml"})
|
||||
@TestExecutionListeners({CleanContextCacheTestExecutionListener.class, DependencyInjectionTestExecutionListener.class, TransactionalTestExecutionListener.class})
|
||||
public class RestPropertyTests extends PropertyTests {
|
||||
|
||||
@BeforeClass
|
||||
|
||||
@@ -16,27 +16,30 @@
|
||||
|
||||
package org.springframework.data.neo4j.rest.integration;
|
||||
|
||||
import org.junit.AfterClass;
|
||||
import org.junit.Before;
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.*;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.springframework.data.neo4j.aspects.LeadRelationship;
|
||||
import org.springframework.data.neo4j.aspects.support.RelationshipEntityTests;
|
||||
import org.springframework.data.neo4j.aspects.Group;
|
||||
import org.springframework.data.neo4j.aspects.Person;
|
||||
import org.springframework.data.neo4j.rest.support.RestTestBase;
|
||||
import org.springframework.test.context.CleanContextCacheTestExecutionListener;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
import org.springframework.test.context.TestExecutionListeners;
|
||||
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
import org.springframework.test.context.support.DependencyInjectionTestExecutionListener;
|
||||
import org.springframework.test.context.transaction.TransactionalTestExecutionListener;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Date;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
/**
|
||||
* @author mh
|
||||
* @since 28.03.11
|
||||
*/
|
||||
|
||||
@RunWith(SpringJUnit4ClassRunner.class)
|
||||
@ContextConfiguration(locations = {"classpath:org/springframework/data/neo4j/aspects/support/Neo4jGraphPersistenceTests-context.xml",
|
||||
"classpath:RestTests-context.xml"})
|
||||
@TestExecutionListeners({CleanContextCacheTestExecutionListener.class, DependencyInjectionTestExecutionListener.class, TransactionalTestExecutionListener.class})
|
||||
public class RestRelationshipEntityTests extends RelationshipEntityTests {
|
||||
|
||||
@BeforeClass
|
||||
@@ -55,4 +58,21 @@ public class RestRelationshipEntityTests extends RelationshipEntityTests {
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
@Transactional
|
||||
public void testSaveRelationship() throws Exception {
|
||||
Person person = personRepository.save(new Person("Michael", 39));
|
||||
Group group = groupRepository.save(new Group());
|
||||
LeadRelationship rel = new LeadRelationship(person,group);
|
||||
LeadRelationship saved = neo4jTemplate.save(rel);
|
||||
LeadRelationship loaded = neo4jTemplate.findOne(saved.getId(),LeadRelationship.class);
|
||||
assertEquals(saved.getUuid(),loaded.getUuid());
|
||||
saved.setCreatedDate(new Date());
|
||||
LeadRelationship saved2 = neo4jTemplate.save(saved);
|
||||
LeadRelationship loaded2 = neo4jTemplate.findOne(saved2.getId(),LeadRelationship.class);
|
||||
assertEquals(saved.getId(),loaded2.getId());
|
||||
SimpleDateFormat format = new SimpleDateFormat("dd-MM-yyyy HH:mm");
|
||||
assertEquals(format.format(saved2.getCreatedDate()),format.format(loaded2.getCreatedDate()));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -33,12 +33,8 @@ import org.springframework.data.neo4j.rest.SpringCypherRestGraphDatabase;
|
||||
import org.springframework.data.neo4j.rest.SpringRestGraphDatabase;
|
||||
import org.springframework.data.neo4j.rest.support.RestTestBase;
|
||||
import org.springframework.data.neo4j.support.Neo4jTemplate;
|
||||
import org.springframework.test.context.CleanContextCacheTestExecutionListener;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
import org.springframework.test.context.TestExecutionListeners;
|
||||
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
import org.springframework.test.context.support.DependencyInjectionTestExecutionListener;
|
||||
import org.springframework.test.context.transaction.TransactionalTestExecutionListener;
|
||||
import org.springframework.transaction.annotation.EnableTransactionManagement;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
@@ -49,7 +45,6 @@ import static org.junit.Assert.assertEquals;
|
||||
*/
|
||||
@RunWith(SpringJUnit4ClassRunner.class)
|
||||
@ContextConfiguration(classes = RestRelationshipTests.MyConfig.class)
|
||||
@TestExecutionListeners({CleanContextCacheTestExecutionListener.class, DependencyInjectionTestExecutionListener.class, TransactionalTestExecutionListener.class})
|
||||
public class RestRelationshipTests {
|
||||
|
||||
@Configuration
|
||||
|
||||
@@ -22,12 +22,8 @@ import org.junit.BeforeClass;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.springframework.data.neo4j.aspects.support.TraversalTests;
|
||||
import org.springframework.data.neo4j.rest.support.RestTestBase;
|
||||
import org.springframework.test.context.CleanContextCacheTestExecutionListener;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
import org.springframework.test.context.TestExecutionListeners;
|
||||
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
import org.springframework.test.context.support.DependencyInjectionTestExecutionListener;
|
||||
import org.springframework.test.context.transaction.TransactionalTestExecutionListener;
|
||||
|
||||
/**
|
||||
* @author mh
|
||||
@@ -36,7 +32,6 @@ import org.springframework.test.context.transaction.TransactionalTestExecutionLi
|
||||
@RunWith( SpringJUnit4ClassRunner.class )
|
||||
@ContextConfiguration( locations = {"classpath:org/springframework/data/neo4j/aspects/support/Neo4jGraphPersistenceTests-context.xml",
|
||||
"classpath:RestTests-context.xml"} )
|
||||
@TestExecutionListeners( {CleanContextCacheTestExecutionListener.class, DependencyInjectionTestExecutionListener.class, TransactionalTestExecutionListener.class} )
|
||||
public class RestTraversalTests extends TraversalTests
|
||||
{
|
||||
|
||||
|
||||
@@ -22,12 +22,10 @@ import org.springframework.dao.DataIntegrityViolationException;
|
||||
import org.springframework.data.neo4j.rest.support.RestTestBase;
|
||||
import org.springframework.data.neo4j.unique.common.CommonUniqueNumericIdClub;
|
||||
import org.springframework.data.neo4j.unique.legacy.UniqueLegacyIndexBasedEntityTests;
|
||||
import org.springframework.test.context.CleanContextCacheTestExecutionListener;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
import org.springframework.test.context.TestExecutionListeners;
|
||||
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
import org.springframework.test.context.support.DependencyInjectionTestExecutionListener;
|
||||
import org.springframework.test.context.transaction.TransactionalTestExecutionListener;
|
||||
import org.springframework.transaction.annotation.Propagation;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
@@ -39,7 +37,6 @@ import static org.junit.Assert.assertEquals;
|
||||
@ContextConfiguration(locations = {
|
||||
"classpath:unique-legacy-test-context.xml",
|
||||
"classpath:RestTests-context.xml"})
|
||||
@TestExecutionListeners({CleanContextCacheTestExecutionListener.class, DependencyInjectionTestExecutionListener.class, TransactionalTestExecutionListener.class})
|
||||
public class RestUniqueEntityTests extends UniqueLegacyIndexBasedEntityTests {
|
||||
|
||||
@BeforeClass
|
||||
@@ -58,9 +55,9 @@ public class RestUniqueEntityTests extends UniqueLegacyIndexBasedEntityTests {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
@Ignore
|
||||
@Test
|
||||
public void shouldOnlyCreateSingleInstanceForUniqueNumericNodeEntity() {
|
||||
super.shouldOnlyCreateSingleInstanceForUniqueNumericNodeEntity();
|
||||
// needs transaction separation due to legacy index endpoints not participating in transactions
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,6 +22,7 @@ import org.neo4j.graphdb.Direction;
|
||||
import org.neo4j.graphdb.Node;
|
||||
import org.neo4j.graphdb.NotFoundException;
|
||||
import org.neo4j.graphdb.Relationship;
|
||||
import org.neo4j.rest.graphdb.query.CypherTransactionExecutionException;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
@@ -73,7 +74,7 @@ public class RestEntityTests extends RestTestBase {
|
||||
assertEquals(null, restGraphDatabase.getNodeById(nodeId));
|
||||
}
|
||||
|
||||
@Test(expected = NotFoundException.class)
|
||||
@Test(expected = RuntimeException.class)
|
||||
public void testRemoveRelationship() {
|
||||
Node refNode = createNode();
|
||||
Node node = createNode();
|
||||
|
||||
@@ -21,18 +21,16 @@ import org.junit.BeforeClass;
|
||||
import org.junit.Ignore;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.neo4j.rest.graphdb.RestGraphDatabase;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.data.neo4j.aspects.support.query.QueryEngineTests;
|
||||
import org.springframework.data.neo4j.core.GraphDatabase;
|
||||
import org.springframework.data.neo4j.rest.SpringCypherRestGraphDatabase;
|
||||
import org.springframework.data.neo4j.rest.SpringCypherRestGraphDatabase;
|
||||
import org.springframework.test.context.CleanContextCacheTestExecutionListener;
|
||||
import org.springframework.data.neo4j.rest.SpringRestGraphDatabase;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
import org.springframework.test.context.TestExecutionListeners;
|
||||
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
import org.springframework.test.context.support.DependencyInjectionTestExecutionListener;
|
||||
import org.springframework.test.context.transaction.BeforeTransaction;
|
||||
import org.springframework.test.context.transaction.TransactionalTestExecutionListener;
|
||||
|
||||
/**
|
||||
* @author mh
|
||||
@@ -40,12 +38,11 @@ import org.springframework.test.context.transaction.TransactionalTestExecutionLi
|
||||
*/
|
||||
@RunWith(SpringJUnit4ClassRunner.class)
|
||||
@ContextConfiguration(locations = {"classpath:org/springframework/data/neo4j/aspects/support/Neo4jGraphPersistenceTests-context.xml",
|
||||
"classpath:RestTests-context.xml"})
|
||||
@TestExecutionListeners({CleanContextCacheTestExecutionListener.class, DependencyInjectionTestExecutionListener.class, TransactionalTestExecutionListener.class})
|
||||
"classpath:RestTests-context-index.xml"})
|
||||
public class RestQueryEngineTests extends QueryEngineTests {
|
||||
|
||||
@Autowired
|
||||
SpringCypherRestGraphDatabase restGraphDatabase;
|
||||
SpringRestGraphDatabase restGraphDatabase;
|
||||
|
||||
@BeforeClass
|
||||
public static void startDb() throws Exception {
|
||||
|
||||
@@ -57,6 +57,7 @@ public class RestTestBase {
|
||||
db = new ImpermanentGraphDatabase();
|
||||
final ServerConfigurator configurator = new ServerConfigurator(db);
|
||||
configurator.configuration().setProperty(Configurator.WEBSERVER_PORT_PROPERTY_KEY,PORT);
|
||||
configurator.configuration().setProperty("dbms.security.auth_enabled",false);
|
||||
final WrappingNeoServerBootstrapper bootstrapper = new WrappingNeoServerBootstrapper(db, configurator);
|
||||
int exit = bootstrapper.start();
|
||||
if (exit != 0 ) throw new IllegalStateException("Server not started correctly.");
|
||||
|
||||
@@ -41,6 +41,7 @@ public class RestTestHelper
|
||||
db = new ImpermanentGraphDatabase();
|
||||
final ServerConfigurator configurator = new ServerConfigurator(db);
|
||||
configurator.configuration().setProperty(Configurator.WEBSERVER_PORT_PROPERTY_KEY,PORT);
|
||||
configurator.configuration().setProperty("dbms.security.auth_enabled",false);
|
||||
final WrappingNeoServerBootstrapper bootstrapper = new WrappingNeoServerBootstrapper(db, configurator);
|
||||
bootstrapper.start();
|
||||
neoServer = bootstrapper.getServer();
|
||||
|
||||
@@ -74,6 +74,7 @@ public class SpringPluginInitializerTests extends SpringPluginInitializer implem
|
||||
};
|
||||
final Configuration configuration = configurator.configuration();
|
||||
configuration.setProperty(Configurator.WEBSERVER_PORT_PROPERTY_KEY, PORT);
|
||||
configuration.setProperty("dbms.security.auth_enabled", false);
|
||||
final WrappingNeoServerBootstrapper bootstrapper = new WrappingNeoServerBootstrapper(db, configurator);
|
||||
touched=0;
|
||||
bootstrapper.start();
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<beans xmlns="http://www.springframework.org/schema/beans"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
|
||||
|
||||
<bean id="graphDatabaseService" class="org.springframework.data.neo4j.rest.SpringRestGraphDatabase" destroy-method="shutdown">
|
||||
<constructor-arg index="0" value="http://localhost:7470/db/data" />
|
||||
</bean>
|
||||
<alias name="graphDatabaseService" alias="graphDatabase"/>
|
||||
</beans>
|
||||
@@ -1,2 +1,4 @@
|
||||
org.neo4j.server.database.location=target/test-db
|
||||
org.neo4j.server.thirdparty_jaxrs_classes=org.springframework.data.neo4j.rest.support=/test
|
||||
dbms.security.auth_enabled=false
|
||||
|
||||
|
||||
@@ -1 +1,2 @@
|
||||
org.neo4j.server.database.location=target/test-db
|
||||
dbms.security.auth_enabled=false
|
||||
|
||||
@@ -25,6 +25,7 @@ import org.springframework.data.domain.Pageable;
|
||||
import org.springframework.data.domain.Slice;
|
||||
import org.springframework.data.neo4j.mapping.MappingPolicy;
|
||||
|
||||
import java.io.Closeable;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
@@ -124,6 +125,12 @@ public class QueryResultBuilder<T> implements Result<T> {
|
||||
((IndexHits) result).close();
|
||||
} else if (result instanceof ClosableIterable) {
|
||||
((ClosableIterable) result).close();
|
||||
} else if (result instanceof AutoCloseable) {
|
||||
try {
|
||||
((AutoCloseable)result).close();
|
||||
} catch (Exception e) {
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
isClosed=true;
|
||||
}
|
||||
|
||||
@@ -147,6 +147,8 @@ public class Neo4jTemplateApiTests {
|
||||
@Test
|
||||
public void testIndexNode() throws Exception {
|
||||
template.index("node", node1, "name","node1");
|
||||
transaction.success();transaction.close();
|
||||
transaction = graphDatabase.beginTx();
|
||||
Index<Node> index = graphDatabase.getIndex("node");
|
||||
Node lookedUpNode= index.get( "name", "node1" ).getSingle();
|
||||
assertThat("same node from index", lookedUpNode, is(node1));
|
||||
|
||||
@@ -38,6 +38,7 @@ import org.springframework.data.neo4j.unique.legacy.repository.UniqueClubReposit
|
||||
import org.springframework.data.neo4j.unique.legacy.repository.UniqueNumericIdClubRepository;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
import org.springframework.transaction.annotation.Propagation;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
|
||||
|
||||
@@ -178,7 +178,7 @@ Users of Spring Data Neo4j have two ways of very concisely configuring it. Eithe
|
||||
|
||||
=== XML namespace
|
||||
|
||||
The XML namespace can be used to configure Spring Data Neo4j. The `config` element provides an XML-based configuration of Spring Data Neo4j in one line. It has four attributes. * `base-package` points to a set of packages (provided as a comma separated String of names) which SDN will scan for locate all of your domain entity classes (`@NodeEntity` and `@RelationshipEntity`). NOTE: Neo4j 2.0 introduced the requirement to separately manage schema and data transactions which altered some options for SDN with regards be being able to automatically detect and register `@NodeEntity` and `@RelationshipEntity`s on the fly. Several approaches were attempted to try and handle this automatically with SDN 3.0.X, none of which worked in a satisfactory manner. This has resulted in the base-package becoming a mandatory field now with entity metadata handling becoming an explicit step in the lifecycle.
|
||||
The XML namespace can be used to configure Spring Data Neo4j. The `config` element provides an XML-based configuration of Spring Data Neo4j in one line. It has four attributes. * `base-package` points to a set of packages (provided as a comma separated String of names) which SDN will scan for locate all of your domain entity classes (`@NodeEntity` and `@RelationshipEntity`). NOTE: Neo4j 2.0 introduced the requirement to separately manage schema and data transactions which altered some options for SDN with regards be being able to automatically detect and register `@NodeEntity` and `@RelationshipEntity` on the fly. Several approaches were attempted to try and handle this automatically with SDN 3.0.X, none of which worked in a satisfactory manner. This has resulted in the base-package becoming a mandatory field now with entity metadata handling becoming an explicit step in the lifecycle.
|
||||
* `graphDatabaseService` points out the Neo4j instance to use.
|
||||
* `storeDirectory` is a convenient alternative (instead of `graphDatabaseService`) to point to a directory where a new `EmbeddedGraphDatabase` will be created.
|
||||
* `entityManagerFactory` is only required for cross-store configuration.
|
||||
|
||||
Reference in New Issue
Block a user