Package com.tinkerpop.rexster.kibbles.batch

Source Code of com.tinkerpop.rexster.kibbles.batch.BatchExtension

package com.tinkerpop.rexster.kibbles.batch;

import com.tinkerpop.blueprints.CloseableIterable;
import com.tinkerpop.blueprints.Edge;
import com.tinkerpop.blueprints.Element;
import com.tinkerpop.blueprints.Graph;
import com.tinkerpop.blueprints.Index;
import com.tinkerpop.blueprints.IndexableGraph;
import com.tinkerpop.blueprints.Vertex;
import com.tinkerpop.blueprints.util.io.graphson.GraphSONMode;
import com.tinkerpop.blueprints.util.io.graphson.GraphSONUtility;
import com.tinkerpop.rexster.RexsterApplicationGraph;
import com.tinkerpop.rexster.RexsterResourceContext;
import com.tinkerpop.rexster.Tokens;
import com.tinkerpop.rexster.extension.AbstractRexsterExtension;
import com.tinkerpop.rexster.extension.ExtensionApi;
import com.tinkerpop.rexster.extension.ExtensionDefinition;
import com.tinkerpop.rexster.extension.ExtensionDescriptor;
import com.tinkerpop.rexster.extension.ExtensionMethod;
import com.tinkerpop.rexster.extension.ExtensionNaming;
import com.tinkerpop.rexster.extension.ExtensionPoint;
import com.tinkerpop.rexster.extension.ExtensionResponse;
import com.tinkerpop.rexster.extension.HttpMethod;
import com.tinkerpop.rexster.extension.RexsterContext;
import com.tinkerpop.rexster.util.ElementHelper;
import com.tinkerpop.rexster.util.RequestObjectHelper;
import org.apache.log4j.Logger;
import org.codehaus.jettison.json.JSONArray;
import org.codehaus.jettison.json.JSONObject;

import javax.ws.rs.core.Response;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;

/**
* This extension allows batch/transactional operations on a graph.
*/
@ExtensionNaming(namespace = BatchExtension.EXTENSION_NAMESPACE, name = BatchExtension.EXTENSION_NAME)
public class BatchExtension extends AbstractRexsterExtension {

    private static final Logger logger = Logger.getLogger(BatchExtension.class);

    public static final String EXTENSION_NAMESPACE = "tp";
    public static final String EXTENSION_NAME = "batch";
    private static final String WILDCARD = "*";

    private static final String API_SHOW_TYPES = "displays the properties of the elements with their native data type (default is false)";
    private static final String API_VALUES = "a list of element identifiers or index values to retrieve from the graph";
    private static final String API_RETURN_KEYS = "an array of element property keys to return (default is to return all element properties)";
    private static final String API_TYPE = "specifies whether to retrieve by identifier or index (default is id)" ;
    private static final String API_KEY = "specifies the index key";

    @ExtensionDefinition(extensionPoint = ExtensionPoint.GRAPH, method = HttpMethod.GET, path = "vertices")
    @ExtensionDescriptor(description = "get a set of vertices from the graph.",
            api = {
                    @ExtensionApi(parameterName = Tokens.REXSTER + "." + Tokens.SHOW_TYPES, description = API_SHOW_TYPES),
                    @ExtensionApi(parameterName = Tokens.REXSTER + "." + Tokens.RETURN_KEYS, description = API_RETURN_KEYS),
                    @ExtensionApi(parameterName = "values", description = API_VALUES),
                    @ExtensionApi(parameterName = "type", description = API_TYPE),
                    @ExtensionApi(parameterName = "key", description = API_KEY)
            })
    public ExtensionResponse getVertices(@RexsterContext final RexsterResourceContext context,
                                         @RexsterContext final Graph graph) {

        final JSONObject requestObject = context.getRequestObject();
        final JSONArray values = requestObject.optJSONArray("values");
        final String type = requestObject.optString("type", "id");
        final String key = requestObject.optString("key");

        final ExtensionResponse error = checkParameters(context, values, type, key);
        if (error != null) {
            return error;
        }

        final boolean showTypes = RequestObjectHelper.getShowTypes(requestObject);
        final GraphSONMode mode = showTypes ? GraphSONMode.EXTENDED : GraphSONMode.NORMAL;
        final Set<String> returnKeys = RequestObjectHelper.getReturnKeys(requestObject, WILDCARD);

        try {

            final JSONArray jsonArray = new JSONArray();

            if (type.equals("id")) {
                for (int ix = 0; ix < values.length(); ix++) {
                    final Vertex vertexFound = graph.getVertex(ElementHelper.getTypedPropertyValue(values.optString(ix)));
                    if (vertexFound != null) {
                        jsonArray.put(GraphSONUtility.jsonFromElement(vertexFound, returnKeys, mode));
                    }
                }
            } else if (type.equals("index")) {
                Index idx = ((IndexableGraph)graph).getIndex(key, Vertex.class);

                for (int ix = 0; ix < values.length(); ix++) {
                    CloseableIterable<Vertex> verticesFound = idx.get(key, ElementHelper.getTypedPropertyValue(values.optString(ix)));
                    for (Vertex vertex : verticesFound) {
                        jsonArray.put(GraphSONUtility.jsonFromElement(vertex, returnKeys, mode));
                    }
                    verticesFound.close();
                }
            } else if (type.equals("keyindex")) {
                for (int ix = 0; ix < values.length(); ix++) {
                    Iterable<Vertex> verticesFound = graph.getVertices(key, ElementHelper.getTypedPropertyValue(values.optString(ix)));
                    for (Vertex vertex : verticesFound) {
                        jsonArray.put(GraphSONUtility.jsonFromElement(vertex, returnKeys, mode));
                    }
                }
            }

            final HashMap<String, Object> resultMap = new HashMap<String, Object>();
            resultMap.put(Tokens.SUCCESS, true);
            resultMap.put(Tokens.RESULTS, jsonArray);

            final JSONObject resultObject = new JSONObject(resultMap);
            return ExtensionResponse.ok(resultObject);

        } catch (Exception mqe) {
            logger.error(mqe);
            return ExtensionResponse.error(
                    "Error retrieving batch of vertices [" + values + "]", generateErrorJson());
        }

    }

