package org.vertexium.accumulo; import com.google.common.base.Function; import com.google.common.collect.ImmutableSet; import com.google.common.collect.Iterables; import org.apache.accumulo.core.data.Key; import org.apache.accumulo.core.data.Value; import org.apache.hadoop.io.Text; import org.vertexium.*; import org.vertexium.accumulo.iterator.VertexIterator; import org.vertexium.accumulo.iterator.model.Edges; import org.vertexium.accumulo.iterator.model.EdgesWithCount; import org.vertexium.accumulo.iterator.model.EdgesWithEdgeInfo; import org.vertexium.accumulo.iterator.model.ElementData; import org.vertexium.accumulo.util.DataInputStreamUtils; import org.vertexium.mutation.ExistingElementMutation; import org.vertexium.mutation.ExistingElementMutationImpl; import org.vertexium.mutation.PropertyDeleteMutation; import org.vertexium.mutation.PropertySoftDeleteMutation; import org.vertexium.query.VertexQuery; import org.vertexium.util.ConvertingIterable; import org.vertexium.util.FilterIterable; import org.vertexium.util.JoinIterable; import org.vertexium.util.LookAheadIterable; import javax.annotation.Nullable; import java.io.ByteArrayInputStream; import java.io.DataInputStream; import java.io.IOException; import java.util.*; import static org.vertexium.util.IterableUtils.count; import static org.vertexium.util.IterableUtils.toList; public class AccumuloVertex extends AccumuloElement implements Vertex { public static final Text CF_SIGNAL = VertexIterator.CF_SIGNAL; public static final Text CF_OUT_EDGE = VertexIterator.CF_OUT_EDGE; public static final Text CF_IN_EDGE = VertexIterator.CF_IN_EDGE; public static final Text CF_OUT_EDGE_SOFT_DELETE = VertexIterator.CF_OUT_EDGE_SOFT_DELETE; public static final Text CF_IN_EDGE_SOFT_DELETE = VertexIterator.CF_IN_EDGE_SOFT_DELETE; public static final Text CF_OUT_EDGE_HIDDEN = VertexIterator.CF_OUT_EDGE_HIDDEN; public static final Text CF_IN_EDGE_HIDDEN = VertexIterator.CF_IN_EDGE_HIDDEN; private final Edges inEdges; private final Edges outEdges; public AccumuloVertex( AccumuloGraph graph, String vertexId, Visibility vertexVisibility, Iterable<Property> properties, Iterable<PropertyDeleteMutation> propertyDeleteMutations, Iterable<PropertySoftDeleteMutation> propertySoftDeleteMutations, Iterable<Visibility> hiddenVisibilities, ImmutableSet<String> extendedDataTableNames, long timestamp, EnumSet<FetchHint> fetchHints, Authorizations authorizations ) { this( graph, vertexId, vertexVisibility, properties, propertyDeleteMutations, propertySoftDeleteMutations, hiddenVisibilities, extendedDataTableNames, new EdgesWithEdgeInfo(), new EdgesWithEdgeInfo(), timestamp, fetchHints, authorizations ); } public AccumuloVertex( AccumuloGraph graph, String vertexId, Visibility vertexVisibility, Iterable<Property> properties, Iterable<PropertyDeleteMutation> propertyDeleteMutations, Iterable<PropertySoftDeleteMutation> propertySoftDeleteMutations, Iterable<Visibility> hiddenVisibilities, ImmutableSet<String> extendedDataTableNames, Edges inEdges, Edges outEdges, long timestamp, EnumSet<FetchHint> fetchHints, Authorizations authorizations ) { super( graph, vertexId, vertexVisibility, properties, propertyDeleteMutations, propertySoftDeleteMutations, hiddenVisibilities, extendedDataTableNames, timestamp, fetchHints, authorizations ); this.inEdges = inEdges; this.outEdges = outEdges; } public static Vertex createFromIteratorValue( AccumuloGraph graph, Key key, Value value, EnumSet<FetchHint> fetchHints, Authorizations authorizations ) { try { String vertexId; Visibility vertexVisibility; Iterable<Property> properties; Iterable<PropertyDeleteMutation> propertyDeleteMutations = new ArrayList<>(); Iterable<PropertySoftDeleteMutation> propertySoftDeleteMutations = new ArrayList<>(); Iterable<Visibility> hiddenVisibilities; Edges inEdges; Edges outEdges; long timestamp; ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(value.get()); final DataInputStream in = new DataInputStream(byteArrayInputStream); DataInputStreamUtils.decodeHeader(in, ElementData.TYPE_ID_VERTEX); vertexId = DataInputStreamUtils.decodeText(in).toString(); timestamp = in.readLong(); vertexVisibility = new Visibility(DataInputStreamUtils.decodeText(in).toString()); hiddenVisibilities = Iterables.transform(DataInputStreamUtils.decodeTextList(in), new Function<Text, Visibility>() { @Nullable @Override public Visibility apply(Text input) { return new Visibility(input.toString()); } }); properties = DataInputStreamUtils.decodeProperties(graph, in, fetchHints); ImmutableSet<String> extendedDataTableNames = DataInputStreamUtils.decodeStringSet(in); outEdges = DataInputStreamUtils.decodeEdges(in, graph.getNameSubstitutionStrategy()); inEdges = DataInputStreamUtils.decodeEdges(in, graph.getNameSubstitutionStrategy()); return new AccumuloVertex( graph, vertexId, vertexVisibility, properties, propertyDeleteMutations, propertySoftDeleteMutations, hiddenVisibilities, extendedDataTableNames, inEdges, outEdges, timestamp, fetchHints, authorizations ); } catch (IOException ex) { throw new VertexiumException("Could not read vertex", ex); } } @Override public Iterable<Edge> getEdges(Direction direction, Authorizations authorizations) { return getEdges(direction, getGraph().getDefaultFetchHints(), authorizations); } @Override public Iterable<Edge> getEdges(Direction direction, EnumSet<FetchHint> fetchHints, Authorizations authorizations) { return getEdges(direction, fetchHints, null, authorizations); } @Override public Iterable<Edge> getEdges(Direction direction, EnumSet<FetchHint> fetchHints, Long endTime, Authorizations authorizations) { return getGraph().getEdges(getEdgeIds(direction, authorizations), fetchHints, endTime, authorizations); } @Override public Iterable<String> getEdgeIds(Direction direction, Authorizations authorizations) { return getEdgeIdsWithOtherVertexId(null, direction, null, authorizations); } @Override public Iterable<Edge> getEdges(Direction direction, String label, Authorizations authorizations) { return getEdges(direction, label, getGraph().getDefaultFetchHints(), authorizations); } @Override public Iterable<Edge> getEdges(Direction direction, String label, EnumSet<FetchHint> fetchHints, Authorizations authorizations) { return getGraph().getEdges(getEdgeIds(direction, labelToArrayOrNull(label), authorizations), fetchHints, authorizations); } @Override public Iterable<String> getEdgeIds(Direction direction, String label, Authorizations authorizations) { return getEdgeIdsWithOtherVertexId(null, direction, labelToArrayOrNull(label), authorizations); } @Override public Iterable<Edge> getEdges(Direction direction, String[] labels, Authorizations authorizations) { return getEdges(direction, labels, getGraph().getDefaultFetchHints(), authorizations); } @Override public Iterable<Edge> getEdges(Direction direction, final String[] labels, EnumSet<FetchHint> fetchHints, Authorizations authorizations) { return getGraph().getEdges(getEdgeIdsWithOtherVertexId(null, direction, labels, authorizations), fetchHints, authorizations); } @Override public Iterable<String> getEdgeIds(final Direction direction, final String[] labels, final Authorizations authorizations) { return getEdgeIdsWithOtherVertexId(null, direction, labels, authorizations); } @Override public Iterable<Edge> getEdges(Vertex otherVertex, Direction direction, Authorizations authorizations) { return getEdges(otherVertex, direction, getGraph().getDefaultFetchHints(), authorizations); } @Override public Iterable<Edge> getEdges(final Vertex otherVertex, Direction direction, EnumSet<FetchHint> fetchHints, Authorizations authorizations) { return getGraph().getEdges(getEdgeIdsWithOtherVertexId(otherVertex.getId(), direction, null, authorizations), fetchHints, authorizations); } @Override public Iterable<String> getEdgeIds(Vertex otherVertex, Direction direction, Authorizations authorizations) { return getEdgeIdsWithOtherVertexId(otherVertex.getId(), direction, null, authorizations); } @Override public Iterable<Edge> getEdges(Vertex otherVertex, Direction direction, String label, Authorizations authorizations) { return getEdges(otherVertex, direction, label, getGraph().getDefaultFetchHints(), authorizations); } @Override public Iterable<Edge> getEdges(final Vertex otherVertex, Direction direction, String label, EnumSet<FetchHint> fetchHints, Authorizations authorizations) { return getGraph().getEdges(getEdgeIdsWithOtherVertexId(otherVertex.getId(), direction, labelToArrayOrNull(label), authorizations), fetchHints, authorizations); } @Override public Iterable<String> getEdgeIds(Vertex otherVertex, Direction direction, String label, Authorizations authorizations) { return getEdgeIdsWithOtherVertexId(otherVertex.getId(), direction, labelToArrayOrNull(label), authorizations); } @Override public Iterable<Edge> getEdges(Vertex otherVertex, Direction direction, String[] labels, Authorizations authorizations) { return getEdges(otherVertex, direction, labels, getGraph().getDefaultFetchHints(), authorizations); } @Override public Iterable<Edge> getEdges(final Vertex otherVertex, Direction direction, String[] labels, EnumSet<FetchHint> fetchHints, Authorizations authorizations) { return getGraph().getEdges(getEdgeIdsWithOtherVertexId(otherVertex.getId(), direction, labels, authorizations), fetchHints, authorizations); } @Override public Iterable<String> getEdgeIds(final Vertex otherVertex, final Direction direction, final String[] labels, final Authorizations authorizations) { return getEdgeIdsWithOtherVertexId(otherVertex.getId(), direction, labels, authorizations); } @Override public int getEdgeCount(Direction direction, Authorizations authorizations) { return count(getEdgeIds(direction, authorizations)); } @Override public Iterable<String> getEdgeLabels(Direction direction, Authorizations authorizations) { Set<String> edgeLabels = new HashSet<>(); if (direction == Direction.IN || direction == Direction.BOTH) { if (inEdges instanceof EdgesWithCount) { edgeLabels.addAll(((EdgesWithCount) inEdges).getLabels()); } else { edgeLabels.addAll(toList(new ConvertingIterable<Map.Entry<Text, org.vertexium.accumulo.iterator.model.EdgeInfo>, String>(getEdgeInfos(Direction.IN)) { @Override protected String convert(Map.Entry<Text, org.vertexium.accumulo.iterator.model.EdgeInfo> o) { return o.getValue().getLabel(); } })); } } if (direction == Direction.OUT || direction == Direction.BOTH) { if (outEdges instanceof EdgesWithCount) { edgeLabels.addAll(((EdgesWithCount) outEdges).getLabels()); } else { edgeLabels.addAll(toList(new ConvertingIterable<Map.Entry<Text, org.vertexium.accumulo.iterator.model.EdgeInfo>, String>(getEdgeInfos(Direction.OUT)) { @Override protected String convert(Map.Entry<Text, org.vertexium.accumulo.iterator.model.EdgeInfo> o) { return o.getValue().getLabel(); } })); } } return edgeLabels; } @Override public Iterable<Vertex> getVertices(Direction direction, Authorizations authorizations) { return getVertices(direction, getGraph().getDefaultFetchHints(), authorizations); } @SuppressWarnings("unused") public Iterable<String> getEdgeIdsWithOtherVertexId(final String otherVertexId, final Direction direction, final String[] labels, final Authorizations authorizations) { return new LookAheadIterable<Map.Entry<Text, org.vertexium.accumulo.iterator.model.EdgeInfo>, String>() { @Override protected boolean isIncluded(Map.Entry<Text, org.vertexium.accumulo.iterator.model.EdgeInfo> edgeInfo, String edgeId) { if (otherVertexId != null) { if (!otherVertexId.equals(edgeInfo.getValue().getVertexId())) { return false; } } if (labels == null || labels.length == 0) { return true; } for (String label : labels) { if (label.equals(edgeInfo.getValue().getLabel())) { return true; } } return false; } @Override protected String convert(Map.Entry<Text, org.vertexium.accumulo.iterator.model.EdgeInfo> edgeInfo) { return edgeInfo.getKey().toString(); } @Override protected Iterator<Map.Entry<Text, org.vertexium.accumulo.iterator.model.EdgeInfo>> createIterator() { return getEdgeInfos(direction).iterator(); } }; } private Iterable<Map.Entry<Text, org.vertexium.accumulo.iterator.model.EdgeInfo>> getEdgeInfos(Direction direction) { switch (direction) { case IN: if (this.inEdges instanceof EdgesWithEdgeInfo) { return ((EdgesWithEdgeInfo) this.inEdges).getEntries(); } throw new VertexiumException("Cannot get edge info"); case OUT: if (this.outEdges instanceof EdgesWithEdgeInfo) { return ((EdgesWithEdgeInfo) this.outEdges).getEntries(); } throw new VertexiumException("Cannot get edge info"); case BOTH: return new JoinIterable<>(getEdgeInfos(Direction.IN), getEdgeInfos(Direction.OUT)); default: throw new VertexiumException("Unexpected direction: " + direction); } } @Override public Iterable<EdgeInfo> getEdgeInfos(Direction direction, Authorizations authorizations) { String[] labels = null; return getEdgeInfos(direction, labels, authorizations); } @Override public Iterable<EdgeInfo> getEdgeInfos(Direction direction, String label, Authorizations authorizations) { return getEdgeInfos(direction, new String[]{label}, authorizations); } @Override public Iterable<org.vertexium.EdgeInfo> getEdgeInfos(Direction direction, final String[] labels, Authorizations authorizations) { Iterable<EdgeInfo> results = new ConvertingIterable<Map.Entry<Text, org.vertexium.accumulo.iterator.model.EdgeInfo>, org.vertexium.EdgeInfo>(getEdgeInfos(direction)) { @Override protected org.vertexium.EdgeInfo convert(Map.Entry<Text, org.vertexium.accumulo.iterator.model.EdgeInfo> o) { final String edgeId = o.getKey().toString(); final org.vertexium.accumulo.iterator.model.EdgeInfo edgeInfo = o.getValue(); return new EdgeInfo() { @Override public String getEdgeId() { return edgeId; } @Override public String getLabel() { return edgeInfo.getLabel(); } @Override public String getVertexId() { return edgeInfo.getVertexId(); } }; } }; if (labels != null) { results = new FilterIterable<EdgeInfo>(results) { @Override protected boolean isIncluded(EdgeInfo o) { for (String label : labels) { if (o.getLabel().equals(label)) { return true; } } return false; } }; } return results; } @Override public Iterable<Vertex> getVertices(Direction direction, EnumSet<FetchHint> fetchHints, final Authorizations authorizations) { return getGraph().getVertices(getVertexIds(direction, authorizations), fetchHints, authorizations); } @Override public Iterable<Vertex> getVertices(Direction direction, String label, Authorizations authorizations) { return getVertices(direction, label, getGraph().getDefaultFetchHints(), authorizations); } @Override public Iterable<Vertex> getVertices(Direction direction, String label, EnumSet<FetchHint> fetchHints, Authorizations authorizations) { return getVertices(direction, labelToArrayOrNull(label), fetchHints, authorizations); } @Override public Iterable<Vertex> getVertices(Direction direction, String[] labels, Authorizations authorizations) { return getVertices(direction, labels, getGraph().getDefaultFetchHints(), authorizations); } @Override public Iterable<Vertex> getVertices(Direction direction, String[] labels, EnumSet<FetchHint> fetchHints, final Authorizations authorizations) { return getGraph().getVertices(getVertexIds(direction, labels, authorizations), fetchHints, authorizations); } @Override public Iterable<String> getVertexIds(Direction direction, String label, Authorizations authorizations) { return getVertexIds(direction, labelToArrayOrNull(label), authorizations); } @Override public Iterable<String> getVertexIds(Direction direction, Authorizations authorizations) { return getVertexIds(direction, (String[]) null, authorizations); } @Override public Iterable<String> getVertexIds(Direction direction, String[] labels, Authorizations authorizations) { switch (direction) { case BOTH: Iterable<String> inVertexIds = getVertexIds(Direction.IN, labels, authorizations); Iterable<String> outVertexIds = getVertexIds(Direction.OUT, labels, authorizations); return new JoinIterable<>(inVertexIds, outVertexIds); case IN: if (this.inEdges instanceof EdgesWithEdgeInfo) { return new GetVertexIdsIterable(((EdgesWithEdgeInfo) this.inEdges).getEdgeInfos(), labels); } throw new VertexiumException("Cannot get vertex ids"); case OUT: if (this.outEdges instanceof EdgesWithEdgeInfo) { return new GetVertexIdsIterable(((EdgesWithEdgeInfo) this.outEdges).getEdgeInfos(), labels); } throw new VertexiumException("Cannot get vertex ids"); default: throw new VertexiumException("Unexpected direction: " + direction); } } @Override public VertexQuery query(Authorizations authorizations) { return query(null, authorizations); } @Override public VertexQuery query(String queryString, Authorizations authorizations) { return getGraph().getSearchIndex().queryVertex(getGraph(), this, queryString, authorizations); } void addOutEdge(Edge edge) { if (this.outEdges instanceof EdgesWithEdgeInfo) { ((EdgesWithEdgeInfo) this.outEdges).add(edge.getId(), new org.vertexium.accumulo.iterator.model.EdgeInfo(edge.getLabel(), edge.getVertexId(Direction.IN))); } else { throw new VertexiumException("Cannot add edge"); } } void removeOutEdge(Edge edge) { if (this.outEdges instanceof EdgesWithEdgeInfo) { ((EdgesWithEdgeInfo) this.outEdges).remove(edge.getId()); } else { throw new VertexiumException("Cannot remove out edge"); } } void addInEdge(Edge edge) { if (this.inEdges instanceof EdgesWithEdgeInfo) { ((EdgesWithEdgeInfo) this.inEdges).add(edge.getId(), new org.vertexium.accumulo.iterator.model.EdgeInfo(edge.getLabel(), edge.getVertexId(Direction.OUT))); } else { throw new VertexiumException("Cannot add edge"); } } void removeInEdge(Edge edge) { if (this.inEdges instanceof EdgesWithEdgeInfo) { ((EdgesWithEdgeInfo) this.inEdges).remove(edge.getId()); } else { throw new VertexiumException("Cannot remove in edge"); } } @Override @SuppressWarnings("unchecked") public ExistingElementMutation<Vertex> prepareMutation() { return new ExistingElementMutationImpl<Vertex>(this) { @Override public Vertex save(Authorizations authorizations) { saveExistingElementMutation(this, authorizations); return getElement(); } }; } private static String[] labelToArrayOrNull(String label) { return label == null ? null : new String[]{label}; } @Override public Iterable<EdgeVertexPair> getEdgeVertexPairs(Direction direction, Authorizations authorizations) { return getEdgeVertexPairs(getEdgeInfos(direction, authorizations), getGraph().getDefaultFetchHints(), null, authorizations); } @Override public Iterable<EdgeVertexPair> getEdgeVertexPairs(Direction direction, EnumSet<FetchHint> fetchHints, Authorizations authorizations) { return getEdgeVertexPairs(getEdgeInfos(direction, authorizations), fetchHints, null, authorizations); } @Override public Iterable<EdgeVertexPair> getEdgeVertexPairs(Direction direction, EnumSet<FetchHint> fetchHints, Long endTime, Authorizations authorizations) { return getEdgeVertexPairs(getEdgeInfos(direction, authorizations), fetchHints, endTime, authorizations); } @Override public Iterable<EdgeVertexPair> getEdgeVertexPairs(Direction direction, String label, Authorizations authorizations) { return getEdgeVertexPairs(getEdgeInfos(direction, label, authorizations), getGraph().getDefaultFetchHints(), null, authorizations); } @Override public Iterable<EdgeVertexPair> getEdgeVertexPairs(Direction direction, String label, EnumSet<FetchHint> fetchHints, Authorizations authorizations) { return getEdgeVertexPairs(getEdgeInfos(direction, label, authorizations), fetchHints, null, authorizations); } @Override public Iterable<EdgeVertexPair> getEdgeVertexPairs(Direction direction, String[] labels, Authorizations authorizations) { return getEdgeVertexPairs(getEdgeInfos(direction, labels, authorizations), getGraph().getDefaultFetchHints(), null, authorizations); } @Override public Iterable<EdgeVertexPair> getEdgeVertexPairs(Direction direction, String[] labels, EnumSet<FetchHint> fetchHints, Authorizations authorizations) { return getEdgeVertexPairs(getEdgeInfos(direction, labels, authorizations), fetchHints, null, authorizations); } private Iterable<EdgeVertexPair> getEdgeVertexPairs(Iterable<EdgeInfo> edgeInfos, EnumSet<FetchHint> fetchHints, Long endTime, Authorizations authorizations) { return EdgeVertexPair.getEdgeVertexPairs(getGraph(), getId(), edgeInfos, fetchHints, endTime, authorizations); } }