All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.neo4j.rest.graphdb.RestAPIIndexImpl Maven / Gradle / Ivy

Go to download

pring Data Neo4j Wrapper for the Neo4j REST API, provides a Graph Database proxy for the remote invocation.

The newest version!
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 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  IndexHits getIndex(Class 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(Collections.emptyList(), 0, entityType, restAPI);
        }
    }
    @Override
    @SuppressWarnings("unchecked")
    public  IndexHits queryIndex(Class 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(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  void removeFromIndex(RestIndex index, T entity, String key, Object value) {
        String indexPath = indexPath(index, key, value);
        deleteIndex(indexPath(indexPath, entity));
        resetIndex(index.getEntityType());
    }

    protected  String indexPath(String indexPath, T restEntity) {
        return indexPath + "/" + ((RestEntity)restEntity).getId();
    }

    @Override
    public  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  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  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 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 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 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 index, String key, Object value, final Map properties, Collection labels) {
        if (index==null || key == null || value==null) throw new IllegalArgumentException("Unique index "+index+" key "+key+" value must not be null");
        final Map data = map("key", key, "value", value, "properties", properties);
        final RequestResult result = getRestRequest().post(uniqueIndexPath(index), data);
        if (result.statusIs(Response.Status.CREATED) || result.statusIs(Response.Status.OK)) {
            RestNode node = (RestNode) 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 index, String key, Object value, final RestNode start, final RestNode end, final String type, final Map 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 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 clazz) {
        return buildPathAutoIndexerBase(clazz).append("/status").toString();
    }

    private StringBuilder buildPathAutoIndexerProperties(Class clazz) {
        return buildPathAutoIndexerBase(clazz).append("/properties");
    }

    private StringBuilder buildPathAutoIndexerBase(Class clazz) {
        return new StringBuilder().append("index/auto/").append(indexTypeName(clazz));
    }


    public RestRequest getRestRequest() {
        return restRequest;
    }

    @Override
    @SuppressWarnings("unchecked")
    public  RestIndex getIndex(String indexName) {
        final RestIndexManager index = this.index();
        if (index.existsForNodes(indexName)) return (RestIndex) index.forNodes(indexName);
        if (index.existsForRelationships(indexName)) return (RestIndex) index.forRelationships(indexName);
        throw new IllegalArgumentException("Index " + indexName + " does not yet exist");
    }

    @Override
    @SuppressWarnings("unchecked")
    public void createIndex(String type, String indexName, Map config) {
        Map data=new HashMap();
        data.put("name",indexName);
        data.put("config",config);
        restRequest.post("index/" + type, data);
        IndexInfo indexInfo = indexInfos.get(type);
        if (indexInfo!=null) indexInfo.setExpired();
    }

    public void resetIndex(Class type) {
        if (Node.class.isAssignableFrom(type)) {
            indexInfo(RestIndexManager.NODE).setExpired();
        }
        if (Relationship.class.isAssignableFrom(type)) {
            indexInfo(RestIndexManager.RELATIONSHIP).setExpired();
        }
    }

    @Override
    @SuppressWarnings("unchecked")
    public  RestIndex createIndex(Class type, String indexName, Map config) {
        resetIndex(type);
        if (Node.class.isAssignableFrom(type)) {
            return (RestIndex) index().forNodes( indexName, config);
        }
        if (Relationship.class.isAssignableFrom(type)) {
            return (RestIndex) index().forRelationships(indexName, config);
        }
        throw new IllegalArgumentException("Required Node or Relationship types to create index, got " + type);
    }

    @Override
    public boolean isAutoIndexingEnabled(Class 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 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 getAutoIndexedProperties(Class forClass) {
        RequestResult response = getRestRequest().get(buildPathAutoIndexerProperties(forClass).toString());
        Collection autoIndexedProperties = (Collection) JsonHelper.readJson(response.getText());
        return new HashSet(autoIndexedProperties);
    }

    @Override
    public void startAutoIndexingProperty(Class forClass, String s) {
        try {
            // we need to use a inputstream instead of the string directly. Otherwise "post" implicitly uses
            // StreamJsonHelper.writeJsonTo which quotes a given string
            InputStream stream = new ByteArrayInputStream(s.getBytes("UTF-8"));
            RequestResult response = getRestRequest().post(buildPathAutoIndexerProperties(forClass).toString(), stream);
            if (response.statusOtherThan(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;
    }
}