    @ExtensionDefinition(extensionPoint = ExtensionPoint.GRAPH, method = HttpMethod.GET, path = "edges")
    @ExtensionDescriptor(description = "get a set of edges from the graph.",
            api = {
                    @ExtensionApi(parameterName = Tokens.REXSTER + "." + Tokens.SHOW_TYPES, description = API_SHOW_TYPES),
                    @ExtensionApi(parameterName = Tokens.REXSTER + "." + Tokens.RETURN_KEYS, description = API_RETURN_KEYS),
                    @ExtensionApi(parameterName = "values", description = API_VALUES),
                    @ExtensionApi(parameterName = "type", description = API_TYPE),
                    @ExtensionApi(parameterName = "key", description = API_KEY)
            })
    public ExtensionResponse getEdges(@RexsterContext final RexsterResourceContext context,
                                      @RexsterContext final Graph graph) {

        final JSONObject requestObject = context.getRequestObject();
        final JSONArray values = requestObject.optJSONArray("values");
        final String type = requestObject.optString("type", "id");
        final String key = requestObject.optString("key");

        final ExtensionResponse error = checkParameters(context, values, type, key);
        if (error != null) {
            return error;
        }

        final boolean showTypes = RequestObjectHelper.getShowTypes(requestObject);
        final GraphSONMode mode = showTypes ? GraphSONMode.EXTENDED : GraphSONMode.NORMAL;
        final Set<String> returnKeys = RequestObjectHelper.getReturnKeys(requestObject, WILDCARD);

        try {

            final JSONArray jsonArray = new JSONArray();

            if (type.equals("id")) {
                for (int ix = 0; ix < values.length(); ix++) {
                    final Edge edgeFound = graph.getEdge(ElementHelper.getTypedPropertyValue(values.optString(ix)));
                    if (edgeFound != null) {
                        jsonArray.put(GraphSONUtility.jsonFromElement(edgeFound, returnKeys, mode));
                    }
                }
            } else if (type.equals("index")) {
                Index idx = ((IndexableGraph)graph).getIndex(key, Edge.class);

                for (int ix = 0; ix < values.length(); ix++) {
                    CloseableIterable<Edge> edgesFound = idx.get(key, ElementHelper.getTypedPropertyValue(values.optString(ix)));
                    for (Edge edge : edgesFound) {
                        jsonArray.put(GraphSONUtility.jsonFromElement(edge, returnKeys, mode));
                    }
                    edgesFound.close();
                }
            } else if (type.equals("keyindex")) {
                for (int ix = 0; ix < values.length(); ix++) {
                    Iterable<Edge> edgesFound = graph.getEdges(key, ElementHelper.getTypedPropertyValue(values.optString(ix)));
                    for (Edge edge : edgesFound) {
                        jsonArray.put(GraphSONUtility.jsonFromElement(edge, returnKeys, mode));
                    }
                }
            }

            final HashMap<String, Object> resultMap = new HashMap<String, Object>();
            resultMap.put(Tokens.SUCCESS, true);
            resultMap.put(Tokens.RESULTS, jsonArray);

            final JSONObject resultObject = new JSONObject(resultMap);
            return ExtensionResponse.ok(resultObject);

        } catch (Exception mqe) {
            logger.error(mqe);
            return ExtensionResponse.error(
                    "Error retrieving batch of edges [" + values + "]", generateErrorJson());
        }

    }

