blob: 26efef6e2bb4383b2c28e5df8fd6cbcd988e88c0 [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.backend.serializer;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import org.apache.hugegraph.HugeException;
import org.apache.hugegraph.HugeGraph;
import org.apache.hugegraph.backend.BackendException;
import org.apache.hugegraph.backend.store.BackendEntry;
import org.apache.hugegraph.config.HugeConfig;
import org.apache.commons.lang.NotImplementedException;
import org.apache.hugegraph.type.HugeType;
import org.apache.hugegraph.util.JsonUtil;
import org.apache.hugegraph.backend.id.EdgeId;
import org.apache.hugegraph.backend.id.Id;
import org.apache.hugegraph.backend.id.IdGenerator;
import org.apache.hugegraph.backend.id.IdUtil;
import org.apache.hugegraph.backend.id.SplicingIdGenerator;
import org.apache.hugegraph.backend.query.Condition;
import org.apache.hugegraph.backend.query.ConditionQuery;
import org.apache.hugegraph.backend.query.IdPrefixQuery;
import org.apache.hugegraph.backend.query.IdRangeQuery;
import org.apache.hugegraph.backend.query.Query;
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.structure.HugeEdge;
import org.apache.hugegraph.structure.HugeEdgeProperty;
import org.apache.hugegraph.structure.HugeElement;
import org.apache.hugegraph.structure.HugeIndex;
import org.apache.hugegraph.structure.HugeIndex.IdWithExpiredTime;
import org.apache.hugegraph.structure.HugeProperty;
import org.apache.hugegraph.structure.HugeVertex;
import org.apache.hugegraph.structure.HugeVertexProperty;
import org.apache.hugegraph.type.define.AggregateType;
import org.apache.hugegraph.type.define.Cardinality;
import org.apache.hugegraph.type.define.DataType;
import org.apache.hugegraph.type.define.Directions;
import org.apache.hugegraph.type.define.Frequency;
import org.apache.hugegraph.type.define.HugeKeys;
import org.apache.hugegraph.type.define.IdStrategy;
import org.apache.hugegraph.type.define.IndexType;
import org.apache.hugegraph.type.define.SchemaStatus;
import org.apache.hugegraph.type.define.WriteType;
import org.apache.hugegraph.util.E;
import com.google.common.collect.ImmutableMap;
public class TextSerializer extends AbstractSerializer {
private static final String VALUE_SPLITOR = TextBackendEntry.VALUE_SPLITOR;
private static final String EDGE_NAME_ENDING =
ConditionQuery.INDEX_SYM_ENDING;
private static final String EDGE_OUT_TYPE = writeType(HugeType.EDGE_OUT);
public TextSerializer(HugeConfig config) {
super(config);
}
@Override
public TextBackendEntry newBackendEntry(HugeType type, Id id) {
return new TextBackendEntry(type, id);
}
private TextBackendEntry newBackendEntry(HugeElement elem) {
Id id = IdGenerator.of(writeEntryId(elem.id()));
return new TextBackendEntry(elem.type(), id);
}
private TextBackendEntry newBackendEntry(SchemaElement elem) {
Id id = IdGenerator.of(writeId(elem.id()));
return new TextBackendEntry(elem.type(), id);
}
@Override
protected TextBackendEntry convertEntry(BackendEntry backendEntry) {
if (!(backendEntry instanceof TextBackendEntry)) {
throw new HugeException("The entry '%s' is not TextBackendEntry",
backendEntry);
}
return (TextBackendEntry) backendEntry;
}
private String formatSyspropName(String name) {
return SplicingIdGenerator.concat(writeType(HugeType.SYS_PROPERTY),
name);
}
private String formatSyspropName(HugeKeys col) {
return this.formatSyspropName(col.string());
}
private String formatPropertyName(String key) {
return SplicingIdGenerator.concat(writeType(HugeType.PROPERTY), key);
}
private String formatPropertyName(HugeProperty<?> prop) {
return this.formatPropertyName(writeId(prop.propertyKey().id()));
}
private String formatPropertyValue(HugeProperty<?> prop) {
// May be a single value or a list of values
return JsonUtil.toJson(prop.value());
}
private String formatPropertyName() {
return HugeType.PROPERTY.string();
}
private String formatPropertyValues(HugeVertex vertex) {
int size = vertex.sizeOfProperties();
StringBuilder sb = new StringBuilder(64 * size);
// Vertex properties
int i = 0;
for (HugeProperty<?> property : vertex.getProperties()) {
sb.append(this.formatPropertyName(property));
sb.append(VALUE_SPLITOR);
sb.append(this.formatPropertyValue(property));
if (++i < size) {
sb.append(VALUE_SPLITOR);
}
}
return sb.toString();
}
private void parseProperty(String colName, String colValue,
HugeElement owner) {
String[] colParts = SplicingIdGenerator.split(colName);
assert colParts.length == 2 : colName;
// Get PropertyKey by PropertyKey id
PropertyKey pkey = owner.graph().propertyKey(readId(colParts[1]));
// Parse value
Object value = JsonUtil.fromJson(colValue, pkey.implementClazz());
// Set properties of vertex/edge
if (pkey.cardinality() == Cardinality.SINGLE) {
owner.addProperty(pkey, value);
} else {
if (!(value instanceof Collection)) {
throw new BackendException(
"Invalid value of non-single property: %s", colValue);
}
for (Object v : (Collection<?>) value) {
v = JsonUtil.castNumber(v, pkey.dataType().clazz());
owner.addProperty(pkey, v);
}
}
}
private void parseProperties(String colValue, HugeVertex vertex) {
if (colValue == null || colValue.isEmpty()) {
return;
}
String[] valParts = colValue.split(VALUE_SPLITOR);
E.checkState(valParts.length % 2 == 0,
"The property key values length must be even number, " +
"but got %s, length is '%s'",
Arrays.toString(valParts), valParts.length);
// Edge properties
for (int i = 0; i < valParts.length; i += 2) {
assert i + 1 < valParts.length;
this.parseProperty(valParts[i], valParts[i + 1], vertex);
}
}
private String formatEdgeName(HugeEdge edge) {
// Edge name: type + edge-label-name + sortKeys + targetVertex
return writeEdgeId(edge.idWithDirection(), false);
}
private String formatEdgeValue(HugeEdge edge) {
StringBuilder sb = new StringBuilder(256 * edge.sizeOfProperties());
// Edge id
sb.append(edge.id().asString());
// Write edge expired time
sb.append(VALUE_SPLITOR);
sb.append(this.formatSyspropName(HugeKeys.EXPIRED_TIME));
sb.append(VALUE_SPLITOR);
sb.append(edge.expiredTime());
// Edge properties
for (HugeProperty<?> property : edge.getProperties()) {
sb.append(VALUE_SPLITOR);
sb.append(this.formatPropertyName(property));
sb.append(VALUE_SPLITOR);
sb.append(this.formatPropertyValue(property));
}
return sb.toString();
}
/**
* Parse an edge from a column item
*/
private void parseEdge(String colName, String colValue,
HugeVertex vertex) {
String[] colParts = EdgeId.split(colName);
HugeGraph graph = vertex.graph();
boolean direction = colParts[0].equals(EDGE_OUT_TYPE);
String sortValues = readEdgeName(colParts[2]);
EdgeLabel edgeLabel = graph.edgeLabelOrNone(readId(colParts[1]));
Id otherVertexId = readEntryId(colParts[3]);
// Construct edge
HugeEdge edge = HugeEdge.constructEdge(vertex, direction, edgeLabel,
sortValues, otherVertexId);
String[] valParts = colValue.split(VALUE_SPLITOR);
// Parse edge expired time
String name = this.formatSyspropName(HugeKeys.EXPIRED_TIME);
E.checkState(valParts[1].equals(name),
"Invalid system property name '%s'", valParts[1]);
edge.expiredTime(JsonUtil.fromJson(valParts[2], Long.class));
// Edge properties
for (int i = 3; i < valParts.length; i += 2) {
this.parseProperty(valParts[i], valParts[i + 1], edge);
}
}
private void parseColumn(String colName, String colValue,
HugeVertex vertex) {
// Column name
String type = SplicingIdGenerator.split(colName)[0];
// Parse property
if (type.equals(writeType(HugeType.PROPERTY))) {
this.parseProperties(colValue, vertex);
}
// Parse edge
else if (type.equals(writeType(HugeType.EDGE_OUT)) ||
type.equals(writeType(HugeType.EDGE_IN))) {
this.parseEdge(colName, colValue, vertex);
}
// Parse system property
else if (type.equals(writeType(HugeType.SYS_PROPERTY))) {
// pass
}
// Invalid entry
else {
E.checkState(false, "Invalid entry with unknown type(%s): %s",
type, colName);
}
}
@Override
public BackendEntry writeVertex(HugeVertex vertex) {
TextBackendEntry entry = newBackendEntry(vertex);
// Write label (NOTE: maybe just with edges if label is null)
if (vertex.schemaLabel() != null) {
entry.column(this.formatSyspropName(HugeKeys.LABEL),
writeId(vertex.schemaLabel().id()));
}
// Write expired time
entry.column(this.formatSyspropName(HugeKeys.EXPIRED_TIME),
writeLong(vertex.expiredTime()));
// Add all properties of a Vertex
entry.column(this.formatPropertyName(),
this.formatPropertyValues(vertex));
return entry;
}
@Override
public BackendEntry writeOlapVertex(HugeVertex vertex) {
throw new NotImplementedException("Unsupported writeOlapVertex()");
}
@Override
public BackendEntry writeVertexProperty(HugeVertexProperty<?> prop) {
throw new NotImplementedException("Unsupported writeVertexProperty()");
}
@Override
public HugeVertex readVertex(HugeGraph graph, BackendEntry backendEntry) {
E.checkNotNull(graph, "serializer graph");
if (backendEntry == null) {
return null;
}
TextBackendEntry entry = this.convertEntry(backendEntry);
// Parse label
String labelId = entry.column(this.formatSyspropName(HugeKeys.LABEL));
VertexLabel vertexLabel = VertexLabel.NONE;
if (labelId != null) {
vertexLabel = graph.vertexLabelOrNone(readId(labelId));
}
Id id = IdUtil.readString(entry.id().asString());
HugeVertex vertex = new HugeVertex(graph, id, vertexLabel);
String expiredTime = entry.column(this.formatSyspropName(
HugeKeys.EXPIRED_TIME));
// Expired time is null when backend entry is fake vertex with edges
if (expiredTime != null) {
vertex.expiredTime(readLong(expiredTime));
}
// Parse all properties or edges of a Vertex
for (String name : entry.columnNames()) {
this.parseColumn(name, entry.column(name), vertex);
}
return vertex;
}
@Override
public BackendEntry writeEdge(HugeEdge edge) {
Id id = IdGenerator.of(edge.idWithDirection().asString());
TextBackendEntry entry = newBackendEntry(edge.type(), id);
entry.column(this.formatEdgeName(edge), this.formatEdgeValue(edge));
return entry;
}
@Override
public BackendEntry writeEdgeProperty(HugeEdgeProperty<?> prop) {
HugeEdge edge = prop.element();
Id id = IdGenerator.of(edge.idWithDirection().asString());
TextBackendEntry entry = newBackendEntry(edge.type(), id);
entry.subId(IdGenerator.of(prop.key()));
entry.column(this.formatEdgeName(edge), this.formatEdgeValue(edge));
return entry;
}
@Override
public HugeEdge readEdge(HugeGraph graph, BackendEntry backendEntry) {
E.checkNotNull(graph, "serializer graph");
// TODO: implement
throw new NotImplementedException("Unsupported readEdge()");
}
@Override
public BackendEntry writeIndex(HugeIndex index) {
TextBackendEntry entry = newBackendEntry(index.type(), index.id());
if (index.fieldValues() == null && index.elementIds().size() == 0) {
/*
* When field-values is null and elementIds size is 0, it is
* meaningful for deletion of index data in secondary/range index.
*/
entry.column(HugeKeys.INDEX_LABEL_ID,
writeId(index.indexLabelId()));
} else {
// TODO: field-values may be a number (range index)
entry.column(formatSyspropName(HugeKeys.FIELD_VALUES),
JsonUtil.toJson(index.fieldValues()));
entry.column(formatSyspropName(HugeKeys.INDEX_LABEL_ID),
writeId(index.indexLabelId()));
entry.column(formatSyspropName(HugeKeys.ELEMENT_IDS),
writeElementId(index.elementId(), index.expiredTime()));
entry.subId(index.elementId());
}
return entry;
}
@Override
public HugeIndex readIndex(HugeGraph graph, ConditionQuery query,
BackendEntry backendEntry) {
E.checkNotNull(graph, "serializer graph");
if (backendEntry == null) {
return null;
}
TextBackendEntry entry = this.convertEntry(backendEntry);
String indexValues = entry.column(
formatSyspropName(HugeKeys.FIELD_VALUES));
String indexLabelId = entry.column(
formatSyspropName(HugeKeys.INDEX_LABEL_ID));
String elemIds = entry.column(
formatSyspropName(HugeKeys.ELEMENT_IDS));
IndexLabel indexLabel = IndexLabel.label(graph, readId(indexLabelId));
HugeIndex index = new HugeIndex(graph, indexLabel);
index.fieldValues(JsonUtil.fromJson(indexValues, Object.class));
for (IdWithExpiredTime elemId : readElementIds(elemIds)) {
long expiredTime = elemId.expiredTime();
Id id;
if (indexLabel.queryType().isEdge()) {
id = EdgeId.parse(elemId.id().asString());
} else {
id = elemId.id();
}
index.elementIds(id, expiredTime);
}
// Memory backend might return empty BackendEntry
return index;
}
@Override
public TextBackendEntry writeId(HugeType type, Id id) {
id = this.writeQueryId(type, id);
return newBackendEntry(type, id);
}
@Override
protected Id writeQueryId(HugeType type, Id id) {
if (type.isEdge()) {
id = IdGenerator.of(writeEdgeId(id, true));
} else if (type.isGraph()) {
id = IdGenerator.of(writeEntryId(id));
} else {
assert type.isSchema();
id = IdGenerator.of(writeId(id));
}
return id;
}
@Override
protected Query writeQueryEdgeCondition(Query query) {
ConditionQuery cq = (ConditionQuery) query;
if (cq.hasRangeCondition()) {
return this.writeQueryEdgeRangeCondition(cq);
} else {
return this.writeQueryEdgePrefixCondition(cq);
}
}
private Query writeQueryEdgeRangeCondition(ConditionQuery cq) {
List<Condition> sortValues = cq.syspropConditions(HugeKeys.SORT_VALUES);
E.checkArgument(sortValues.size() >= 1 && sortValues.size() <= 2,
"Edge range query must be with sort-values range");
// Would ignore target vertex
Object vertex = cq.condition(HugeKeys.OWNER_VERTEX);
Object direction = cq.condition(HugeKeys.DIRECTION);
if (direction == null) {
direction = Directions.OUT;
}
Object label = cq.condition(HugeKeys.LABEL);
List<String> start = new ArrayList<>(cq.conditionsSize());
start.add(writeEntryId((Id) vertex));
start.add(writeType(((Directions) direction).type()));
start.add(writeId((Id) label));
List<String> end = new ArrayList<>(start);
Condition.RangeConditions range = new Condition.RangeConditions(sortValues);
if (range.keyMin() != null) {
start.add((String) range.keyMin());
}
if (range.keyMax() != null) {
end.add((String) range.keyMax());
}
// Sort-value will be empty if there is no start sort-value
String startId = EdgeId.concat(start.toArray(new String[0]));
// Set endId as prefix if there is no end sort-value
String endId = EdgeId.concat(end.toArray(new String[0]));
if (range.keyMax() == null) {
return new IdPrefixQuery(cq, IdGenerator.of(startId),
range.keyMinEq(), IdGenerator.of(endId));
}
return new IdRangeQuery(cq, IdGenerator.of(startId), range.keyMinEq(),
IdGenerator.of(endId), range.keyMaxEq());
}
private Query writeQueryEdgePrefixCondition(ConditionQuery cq) {
// Convert query-by-condition to query-by-id
List<String> condParts = new ArrayList<>(cq.conditionsSize());
for (HugeKeys key : EdgeId.KEYS) {
Object value = cq.condition(key);
if (value == null) {
break;
}
// Serialize condition value
if (key == HugeKeys.OWNER_VERTEX || key == HugeKeys.OTHER_VERTEX) {
condParts.add(writeEntryId((Id) value));
} else if (key == HugeKeys.DIRECTION) {
condParts.add(writeType(((Directions) value).type()));
} else if (key == HugeKeys.LABEL) {
condParts.add(writeId((Id) value));
} else {
condParts.add(value.toString());
}
}
if (condParts.size() > 0) {
// Conditions to id
String id = EdgeId.concat(condParts.toArray(new String[0]));
return new IdPrefixQuery(cq, IdGenerator.of(id));
}
return null;
}
@Override
protected Query writeQueryCondition(Query query) {
ConditionQuery result = (ConditionQuery) query;
// No user-prop when serialize
assert result.allSysprop();
for (Condition.Relation r : result.relations()) {
// Serialize key
if (query.resultType().isSchema()) {
r.serialKey(((HugeKeys) r.key()).string());
} else {
r.serialKey(formatSyspropName((HugeKeys) r.key()));
}
if (r.value() instanceof Id) {
// Serialize id value
r.serialValue(writeId((Id) r.value()));
} else {
// Serialize other type value
r.serialValue(JsonUtil.toJson(r.value()));
}
if (r.relation() == Condition.RelationType.CONTAINS_KEY) {
// Serialize has-key
String key = (String) r.serialValue();
r.serialValue(formatPropertyName(key));
}
}
return result;
}
@Override
public BackendEntry writeVertexLabel(VertexLabel vertexLabel) {
TextBackendEntry entry = newBackendEntry(vertexLabel);
entry.column(HugeKeys.NAME, JsonUtil.toJson(vertexLabel.name()));
entry.column(HugeKeys.ID_STRATEGY,
JsonUtil.toJson(vertexLabel.idStrategy()));
entry.column(HugeKeys.PROPERTIES,
writeIds(vertexLabel.properties()));
entry.column(HugeKeys.PRIMARY_KEYS,
writeIds(vertexLabel.primaryKeys()));
entry.column(HugeKeys.NULLABLE_KEYS,
writeIds(vertexLabel.nullableKeys()));
entry.column(HugeKeys.INDEX_LABELS,
writeIds(vertexLabel.indexLabels()));
entry.column(HugeKeys.ENABLE_LABEL_INDEX,
JsonUtil.toJson(vertexLabel.enableLabelIndex()));
writeUserdata(vertexLabel, entry);
entry.column(HugeKeys.STATUS,
JsonUtil.toJson(vertexLabel.status()));
return entry;
}
@Override
public VertexLabel readVertexLabel(HugeGraph graph,
BackendEntry backendEntry) {
if (backendEntry == null) {
return null;
}
TextBackendEntry entry = this.convertEntry(backendEntry);
Id id = readId(entry.id());
String name = JsonUtil.fromJson(entry.column(HugeKeys.NAME),
String.class);
String idStrategy = entry.column(HugeKeys.ID_STRATEGY);
String properties = entry.column(HugeKeys.PROPERTIES);
String primaryKeys = entry.column(HugeKeys.PRIMARY_KEYS);
String nullableKeys = entry.column(HugeKeys.NULLABLE_KEYS);
String indexLabels = entry.column(HugeKeys.INDEX_LABELS);
String enableLabelIndex = entry.column(HugeKeys.ENABLE_LABEL_INDEX);
String status = entry.column(HugeKeys.STATUS);
VertexLabel vertexLabel = new VertexLabel(graph, id, name);
vertexLabel.idStrategy(JsonUtil.fromJson(idStrategy,
IdStrategy.class));
vertexLabel.properties(readIds(properties));
vertexLabel.primaryKeys(readIds(primaryKeys));
vertexLabel.nullableKeys(readIds(nullableKeys));
vertexLabel.addIndexLabels(readIds(indexLabels));
vertexLabel.enableLabelIndex(JsonUtil.fromJson(enableLabelIndex,
Boolean.class));
readUserdata(vertexLabel, entry);
vertexLabel.status(JsonUtil.fromJson(status, SchemaStatus.class));
return vertexLabel;
}
@Override
public BackendEntry writeEdgeLabel(EdgeLabel edgeLabel) {
TextBackendEntry entry = newBackendEntry(edgeLabel);
entry.column(HugeKeys.NAME, JsonUtil.toJson(edgeLabel.name()));
entry.column(HugeKeys.SOURCE_LABEL, writeId(edgeLabel.sourceLabel()));
entry.column(HugeKeys.TARGET_LABEL, writeId(edgeLabel.targetLabel()));
entry.column(HugeKeys.FREQUENCY,
JsonUtil.toJson(edgeLabel.frequency()));
entry.column(HugeKeys.PROPERTIES, writeIds(edgeLabel.properties()));
entry.column(HugeKeys.SORT_KEYS, writeIds(edgeLabel.sortKeys()));
entry.column(HugeKeys.NULLABLE_KEYS,
writeIds(edgeLabel.nullableKeys()));
entry.column(HugeKeys.INDEX_LABELS, writeIds(edgeLabel.indexLabels()));
entry.column(HugeKeys.ENABLE_LABEL_INDEX,
JsonUtil.toJson(edgeLabel.enableLabelIndex()));
writeUserdata(edgeLabel, entry);
entry.column(HugeKeys.STATUS,
JsonUtil.toJson(edgeLabel.status()));
entry.column(HugeKeys.TTL, JsonUtil.toJson(edgeLabel.ttl()));
entry.column(HugeKeys.TTL_START_TIME,
writeId(edgeLabel.ttlStartTime()));
return entry;
}
@Override
public EdgeLabel readEdgeLabel(HugeGraph graph,
BackendEntry backendEntry) {
if (backendEntry == null) {
return null;
}
TextBackendEntry entry = this.convertEntry(backendEntry);
Id id = readId(entry.id());
String name = JsonUtil.fromJson(entry.column(HugeKeys.NAME),
String.class);
String sourceLabel = entry.column(HugeKeys.SOURCE_LABEL);
String targetLabel = entry.column(HugeKeys.TARGET_LABEL);
String frequency = entry.column(HugeKeys.FREQUENCY);
String sortKeys = entry.column(HugeKeys.SORT_KEYS);
String nullablekeys = entry.column(HugeKeys.NULLABLE_KEYS);
String properties = entry.column(HugeKeys.PROPERTIES);
String indexLabels = entry.column(HugeKeys.INDEX_LABELS);
String enableLabelIndex = entry.column(HugeKeys.ENABLE_LABEL_INDEX);
String status = entry.column(HugeKeys.STATUS);
String ttl = entry.column(HugeKeys.TTL);
String ttlStartTime = entry.column(HugeKeys.TTL_START_TIME);
EdgeLabel edgeLabel = new EdgeLabel(graph, id, name);
edgeLabel.sourceLabel(readId(sourceLabel));
edgeLabel.targetLabel(readId(targetLabel));
edgeLabel.frequency(JsonUtil.fromJson(frequency, Frequency.class));
edgeLabel.properties(readIds(properties));
edgeLabel.sortKeys(readIds(sortKeys));
edgeLabel.nullableKeys(readIds(nullablekeys));
edgeLabel.addIndexLabels(readIds(indexLabels));
edgeLabel.enableLabelIndex(JsonUtil.fromJson(enableLabelIndex,
Boolean.class));
readUserdata(edgeLabel, entry);
edgeLabel.status(JsonUtil.fromJson(status, SchemaStatus.class));
edgeLabel.ttl(JsonUtil.fromJson(ttl, Long.class));
edgeLabel.ttlStartTime(readId(ttlStartTime));
return edgeLabel;
}
@Override
public BackendEntry writePropertyKey(PropertyKey propertyKey) {
TextBackendEntry entry = newBackendEntry(propertyKey);
entry.column(HugeKeys.NAME, JsonUtil.toJson(propertyKey.name()));
entry.column(HugeKeys.DATA_TYPE,
JsonUtil.toJson(propertyKey.dataType()));
entry.column(HugeKeys.CARDINALITY,
JsonUtil.toJson(propertyKey.cardinality()));
entry.column(HugeKeys.AGGREGATE_TYPE,
JsonUtil.toJson(propertyKey.aggregateType()));
entry.column(HugeKeys.WRITE_TYPE,
JsonUtil.toJson(propertyKey.writeType()));
entry.column(HugeKeys.PROPERTIES, writeIds(propertyKey.properties()));
writeUserdata(propertyKey, entry);
entry.column(HugeKeys.STATUS,
JsonUtil.toJson(propertyKey.status()));
return entry;
}
@Override
public PropertyKey readPropertyKey(HugeGraph graph,
BackendEntry backendEntry) {
if (backendEntry == null) {
return null;
}
TextBackendEntry entry = this.convertEntry(backendEntry);
Id id = readId(entry.id());
String name = JsonUtil.fromJson(entry.column(HugeKeys.NAME),
String.class);
String dataType = entry.column(HugeKeys.DATA_TYPE);
String cardinality = entry.column(HugeKeys.CARDINALITY);
String aggregateType = entry.column(HugeKeys.AGGREGATE_TYPE);
String writeType = entry.column(HugeKeys.WRITE_TYPE);
String properties = entry.column(HugeKeys.PROPERTIES);
String status = entry.column(HugeKeys.STATUS);
PropertyKey propertyKey = new PropertyKey(graph, id, name);
propertyKey.dataType(JsonUtil.fromJson(dataType, DataType.class));
propertyKey.cardinality(JsonUtil.fromJson(cardinality,
Cardinality.class));
propertyKey.aggregateType(JsonUtil.fromJson(aggregateType,
AggregateType.class));
propertyKey.writeType(JsonUtil.fromJson(writeType,
WriteType.class));
propertyKey.properties(readIds(properties));
readUserdata(propertyKey, entry);
propertyKey.status(JsonUtil.fromJson(status, SchemaStatus.class));
return propertyKey;
}
@Override
public BackendEntry writeIndexLabel(IndexLabel indexLabel) {
TextBackendEntry entry = newBackendEntry(indexLabel);
entry.column(HugeKeys.NAME, JsonUtil.toJson(indexLabel.name()));
entry.column(HugeKeys.BASE_TYPE,
JsonUtil.toJson(indexLabel.baseType()));
entry.column(HugeKeys.BASE_VALUE, writeId(indexLabel.baseValue()));
entry.column(HugeKeys.INDEX_TYPE,
JsonUtil.toJson(indexLabel.indexType()));
entry.column(HugeKeys.FIELDS, writeIds(indexLabel.indexFields()));
writeUserdata(indexLabel, entry);
entry.column(HugeKeys.STATUS,
JsonUtil.toJson(indexLabel.status()));
return entry;
}
@Override
public IndexLabel readIndexLabel(HugeGraph graph,
BackendEntry backendEntry) {
if (backendEntry == null) {
return null;
}
TextBackendEntry entry = this.convertEntry(backendEntry);
Id id = readId(entry.id());
String name = JsonUtil.fromJson(entry.column(HugeKeys.NAME),
String.class);
String baseType = entry.column(HugeKeys.BASE_TYPE);
String baseValue = entry.column(HugeKeys.BASE_VALUE);
String indexType = entry.column(HugeKeys.INDEX_TYPE);
String indexFields = entry.column(HugeKeys.FIELDS);
String status = entry.column(HugeKeys.STATUS);
IndexLabel indexLabel = new IndexLabel(graph, id, name);
indexLabel.baseType(JsonUtil.fromJson(baseType, HugeType.class));
indexLabel.baseValue(readId(baseValue));
indexLabel.indexType(JsonUtil.fromJson(indexType, IndexType.class));
indexLabel.indexFields(readIds(indexFields));
readUserdata(indexLabel, entry);
indexLabel.status(JsonUtil.fromJson(status, SchemaStatus.class));
return indexLabel;
}
private String writeEdgeId(Id id, boolean withOwnerVertex) {
EdgeId edgeId;
if (id instanceof EdgeId) {
edgeId = (EdgeId) id;
} else {
edgeId = EdgeId.parse(id.asString());
}
List<String> list = new ArrayList<>(5);
if (withOwnerVertex) {
list.add(writeEntryId(edgeId.ownerVertexId()));
}
// Edge name: type + edge-label-name + sortKeys + targetVertex
list.add(writeType(edgeId.direction().type()));
list.add(writeId(edgeId.edgeLabelId()));
list.add(writeEdgeName(edgeId.sortValues()));
list.add(writeEntryId(edgeId.otherVertexId()));
return EdgeId.concat(list.toArray(new String[0]));
}
private static String writeType(HugeType type) {
return type.string();
}
private static String writeEntryId(Id id) {
return IdUtil.writeString(id);
}
private static Id readEntryId(String id) {
return IdUtil.readString(id);
}
private static String writeEdgeName(String name) {
return name + EDGE_NAME_ENDING;
}
private static String readEdgeName(String name) {
E.checkState(name.endsWith(EDGE_NAME_ENDING),
"Invalid edge name: %s", name);
return name.substring(0, name.length() - 1);
}
private static String writeId(Id id) {
if (id.number()) {
return JsonUtil.toJson(id.asLong());
} else {
return JsonUtil.toJson(id.asString());
}
}
private static Id readId(String id) {
Object value = JsonUtil.fromJson(id, Object.class);
if (value instanceof Number) {
return IdGenerator.of(((Number) value).longValue());
} else {
assert value instanceof String;
return IdGenerator.of(value.toString());
}
}
private static Id readId(Id id) {
return readId(id.asString());
}
private static String writeIds(Collection<Id> ids) {
Object[] array = new Object[ids.size()];
int i = 0;
for (Id id : ids) {
if (id.number()) {
array[i++] = id.asLong();
} else {
array[i++] = id.asString();
}
}
return JsonUtil.toJson(array);
}
private static Id[] readIds(String str) {
Object[] values = JsonUtil.fromJson(str, Object[].class);
Id[] ids = new Id[values.length];
for (int i = 0; i < values.length; i++) {
Object value = values[i];
if (value instanceof Number) {
ids[i] = IdGenerator.of(((Number) value).longValue());
} else {
assert value instanceof String;
ids[i] = IdGenerator.of(value.toString());
}
}
return ids;
}
private static String writeElementId(Id id, long expiredTime) {
Object[] array = new Object[1];
Object idValue = id.number() ? id.asLong() : id.asString();
if (expiredTime <= 0L) {
array[0] = id;
} else {
array[0] = ImmutableMap.of(HugeKeys.ID.string(), idValue,
HugeKeys.EXPIRED_TIME.string(),
expiredTime);
}
return JsonUtil.toJson(array);
}
private static IdWithExpiredTime[] readElementIds(String str) {
Object[] values = JsonUtil.fromJson(str, Object[].class);
IdWithExpiredTime[] ids = new IdWithExpiredTime[values.length];
for (int i = 0; i < values.length; i++) {
Object idValue;
long expiredTime;
if (values[i] instanceof Map) {
@SuppressWarnings("unchecked")
Map<String, Object> map = (Map<String, Object>) values[i];
idValue = map.get(HugeKeys.ID.string());
expiredTime = ((Number) map.get(
HugeKeys.EXPIRED_TIME.string())).longValue();
} else {
idValue = values[i];
expiredTime = 0L;
}
Id id;
if (idValue instanceof Number) {
id = IdGenerator.of(((Number) idValue).longValue());
} else {
assert idValue instanceof String;
id = IdGenerator.of(idValue.toString());
}
ids[i] = new IdWithExpiredTime(id, expiredTime);
}
return ids;
}
private static String writeLong(long value) {
return JsonUtil.toJson(value);
}
private static long readLong(String value) {
return Long.parseLong(value);
}
private static void writeUserdata(SchemaElement schema,
TextBackendEntry entry) {
entry.column(HugeKeys.USER_DATA, JsonUtil.toJson(schema.userdata()));
}
private static void readUserdata(SchemaElement schema,
TextBackendEntry entry) {
// Parse all user data of a schema element
String userdataStr = entry.column(HugeKeys.USER_DATA);
@SuppressWarnings("unchecked")
Map<String, Object> userdata = JsonUtil.fromJson(userdataStr,
Map.class);
for (Map.Entry<String, Object> e : userdata.entrySet()) {
schema.userdata(e.getKey(), e.getValue());
}
}
}