blob: 6980b48941f9db9f8728991f6a6e7da44841734f [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.tinkerpop.gremlin.structure.io.graphson;
import org.apache.tinkerpop.gremlin.structure.Direction;
import org.apache.tinkerpop.gremlin.structure.Edge;
import org.apache.tinkerpop.gremlin.structure.Element;
import org.apache.tinkerpop.gremlin.structure.Graph;
import org.apache.tinkerpop.gremlin.structure.Property;
import org.apache.tinkerpop.gremlin.structure.Vertex;
import org.apache.tinkerpop.gremlin.structure.VertexProperty;
import org.apache.tinkerpop.gremlin.structure.io.GraphWriter;
import org.apache.tinkerpop.gremlin.structure.io.Mapper;
import org.apache.tinkerpop.gremlin.structure.util.star.DirectionalStarGraph;
import org.apache.tinkerpop.gremlin.structure.util.star.StarGraph;
import org.apache.tinkerpop.shaded.jackson.databind.ObjectMapper;
import java.io.BufferedWriter;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.util.Iterator;
import java.util.function.Function;
/**
* A @{link GraphWriter} that writes a graph and its elements to a JSON-based representation. This implementation
* only supports JSON data types and is therefore lossy with respect to data types (e.g. a float will become a double).
* Further note that serialized {@code Map} objects do not support complex types for keys. {@link Edge} and
* {@link Vertex} objects are serialized to {@code Map} instances. If an
* {@link Element} is used as a key, it is coerced to its identifier. Other complex
* objects are converted via {@link Object#toString()} unless a mapper serializer is supplied.
*
* @author Stephen Mallette (http://stephen.genoprime.com)
*/
public final class GraphSONWriter implements GraphWriter {
private final ObjectMapper mapper;
private final boolean wrapAdjacencyList;
private GraphSONWriter(final Builder builder) {
mapper = builder.mapper.createMapper();
wrapAdjacencyList = builder.wrapAdjacencyList;
}
/**
* Writes a {@link Graph} to stream in an adjacency list format where vertices are written with edges from both
* directions. Under this serialization model, edges are grouped by label.
*
* @param outputStream the stream to write to.
* @param g the graph to write to stream.
*/
@Override
public void writeGraph(final OutputStream outputStream, final Graph g) throws IOException {
writeVertices(outputStream, g.vertices(), Direction.BOTH);
}
/**
* Writes a single {@link Vertex} to stream where edges only from the specified direction are written.
* Under this serialization model, edges are grouped by label.
*
* @param direction the direction of edges to write or null if no edges are to be written.
*/
@Override
public void writeVertex(final OutputStream outputStream, final Vertex v, final Direction direction) throws IOException {
mapper.writeValue(outputStream, new DirectionalStarGraph(StarGraph.of(v), direction));
}
/**
* Writes a single {@link Vertex} with no edges serialized.
*
* @param outputStream the stream to write to.
* @param v the vertex to write.
*/
@Override
public void writeVertex(final OutputStream outputStream, final Vertex v) throws IOException {
writeVertex(outputStream, v, null);
}
/**
* Writes a list of vertices in adjacency list format where vertices are written with edges from both
* directions. Under this serialization model, edges are grouped by label.
*
* @param outputStream the stream to write to.
* @param vertexIterator a traversal that returns a list of vertices.
* @param direction if direction is null then no edges are written.
*/
@Override
public void writeVertices(final OutputStream outputStream, final Iterator<Vertex> vertexIterator, final Direction direction) throws IOException {
final BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(outputStream));
try (final ByteArrayOutputStream baos = new ByteArrayOutputStream()) {
if (wrapAdjacencyList) writer.write("{\"" + GraphSONTokens.VERTICES + "\":[");
while (vertexIterator.hasNext()) {
writeVertex(baos, vertexIterator.next(), direction);
writer.write(new String(baos.toByteArray()));
if (wrapAdjacencyList) {
if (vertexIterator.hasNext())
writer.write(",");
} else {
writer.newLine();
}
baos.reset();
}
if (wrapAdjacencyList) writer.write("]}");
}
writer.flush();
}
/**
* Writes a list of vertices without edges.
*
* @param outputStream the stream to write to.
* @param vertexIterator a iterator that returns a list of vertices.
*/
@Override
public void writeVertices(final OutputStream outputStream, final Iterator<Vertex> vertexIterator) throws IOException {
writeVertices(outputStream, vertexIterator, null);
}
/**
* Writes an {@link Edge} object to the stream. Note that this format is different from the format of an
* {@link Edge} when serialized with a {@link Vertex} as done with
* {@link #writeVertex(OutputStream, Vertex, Direction)} or
* {@link #writeVertices(OutputStream, Iterator, Direction)} in that the edge label is part of the object and
* vertex labels are included with their identifiers.
*
* @param outputStream the stream to write to.
* @param e the edge to write.
*/
@Override
public void writeEdge(final OutputStream outputStream, final Edge e) throws IOException {
mapper.writeValue(outputStream, e);
}
/**
* Write a {@link VertexProperty} object to the stream.
*
* @param outputStream the stream to write to.
* @param vp the vertex property to write.
*/
@Override
public void writeVertexProperty(final OutputStream outputStream, final VertexProperty vp) throws IOException {
mapper.writeValue(outputStream, vp);
}
/**
* Write a {@link Property} object to the stream.
*
* @param outputStream the stream to write to.
* @param p the property to write.
*/
@Override
public void writeProperty(final OutputStream outputStream, final Property p) throws IOException {
mapper.writeValue(outputStream, p);
}
/**
* Writes an arbitrary object to the stream. Note that Gremlin Server uses this method when serializing output,
* thus the format of the GraphSON for a {@link Vertex} will be somewhat different from the format supplied
* when using {@link #writeVertex(OutputStream, Vertex, Direction)}. For example, edges will never be included.
*
* @param outputStream the stream to write to
* @param object the object to write which will use the standard serializer set
*/
@Override
public void writeObject(final OutputStream outputStream, final Object object) throws IOException {
this.mapper.writeValue(outputStream, object);
}
public static Builder build() {
return new Builder();
}
public static class Builder implements WriterBuilder<GraphSONWriter> {
private Mapper<ObjectMapper> mapper = GraphSONMapper.build().create();
private boolean wrapAdjacencyList = false;
private Builder() { }
/**
* Override all of the builder options with this mapper. If this value is set to something other than
* null then that value will be used to construct the writer.
*/
public Builder mapper(final Mapper<ObjectMapper> mapper) {
this.mapper = mapper;
return this;
}
/**
* Wraps the output of {@link #writeGraph(OutputStream, Graph)}, {@link #writeVertices(OutputStream, Iterator)}
* and {@link #writeVertices(OutputStream, Iterator, Direction)} in a JSON object. By default, this value
* is {@code false} which means that the output is such that there is one JSON object (vertex) per line.
* When {@code true} the line breaks are not written and instead a valid JSON object is formed where the
* vertices are part of a JSON array in a key called "vertices".
* <p/>
* By setting this value to {@code true}, the generated JSON is no longer "splittable" by line and thus not
* suitable for OLAP processing. Furthermore, reading this format of the JSON with
* {@link GraphSONReader#readGraph(InputStream, Graph)} or
* {@link GraphSONReader#readVertices(InputStream, Function, Function, Direction)} requires that the
* entire JSON object be read into memory, so it is best saved for "small" graphs.
*/
public Builder wrapAdjacencyList(final boolean wrapAdjacencyListInObject) {
this.wrapAdjacencyList = wrapAdjacencyListInObject;
return this;
}
public GraphSONWriter create() {
return new GraphSONWriter(this);
}
}
}