All Downloads are FREE. Search and download functionalities are using the official Maven repository.

io.mindmaps.graql.internal.query.DeleteQueryImpl Maven / Gradle / Ivy

/*
 * MindmapsDB - A Distributed Semantic Database
 * Copyright (C) 2016  Mindmaps Research Ltd
 *
 * MindmapsDB is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * MindmapsDB 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 General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with MindmapsDB. If not, see .
 */

package io.mindmaps.graql.internal.query;

import com.google.common.collect.ImmutableMap;
import io.mindmaps.MindmapsGraph;
import io.mindmaps.concept.Concept;
import io.mindmaps.concept.Resource;
import io.mindmaps.exception.ConceptException;
import io.mindmaps.graql.DeleteQuery;
import io.mindmaps.graql.MatchQuery;
import io.mindmaps.graql.admin.DeleteQueryAdmin;
import io.mindmaps.graql.admin.MatchQueryAdmin;
import io.mindmaps.graql.admin.VarAdmin;
import io.mindmaps.graql.internal.validation.DeleteQueryValidator;
import io.mindmaps.util.ErrorMessage;

import java.util.Collection;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * A DeleteQuery that will execute deletions for every result of a MatchQuery
 */
class DeleteQueryImpl implements DeleteQueryAdmin {
    private final ImmutableMap deleters;
    private final MatchQueryAdmin matchQuery;

    /**
     * @param deleters a collection of variable patterns to delete
     * @param matchQuery a pattern to match and delete for each result
     */
    DeleteQueryImpl(Collection deleters, MatchQuery matchQuery) {
        Map deletersMap =
                deleters.stream().collect(Collectors.toMap(VarAdmin::getName, Function.identity()));
        this.deleters = ImmutableMap.copyOf(deletersMap);

        this.matchQuery = matchQuery.admin();

        matchQuery.admin().getGraph().ifPresent(
                graph -> new DeleteQueryValidator(this).validate(graph)
        );
    }

    @Override
    public Void execute() {
        matchQuery.forEach(results -> results.forEach(this::deleteResult));
        return null;
    }

    @Override
    public Stream resultsString() {
        execute();
        return Stream.empty();
    }

    @Override
    public boolean isReadOnly() {
        return false;
    }

    @Override
    public DeleteQuery withGraph(MindmapsGraph graph) {
        return Queries.delete(deleters.values(), matchQuery.withGraph(graph));
    }

    @Override
    public DeleteQueryAdmin admin() {
        return this;
    }

    /**
     * Delete a result from a query. This may involve deleting the whole concept or specific edges, depending
     * on what deleters were provided.
     * @param name the variable name to delete
     * @param result the concept that matches the variable in the graph
     */
    private void deleteResult(String name, Concept result) {
        VarAdmin deleter = deleters.get(name);

        // Check if this has been requested to be deleted
        if (deleter == null) return;

        String id = result.getId();

        if (deleter.hasNoProperties()) {
            // Delete whole concept if nothing specified to delete
            deleteConcept(id);
        } else {
            deleter.getHasRoles().forEach(
                    role -> role.getId().ifPresent(
                            typeName -> getGraph().getRelationType(id).deleteHasRole(getGraph().getRoleType(typeName))
                    )
            );

            deleter.getPlaysRoles().forEach(
                    role -> role.getId().ifPresent(
                            typeName -> getGraph().getType(id).deletePlaysRole(getGraph().getRoleType(typeName))
                    )
            );

            deleter.getScopes().forEach(
                    scope -> scope.getId().ifPresent(
                            scopeName -> getGraph().getRelation(id).deleteScope(getGraph().getInstance(scopeName))
                    )
            );

            deleter.getResourcePredicates().forEach((type, predicates) -> {
                Set values = new HashSet<>();
                predicates.forEach(predicate -> predicate.equalsValue().ifPresent(values::add));
                deleteResources(id, type, values);
            });
        }
    }

    /**
     * Delete a concept by ID, rethrowing errors as RuntimeExceptions
     * @param id an ID to delete in the graph
     */
    private void deleteConcept(String id) {
        try {
            getGraph().getConcept(id).delete();
        } catch (ConceptException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * Delete all resources of the given type and given values on the given concept
     * @param id the ID of a concept
     * @param type a variable representing the resource type
     * @param values a set of values of resources
     */
    private void deleteResources(String id, VarAdmin type, Set values) {
        String typeId = type.getId().orElseThrow(
                () -> new IllegalStateException(ErrorMessage.DELETE_RESOURCE_TYPE_NO_ID.getMessage(id))
        );

        resources(id).stream()
                .filter(r -> r.type().getId().equals(typeId))
                .filter(r -> values.isEmpty() || values.contains(r.getValue()))
                .forEach(Concept::delete);
    }

    /**
     * @param id the ID of a concept
     * @return all resources on the given concept
     */
    private Collection> resources(String id) {
        // Get resources attached to a concept
        // This method is necessary because the 'resource' method appears in 3 separate interfaces
        Concept concept = getGraph().getConcept(id);

        if (concept.isEntity()) {
            return concept.asEntity().resources();
        } else if (concept.isRelation()) {
            return concept.asRelation().resources();
        } else if (concept.isRule()) {
            return concept.asRule().resources();
        } else {
            return new HashSet<>();
        }
    }

    private MindmapsGraph getGraph() {
        return matchQuery.getGraph().orElseThrow(
                () -> new IllegalStateException(ErrorMessage.NO_GRAPH.getMessage())
        );
    }

    @Override
    public Collection getDeleters() {
        return deleters.values();
    }

    @Override
    public MatchQuery getMatchQuery() {
        return matchQuery;
    }
}