| /* |
| * 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.commons.lang.NotImplementedException; |
| import org.apache.hugegraph.HugeException; |
| import org.apache.hugegraph.HugeGraph; |
| import org.apache.hugegraph.backend.BackendException; |
| 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.backend.store.BackendEntry; |
| import org.apache.hugegraph.config.HugeConfig; |
| 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.HugeType; |
| 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 org.apache.hugegraph.util.JsonUtil; |
| |
| 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()); |
| } |
| } |
| } |