ai.grakn.kb.internal.concept.ElementFactory Maven / Gradle / Ivy
/*
* GRAKN.AI - THE KNOWLEDGE GRAPH
* Copyright (C) 2018 Grakn Labs Ltd
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see .
*/
package ai.grakn.kb.internal.concept;
import ai.grakn.concept.AttributeType;
import ai.grakn.concept.Concept;
import ai.grakn.concept.ConceptId;
import ai.grakn.concept.EntityType;
import ai.grakn.concept.RelationshipType;
import ai.grakn.concept.Role;
import ai.grakn.concept.Rule;
import ai.grakn.exception.GraknTxOperationException;
import ai.grakn.exception.TemporaryWriteException;
import ai.grakn.graql.Pattern;
import ai.grakn.kb.internal.EmbeddedGraknTx;
import ai.grakn.kb.internal.structure.AbstractElement;
import ai.grakn.kb.internal.structure.EdgeElement;
import ai.grakn.kb.internal.structure.Shard;
import ai.grakn.kb.internal.structure.VertexElement;
import ai.grakn.util.Schema;
import org.apache.tinkerpop.gremlin.structure.Direction;
import org.apache.tinkerpop.gremlin.structure.Edge;
import org.apache.tinkerpop.gremlin.structure.Vertex;
import java.util.Locale;
import java.util.Objects;
import java.util.Optional;
import java.util.function.Function;
import static ai.grakn.util.Schema.BaseType.RELATIONSHIP_TYPE;
/**
*
* Constructs Concepts And Edges
*
*
*
* This class turns Tinkerpop {@link Vertex} and {@link org.apache.tinkerpop.gremlin.structure.Edge}
* into Grakn {@link Concept} and {@link EdgeElement}.
*
* Construction is only successful if the vertex and edge properties contain the needed information.
* A concept must include a label which is a {@link ai.grakn.util.Schema.BaseType}.
* An edge must include a label which is a {@link ai.grakn.util.Schema.EdgeLabel}.
*
*
* @author fppt
*/
public final class ElementFactory {
private final EmbeddedGraknTx tx;
public ElementFactory(EmbeddedGraknTx tx){
this.tx = tx;
}
private X getOrBuildConcept(E element, ConceptId conceptId, Function conceptBuilder){
if(!tx.txCache().isConceptCached(conceptId)){
X newConcept = conceptBuilder.apply(element);
tx.txCache().cacheConcept(newConcept);
}
return tx.txCache().getCachedConcept(conceptId);
}
private X getOrBuildConcept(VertexElement element, Function conceptBuilder){
ConceptId conceptId = ConceptId.of(element.property(Schema.VertexProperty.ID));
return getOrBuildConcept(element, conceptId, conceptBuilder);
}
private X getOrBuildConcept(EdgeElement element, Function conceptBuilder){
ConceptId conceptId = ConceptId.of(element.id().getValue());
return getOrBuildConcept(element, conceptId, conceptBuilder);
}
// ---------------------------------------- Building Attribute Types -----------------------------------------------
public AttributeTypeImpl buildAttributeType(VertexElement vertex, AttributeType type, AttributeType.DataType dataType){
return getOrBuildConcept(vertex, (v) -> AttributeTypeImpl.create(v, type, dataType));
}
// ------------------------------------------ Building Attribute
AttributeImpl buildAttribute(VertexElement vertex, AttributeType type, Object persitedValue){
return getOrBuildConcept(vertex, (v) -> AttributeImpl.create(v, type, persitedValue));
}
// ---------------------------------------- Building Relationship Types -----------------------------------------------
public RelationshipTypeImpl buildRelationshipType(VertexElement vertex, RelationshipType type){
return getOrBuildConcept(vertex, (v) -> RelationshipTypeImpl.create(v, type));
}
// -------------------------------------------- Building Relations
RelationshipImpl buildRelation(VertexElement vertex, RelationshipType type){
return getOrBuildConcept(vertex, (v) -> RelationshipImpl.create(buildRelationReified(v, type)));
}
public RelationshipImpl buildRelation(EdgeElement edge, RelationshipType type, Role owner, Role value){
return getOrBuildConcept(edge, (e) -> RelationshipImpl.create(RelationshipEdge.create(type, owner, value, edge)));
}
RelationshipImpl buildRelation(EdgeElement edge){
return getOrBuildConcept(edge, (e) -> RelationshipImpl.create(RelationshipEdge.get(edge)));
}
RelationshipReified buildRelationReified(VertexElement vertex, RelationshipType type){
return RelationshipReified.create(vertex, type);
}
// ----------------------------------------- Building Entity Types ------------------------------------------------
public EntityTypeImpl buildEntityType(VertexElement vertex, EntityType type){
return getOrBuildConcept(vertex, (v) -> EntityTypeImpl.create(v, type));
}
// ------------------------------------------- Building Entities
EntityImpl buildEntity(VertexElement vertex, EntityType type){
return getOrBuildConcept(vertex, (v) -> EntityImpl.create(v, type));
}
// ----------------------------------------- Building Rules --------------------------------------------------
public RuleImpl buildRule(VertexElement vertex, Rule type, Pattern when, Pattern then){
return getOrBuildConcept(vertex, (v) -> RuleImpl.create(v, type, when, then));
}
// ------------------------------------------ Building Roles Types ------------------------------------------------
public RoleImpl buildRole(VertexElement vertex, Role type){
return getOrBuildConcept(vertex, (v) -> RoleImpl.create(v, type));
}
/**
* Constructors are called directly because this is only called when reading a known vertex or concept.
* Thus tracking the concept can be skipped.
*
* @param vertex A vertex of an unknown type
* @return A concept built to the correct type
*/
public X buildConcept(Vertex vertex){
return buildConcept(buildVertexElement(vertex));
}
public X buildConcept(VertexElement vertexElement){
Schema.BaseType type;
try {
type = getBaseType(vertexElement);
} catch (IllegalStateException e){
throw TemporaryWriteException.indexOverlap(vertexElement.element(), e);
}
ConceptId conceptId = ConceptId.of(vertexElement.property(Schema.VertexProperty.ID));
if(!tx.txCache().isConceptCached(conceptId)){
Concept concept;
switch (type) {
case RELATIONSHIP:
concept = RelationshipImpl.create(RelationshipReified.get(vertexElement));
break;
case TYPE:
concept = new TypeImpl(vertexElement);
break;
case ROLE:
concept = RoleImpl.get(vertexElement);
break;
case RELATIONSHIP_TYPE:
concept = RelationshipTypeImpl.get(vertexElement);
break;
case ENTITY:
concept = EntityImpl.get(vertexElement);
break;
case ENTITY_TYPE:
concept = EntityTypeImpl.get(vertexElement);
break;
case ATTRIBUTE_TYPE:
concept = AttributeTypeImpl.get(vertexElement);
break;
case ATTRIBUTE:
concept = AttributeImpl.get(vertexElement);
break;
case RULE:
concept = RuleImpl.get(vertexElement);
break;
default:
throw GraknTxOperationException.unknownConcept(type.name());
}
tx.txCache().cacheConcept(concept);
}
return tx.txCache().getCachedConcept(conceptId);
}
/**
* Constructors are called directly because this is only called when reading a known {@link Edge} or {@link Concept}.
* Thus tracking the concept can be skipped.
*
* @param edge A {@link Edge} of an unknown type
* @return A concept built to the correct type
*/
public X buildConcept(Edge edge){
return buildConcept(buildEdgeElement(edge));
}
public X buildConcept(EdgeElement edgeElement){
Schema.EdgeLabel label = Schema.EdgeLabel.valueOf(edgeElement.label().toUpperCase(Locale.getDefault()));
ConceptId conceptId = ConceptId.of(edgeElement.id().getValue());
if(!tx.txCache().isConceptCached(conceptId)){
Concept concept;
switch (label) {
case ATTRIBUTE:
concept = RelationshipImpl.create(RelationshipEdge.get(edgeElement));
break;
default:
throw GraknTxOperationException.unknownConcept(label.name());
}
tx.txCache().cacheConcept(concept);
}
return tx.txCache().getCachedConcept(conceptId);
}
/**
* This is a helper method to get the base type of a vertex.
* It first tried to get the base type via the label.
* If this is not possible it then tries to get the base type via the Shard Edge.
*
* @param vertex The vertex to build a concept from
* @return The base type of the vertex, if it is a valid concept.
*/
private Schema.BaseType getBaseType(VertexElement vertex){
try {
return Schema.BaseType.valueOf(vertex.label());
} catch (IllegalArgumentException e){
//Base type appears to be invalid. Let's try getting the type via the shard edge
Optional type = vertex.getEdgesOfType(Direction.OUT, Schema.EdgeLabel.SHARD).
map(EdgeElement::target).findAny();
if(type.isPresent()){
String label = type.get().label();
if(label.equals(Schema.BaseType.ENTITY_TYPE.name())) return Schema.BaseType.ENTITY;
if(label.equals(RELATIONSHIP_TYPE.name())) return Schema.BaseType.RELATIONSHIP;
if(label.equals(Schema.BaseType.ATTRIBUTE_TYPE.name())) return Schema.BaseType.ATTRIBUTE;
}
}
throw new IllegalStateException("Could not determine the base type of vertex [" + vertex + "]");
}
// ---------------------------------------- Non Concept Construction -----------------------------------------------
public EdgeElement buildEdgeElement(Edge edge){
return new EdgeElement(tx, edge);
}
Shard buildShard(ConceptImpl shardOwner, VertexElement vertexElement){
return new Shard(shardOwner, vertexElement);
}
Shard buildShard(VertexElement vertexElement){
return new Shard(vertexElement);
}
Shard buildShard(Vertex vertex){
return new Shard(buildVertexElement(vertex));
}
/**
* Builds a {@link VertexElement} from an already existing Vertex. An empty optional is returned if the passed in
* vertex is not valid. A vertex is not valid if it is null or has been deleted
*
* @param vertex A vertex which can possibly be turned into a {@link VertexElement}
* @return A {@link VertexElement} of
*/
public VertexElement buildVertexElement(Vertex vertex){
if(!tx.isValidElement(vertex)){
Objects.requireNonNull(vertex);
throw GraknTxOperationException.invalidElement(vertex);
}
return new VertexElement(tx, vertex);
}
/**
* Creates a new {@link VertexElement} with a {@link ConceptId} which can optionally be set.
*
* @param baseType The {@link Schema.BaseType}
* @param conceptIds the optional {@link ConceptId} to set as the new {@link ConceptId}
* @return a new {@link VertexElement}
*/
public VertexElement addVertexElement(Schema.BaseType baseType, ConceptId ... conceptIds) {
Vertex vertex = tx.getTinkerPopGraph().addVertex(baseType.name());
String newConceptId = Schema.PREFIX_VERTEX + vertex.id().toString();
if(conceptIds.length > 1){
throw new IllegalArgumentException("Cannot provide more than one concept id when creating a new concept");
} else if (conceptIds.length == 1){
newConceptId = conceptIds[0].getValue();
}
vertex.property(Schema.VertexProperty.ID.name(), newConceptId);
tx.txCache().writeOccurred();
return new VertexElement(tx, vertex);
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy