blob: 723cbc2d2763c3cccfdb6105e58be3a959375b2c [file] [log] [blame]
/*
* Copyright 2017 HugeGraph Authors
*
* 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 com.baidu.hugegraph.serializer.direct.struct;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.UUID;
import java.util.function.Supplier;
import org.apache.tinkerpop.gremlin.structure.Property;
import org.apache.tinkerpop.gremlin.structure.T;
import org.apache.tinkerpop.gremlin.structure.util.ElementHelper;
import org.eclipse.collections.api.iterator.IntIterator;
import org.eclipse.collections.api.map.primitive.MutableIntObjectMap;
import com.baidu.hugegraph.backend.id.EdgeId;
import com.baidu.hugegraph.backend.id.IdGenerator;
import com.baidu.hugegraph.perf.PerfUtil.Watched;
import com.baidu.hugegraph.schema.PropertyKey;
import com.baidu.hugegraph.schema.SchemaLabel;
import com.baidu.hugegraph.serializer.direct.BytesBuffer;
import com.baidu.hugegraph.serializer.direct.util.Id;
import com.baidu.hugegraph.structure.Element;
import com.baidu.hugegraph.type.HugeType;
import com.baidu.hugegraph.type.Idfiable;
import com.baidu.hugegraph.type.define.Cardinality;
import com.baidu.hugegraph.util.CollectionUtil;
import com.baidu.hugegraph.util.E;
import com.baidu.hugegraph.util.InsertionOrderUtil;
import com.baidu.hugegraph.util.collection.CollectionFactory;
public abstract class HugeElement implements Element, GraphType, Idfiable {
private static final MutableIntObjectMap<HugeProperty<?>> EMPTY_MAP =
CollectionFactory.newIntObjectMap();
private static final int MAX_PROPERTIES = BytesBuffer.UINT16_MAX;
private MutableIntObjectMap<HugeProperty<?>> properties;
String label;
public HugeElement() {
this.properties = EMPTY_MAP;
}
public String label() {
return this.label;
}
public abstract SchemaLabel schemaLabel();
protected abstract <V> HugeProperty<V> newProperty(PropertyKey pk, V val);
protected abstract <V> void onUpdateProperty(Cardinality cardinality,
HugeProperty<V> prop);
protected abstract boolean ensureFilledProperties(boolean throwIfNotExist);
public Set<Id> getPropertyKeys() {
Set<Id> propKeys = InsertionOrderUtil.newSet();
IntIterator keys = this.properties.keysView().intIterator();
while (keys.hasNext()) {
propKeys.add(IdGenerator.of(keys.next()));
}
return propKeys;
}
public Collection<HugeProperty<?>> getProperties() {
return this.properties.values();
}
public Collection<HugeProperty<?>> getFilledProperties() {
this.ensureFilledProperties(true);
return this.getProperties();
}
public Map<Id, Object> getPropertiesMap() {
Map<Id, Object> props = InsertionOrderUtil.newMap();
for (HugeProperty<?> prop : this.properties.values()) {
props.put(prop.propertyKey().id(), prop.value());
}
// TODO: return MutableIntObjectMap<Object> for this method?
return props;
}
public Collection<HugeProperty<?>> getAggregateProperties() {
List<HugeProperty<?>> aggrProps = InsertionOrderUtil.newList();
for (HugeProperty<?> prop : this.properties.values()) {
if (prop.type().isAggregateProperty()) {
aggrProps.add(prop);
}
}
return aggrProps;
}
@SuppressWarnings("unchecked")
public <V> HugeProperty<V> getProperty(Id key) {
return (HugeProperty<V>) this.properties.get(intFromId(key));
}
@SuppressWarnings("unchecked")
public <V> V getPropertyValue(Id key) {
HugeProperty<?> prop = this.properties.get(intFromId(key));
if (prop == null) {
return null;
}
return (V) prop.value();
}
public boolean hasProperty(Id key) {
return this.properties.containsKey(intFromId(key));
}
public boolean hasProperties() {
return this.properties.size() > 0;
}
public int sizeOfProperties() {
return this.properties.size();
}
public int sizeOfSubProperties() {
int size = 0;
for (HugeProperty<?> p : this.properties.values()) {
size++;
if (p.propertyKey().cardinality() != Cardinality.SINGLE &&
p.value() instanceof Collection) {
size += ((Collection<?>) p.value()).size();
}
}
return size;
}
@Watched(prefix = "element")
public <V> HugeProperty<?> setProperty(HugeProperty<V> prop) {
if (this.properties == EMPTY_MAP) {
this.properties = CollectionFactory.newIntObjectMap();
}
PropertyKey pkey = prop.propertyKey();
E.checkArgument(this.properties.containsKey(intFromId(pkey.id())) ||
this.properties.size() < MAX_PROPERTIES,
"Exceeded the maximum number of properties");
return this.properties.put(intFromId(pkey.id()), prop);
}
public <V> HugeProperty<?> removeProperty(Id key) {
return this.properties.remove(intFromId(key));
}
public <V> HugeProperty<V> addProperty(PropertyKey pkey, V value) {
return this.addProperty(pkey, value, false);
}
@Watched(prefix = "element")
public <V> HugeProperty<V> addProperty(PropertyKey pkey, V value,
boolean notify) {
HugeProperty<V> prop = null;
switch (pkey.cardinality()) {
case SINGLE:
prop = this.newProperty(pkey, value);
if (notify) {
/*
* NOTE: this method should be called before setProperty()
* because tx need to delete index without the new property
*/
this.onUpdateProperty(pkey.cardinality(), prop);
}
this.setProperty(prop);
break;
case SET:
prop = this.addProperty(pkey, value, HashSet::new);
if (notify) {
this.onUpdateProperty(pkey.cardinality(), prop);
}
break;
case LIST:
prop = this.addProperty(pkey, value, ArrayList::new);
if (notify) {
this.onUpdateProperty(pkey.cardinality(), prop);
}
break;
default:
assert false;
break;
}
return prop;
}
@Watched(prefix = "element")
@SuppressWarnings({ "rawtypes", "unchecked" })
private <V> HugeProperty<V> addProperty(PropertyKey pkey, V value,
Supplier<Collection<V>> supplier) {
assert pkey.cardinality().multiple();
HugeProperty<Collection<V>> property;
if (this.hasProperty(pkey.id())) {
property = this.getProperty(pkey.id());
} else {
property = this.newProperty(pkey, supplier.get());
this.setProperty(property);
}
Collection<V> values;
if (pkey.cardinality() == Cardinality.SET) {
if (value instanceof Set) {
values = (Set<V>) value;
} else {
values = CollectionUtil.toSet(value);
}
} else {
assert pkey.cardinality() == Cardinality.LIST;
if (value instanceof List) {
values = (List<V>) value;
} else {
values = CollectionUtil.toList(value);
}
}
property.value().addAll(pkey.validValueOrThrow(values));
// Any better ways?
return property;
}
public void resetProperties() {
this.properties = CollectionFactory.newIntObjectMap();
this.propLoaded = false;
}
protected void copyProperties(HugeElement element) {
if (element.properties == EMPTY_MAP) {
this.properties = EMPTY_MAP;
} else {
this.properties = CollectionFactory.newIntObjectMap(
element.properties);
}
this.propLoaded = true;
}
public HugeElement copyAsFresh() {
HugeElement elem = this.copy();
elem.fresh = true;
return elem;
}
public abstract HugeElement copy();
@Override
public boolean equals(Object obj) {
if (!(obj instanceof Element)) {
return false;
}
Element other = (Element) obj;
if (this.id() == null) {
return false;
}
return this.id().equals(other.id());
}
/**
* Classify parameter list(pairs) from call request
* @param keyValues The property key-value pair of the vertex or edge
* @return Key-value pairs that are classified and processed
*/
@Watched(prefix = "element")
public static final ElementKeys classifyKeys(Object... keyValues) {
ElementKeys elemKeys = new ElementKeys();
if ((keyValues.length & 1) == 1) {
throw Element.Exceptions.providedKeyValuesMustBeAMultipleOfTwo();
}
for (int i = 0; i < keyValues.length; i = i + 2) {
Object key = keyValues[i];
Object val = keyValues[i + 1];
if (!(key instanceof String) && !(key instanceof T)) {
throw Element.Exceptions
.providedKeyValuesMustHaveALegalKeyOnEvenIndices();
}
if (val == null) {
throw Property.Exceptions.propertyDoesNotExist();
}
if (key.equals(T.id)) {
elemKeys.id = val;
} else if (key.equals(T.label)) {
elemKeys.label = val;
} else {
elemKeys.keys.add(key.toString());
}
}
return elemKeys;
}
public static final Id getIdValue(HugeType type, Object idValue) {
assert type.isGraph();
Id id = getIdValue(idValue);
if (type.isVertex()) {
return id;
} else {
if (id == null || id instanceof EdgeId) {
return id;
}
return EdgeId.parse(id.asString());
}
}
@Watched(prefix = "element")
protected static Id getIdValue(Object idValue) {
if (idValue == null) {
return null;
}
if (idValue instanceof String) {
// String id
return IdGenerator.of((String) idValue);
} else if (idValue instanceof Number) {
// Long id
return IdGenerator.of(((Number) idValue).longValue());
} else if (idValue instanceof UUID) {
// UUID id
return IdGenerator.of((UUID) idValue);
} else if (idValue instanceof Id) {
// Id itself
return (Id) idValue;
} else if (idValue instanceof Element) {
// Element
return (Id) ((Element) idValue).id();
}
// Throw if error type
throw new UnsupportedOperationException(String.format(
"Invalid element id: %s(%s)",
idValue, idValue.getClass().getSimpleName()));
}
@Watched(prefix = "element")
public static final Object getLabelValue(Object... keyValues) {
Object labelValue = null;
for (int i = 0; i < keyValues.length; i = i + 2) {
if (keyValues[i].equals(T.label)) {
labelValue = keyValues[i + 1];
if (labelValue instanceof String) {
ElementHelper.validateLabel((String) labelValue);
}
break;
}
}
return labelValue;
}
public static int intFromId(Id id) {
E.checkArgument(id instanceof IdGenerator.LongId,
"Can't get number from %s(%s)", id, id.getClass());
return ((IdGenerator.LongId) id).intValue();
}
public static final class ElementKeys {
private Object label = null;
private Object id = null;
private Set<String> keys = new HashSet<>();
public Object label() {
return this.label;
}
public void label(Object label) {
this.label = label;
}
public Object id() {
return this.id;
}
public void id(Object id) {
this.id = id;
}
public Set<String> keys() {
return this.keys;
}
public void keys(Set<String> keys) {
this.keys = keys;
}
}
}