/*
*
* * Copyright 2014 Orient Technologies LTD (info(at)orientechnologies.com)
* *
* * Licensed under the Apache License, Version 2.0 (the "License");
* * you may not use this file except in compliance with the License.
* * You may obtain a copy of the License at
* *
* * http://www.apache.org/licenses/LICENSE-2.0
* *
* * Unless required by applicable law or agreed to in writing, software
* * distributed under the License is distributed on an "AS IS" BASIS,
* * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* * See the License for the specific language governing permissions and
* * limitations under the License.
* *
* * For more information: http://www.orientechnologies.com
*
*/
package com.orientechnologies.orient.graph.sql;
import com.orientechnologies.common.util.OPair;
import com.orientechnologies.orient.core.command.OCommandDistributedReplicateRequest;
import com.orientechnologies.orient.core.command.OCommandRequest;
import com.orientechnologies.orient.core.command.OCommandRequestText;
import com.orientechnologies.orient.core.db.document.ODatabaseDocument;
import com.orientechnologies.orient.core.db.record.OIdentifiable;
import com.orientechnologies.orient.core.exception.OCommandExecutionException;
import com.orientechnologies.orient.core.metadata.OMetadataInternal;
import com.orientechnologies.orient.core.metadata.schema.OClass;
import com.orientechnologies.orient.core.sql.*;
import com.orientechnologies.orient.core.sql.filter.OSQLFilterItem;
import com.orientechnologies.orient.core.sql.functions.OSQLFunctionRuntime;
import com.tinkerpop.blueprints.impls.orient.OrientBaseGraph;
import com.tinkerpop.blueprints.impls.orient.OrientEdge;
import com.tinkerpop.blueprints.impls.orient.OrientEdgeType;
import com.tinkerpop.blueprints.impls.orient.OrientVertex;
import java.util.*;
/**
* SQL CREATE EDGE command.
*
* @author Luca Garulli
*/
public class OCommandExecutorSQLCreateEdge extends OCommandExecutorSQLSetAware implements OCommandDistributedReplicateRequest {
public static final String NAME = "CREATE EDGE";
private static final String KEYWORD_BATCH = "BATCH";
private String from;
private String to;
private OClass clazz;
private String edgeLabel;
private String clusterName;
private List<OPair<String, Object>> fields;
private int batch = 100;
@SuppressWarnings("unchecked")
public OCommandExecutorSQLCreateEdge parse(final OCommandRequest iRequest) {
final OCommandRequestText textRequest = (OCommandRequestText) iRequest;
String queryText = textRequest.getText();
String originalQuery = queryText;
try {
// System.out.println("NEW PARSER FROM: " + queryText);
queryText = preParse(queryText, iRequest);
// System.out.println("NEW PARSER TO: " + queryText);
textRequest.setText(queryText);
final ODatabaseDocument database = getDatabase();
init((OCommandRequestText) iRequest);
parserRequiredKeyword("CREATE");
parserRequiredKeyword("EDGE");
String className = null;
String tempLower = parseOptionalWord(false);
String temp = tempLower == null ? null : tempLower.toUpperCase();
while (temp != null) {
if (temp.equals("CLUSTER")) {
clusterName = parserRequiredWord(false);
} else if (temp.equals(KEYWORD_FROM)) {
from = parserRequiredWord(false, "Syntax error", " =><,\r\n", true);
} else if (temp.equals("TO")) {
to = parserRequiredWord(false, "Syntax error", " =><,\r\n", true);
} else if (temp.equals(KEYWORD_SET)) {
fields = new ArrayList<OPair<String, Object>>();
parseSetFields(clazz, fields);
} else if (temp.equals(KEYWORD_CONTENT)) {
parseContent();
} else if (temp.equals(KEYWORD_BATCH)) {
temp = parserNextWord(true);
if (temp != null)
batch = Integer.parseInt(temp);
} else if (className == null && temp.length() > 0) {
className = tempLower;
OrientBaseGraph graph = OrientBaseGraph.getActiveGraph();
if (graph != null && graph.isUseClassForEdgeLabel()) {
clazz = ((OMetadataInternal) database.getMetadata()).getImmutableSchemaSnapshot().getClass(temp);
} else {
clazz = ((OMetadataInternal) database.getMetadata()).getImmutableSchemaSnapshot().getClass("E");
}
}
temp = parseOptionalWord(true);
if (parserIsEnded())
break;
}
if (className == null) {
// ASSIGN DEFAULT CLASS
className = OrientEdgeType.CLASS_NAME;
clazz = ((OMetadataInternal) database.getMetadata()).getImmutableSchemaSnapshot().getClass(className);
}
// GET/CHECK CLASS NAME
if (clazz == null)
throw new OCommandSQLParsingException("Class '" + className + "' was not found");
edgeLabel = className;
} finally {
textRequest.setText(originalQuery);
}
return this;
}
/**
* Execute the command and return the ODocument object created.
*/
public Object execute(final Map<Object, Object> iArgs) {
if (clazz == null)
throw new OCommandExecutionException("Cannot execute the command because it has not been parsed yet");
return OGraphCommandExecutorSQLFactory.runInConfiguredTxMode(new OGraphCommandExecutorSQLFactory.GraphCallBack<List<Object>>() {
@Override
public List<Object> call(OrientBaseGraph graph) {
final Set<OIdentifiable> fromIds = OSQLEngine.getInstance().parseRIDTarget(graph.getRawGraph(), from, context, iArgs);
final Set<OIdentifiable> toIds = OSQLEngine.getInstance().parseRIDTarget(graph.getRawGraph(), to, context, iArgs);
// CREATE EDGES
final List<Object> edges = new ArrayList<Object>();
for (OIdentifiable from : fromIds) {
final OrientVertex fromVertex = graph.getVertex(from);
if (fromVertex == null)
throw new OCommandExecutionException("Source vertex '" + from + "' not exists");
for (OIdentifiable to : toIds) {
final OrientVertex toVertex;
if (from.equals(to)) {
toVertex = fromVertex;
} else {
toVertex = graph.getVertex(to);
}
if (fields != null)
// EVALUATE FIELDS
for (final OPair<String, Object> f : fields) {
if (f.getValue() instanceof OSQLFunctionRuntime) {
f.setValue(((OSQLFunctionRuntime) f.getValue()).getValue(to, null, context));
}else if(f.getValue() instanceof OSQLFilterItem){
f.setValue(((OSQLFilterItem) f.getValue()).getValue(to, null, context));
}
}
OrientEdge edge = null;
if (content != null) {
if (fields != null)
// MERGE CONTENT WITH FIELDS
fields.addAll(OPair.convertFromMap(content.toMap()));
else
fields = OPair.convertFromMap(content.toMap());
}
edge = fromVertex.addEdge(null, toVertex, edgeLabel, clusterName, fields);
if (fields != null && !fields.isEmpty()) {
if (edge.isLightweight())
edge.convertToDocument();
OSQLHelper.bindParameters(edge.getRecord(), fields, new OCommandParameters(iArgs), context);
}
edge.save(clusterName);
edges.add(edge);
if (batch > 0 && edges.size() % batch == 0) {
graph.commit();
graph.begin();
}
}
}
if (edges.isEmpty()) {
if (fromIds.isEmpty())
throw new OCommandExecutionException("No edge has been created because no source vertices");
else if (toIds.isEmpty())
throw new OCommandExecutionException("No edge has been created because no target vertices");
throw new OCommandExecutionException("No edge has been created between " + fromIds + " and " + toIds);
}
return edges;
}
});
}
@Override
public QUORUM_TYPE getQuorumType() {
return QUORUM_TYPE.WRITE;
}
@Override
public Set<String> getInvolvedClusters() {
if (clazz != null)
return Collections.singleton(getDatabase().getClusterNameById(clazz.getClusterSelection().getCluster(clazz, null)));
else if (clusterName != null)
return getInvolvedClustersOfClusters(Collections.singleton(clusterName));
return Collections.EMPTY_SET;
}
@Override
public OCommandDistributedReplicateRequest.DISTRIBUTED_EXECUTION_MODE getDistributedExecutionMode() {
return DISTRIBUTED_EXECUTION_MODE.LOCAL;
}
@Override
public String getSyntax() {
return "CREATE EDGE [<class>] [CLUSTER <cluster>] FROM <rid>|(<query>|[<rid>]*) TO <rid>|(<query>|[<rid>]*) [SET <field> = <expression>[,]*]|CONTENT {<JSON>} [RETRY <retry> [WAIT <pauseBetweenRetriesInMs]] [BATCH <batch-size>]";
}
}