Support batch updating elements' property by multiple strategy (#493)

support strategies:
  SUM, BIGGER, SMALLER on Number or Date
  UNION, INTERSECTION, APPEND, ELIMINATE on Collection
This commit is contained in:
imbajin 2019-07-23 18:36:49 +08:00 committed by Jermy Li
parent ffb09f1359
commit 3fcfce2bb7
9 changed files with 552 additions and 55 deletions

View File

@ -148,10 +148,20 @@ public class API {
protected static void checkCreatingBody(
Collection<? extends Checkable> bodys) {
E.checkArgumentNotNull(bodys, "The request body can't be empty");
for (Checkable body : bodys) {
E.checkArgument(body != null,
"The batch body can't contain null record");
body.checkCreate(true);
}
}
protected static void checkUpdatingBody(
Collection<? extends Checkable> bodys) {
E.checkArgumentNotNull(bodys, "The request body can't be empty");
for (Checkable body : bodys) {
E.checkArgumentNotNull(body,
"The batch body can't contain null record");
body.checkCreate(true);
body.checkUpdate();
}
}

View File

@ -19,20 +19,27 @@
package com.baidu.hugegraph.api.graph;
import java.util.Map;
import java.util.concurrent.Callable;
import java.util.concurrent.atomic.AtomicInteger;
import org.apache.tinkerpop.gremlin.structure.Element;
import org.slf4j.Logger;
import com.baidu.hugegraph.HugeException;
import com.baidu.hugegraph.HugeGraph;
import com.baidu.hugegraph.api.API;
import com.baidu.hugegraph.api.schema.Checkable;
import com.baidu.hugegraph.config.HugeConfig;
import com.baidu.hugegraph.config.ServerOptions;
import com.baidu.hugegraph.metrics.MetricsUtil;
import com.baidu.hugegraph.server.RestServer;
import com.baidu.hugegraph.structure.HugeElement;
import com.baidu.hugegraph.util.E;
import com.baidu.hugegraph.util.Log;
import com.codahale.metrics.Meter;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty;
public class BatchAPI extends API {
@ -71,4 +78,82 @@ public class BatchAPI extends API {
batchWriteThreads.decrementAndGet();
}
}
@JsonIgnoreProperties(value = {"type"})
protected static abstract class JsonElement implements Checkable {
@JsonProperty("id")
public Object id;
@JsonProperty("label")
public String label;
@JsonProperty("properties")
public Map<String, Object> properties;
@JsonProperty("type")
public String type;
@Override
public abstract void checkCreate(boolean isBatch);
@Override
public abstract void checkUpdate();
protected abstract Object[] properties();
}
protected void updateExistElement(JsonElement oldElement,
JsonElement newElement,
Map<String, UpdateStrategy> strategies) {
if (oldElement == null) {
return;
}
E.checkArgument(newElement != null, "The json element can't be null");
for (Map.Entry<String, UpdateStrategy> kv : strategies.entrySet()) {
String key = kv.getKey();
UpdateStrategy updateStrategy = kv.getValue();
if (oldElement.properties.get(key) != null) {
Object value = updateStrategy.checkAndUpdateProperty(
oldElement.properties.get(key),
newElement.properties.get(key));
newElement.properties.put(key, value);
}
}
}
protected void updateExistElement(HugeGraph g,
Element oldElement,
JsonElement newElement,
Map<String, UpdateStrategy> strategies) {
if (oldElement == null) {
return;
}
E.checkArgument(newElement != null, "The json element can't be null");
for (Map.Entry<String, UpdateStrategy> kv : strategies.entrySet()) {
String key = kv.getKey();
UpdateStrategy updateStrategy = kv.getValue();
if (oldElement.property(key).isPresent() &&
newElement.properties.containsKey(key)) {
Object value = updateStrategy.checkAndUpdateProperty(
oldElement.property(key).value(),
newElement.properties.get(key));
value = g.propertyKey(key).convValue(value, false);
newElement.properties.put(key, value);
}
}
}
protected static void updateProperties(HugeElement element,
JsonElement jsonElement,
boolean append) {
for (Map.Entry<String, Object> e : jsonElement.properties.entrySet()) {
String key = e.getKey();
Object value = e.getValue();
if (append) {
element.property(key, value);
} else {
element.property(key).remove();
}
}
}
}