    @ExtensionDefinition(extensionPoint = ExtensionPoint.GRAPH, method = HttpMethod.POST, path = "tx", autoCommitTransaction = true)
    @ExtensionDescriptor(description = "post a transaction to the graph.")
    public ExtensionResponse postTx(@RexsterContext RexsterResourceContext context,
                                    @RexsterContext Graph graph,
                                    @RexsterContext RexsterApplicationGraph rag) {

        final JSONObject transactionJson = context.getRequestObject();
        if (transactionJson == null) {
            final ExtensionMethod extMethod = context.getExtensionMethod();
            return ExtensionResponse.error(
                    "no transaction JSON posted",
                    null,
                    Response.Status.BAD_REQUEST.getStatusCode(),
                    null,
                    generateErrorJson(extMethod.getExtensionApiAsJson()));
        }

        try {
            final JSONArray txArray = transactionJson.optJSONArray("tx");
            String currentAction;
            for (int ix = 0; ix < txArray.length(); ix++) {
                final JSONObject txElement = txArray.optJSONObject(ix);
                currentAction = txElement.optString("_action");
                if (currentAction.equals("create")) {
                    create(txElement, graph);
                } else if (currentAction.equals("update")) {
                    update(txElement, graph);
                } else if (currentAction.equals("delete")) {
                    delete(txElement, graph);
                }
            }

            final Map<String, Object> resultMap = new HashMap<String, Object>();
            resultMap.put(Tokens.SUCCESS, true);
            resultMap.put("txProcessed", txArray.length());

            return ExtensionResponse.ok(new JSONObject(resultMap));

        } catch (IllegalArgumentException iae) {
            logger.error(iae);

            final ExtensionMethod extMethod = context.getExtensionMethod();
            return ExtensionResponse.error(
                    iae.getMessage(),
                    null,
                    Response.Status.BAD_REQUEST.getStatusCode(),
                    null,
                    generateErrorJson(extMethod.getExtensionApiAsJson()));
        } catch (Exception ex) {
            logger.error(ex);
            return ExtensionResponse.error(
                    "Error executing transaction: " + ex.getMessage(), generateErrorJson());
        }
    }

    private void create(final JSONObject elementAsJson, final Graph graph) throws Exception {
        final String id = elementAsJson.optString(Tokens._ID);
        final String elementType = elementAsJson.optString(Tokens._TYPE);

        if (elementType == null) {
            throw new IllegalArgumentException("each element in the transaction must have an " + Tokens._TYPE + " key");
        }

        if (!elementType.equals(Tokens.VERTEX) && !elementType.equals(Tokens.EDGE)) {
            throw new IllegalArgumentException("the " + Tokens._TYPE + " element in the transaction must be either " + Tokens.VERTEX + " or " + Tokens.EDGE);
        }

        Element graphElementCreated = null;
        if (elementType.equals(Tokens.VERTEX)) {
            graphElementCreated = graph.getVertex(id);

            if (graphElementCreated != null) {
                throw new Exception("Vertex with id " + id + " already exists.");
            }

            graphElementCreated = graph.addVertex(id);

        } else if (elementType.equals(Tokens.EDGE)) {

            String inV = null;
            Object temp = elementAsJson.opt(Tokens._IN_V);
            if (null != temp)
                inV = temp.toString();
            String outV = null;
            temp = elementAsJson.opt(Tokens._OUT_V);
            if (null != temp)
                outV = temp.toString();
            String label = null;
            temp = elementAsJson.opt(Tokens._LABEL);
            if (null != temp)
                label = temp.toString();

            if (outV == null || inV == null || outV.isEmpty() || inV.isEmpty()) {
                throw new IllegalArgumentException("an edge must specify a " + Tokens._IN_V + " and " + Tokens._OUT_V);
            }

            graphElementCreated = graph.getEdge(id);

            if (graphElementCreated != null) {
                throw new Exception("Edge with id " + id + " already exists.");
            }

            // there is no edge but the in/out vertex params and label are present so
            // validate that the vertexes are present before creating the edge
            final Vertex out = graph.getVertex(outV);
            final Vertex in = graph.getVertex(inV);
            if (out != null && in != null) {
                // in/out vertexes are found so edge can be created
                graphElementCreated = graph.addEdge(id, out, in, label);
            } else {
                throw new Exception("the " + Tokens._IN_V + " or " + Tokens._OUT_V + " vertices could not be found.");
            }

        }

        if (graphElementCreated != null) {
            Iterator keys = elementAsJson.keys();
            while (keys.hasNext()) {
                String key = keys.next().toString();
                if (!key.startsWith(Tokens.UNDERSCORE)) {
                    graphElementCreated.setProperty(key, ElementHelper.getTypedPropertyValue(elementAsJson.getString(key)));
                }
            }
        }
    }

