blob: 035499c5983c879936e932836c98bcecfcdb745b [file] [log] [blame]
/*
* 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 org.apache.hugegraph.serializer;
import java.io.ByteArrayOutputStream;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import org.apache.hugegraph.HugeException;
import org.apache.hugegraph.api.API;
import org.apache.hugegraph.auth.SchemaDefine.AuthElement;
import org.apache.hugegraph.backend.id.Id;
import org.apache.hugegraph.backend.page.PageInfo;
import org.apache.hugegraph.iterator.Metadatable;
import org.apache.hugegraph.schema.EdgeLabel;
import org.apache.hugegraph.schema.IndexLabel;
import org.apache.hugegraph.schema.PropertyKey;
import org.apache.hugegraph.schema.SchemaElement;
import org.apache.hugegraph.schema.VertexLabel;
import org.apache.hugegraph.traversal.algorithm.CustomizedCrosspointsTraverser.CrosspointsPaths;
import org.apache.hugegraph.traversal.algorithm.FusiformSimilarityTraverser.SimilarsMap;
import org.apache.hugegraph.traversal.algorithm.HugeTraverser;
import org.apache.hugegraph.traversal.algorithm.SingleSourceShortestPathTraverser.NodeWithWeight;
import org.apache.hugegraph.traversal.algorithm.SingleSourceShortestPathTraverser.WeightedPaths;
import org.apache.hugegraph.traversal.optimize.TraversalUtil;
import org.apache.hugegraph.util.JsonUtil;
import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversal;
import org.apache.tinkerpop.gremlin.structure.Edge;
import org.apache.tinkerpop.gremlin.structure.Vertex;
import org.apache.tinkerpop.gremlin.structure.util.CloseableIterator;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
public class JsonSerializer implements Serializer {
private static final int LBUF_SIZE = 1024;
private static final String MEASURE_KEY = "measure";
private static final JsonSerializer INSTANCE = new JsonSerializer();
private Map<String, Object> apiMeasure = null;
private JsonSerializer() {
}
private JsonSerializer(Map<String, Object> apiMeasure) {
this.apiMeasure = apiMeasure;
}
public static JsonSerializer instance() {
return INSTANCE;
}
public static JsonSerializer instance(Map<String, Object> apiMeasure) {
return new JsonSerializer(apiMeasure);
}
@Override
public String writeMap(Map<?, ?> map) {
ImmutableMap.Builder<Object, Object> builder = ImmutableMap.builder();
builder.putAll(map);
if (this.apiMeasure != null) {
builder.put(MEASURE_KEY, this.apiMeasure);
}
return JsonUtil.toJson(builder.build());
}
@Override
public String writeList(String label, Collection<?> list) {
try (ByteArrayOutputStream out = new ByteArrayOutputStream(LBUF_SIZE)) {
out.write(String.format("{\"%s\": ", label).getBytes(API.CHARSET));
out.write(JsonUtil.toJson(list).getBytes(API.CHARSET));
if (this.apiMeasure != null) {
out.write(String.format(",\"%s\": ", MEASURE_KEY).getBytes(API.CHARSET));
out.write(JsonUtil.toJson(this.apiMeasure).getBytes(API.CHARSET));
}
out.write("}".getBytes(API.CHARSET));
return out.toString(API.CHARSET);
} catch (Exception e) {
throw new HugeException("Failed to serialize %s", e, label);
}
}
private String writeIterator(String label, Iterator<?> iter,
boolean paging) {
// Early throw if needed
iter.hasNext();
// Serialize Iterator
try (ByteArrayOutputStream out = new ByteArrayOutputStream(LBUF_SIZE)) {
out.write("{".getBytes(API.CHARSET));
out.write(String.format("\"%s\":[", label).getBytes(API.CHARSET));
// Write data
boolean first = true;
while (iter.hasNext()) {
if (!first) {
out.write(",".getBytes(API.CHARSET));
} else {
first = false;
}
out.write(JsonUtil.toJson(iter.next()).getBytes(API.CHARSET));
}
out.write("]".getBytes(API.CHARSET));
// Write page
if (paging) {
String page;
if (iter instanceof GraphTraversal<?, ?>) {
page = TraversalUtil.page((GraphTraversal<?, ?>) iter);
} else if (iter instanceof Metadatable) {
page = PageInfo.pageInfo(iter);
} else {
throw new HugeException("Invalid paging iterator: %s",
iter.getClass());
}
if (page != null) {
page = String.format(",\"page\": \"%s\"", page);
} else {
page = ",\"page\": null";
}
out.write(page.getBytes(API.CHARSET));
}
if (this.apiMeasure != null) {
out.write(String.format(",\"%s\":[", MEASURE_KEY).getBytes(API.CHARSET));
out.write(JsonUtil.toJson(this.apiMeasure).getBytes(API.CHARSET));
}
out.write("}".getBytes(API.CHARSET));
return out.toString(API.CHARSET);
} catch (HugeException e) {
throw e;
} catch (Exception e) {
throw new HugeException("Failed to serialize %s", e, label);
} finally {
try {
CloseableIterator.closeIterator(iter);
} catch (Exception e) {
throw new HugeException("Failed to close for %s", e, label);
}
}
}
@Override
public String writePropertyKey(PropertyKey propertyKey) {
return JsonUtil.toJson(propertyKey);
}
@Override
public String writeTaskWithSchema(
SchemaElement.TaskWithSchema taskWithSchema) {
StringBuilder builder = new StringBuilder();
long id = taskWithSchema.task() == null ?
0L : taskWithSchema.task().asLong();
SchemaElement schemaElement = taskWithSchema.schemaElement();
String type;
String schema;
if (schemaElement instanceof PropertyKey) {
type = "property_key";
schema = this.writePropertyKey((PropertyKey) schemaElement);
} else if (schemaElement instanceof IndexLabel) {
type = "index_label";
schema = this.writeIndexlabel((IndexLabel) schemaElement);
} else {
throw new HugeException("Invalid schema element '%s' in " +
"TaskWithSchema, only support " +
"[PropertyKey, IndexLabel]", schemaElement);
}
builder.append("{\"").append(type).append("\": ")
.append(schema).append(", \"task_id\": ")
.append(id);
if (this.apiMeasure != null) {
builder.append(String.format(",\"%s\":[", MEASURE_KEY));
builder.append(JsonUtil.toJson(this.apiMeasure));
}
return builder.append("}").toString();
}
@Override
public String writePropertyKeys(List<PropertyKey> propertyKeys) {
return writeList("propertykeys", propertyKeys);
}
@Override
public String writeVertexLabel(VertexLabel vertexLabel) {
return JsonUtil.toJson(vertexLabel);
}
@Override
public String writeVertexLabels(List<VertexLabel> vertexLabels) {
return writeList("vertexlabels", vertexLabels);
}
@Override
public String writeEdgeLabel(EdgeLabel edgeLabel) {
return JsonUtil.toJson(edgeLabel);
}
@Override
public String writeEdgeLabels(List<EdgeLabel> edgeLabels) {
return writeList("edgelabels", edgeLabels);
}
@Override
public String writeIndexlabel(IndexLabel indexLabel) {
return JsonUtil.toJson(indexLabel);
}
@Override
public String writeIndexlabels(List<IndexLabel> indexLabels) {
return writeList("indexlabels", indexLabels);
}
@Override
public String writeVertex(Vertex vertex) {
return JsonUtil.toJson(vertex);
}
@Override
public String writeVertices(Iterator<Vertex> vertices, boolean paging) {
return this.writeIterator("vertices", vertices, paging);
}
@Override
public String writeEdge(Edge edge) {
return JsonUtil.toJson(edge);
}
@Override
public String writeEdges(Iterator<Edge> edges, boolean paging) {
return this.writeIterator("edges", edges, paging);
}
@Override
public String writeIds(List<Id> ids) {
return JsonUtil.toJson(ids);
}
@Override
public String writeAuthElement(AuthElement elem) {
return this.writeMap(elem.asMap());
}
@Override
public <V extends AuthElement> String writeAuthElements(String label,
List<V> elems) {
List<Object> list = new ArrayList<>(elems.size());
for (V elem : elems) {
list.add(elem.asMap());
}
return this.writeList(label, list);
}
@Override
public String writePaths(String name, Collection<HugeTraverser.Path> paths,
boolean withCrossPoint, Iterator<?> vertices,
Iterator<?> edges) {
List<Map<String, Object>> pathList = new ArrayList<>(paths.size());
for (HugeTraverser.Path path : paths) {
pathList.add(path.toMap(withCrossPoint));
}
ImmutableMap.Builder<Object, Object> builder = ImmutableMap.builder();
builder.put(name, pathList);
if (vertices != null) {
builder.put("vertices", vertices);
}
if (edges != null) {
builder.put("edges", edges);
}
if (this.apiMeasure != null) {
builder.put(MEASURE_KEY, this.apiMeasure);
}
return JsonUtil.toJson(builder.build());
}
@Override
public String writeCrosspoints(CrosspointsPaths paths,
Iterator<?> vertices,
Iterator<?> edges,
boolean withPath) {
List<Map<String, Object>> pathList;
if (withPath) {
pathList = new ArrayList<>();
for (HugeTraverser.Path path : paths.paths()) {
pathList.add(path.toMap(false));
}
} else {
pathList = ImmutableList.of();
}
ImmutableMap.Builder<Object, Object> builder = ImmutableMap.builder()
.put("crosspoints",
paths.crosspoints())
.put("paths", pathList)
.put("vertices", vertices)
.put("edges", edges);
if (this.apiMeasure != null) {
builder.put(MEASURE_KEY, this.apiMeasure);
}
return JsonUtil.toJson(builder.build());
}
@Override
public String writeSimilars(SimilarsMap similars,
Iterator<?> vertices) {
ImmutableMap.Builder<Object, Object> builder = ImmutableMap.builder()
.put("similars",
similars.toMap())
.put("vertices", vertices);
if (this.apiMeasure != null) {
builder.put(MEASURE_KEY, this.apiMeasure);
}
return JsonUtil.toJson(builder.build());
}
@Override
public String writeWeightedPath(NodeWithWeight path, Iterator<?> vertices,
Iterator<?> edges) {
Map<String, Object> pathMap = path == null ?
ImmutableMap.of() : path.toMap();
ImmutableMap.Builder<Object, Object> builder = ImmutableMap.builder()
.put("path", pathMap)
.put("vertices", vertices)
.put("edges", edges);
if (this.apiMeasure != null) {
builder.put(MEASURE_KEY, this.apiMeasure);
}
return JsonUtil.toJson(builder.build());
}
@Override
public String writeWeightedPaths(WeightedPaths paths, Iterator<?> vertices,
Iterator<?> edges) {
Map<Id, Map<String, Object>> pathMap = paths == null ?
ImmutableMap.of() :
paths.toMap();
ImmutableMap.Builder<Object, Object> builder = ImmutableMap.builder()
.put("paths", pathMap)
.put("vertices", vertices)
.put("edges", edges);
if (this.apiMeasure != null) {
builder.put(MEASURE_KEY, this.apiMeasure);
}
return JsonUtil.toJson(builder.build());
}
@Override
public String writeNodesWithPath(String name, List<Id> nodes, long size,
Collection<HugeTraverser.Path> paths,
Iterator<?> vertices, Iterator<?> edges) {
List<Map<String, Object>> pathList = new ArrayList<>();
for (HugeTraverser.Path path : paths) {
pathList.add(path.toMap(false));
}
ImmutableMap.Builder<Object, Object> builder = ImmutableMap.builder()
.put(name, nodes)
.put("size", size)
.put("paths", pathList)
.put("vertices", vertices)
.put("edges", edges);
if (this.apiMeasure != null) {
builder.put(MEASURE_KEY, this.apiMeasure);
}
return JsonUtil.toJson(builder.build());
}
}