grakn.core.graql.executor.property.RelationExecutor.java Source code

Java tutorial

Introduction

Here is the source code for grakn.core.graql.executor.property.RelationExecutor.java

Source

/*
 * 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 <https://www.gnu.org/licenses/>.
 */

package grakn.core.graql.executor.property;

import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Sets;
import grakn.core.concept.Concept;
import grakn.core.concept.ConceptId;
import grakn.core.concept.Label;
import grakn.core.concept.thing.Relation;
import grakn.core.concept.thing.Thing;
import grakn.core.concept.type.Role;
import grakn.core.graql.exception.GraqlQueryException;
import grakn.core.graql.exception.GraqlSemanticException;
import grakn.core.graql.executor.WriteExecutor;
import grakn.core.graql.gremlin.EquivalentFragmentSet;
import grakn.core.graql.gremlin.sets.EquivalentFragmentSets;
import grakn.core.graql.reasoner.atom.Atomic;
import grakn.core.graql.reasoner.atom.binary.RelationAtom;
import grakn.core.graql.reasoner.atom.predicate.IdPredicate;
import grakn.core.graql.reasoner.query.ReasonerQuery;
import graql.lang.property.IsaProperty;
import graql.lang.property.RelationProperty;
import graql.lang.property.VarProperty;
import graql.lang.statement.Statement;
import graql.lang.statement.Variable;

import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import static grakn.core.graql.gremlin.sets.EquivalentFragmentSets.rolePlayer;
import static grakn.core.graql.reasoner.utils.ReasonerUtils.getUserDefinedIdPredicate;

public class RelationExecutor implements PropertyExecutor.Insertable {

    private final Variable var;
    private final RelationProperty property;

    RelationExecutor(Variable var, RelationProperty property) {
        this.var = var;
        this.property = property;
    }

    @Override
    public Set<EquivalentFragmentSet> matchFragments() {
        Collection<Variable> castingNames = new HashSet<>();

        ImmutableSet<EquivalentFragmentSet> traversals = property.relationPlayers().stream()
                .flatMap(relationPlayer -> {
                    Variable castingName = new Variable();
                    castingNames.add(castingName);
                    return fragmentSetsFromRolePlayer(castingName, relationPlayer);
                }).collect(ImmutableSet.toImmutableSet());

        ImmutableSet<EquivalentFragmentSet> distinctCastingTraversals = castingNames.stream()
                .flatMap(castingName -> castingNames.stream().filter(otherName -> !otherName.equals(castingName))
                        .map(otherName -> EquivalentFragmentSets.neq(property, castingName, otherName)))
                .collect(ImmutableSet.toImmutableSet());

        return Sets.union(traversals, distinctCastingTraversals);
    }

    private Stream<EquivalentFragmentSet> fragmentSetsFromRolePlayer(Variable castingName,
            RelationProperty.RolePlayer relationPlayer) {
        Optional<Statement> roleType = relationPlayer.getRole();

        if (roleType.isPresent()) {
            // Patterns for variable of a relation with a role player of a given type
            return Stream.of(
                    rolePlayer(property, var, castingName, relationPlayer.getPlayer().var(), roleType.get().var()));
        } else {
            // Patterns for variable of a relation with a role player of a any type
            return Stream.of(rolePlayer(property, var, castingName, relationPlayer.getPlayer().var(), null));
        }
    }

    @Override
    public Atomic atomic(ReasonerQuery parent, Statement statement, Set<Statement> otherStatements) {
        //set varName as user defined if reified
        //reified if contains more properties than the RelationProperty itself and potential IsaProperty
        boolean isReified = statement.properties().stream().filter(prop -> !RelationProperty.class.isInstance(prop))
                .anyMatch(prop -> !IsaProperty.class.isInstance(prop));
        Statement relVar = isReified ? new Statement(var.asReturnedVar()) : new Statement(var);

        for (RelationProperty.RolePlayer rp : property.relationPlayers()) {
            Statement rolePattern = rp.getRole().orElse(null);
            Statement rolePlayer = rp.getPlayer();
            if (rolePattern != null) {
                Variable roleVar = rolePattern.var();
                //look for indirect role definitions
                IdPredicate roleId = getUserDefinedIdPredicate(roleVar, otherStatements, parent);
                if (roleId != null) {
                    Concept concept = parent.tx().getConcept(roleId.getPredicate());
                    if (concept != null) {
                        if (concept.isRole()) {
                            Label roleLabel = concept.asSchemaConcept().label();
                            rolePattern = new Statement(roleVar).type(roleLabel.getValue());
                        } else {
                            throw GraqlQueryException.nonRoleIdAssignedToRoleVariable(statement);
                        }
                    }
                }
                relVar = relVar.rel(rolePattern, rolePlayer);
            } else
                relVar = relVar.rel(rolePlayer);
        }

        //isa part
        IsaProperty isaProp = statement.getProperty(IsaProperty.class).orElse(null);
        IdPredicate predicate = null;

        //if no isa property present generate type variable
        Variable typeVariable = isaProp != null ? isaProp.type().var() : new Variable();

        //Isa present
        if (isaProp != null) {
            Statement isaVar = isaProp.type();
            String label = isaVar.getType().orElse(null);
            if (label != null) {
                predicate = IdPredicate.create(typeVariable, Label.of(label), parent);
            } else {
                typeVariable = isaVar.var();
                predicate = getUserDefinedIdPredicate(typeVariable, otherStatements, parent);
            }
        }
        ConceptId predicateId = predicate != null ? predicate.getPredicate() : null;
        relVar = isaProp != null && isaProp.isExplicit() ? relVar.isaX(new Statement(typeVariable.asReturnedVar()))
                : relVar.isa(new Statement(typeVariable.asReturnedVar()));
        return RelationAtom.create(relVar, typeVariable, predicateId, parent);
    }

    @Override
    public Set<PropertyExecutor.Writer> insertExecutors() {
        return ImmutableSet.of(new InsertRelation());
    }

    class InsertRelation implements PropertyExecutor.Writer {

        @Override
        public Variable var() {
            return var;
        }

        @Override
        public VarProperty property() {
            return property;
        }

        @Override
        public Set<Variable> requiredVars() {
            Set<Variable> relationPlayers = property.relationPlayers().stream()
                    .flatMap(relationPlayer -> Stream.of(relationPlayer.getPlayer(), getRole(relationPlayer)))
                    .map(statement -> statement.var()).collect(Collectors.toSet());

            relationPlayers.add(var);

            return Collections.unmodifiableSet(relationPlayers);
        }

        @Override
        public Set<Variable> producedVars() {
            return ImmutableSet.of();
        }

        @Override
        public void execute(WriteExecutor executor) {
            Relation relation = executor.getConcept(var).asRelation();
            property.relationPlayers().forEach(relationPlayer -> {
                Statement roleVar = getRole(relationPlayer);

                Role role = executor.getConcept(roleVar.var()).asRole();
                Thing roleplayer = executor.getConcept(relationPlayer.getPlayer().var()).asThing();
                relation.assign(role, roleplayer);
            });
        }

        private Statement getRole(RelationProperty.RolePlayer relationPlayer) {
            return relationPlayer.getRole().orElseThrow(GraqlSemanticException::insertRolePlayerWithoutRoleType);
        }
    }
}