    private void update(final JSONObject elementAsJson, final Graph graph) throws Exception {
        final String id = elementAsJson.optString(Tokens._ID);
        if (id == null || id.isEmpty()) {
            throw new IllegalArgumentException("each element in the transaction must have an " + Tokens._ID + " key");
        }

        final String elementType = elementAsJson.optString(Tokens._TYPE);

        if (elementType == null) {
            throw new IllegalArgumentException("each element in the transaction must have an " + Tokens._TYPE + " key");
        }

        if (!elementType.equals(Tokens.VERTEX) && !elementType.equals(Tokens.EDGE)) {
            throw new IllegalArgumentException("the " + Tokens._TYPE + " element in the transaction must be either " + Tokens.VERTEX + " or " + Tokens.EDGE);
        }

        Element graphElementUpdated = null;
        if (elementType.equals(Tokens.VERTEX)) {
            graphElementUpdated = graph.getVertex(id);
        } else if (elementType.equals(Tokens.EDGE)) {
            graphElementUpdated = graph.getEdge(id);
        }

        if (graphElementUpdated != null) {
            Iterator keys = elementAsJson.keys();
            while (keys.hasNext()) {
                String key = keys.next().toString();
                if (!key.startsWith(Tokens.UNDERSCORE)) {
                    graphElementUpdated.setProperty(key, ElementHelper.getTypedPropertyValue(elementAsJson.getString(key)));
                }
            }
        }
    }

    private void delete(final JSONObject elementAsJson, final Graph graph) throws Exception {
        final String id = elementAsJson.optString(Tokens._ID);
        if (id == null || id.isEmpty()) {
            throw new IllegalArgumentException("each element in the transaction must have an " + Tokens._ID + " key");
        }

        final String elementType = elementAsJson.optString(Tokens._TYPE);

        if (elementType == null) {
            throw new IllegalArgumentException("each element in the transaction must have an " + Tokens._TYPE + " key");
        }

        if (!elementType.equals(Tokens.VERTEX) && !elementType.equals(Tokens.EDGE)) {
            throw new IllegalArgumentException("the " + Tokens._TYPE + " element in the transaction must be either " + Tokens.VERTEX + " or " + Tokens.EDGE);
        }

        final JSONArray keysToDelete = elementAsJson.optJSONArray("_keys");

        Element graphElementDeleted = null;
        if (elementType.equals(Tokens.VERTEX)) {
            graphElementDeleted = graph.getVertex(id);
        } else if (elementType.equals(Tokens.EDGE)) {
            graphElementDeleted = graph.getEdge(id);
        }

        if (graphElementDeleted != null) {
            if (keysToDelete != null && keysToDelete.length() > 0) {
                // just delete keys from the element
                for (int ix = 0; ix < keysToDelete.length(); ix++) {
                    graphElementDeleted.removeProperty(keysToDelete.optString(ix));
                }
            } else {
                // delete the whole element
                if (elementType.equals(Tokens.VERTEX)) {
                    graph.removeVertex((Vertex) graphElementDeleted);
                } else if (elementType.equals(Tokens.EDGE)) {
                    graph.removeEdge((Edge) graphElementDeleted);
                }
            }
        }
    }

    private ExtensionResponse checkParameters(RexsterResourceContext context, JSONArray values, String type, String key) {
        final ExtensionMethod extMethod = context.getExtensionMethod();
        String errorMessage = null;

        if (values == null || values.length() == 0) {
            errorMessage = "the values parameter cannot be empty";
        else if ((type.equals("index") || type.equals("keyindex")) && key.isEmpty()) {
            errorMessage = "the key parameter cannot be empty";
        }

        return (errorMessage != null)
            ? ExtensionResponse.error(
                    errorMessage,
                    null,
                    Response.Status.BAD_REQUEST.getStatusCode(),
                    null,
                    extMethod != null ? generateErrorJson(extMethod.getExtensionApiAsJson()) : null)
            : null;
    }
}
TOP

Related Classes of com.tinkerpop.rexster.kibbles.batch.BatchExtension

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.