View File

@ -20,6 +20,7 @@
package com.baidu.hugegraph.api.graph;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
@ -50,8 +51,9 @@ import com.baidu.hugegraph.api.API;
import com.baidu.hugegraph.api.filter.CompressInterceptor.Compress;
import com.baidu.hugegraph.api.filter.DecompressInterceptor.Decompress;
import com.baidu.hugegraph.api.filter.StatusFilter.Status;
import com.baidu.hugegraph.api.schema.Checkable;
import com.baidu.hugegraph.backend.id.EdgeId;
import com.baidu.hugegraph.backend.id.Id;
import com.baidu.hugegraph.backend.id.SplicingIdGenerator;
import com.baidu.hugegraph.backend.tx.SchemaTransaction;
import com.baidu.hugegraph.config.HugeConfig;
import com.baidu.hugegraph.config.ServerOptions;
@ -64,10 +66,10 @@ import com.baidu.hugegraph.server.RestServer;
import com.baidu.hugegraph.structure.HugeEdge;
import com.baidu.hugegraph.structure.HugeVertex;
import com.baidu.hugegraph.type.HugeType;
import com.baidu.hugegraph.type.define.Directions;
import com.baidu.hugegraph.util.E;
import com.baidu.hugegraph.util.Log;
import com.codahale.metrics.annotation.Timed;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty;
@Path("graphs/{graph}/graph/edges")
@ -130,9 +132,9 @@ public class EdgeAPI extends BatchAPI {
List<JsonEdge> jsonEdges) {
LOG.debug("Graph [{}] create edges: {}", graph, jsonEdges);
checkCreatingBody(jsonEdges);
checkBatchSize(config, jsonEdges);
HugeGraph g = graph(manager, graph);
checkBatchSize(config, jsonEdges);
TriFunction<HugeGraph, Object, String, Vertex> getVertex =
checkVertex ? EdgeAPI::getVertex : EdgeAPI::newVertex;
@ -157,6 +159,64 @@ public class EdgeAPI extends BatchAPI {
});
}
/**
* Batch update steps are same like vertices
*/
@PUT
@Timed
@Decompress
@Path("batch")
@Consumes(APPLICATION_JSON)
@Produces(APPLICATION_JSON_WITH_CHARSET)
public String update(@Context HugeConfig config,
@Context GraphManager manager,
@PathParam("graph") String graph,
BatchEdgeRequest req) {
BatchEdgeRequest.checkUpdate(req);
LOG.debug("Graph [{}] update edges: {}", graph, req);
checkUpdatingBody(req.jsonEdges);
checkBatchSize(config, req.jsonEdges);
HugeGraph g = graph(manager, graph);
Map<Id, JsonEdge> map = new HashMap<>(req.jsonEdges.size());
TriFunction<HugeGraph, Object, String, Vertex> getVertex =
req.checkVertex ? EdgeAPI::getVertex : EdgeAPI::newVertex;
return this.commit(config, g, map.size(), () -> {
// 1.Put all newEdges' properties into map (combine first)
req.jsonEdges.forEach(newEdge -> {
Id newEdgeId = getEdgeId(g, newEdge);
JsonEdge oldEdge = map.get(newEdgeId);
this.updateExistElement(oldEdge, newEdge,
req.updateStrategies);
map.put(newEdgeId, newEdge);
});
// 2.Get all oldEdges and update with new ones
Object[] ids = map.keySet().toArray();
Iterator<Edge> oldEdges = g.edges(ids);
oldEdges.forEachRemaining(oldEdge -> {
JsonEdge newEdge = map.get(oldEdge.id());
this.updateExistElement(g, oldEdge, newEdge,
req.updateStrategies);
});
// 3.Add all finalEdges
List<Edge> edges = new ArrayList<>(map.size());
map.values().forEach(finalEdge -> {
Vertex srcVertex = getVertex.apply(g, finalEdge.source,
finalEdge.sourceLabel);
Vertex tgtVertex = getVertex.apply(g, finalEdge.target,
finalEdge.targetLabel);
edges.add(srcVertex.addEdge(finalEdge.label, tgtVertex,
finalEdge.properties()));
});
// If return ids, the ids.size() maybe different with the origins'
return manager.serializer(g).writeEdges(edges.iterator(), false);
});
}
@PUT
@Timed
@Path("{id}")
@ -191,17 +251,7 @@ public class EdgeAPI extends BatchAPI {
id, key);
}
commit(g, () -> {
for (Map.Entry<String, Object> e : jsonEdge.properties.entrySet()) {
String key = e.getKey();
Object value = e.getValue();
if (append) {
edge.property(key, value);
} else {
edge.property(key).remove();
}
}
});
commit(g, () -> updateProperties(edge, jsonEdge, append));
return manager.serializer(g).writeEdge(edge);
}
@ -312,9 +362,13 @@ public class EdgeAPI extends BatchAPI {
int max = config.get(ServerOptions.MAX_EDGES_PER_BATCH);
if (edges.size() > max) {
throw new IllegalArgumentException(String.format(
"Too many counts of edges for one time post, " +
"Too many edges for one time post, " +
"the maximum number is '%s'", max));
}
if (edges.size() == 0) {
throw new IllegalArgumentException(String.format(
"The number of edges can't be 0"));
}
}
private static Vertex getVertex(HugeGraph graph, Object id, String label) {
@ -350,25 +404,77 @@ public class EdgeAPI extends BatchAPI {
}
}
@JsonIgnoreProperties(value = {"type"})
private static class JsonEdge implements Checkable {
private Id getEdgeId(HugeGraph g, JsonEdge newEdge) {
if (newEdge.id != null) {
return EdgeId.parse(newEdge.id.toString());
}
String sortKeys = "";
Id labelId = g.edgeLabel(newEdge.label).id();
List<Id> sortKeyIds = g.edgeLabel(labelId).sortKeys();
if (!sortKeyIds.isEmpty()) {
List<Object> sortKeyValues = new ArrayList<>(sortKeyIds.size());
sortKeyIds.forEach(skId -> {
String sortKey = g.propertyKey(skId).name();
Object sortKeyValue = newEdge.properties.get(sortKey);
E.checkArgument(sortKeyValue != null,
"The value of sort key '%s' can't be null",
sortKey);
sortKeyValues.add(sortKeyValue);
});
sortKeys = SplicingIdGenerator.concatValues(sortKeyValues);
}
// TODO: How to get Direction from JsonEdge easily? or any better way?
EdgeId edgeId = new EdgeId(HugeVertex.getIdValue(newEdge.source),
Directions.OUT, labelId, sortKeys,
HugeVertex.getIdValue(newEdge.target));
return edgeId;
}
protected static class BatchEdgeRequest {
@JsonProperty("edges")
public List<JsonEdge> jsonEdges;
@JsonProperty("update_strategies")
public Map<String, UpdateStrategy> updateStrategies;
@JsonProperty("check_vertex")
public boolean checkVertex = false;
@JsonProperty("create_if_not_exist")
public boolean createIfNotExist = true;
private static void checkUpdate(BatchEdgeRequest req) {
E.checkArgumentNotNull(req, "BatchEdgeRequest can't be null");
E.checkArgumentNotNull(req.jsonEdges,
"Parameter 'edges' can't be null");
E.checkArgument(req.updateStrategies != null &&
!req.updateStrategies.isEmpty(),
"Parameter 'update_strategies' can't be empty");
E.checkArgument(req.createIfNotExist == true,
"Parameter 'create_if_not_exist' " +
"dose not support false now");
}
@Override
public String toString() {
return String.format("BatchEdgeRequest{jsonEdges=%s," +
"updateStrategies=%s," +
"checkVertex=%s,createIfNotExist=%s}",
this.jsonEdges, this.updateStrategies,
this.checkVertex, this.createIfNotExist);
}
}
private static class JsonEdge extends JsonElement {
@JsonProperty("id")
public String id;
@JsonProperty("outV")
public Object source;
@JsonProperty("outVLabel")
public String sourceLabel;
@JsonProperty("label")
public String label;
@JsonProperty("inV")
public Object target;
@JsonProperty("inVLabel")
public String targetLabel;
@JsonProperty("properties")
public Map<String, Object> properties;
@JsonProperty("type")
public String type;
@Override
public void checkCreate(boolean isBatch) {
@ -405,6 +511,7 @@ public class EdgeAPI extends BatchAPI {
}
}
@Override
public Object[] properties() {
return API.properties(this.properties);
}

View File

@ -0,0 +1,193 @@
/*
* Copyright 2017 HugeGraph Authors
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with this
* work for additional information regarding copyright ownership. The ASF
* licenses this file to You 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.
*/
package com.baidu.hugegraph.api.graph;
import java.math.BigDecimal;
import java.util.Collection;
import java.util.Date;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import com.baidu.hugegraph.util.E;
import com.baidu.hugegraph.util.NumericUtil;
import com.google.common.collect.Sets;
public enum UpdateStrategy {
// Only number support sum
SUM {
@Override
Object updatePropertyValue(Object oldProperty, Object newProperty) {
// TODO: Improve preformance? (like write a method in common module)
BigDecimal oldNumber = new BigDecimal(oldProperty.toString());
BigDecimal newNumber = new BigDecimal(newProperty.toString());
return oldNumber.add(newNumber);
}
@Override
void checkPropertyType(Object oldProperty, Object newProperty) {
E.checkArgument(oldProperty instanceof Number &&
newProperty instanceof Number,
this.formatError(oldProperty, newProperty,
"Number"));
}
},
// Only Date & Number support compare
BIGGER {
@Override
Object updatePropertyValue(Object oldProperty, Object newProperty) {
return compareNumber(oldProperty, newProperty, BIGGER);
}
@Override
void checkPropertyType(Object oldProperty, Object newProperty) {
E.checkArgument((oldProperty instanceof Date ||
oldProperty instanceof Number) &&
(newProperty instanceof Date ||
newProperty instanceof Number),
this.formatError(oldProperty, newProperty,
"Date or Number"));
}
},
SMALLER {
@Override
Object updatePropertyValue(Object oldProperty, Object newProperty) {
return compareNumber(oldProperty, newProperty, SMALLER);
}
@Override
void checkPropertyType(Object oldProperty, Object newProperty) {
E.checkArgument((oldProperty instanceof Date ||
oldProperty instanceof Number) &&
(newProperty instanceof Date ||
newProperty instanceof Number),
this.formatError(oldProperty, newProperty,
"Date or Number"));
}
},
// Only Set support union & intersection
UNION {
@Override
Object updatePropertyValue(Object oldProperty, Object newProperty) {
return combineSet(oldProperty, newProperty, UNION);
}
@Override
void checkPropertyType(Object oldProperty, Object newProperty) {
// JsonElements are always List-type, so allows two type now.
this.checkCollectionType(oldProperty, newProperty);
}
},
INTERSECTION {
@Override
Object updatePropertyValue(Object oldProperty, Object newProperty) {
return combineSet(oldProperty, newProperty, INTERSECTION);
}
@Override
void checkPropertyType(Object oldProperty, Object newProperty) {
this.checkCollectionType(oldProperty, newProperty);
}
},
// Batch update Set should use union because of higher efficiency
APPEND {
@Override
@SuppressWarnings({ "rawtypes", "unchecked" })
Object updatePropertyValue(Object oldProperty, Object newProperty) {
((Collection) oldProperty).addAll((Collection) newProperty);
return oldProperty;
}
@Override
void checkPropertyType(Object oldProperty, Object newProperty) {
this.checkCollectionType(oldProperty, newProperty);
}
},
ELIMINATE {
@Override
@SuppressWarnings({ "rawtypes", "unchecked" })
Object updatePropertyValue(Object oldProperty, Object newProperty) {
((Collection) oldProperty).removeAll((Collection) newProperty);
return oldProperty;
}
@Override
void checkPropertyType(Object oldProperty, Object newProperty) {
this.checkCollectionType(oldProperty, newProperty);
}
};
abstract Object updatePropertyValue(Object oldProperty, Object newProperty);
abstract void checkPropertyType(Object oldProperty, Object newProperty);
public Object checkAndUpdateProperty(Object oldProperty,
Object newProperty) {
this.checkPropertyType(oldProperty, newProperty);
return this.updatePropertyValue(oldProperty, newProperty);
}
protected String formatError(Object oldProperty, Object newProperty,
String className) {
return String.format("Property type must be %s for strategy %s, " +
"but got type %s, %s", className, this,
oldProperty.getClass().getSimpleName(),
newProperty.getClass().getSimpleName());
}
protected void checkCollectionType(Object oldProperty,
Object newProperty) {
E.checkArgument((oldProperty instanceof Set ||
oldProperty instanceof List) &&
(newProperty instanceof Set ||
newProperty instanceof List),
this.formatError(oldProperty, newProperty,
"Set or List"));
}
protected static Object compareNumber(Object oldProperty,
Object newProperty,
UpdateStrategy strategy) {
Number oldNum = NumericUtil.convertToNumber(oldProperty);
Number newNum = NumericUtil.convertToNumber(newProperty);
int result = NumericUtil.compareNumber(oldNum, newNum);
return strategy == BIGGER ? (result > 0 ? oldProperty : newProperty) :
(result < 0 ? oldProperty : newProperty);
}
protected static Set<?> combineSet(Object oldProperty, Object newProperty,
UpdateStrategy strategy) {
Set<?> oldSet = oldProperty instanceof Set ?
(Set<?>) oldProperty :
new HashSet<>((List<?>) oldProperty);
Set<?> newSet = newProperty instanceof Set ?
(Set<?>) newProperty :
new HashSet<>((List<?>) newProperty);
return strategy == UNION ? Sets.union(oldSet, newSet) :
Sets.intersection(oldSet, newSet);
}
}

View File

@ -21,6 +21,7 @@ package com.baidu.hugegraph.api.graph;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
@ -48,8 +49,8 @@ import com.baidu.hugegraph.api.API;
import com.baidu.hugegraph.api.filter.CompressInterceptor.Compress;
import com.baidu.hugegraph.api.filter.DecompressInterceptor.Decompress;
import com.baidu.hugegraph.api.filter.StatusFilter.Status;
import com.baidu.hugegraph.api.schema.Checkable;
import com.baidu.hugegraph.backend.id.Id;
import com.baidu.hugegraph.backend.id.SplicingIdGenerator;
import com.baidu.hugegraph.config.HugeConfig;
import com.baidu.hugegraph.config.ServerOptions;
import com.baidu.hugegraph.core.GraphManager;
@ -58,11 +59,11 @@ import com.baidu.hugegraph.schema.VertexLabel;
import com.baidu.hugegraph.server.RestServer;
import com.baidu.hugegraph.structure.HugeVertex;
import com.baidu.hugegraph.type.HugeType;
import com.baidu.hugegraph.type.define.IdStrategy;
import com.baidu.hugegraph.util.E;
import com.baidu.hugegraph.util.JsonUtil;
import com.baidu.hugegraph.util.Log;
import com.codahale.metrics.annotation.Timed;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty;
@Path("graphs/{graph}/graph/vertices")
@ -101,9 +102,9 @@ public class VertexAPI extends BatchAPI {
List<JsonVertex> jsonVertices) {
LOG.debug("Graph [{}] create vertices: {}", graph, jsonVertices);
checkCreatingBody(jsonVertices);
checkBatchSize(config, jsonVertices);
HugeGraph g = graph(manager, graph);
checkBatchSize(config, jsonVertices);
return this.commit(config, g, jsonVertices.size(), () -> {
List<String> ids = new ArrayList<>(jsonVertices.size());
@ -114,6 +115,64 @@ public class VertexAPI extends BatchAPI {
});
}
/**
* Batch update steps like:
* 1. Get all newVertices' ID & combine first
* 2. Get all oldVertices & update
* 3. Add the final vertex together
*/
@PUT
@Timed
@Decompress
@Path("batch")
@Consumes(APPLICATION_JSON)
@Produces(APPLICATION_JSON_WITH_CHARSET)
public String update(@Context HugeConfig config,
@Context GraphManager manager,
@PathParam("graph") String graph,
BatchVertexRequest req) {
BatchVertexRequest.checkUpdate(req);
LOG.debug("Graph [{}] update vertices: {}", graph, req);
checkUpdatingBody(req.jsonVertices);
checkBatchSize(config, req.jsonVertices);
HugeGraph g = graph(manager, graph);
Map<Id, JsonVertex> map = new HashMap<>(req.jsonVertices.size());
return this.commit(config, g, map.size(), () -> {
/*
* 1.Put all newVertices' properties into map (combine first)
* - Consider primary-key & user-define ID mode first
*/
req.jsonVertices.forEach(newVertex -> {
Id newVertexId = getVertexId(g, newVertex);
JsonVertex oldVertex = map.get(newVertexId);
this.updateExistElement(oldVertex, newVertex,
req.updateStrategies);
map.put(newVertexId, newVertex);
});
// 2.Get all oldVertices and update with new vertices
Object[] ids = map.keySet().toArray();
Iterator<Vertex> oldVertices = g.vertices(ids);
oldVertices.forEachRemaining(oldVertex -> {
JsonVertex newVertex = map.get(oldVertex.id());
this.updateExistElement(g, oldVertex, newVertex,
req.updateStrategies);
});
// 3.Add finalVertices and return them
List<Vertex> vertices = new ArrayList<>(map.size());
map.values().forEach(finalVertex -> {
vertices.add(g.addVertex(finalVertex.properties()));
});
// If return ids, the ids.size() maybe different with the origins'
return manager.serializer(g)
.writeVertices(vertices.iterator(), false);
});
}
@PUT
@Timed
@Path("{id}")
@ -143,18 +202,7 @@ public class VertexAPI extends BatchAPI {
id, key);
}
commit(g, () -> {
for (Map.Entry<String, Object> e :
jsonVertex.properties.entrySet()) {
String key = e.getKey();
Object value = e.getValue();
if (append) {
vertex.property(key, value);
} else {
vertex.property(key).remove();
}
}
});
commit(g, () -> updateProperties(vertex, jsonVertex, append));
return manager.serializer(g).writeVertex(vertex);
}
@ -259,22 +307,74 @@ public class VertexAPI extends BatchAPI {
int max = config.get(ServerOptions.MAX_VERTICES_PER_BATCH);
if (vertices.size() > max) {
throw new IllegalArgumentException(String.format(
"Too many counts of vertices for one time post, " +
"Too many vertices for one time post, " +
"the maximum number is '%s'", max));
}
if (vertices.size() == 0) {
throw new IllegalArgumentException(String.format(
"The number of vertices can't be 0"));
}
}
@JsonIgnoreProperties(value = {"type"})
private static class JsonVertex implements Checkable {
private Id getVertexId(HugeGraph g, JsonVertex vertex) {
VertexLabel vertexLabel = g.vertexLabel(vertex.label);
String labelId = vertexLabel.id().asString();
IdStrategy idStrategy = vertexLabel.idStrategy();
E.checkArgument(idStrategy != IdStrategy.AUTOMATIC,
"Automatic Id strategy is not supported now");
@JsonProperty("id")
public Object id;
@JsonProperty("label")
public String label;
@JsonProperty("properties")
public Map<String, Object> properties;
@JsonProperty("type")
public String type;
if (idStrategy == IdStrategy.PRIMARY_KEY) {
List<Id> pkIds = vertexLabel.primaryKeys();
List<Object> pkValues = new ArrayList<>(pkIds.size());
for (Id pkId : pkIds) {
String propertyKey = g.propertyKey(pkId).name();
Object propertyValue = vertex.properties.get(propertyKey);
E.checkArgument(propertyValue != null,
"The value of primary key '%s' can't be null",
propertyKey);
pkValues.add(propertyValue);
}
String value = SplicingIdGenerator.concatValues(pkValues);
return SplicingIdGenerator.splicing(labelId, value);
} else {
assert idStrategy == IdStrategy.CUSTOMIZE_NUMBER ||
idStrategy == IdStrategy.CUSTOMIZE_STRING;
return HugeVertex.getIdValue(vertex.id);
}
}
private static class BatchVertexRequest {
@JsonProperty("vertices")
public List<JsonVertex> jsonVertices;
@JsonProperty("update_strategies")
public Map<String, UpdateStrategy> updateStrategies;
@JsonProperty("create_if_not_exist")
public boolean createIfNotExist = true;
private static void checkUpdate(BatchVertexRequest req) {
E.checkArgumentNotNull(req, "BatchVertexRequest can't be null");
E.checkArgumentNotNull(req.jsonVertices,
"Parameter 'vertices' can't be null");
E.checkArgument(req.updateStrategies != null &&
!req.updateStrategies.isEmpty(),
"Parameter 'update_strategies' can't be empty");
E.checkArgument(req.createIfNotExist == true,
"Parameter 'create_if_not_exist' " +
"dose not support false now");
}
@Override
public String toString() {
return String.format("BatchVertexRequest{jsonVertices=%s," +
"updateStrategies=%s,createIfNotExist=%s}",
this.jsonVertices, this.updateStrategies,
this.createIfNotExist);
}
}
private static class JsonVertex extends JsonElement {
@Override
public void checkCreate(boolean isBatch) {
@ -297,6 +397,7 @@ public class VertexAPI extends BatchAPI {
}
}
@Override
public Object[] properties() {
Object[] props = API.properties(this.properties);
List<Object> list = new ArrayList<>(Arrays.asList(props));

View File

@ -144,7 +144,7 @@ public class NeighborRankAPI extends API {
"The degree must be > 0, but got: %s",
this.degree);
E.checkArgument(this.top > 0 && this.top <= MAX_TOP,
"The top of each layer cannot exceed %s", MAX_TOP);
"The top of each layer can't exceed %s", MAX_TOP);
Map<Id, String> labelIds = new HashMap<>();
if (this.labels != null) {
for (String label : this.labels) {

View File

@ -88,6 +88,7 @@ public final class ApiVersion {
* version 0.10:
* [0.39] Issue-522: Add profile RESTful API
* [0.40] Issue-523: Add source_in_ring args for rings RESTful API
* [0.41] Issue-493: Support batch updating properties by multiple strategy
*/
// The second parameter of Version.of() is for IDE running without JAR

View File

@ -1150,7 +1150,7 @@ public class BinarySerializer extends AbstractSerializer {
private byte[] writeIds(Collection<Id> ids) {
E.checkState(ids.size() <= BytesBuffer.UINT16_MAX,
"The number of properties of vertex/edge label " +
"cannot exceed '%s'", BytesBuffer.UINT16_MAX);
"can't exceed '%s'", BytesBuffer.UINT16_MAX);
int size = 2;
for (Id id : ids) {
size += (1 + id.length());

View File

@ -145,7 +145,7 @@ public class RocksDBSstSessions extends RocksDBSessions {
@Override
protected synchronized void doClose() {
final String NO_ENTRIES = "Cannot create sst file with no entries";
final String NO_ENTRIES = "Can't create sst file with no entries";
for (SstFileWriter sst : this.tables.values()) {
E.checkState(sst.isOwningHandle(), "SstFileWriter closed");