org.vertexium.inmemory.InMemoryTableElement Maven / Gradle / Ivy
package org.vertexium.inmemory;
import com.google.common.collect.Maps;
import org.vertexium.*;
import org.vertexium.HistoricalPropertyValue.HistoricalPropertyValueBuilder;
import org.vertexium.inmemory.mutations.*;
import org.vertexium.property.MutablePropertyImpl;
import org.vertexium.property.StreamingPropertyValue;
import org.vertexium.property.StreamingPropertyValueRef;
import org.vertexium.util.ConvertingIterable;
import org.vertexium.util.FilterIterable;
import org.vertexium.util.IncreasingTime;
import org.vertexium.util.LookAheadIterable;
import java.io.Serializable;
import java.util.*;
public abstract class InMemoryTableElement implements Serializable {
private final String id;
private TreeSet mutations = new TreeSet<>();
protected InMemoryTableElement(String id) {
this.id = id;
}
public String getId() {
return id;
}
public void addAll(Mutation... newMutations) {
Collections.addAll(mutations, newMutations);
}
public long getFirstTimestamp() {
return findFirstMutation(ElementTimestampMutation.class).getTimestamp();
}
protected T findLastMutation(Class clazz) {
T last = null;
for (Mutation m : this.mutations) {
if (clazz.isAssignableFrom(m.getClass())) {
//noinspection unchecked
last = (T) m;
}
}
return last;
}
protected T findFirstMutation(Class clazz) {
for (Mutation m : this.mutations) {
if (clazz.isAssignableFrom(m.getClass())) {
//noinspection unchecked
return (T) m;
}
}
return null;
}
protected Iterable findMutations(final Class clazz) {
return new ConvertingIterable(new FilterIterable(this.mutations) {
@Override
protected boolean isIncluded(Mutation m) {
return clazz.isAssignableFrom(m.getClass());
}
}) {
@Override
protected T convert(Mutation o) {
//noinspection unchecked
return (T) o;
}
};
}
public Visibility getVisibility() {
return findLastMutation(AlterVisibilityMutation.class).getNewVisibility();
}
public long getTimestamp() {
return findLastMutation(ElementTimestampMutation.class).getTimestamp();
}
private List findPropertyMutations(Property p) {
return findPropertyMutations(p.getKey(), p.getName(), p.getVisibility());
}
public Property deleteProperty(String key, String name, Authorizations authorizations) {
return deleteProperty(key, name, null, authorizations);
}
public Property getProperty(String key, String name, Visibility visibility, EnumSet fetchHints, Authorizations authorizations) {
List propertyMutations = findPropertyMutations(key, name, visibility);
if (propertyMutations == null || propertyMutations.size() == 0) {
return null;
}
return toProperty(propertyMutations, fetchHints, authorizations);
}
public Property deleteProperty(String key, String name, Visibility visibility, Authorizations authorizations) {
Property p = getProperty(key, name, visibility, FetchHint.ALL_INCLUDING_HIDDEN, authorizations);
if (p != null) {
deleteProperty(p);
}
return p;
}
protected void deleteProperty(Property p) {
List propertyMutations = findPropertyMutations(p);
this.mutations.removeAll(propertyMutations);
}
private List findPropertyMutations(String key, String name, Visibility visibility) {
List results = new ArrayList<>();
for (Mutation m : this.mutations) {
if (!(m instanceof PropertyMutation)) {
continue;
}
PropertyMutation pm = (PropertyMutation) m;
if ((key == null || pm.getPropertyKey().equals(key))
&& (name == null || pm.getPropertyName().equals(name))
&& (visibility == null || pm.getPropertyVisibility().equals(visibility))) {
results.add(pm);
}
}
return results;
}
public Iterable getHistoricalPropertyValues(
String key,
String name,
Visibility visibility,
Long startTime,
Long endTime,
Authorizations authorizations
) {
List propertyMutations = findPropertyMutations(key, name, visibility);
List historicalPropertyValues = new ArrayList<>();
/*
* There is the expectation that historical property values are a snapshot of the property in
* time. This method attempts to reconstruct the property current state from mutations.
*/
Map currentPropertyBuilders = Maps.newHashMap();
Set hiddenVisibilities = new HashSet<>();
for (PropertyMutation m : propertyMutations) {
String propertyIdentifier = m.getPropertyKey() + m.getPropertyName();
HistoricalPropertyValueBuilder builder = currentPropertyBuilders.computeIfAbsent(
propertyIdentifier,
k -> new HistoricalPropertyValueBuilder(m.getPropertyKey(), m.getPropertyName(), m.getTimestamp())
);
if (startTime != null && m.getTimestamp() < startTime) {
continue;
}
if (endTime != null && m.getTimestamp() > endTime) {
continue;
}
if (!canRead(m.getVisibility(), authorizations)) {
continue;
}
// Ignore workspace interactions to avoid duplicated entries
if (m.getVisibility() != null && m.getPropertyVisibility().getVisibilityString().matches("(.*)WORKSPACE(.*)")) {
continue;
}
if (m instanceof SoftDeletePropertyMutation) {
builder.isDeleted(true);
builder.timestamp(m.getTimestamp());
historicalPropertyValues.add(builder.build());
} else if (m instanceof AddPropertyMetadataMutation) {
builder.metadata(((AddPropertyMetadataMutation) m).getMetadata());
builder.timestamp(m.getTimestamp());
} else if (m instanceof MarkPropertyHiddenMutation) {
// Ignore
} else if (m instanceof MarkPropertyVisibleMutation) {
// Ignore
} else if (m instanceof AddPropertyValueMutation) {
AddPropertyValueMutation apvm = (AddPropertyValueMutation) m;
Object value = apvm.getValue();
value = loadIfStreamingPropertyValue(value, m.getTimestamp());
builder.propertyVisibility(m.getPropertyVisibility())
.timestamp(m.getTimestamp())
.value(value)
.metadata(apvm.getMetadata())
.hiddenVisibilities(hiddenVisibilities)
.isDeleted(false);
// Property modifications use a soft delete immediately followed by an AddPropertyValueMutation.
// If the condition occurs, remove the delete event from the set.
if (historicalPropertyValues.size() > 0) {
HistoricalPropertyValue last = historicalPropertyValues.get(historicalPropertyValues.size() - 1);
if (propertyIdentifier.equals(last.getPropertyKey() + last.getPropertyName()) && last.isDeleted()) {
historicalPropertyValues.remove(historicalPropertyValues.size() - 1);
}
}
historicalPropertyValues.add(builder.build());
} else {
throw new VertexiumException("Unhandled PropertyMutation: " + m.getClass().getName());
}
}
Collections.reverse(historicalPropertyValues);
return historicalPropertyValues;
}
public Iterable getProperties(final EnumSet fetchHints, Long endTime, final Authorizations authorizations) {
final TreeMap> propertiesMutations = new TreeMap<>();
for (PropertyMutation m : findMutations(PropertyMutation.class)) {
if (endTime != null && m.getTimestamp() > endTime) {
continue;
}
String mapKey = toMapKey(m);
List propertyMutations = propertiesMutations.computeIfAbsent(mapKey, k -> new ArrayList<>());
propertyMutations.add(m);
}
return new LookAheadIterable, Property>() {
@Override
protected boolean isIncluded(List src, Property property) {
return property != null;
}
@Override
protected Property convert(List propertyMutations) {
return toProperty(propertyMutations, fetchHints, authorizations);
}
@Override
protected Iterator> createIterator() {
return propertiesMutations.values().iterator();
}
};
}
private Property toProperty(List propertyMutations, EnumSet fetchHints, Authorizations authorizations) {
String propertyKey = null;
String propertyName = null;
Object value = null;
Metadata metadata = null;
long timestamp = 0;
Set hiddenVisibilities = new HashSet<>();
Visibility visibility = null;
boolean softDeleted = false;
boolean hidden = false;
for (PropertyMutation m : propertyMutations) {
if (!canRead(m.getVisibility(), authorizations)) {
continue;
}
propertyKey = m.getPropertyKey();
propertyName = m.getPropertyName();
visibility = m.getPropertyVisibility();
if (m.getTimestamp() > timestamp) {
timestamp = m.getTimestamp();
}
if (m instanceof AddPropertyValueMutation) {
AddPropertyValueMutation apvm = (AddPropertyValueMutation) m;
value = apvm.getValue();
metadata = apvm.getMetadata();
softDeleted = false;
} else if (m instanceof AddPropertyMetadataMutation) {
AddPropertyMetadataMutation apmm = (AddPropertyMetadataMutation) m;
metadata = apmm.getMetadata();
} else if (m instanceof SoftDeletePropertyMutation) {
softDeleted = true;
} else if (m instanceof MarkPropertyHiddenMutation) {
hidden = true;
hiddenVisibilities.add(m.getVisibility());
} else if (m instanceof MarkPropertyVisibleMutation) {
hidden = false;
hiddenVisibilities.remove(m.getVisibility());
} else {
throw new VertexiumException("Unhandled PropertyMutation: " + m.getClass().getName());
}
}
if (softDeleted) {
return null;
}
if (!fetchHints.contains(FetchHint.INCLUDE_HIDDEN) && hidden) {
return null;
}
if (propertyKey == null) {
return null;
}
value = loadIfStreamingPropertyValue(value, timestamp);
return new MutablePropertyImpl(propertyKey, propertyName, value, metadata, timestamp, hiddenVisibilities, visibility, fetchHints);
}
private Object loadIfStreamingPropertyValue(Object value, long timestamp) {
if (value instanceof StreamingPropertyValueRef) {
value = loadStreamingPropertyValue((StreamingPropertyValueRef) value, timestamp);
}
return value;
}
protected StreamingPropertyValue loadStreamingPropertyValue(StreamingPropertyValueRef> streamingPropertyValueRef, long timestamp) {
// There's no need to have a Graph object for the pure in-memory implementation. Subclasses should override.
return streamingPropertyValueRef.toStreamingPropertyValue(null, timestamp);
}
private String toMapKey(PropertyMutation m) {
return m.getPropertyName() + m.getPropertyKey() + m.getPropertyVisibility().getVisibilityString();
}
public void appendSoftDeleteMutation(Long timestamp) {
if (timestamp == null) {
timestamp = IncreasingTime.currentTimeMillis();
}
this.mutations.add(new SoftDeleteMutation(timestamp));
}
public void appendMarkHiddenMutation(Visibility visibility) {
long timestamp = IncreasingTime.currentTimeMillis();
this.mutations.add(new MarkHiddenMutation(timestamp, visibility));
}
public void appendMarkVisibleMutation(Visibility visibility) {
long timestamp = IncreasingTime.currentTimeMillis();
this.mutations.add(new MarkVisibleMutation(timestamp, visibility));
}
public Property appendMarkPropertyHiddenMutation(
String key,
String name,
Visibility propertyVisibility,
Long timestamp,
Visibility visibility,
Authorizations authorizations
) {
Property prop = getProperty(key, name, propertyVisibility, FetchHint.ALL_INCLUDING_HIDDEN, authorizations);
if (timestamp == null) {
timestamp = IncreasingTime.currentTimeMillis();
}
this.mutations.add(new MarkPropertyHiddenMutation(key, name, propertyVisibility, timestamp, visibility));
return prop;
}
public Property appendMarkPropertyVisibleMutation(
String key,
String name,
Visibility propertyVisibility,
Long timestamp,
Visibility visibility,
Authorizations authorizations
) {
Property prop = getProperty(key, name, propertyVisibility, FetchHint.ALL_INCLUDING_HIDDEN, authorizations);
if (timestamp == null) {
timestamp = IncreasingTime.currentTimeMillis();
}
this.mutations.add(new MarkPropertyVisibleMutation(key, name, propertyVisibility, timestamp, visibility));
return prop;
}
public void appendSoftDeletePropertyMutation(String key, String name, Visibility propertyVisibility, Long timestamp) {
if (timestamp == null) {
timestamp = IncreasingTime.currentTimeMillis();
}
this.mutations.add(new SoftDeletePropertyMutation(timestamp, key, name, propertyVisibility));
}
public void appendAlterVisibilityMutation(Visibility newVisibility) {
long timestamp = IncreasingTime.currentTimeMillis();
this.mutations.add(new AlterVisibilityMutation(timestamp, newVisibility));
}
public void appendAddPropertyValueMutation(String key, String name, Object value, Metadata metadata, Visibility visibility, Long timestamp) {
if (timestamp == null) {
timestamp = IncreasingTime.currentTimeMillis();
}
this.mutations.add(new AddPropertyValueMutation(timestamp, key, name, value, metadata, visibility));
}
public void appendAddPropertyMetadataMutation(String key, String name, Metadata metadata, Visibility visibility, Long timestamp) {
if (timestamp == null) {
timestamp = IncreasingTime.currentTimeMillis();
}
this.mutations.add(new AddPropertyMetadataMutation(timestamp, key, name, metadata, visibility));
}
public void appendAlterEdgeLabelMutation(long timestamp, String newEdgeLabel) {
this.mutations.add(new AlterEdgeLabelMutation(timestamp, newEdgeLabel));
}
protected List getFilteredMutations(boolean includeHidden, Long endTime, Authorizations authorizations) {
List mutations = new ArrayList<>();
for (Mutation m : this.mutations) {
if (!canRead(m.getVisibility(), authorizations)) {
continue;
}
if (endTime != null && m.getTimestamp() > endTime) {
continue;
}
if (includeHidden) {
if (m instanceof MarkHiddenMutation || m instanceof MarkPropertyHiddenMutation) {
continue;
}
}
mutations.add(m);
}
return mutations;
}
public boolean canRead(Authorizations authorizations) {
// this is just a shortcut so that we don't need to construct evaluators and visibility objects to check for an empty string.
//noinspection SimplifiableIfStatement
if (getVisibility().getVisibilityString().length() == 0) {
return true;
}
return authorizations.canRead(getVisibility());
}
private static boolean canRead(Visibility visibility, Authorizations authorizations) {
// this is just a shortcut so that we don't need to construct evaluators and visibility objects to check for an empty string.
//noinspection SimplifiableIfStatement
if (visibility.getVisibilityString().length() == 0) {
return true;
}
return authorizations.canRead(visibility);
}
public Set getHiddenVisibilities() {
Set results = new HashSet<>();
for (Mutation m : this.mutations) {
if (m instanceof MarkHiddenMutation) {
results.add(m.getVisibility());
} else if (m instanceof MarkVisibleMutation) {
results.remove(m.getVisibility());
}
}
return results;
}
public boolean isHidden(Authorizations authorizations) {
for (Visibility visibility : getHiddenVisibilities()) {
if (authorizations.canRead(visibility)) {
return true;
}
}
return false;
}
public TElement createElement(InMemoryGraph graph, EnumSet fetchHints, Authorizations authorizations) {
return createElement(graph, fetchHints, null, authorizations);
}
public final TElement createElement(InMemoryGraph graph, EnumSet fetchHints, Long endTime, Authorizations authorizations) {
if (endTime != null && getFirstTimestamp() > endTime) {
return null;
}
if (isDeleted(endTime, authorizations)) {
return null;
}
return createElementInternal(graph, fetchHints, endTime, authorizations);
}
public boolean isDeleted(Long endTime, Authorizations authorizations) {
boolean deleted = false;
for (Mutation m : this.mutations) {
if (!canRead(m.getVisibility(), authorizations)) {
continue;
}
if (endTime != null && m.getTimestamp() > endTime) {
continue;
}
if (m instanceof SoftDeleteMutation) {
deleted = true;
} else if (m instanceof ElementTimestampMutation) {
deleted = false;
}
}
return deleted;
}
protected abstract TElement createElementInternal(InMemoryGraph graph, EnumSet fetchHints, Long endTime, Authorizations authorizations);
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy