overflowdb.NodeLayoutInformation Maven / Gradle / Ivy
package overflowdb;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.SortedSet;
import java.util.TreeSet;
/**
* Contains all static node-specific information for serialization / deserialization.
*
* Please make sure to instantiate only one instance per node type to not waste memory.
*/
public class NodeLayoutInformation {
/** unique id for this node's label
* This is mostly an optimization for storage - we could as well serialize labels as string, but numbers are more efficient.
* Since we know our schema at compile time, we can assign unique ids for each label.
* */
public final int labelId;
private final Set propertyKeys;
private final String[] allowedOutEdgeLabels;
private final String[] allowedInEdgeLabels;
/* position for given OUT edge label in edgeOffsets */
private final Map outEdgeToOffsetPosition;
/* position for given IN edge label in edgeOffsets */
private final Map inEdgeToOffsetPosition;
/* possible edge property keys, grouped by edge label.
* n.b. property keys are of type `HashSet` (rather than just `Set`) to ensure `.size` has constant time */
private final Map> edgePropertyKeysByLabel;
/* maps offsetPos -> number of edge properties*/
private final int[] edgePropertyCountByOffsetPosition;
/* position in stride (entry within `adjacentNodesWithProperties`) for a given edge label and edge property key
* 1-based, because index `0` is the adjacent node ref */
private final Map edgeLabelAndKeyToStrideIndex;
public NodeLayoutInformation(int labelId,
Set propertyKeys,
List outEdgeLayouts,
List inEdgeLayouts) {
this.labelId = labelId;
this.propertyKeys = propertyKeys;
Set allEdgeLayouts = new HashSet<>();
allEdgeLayouts.addAll(outEdgeLayouts);
allEdgeLayouts.addAll(inEdgeLayouts);
edgePropertyKeysByLabel = createEdgePropertyKeysByLabel(allEdgeLayouts);
edgeLabelAndKeyToStrideIndex = createEdgeLabelAndKeyToStrideIndex(allEdgeLayouts);
/* create unique offsets for each edge type and direction
* ordered by input order (first out, then in, same order as in outEdgeLayouts)
* this ensures that (1) we get the same offsets between restarts and (2) the offsets are easily apparent from the
* constructor call. Downstream may rely on the offsets, beware before changing!
* schema changes will change layout*/
allowedOutEdgeLabels = new String[outEdgeLayouts.size()];
allowedInEdgeLabels = new String[inEdgeLayouts.size()];
int offsetPosition = 0;
edgePropertyCountByOffsetPosition = new int[outEdgeLayouts.size() + inEdgeLayouts.size()];
int i = 0;
outEdgeToOffsetPosition = new HashMap<>(outEdgeLayouts.size());
for (EdgeLayoutInformation edgeLayout : outEdgeLayouts) {
edgePropertyCountByOffsetPosition[offsetPosition] = edgePropertyKeysByLabel.get(edgeLayout.label).size();
outEdgeToOffsetPosition.put(edgeLayout.label, offsetPosition++);
allowedOutEdgeLabels[i++] = edgeLayout.label;
}
i = 0;
inEdgeToOffsetPosition = new HashMap<>(inEdgeLayouts.size());
for (EdgeLayoutInformation edgeLayout : inEdgeLayouts) {
edgePropertyCountByOffsetPosition[offsetPosition] = edgePropertyKeysByLabel.get(edgeLayout.label).size();
inEdgeToOffsetPosition.put(edgeLayout.label, offsetPosition++);
allowedInEdgeLabels[i++] = edgeLayout.label;
}
}
private Map> createEdgePropertyKeysByLabel(Set allEdgeLayouts) {
Map> edgePropertyKeysByLabel = new HashMap<>(allEdgeLayouts.size());
for (EdgeLayoutInformation edgeLayout : allEdgeLayouts) {
edgePropertyKeysByLabel.put(edgeLayout.label, new HashSet<>(edgeLayout.propertyKeys));
}
return edgePropertyKeysByLabel;
}
private Map createEdgeLabelAndKeyToStrideIndex(Set allEdgeLayouts) {
Map edgeLabelAndKeyToStrideIndex = new HashMap<>(allEdgeLayouts.size());
for (EdgeLayoutInformation edgeLayout : allEdgeLayouts) {
/* 1-based, because index `0` is the adjacent node ref */
int strideIndex = 1;
/* sort property keys to ensure we get the same offsets between restarts
* n.b. this doesn't support schema changes */
for (String propertyKey : sorted(edgeLayout.propertyKeys)) {
edgeLabelAndKeyToStrideIndex.put(new LabelAndKey(edgeLayout.label, propertyKey), strideIndex++);
}
}
return edgeLabelAndKeyToStrideIndex;
}
private Iterable sorted(Set propertyKeys) {
SortedSet sortedSet = new TreeSet<>(String::compareTo);
sortedSet.addAll(propertyKeys);
return sortedSet;
}
private SortedSet sortByLabel(List outEdgeLayouts) {
SortedSet sorted = new TreeSet<>(Comparator.comparing(a -> a.label));
sorted.addAll(outEdgeLayouts);
return sorted;
}
public Set propertyKeys() {
return propertyKeys;
}
public String[] allowedOutEdgeLabels() {
return allowedOutEdgeLabels;
}
public String[] allowedInEdgeLabels() {
return allowedInEdgeLabels;
}
public Set edgePropertyKeys(String edgeLabel) {
return edgePropertyKeysByLabel.get(edgeLabel);
}
/* The number fo different IN|OUT edge relations. E.g. a node has AST edges in and out, then we would have 2.
* If in addition it has incoming ref edges it would have 3. */
public int numberOfDifferentAdjacentTypes() {
return outEdgeToOffsetPosition.size() + inEdgeToOffsetPosition.size();
}
/* position for given OUT edge label in OverflowDbNode.edgeOffsets */
public Integer outEdgeToOffsetPosition(String edgeLabel) {
return outEdgeToOffsetPosition.get(edgeLabel);
}
/* position for given IN edge label in OverflowDbNode.edgeOffsets */
public Integer inEdgeToOffsetPosition(String edgeLabel) {
return inEdgeToOffsetPosition.get(edgeLabel);
}
/**
* @return The offset relative to the adjacent node in the adjacentNodesWithProperties array starting from 1.
* Return -1 if key does not exist for given edgeLabel.
*/
public int getOffsetRelativeToAdjacentNodeRef(String edgeLabel, String key) {
return edgeLabelAndKeyToStrideIndex.getOrDefault(new LabelAndKey(edgeLabel, key), -1);
}
/* gets edge property count by offsetPos*/
public final int getEdgePropertyCountByOffsetPos(int offsetPos) {
return edgePropertyCountByOffsetPosition[offsetPos];
}
class LabelAndKey {
final String label;
final String propertyKey;
LabelAndKey(String label, String propertyKey) {
this.label = label;
this.propertyKey = propertyKey;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
LabelAndKey that = (LabelAndKey) o;
return label.equals(that.label) &&
propertyKey.equals(that.propertyKey);
}
@Override
public int hashCode() {
return Objects.hash(label, propertyKey);
}
}
}