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

uk.gov.gchq.gaffer.tinkerpop.process.traversal.step.GafferPopGraphStep Maven / Gradle / Ivy

There is a newer version: 2.3.0
Show newest version
/*
 * Copyright 2024 Crown Copyright
 *
 * Licensed 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 uk.gov.gchq.gaffer.tinkerpop.process.traversal.step;

import org.apache.tinkerpop.gremlin.process.traversal.Compare;
import org.apache.tinkerpop.gremlin.process.traversal.Contains;
import org.apache.tinkerpop.gremlin.process.traversal.step.HasContainerHolder;
import org.apache.tinkerpop.gremlin.process.traversal.step.map.GraphStep;
import org.apache.tinkerpop.gremlin.process.traversal.step.util.HasContainer;
import org.apache.tinkerpop.gremlin.process.traversal.strategy.decoration.OptionsStrategy;
import org.apache.tinkerpop.gremlin.structure.Direction;
import org.apache.tinkerpop.gremlin.structure.Edge;
import org.apache.tinkerpop.gremlin.structure.Element;
import org.apache.tinkerpop.gremlin.structure.T;
import org.apache.tinkerpop.gremlin.structure.Vertex;
import org.apache.tinkerpop.gremlin.util.iterator.IteratorUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import uk.gov.gchq.gaffer.data.element.function.ElementFilter;
import uk.gov.gchq.gaffer.data.elementdefinition.view.GlobalViewElementDefinition;
import uk.gov.gchq.gaffer.data.elementdefinition.view.View;
import uk.gov.gchq.gaffer.data.elementdefinition.view.ViewElementDefinition;
import uk.gov.gchq.gaffer.tinkerpop.GafferPopGraph;
import uk.gov.gchq.gaffer.tinkerpop.GafferPopGraph.HasStepFilterStage;
import uk.gov.gchq.gaffer.tinkerpop.GafferPopGraphVariables;
import uk.gov.gchq.gaffer.tinkerpop.process.traversal.step.util.GafferPopHasContainer;
import uk.gov.gchq.koryphe.impl.predicate.Exists;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;

/**
 * Custom GafferPop GraphStep provides Gaffer specific optimisations
 * for the initial GraphStep in a query. Also responsible for parsing
 * any options passed via a 'with()' call on the query.
 *
 * 
 * g.with("userId", "user").V()   // userId extracted to be used in the operation executions
 * g.with("dataAuths", "write-access,read-access").V()   // user access controls to apply on the user
 * g.with("operationOptions", ["graphId:graph1", "opt1:val1"]).V()   // operation options extracted and applied
 * 
*/ public class GafferPopGraphStep extends GraphStep implements HasContainerHolder { private static final Logger LOGGER = LoggerFactory.getLogger(GafferPopGraphStep.class); private final List hasContainers = new ArrayList<>(); public GafferPopGraphStep(final GraphStep originalGraphStep) { super(originalGraphStep.getTraversal(), originalGraphStep.getReturnClass(), originalGraphStep.isStartStep(), originalGraphStep.getIds()); LOGGER.debug("Running custom GraphStep on GafferPopGraph"); originalGraphStep.getLabels().forEach(this::addLabel); // Save reference to the graph GafferPopGraph graph = (GafferPopGraph) originalGraphStep.getTraversal().getGraph().get(); // Restore variables to defaults before parsing options graph.setDefaultVariables((GafferPopGraphVariables) graph.variables()); // Find any options on the traversal Optional optionsStrategy = originalGraphStep.getTraversal().getStrategies().getStrategy(OptionsStrategy.class); if (optionsStrategy.isPresent()) { LOGGER.debug("Found options on requested traversal"); optionsStrategy.get().getOptions().forEach((k, v) -> { if (graph.variables().asMap().containsKey(k)) { graph.variables().set(k, v); } }); } // Set the output iterator to the relevant filtered output from the class methods this.setIteratorSupplier(() -> (Iterator) (Vertex.class.isAssignableFrom(this.returnClass) ? this.vertices(graph) : this.edges(graph))); } private Iterator edges(final GafferPopGraph graph) { // Check for the labels being searched for to construct a View to filter with List labels = getRequestedLabels(); // Get the View needed to for the property predicates String filterStage = ((GafferPopGraphVariables) graph.variables()).getHasStepFilterStage(); View view = createEdgeViewFromPredicates(filterStage, labels); if (view != null) { // Find using view to filter results return graph.edgesWithView(Arrays.asList(this.ids), Direction.BOTH, view); } else if (!labels.isEmpty()) { // Find using labels to filter results return graph.edges(Arrays.asList(this.ids), Direction.BOTH, labels.toArray(new String[0])); } else if (this.ids == null) { return Collections.emptyIterator(); } // linear scan as fallback LOGGER.debug("Using fallback filter method: {} hasContainers found", hasContainers.size()); return IteratorUtils.filter(graph.edges(this.ids), edge -> HasContainer.testAll(edge, hasContainers)); } private Iterator vertices(final GafferPopGraph graph) { // Check for the labels being searched for to construct a View to filter with List labels = getRequestedLabels(); // Get the View needed to for the property predicates String filterStage = ((GafferPopGraphVariables) graph.variables()).getHasStepFilterStage(); View view = createVertexViewFromPredicates(filterStage, labels); if (view != null) { // Find using view to filter results return graph.verticesWithView(Arrays.asList(this.ids), view); } else if (!labels.isEmpty()) { // Find using labels to filter results return graph.vertices(Arrays.asList(this.ids), labels.toArray(new String[0])); } else if (this.ids == null) { return Collections.emptyIterator(); } // linear scan as fallback LOGGER.debug("Using fallback filter method: {} hasContainers found", hasContainers.size()); return IteratorUtils.filter(graph.vertices(this.ids), vertex -> HasContainer.testAll(vertex, hasContainers)); } /** * Checks all the HasContainers to see what labels have been * requested for. * * @return List of labels requested in the {@link GraphStep} */ private List getRequestedLabels() { return hasContainers.stream() .filter(hc -> hc.getKey() != null && hc.getKey().equals(T.label.getAccessor())) .filter(hc -> Compare.eq == hc.getBiPredicate() || Contains.within == hc.getBiPredicate()) .filter(hc -> hc.getValue() != null) // Incase of ~label.within([]) predicate // map to value list and then flatten .map(hc -> hc.getValue() instanceof List ? (List) hc.getValue() : Collections.singletonList((String) hc.getValue())) .flatMap(Collection::stream) .collect(Collectors.toList()); } /** * Creates a View from the predicates in the HasContainers. * This can be used to filter vertices. * * @param filterStage the stage to apply the filters * @param labels the vertex labels to apply the filters to * @return View containing the filters */ private View createVertexViewFromPredicates(final String filterStage, final List labels) { ViewElementDefinition viewElementDefinition = createElementDefFromPredicates(filterStage, labels.isEmpty()); if (viewElementDefinition == null) { return null; } View.Builder viewBuilder = new View.Builder(); if (viewElementDefinition instanceof GlobalViewElementDefinition) { // Apply filters to all vertices viewBuilder.globalElements((GlobalViewElementDefinition) viewElementDefinition); } else { // Apply filters to each of the labels labels.forEach(l -> viewBuilder.entity(l, viewElementDefinition)); } return viewBuilder.build(); } /** * Creates a View from the predicates in the HasContainers. * This can be used to filter edges. * * @param filterStage the stage to apply the filters * @param labels the edge labels to apply the filters to * @return View containing the filters */ private View createEdgeViewFromPredicates(final String filterStage, final List labels) { ViewElementDefinition viewElementDefinition = createElementDefFromPredicates(filterStage, labels.isEmpty()); if (viewElementDefinition == null) { return null; } View.Builder viewBuilder = new View.Builder(); if (viewElementDefinition instanceof GlobalViewElementDefinition) { // Apply filters to all edges viewBuilder.globalEdges((GlobalViewElementDefinition) viewElementDefinition); } else { // Apply filters to each of the labels labels.forEach(l -> viewBuilder.edge(l, viewElementDefinition)); } return viewBuilder.build(); } /** * Creates a ViewElementDefinition from the predicates in the HasContainers. * This can be used in a View to filter entities. * * @param filterStage the stage to apply the filters * @param createGlobal whether to return a GlobalViewElementDefinition * or a ViewElementDefinition * @return ViewElementDefinition containing the filters */ private ViewElementDefinition createElementDefFromPredicates(final String filterStage, final boolean createGlobal) { List predicateContainers = getRequestedPredicates(); // No predicates found if (predicateContainers.isEmpty()) { return null; } // Add each predicate to the filter ElementFilter.Builder filterBuilder = new ElementFilter.Builder(); predicateContainers.forEach( hc -> filterBuilder.select(hc.getKey()) // Only apply the HC predicate to properties that exist .execute(new Exists()) .select(hc.getKey()) .execute(hc.getGafferPredicate())); ElementFilter elementFilter = filterBuilder.build(); ViewElementDefinition.BaseBuilder vBuilder = createGlobal ? new GlobalViewElementDefinition.Builder() : new ViewElementDefinition.Builder(); // Decide when to apply the filter HasStepFilterStage hasStepFilterStage; try { hasStepFilterStage = HasStepFilterStage.valueOf(filterStage); } catch (final IllegalArgumentException e) { LOGGER.warn("Unknown hasStepFilterStage: {}. Defaulting to {}", filterStage, GafferPopGraph.DEFAULT_HAS_STEP_FILTER_STAGE); hasStepFilterStage = GafferPopGraph.DEFAULT_HAS_STEP_FILTER_STAGE; } switch (hasStepFilterStage) { case POST_TRANSFORM: vBuilder.postTransformFilter(elementFilter); break; case POST_AGGREGATION: vBuilder.postAggregationFilter(elementFilter); break; case PRE_AGGREGATION: vBuilder.preAggregationFilter(elementFilter); break; default: vBuilder.preAggregationFilter(elementFilter); break; } return vBuilder.build(); } /** * Checks all the HasContainers to see which predicates have been requested * * @return List of HasContainers with predicates */ private List getRequestedPredicates() { // Don't filter out null hc.getValue() incase of AndP/OrP return hasContainers.stream() .filter(hc -> hc.getKey() != null) .filter(hc -> !hc.getKey().equals(T.label.getAccessor())) .filter(hc -> !hc.getKey().equals(T.id.getAccessor())) .map(hc -> (GafferPopHasContainer) hc) .collect(Collectors.toList()); } @Override public List getHasContainers() { return Collections.unmodifiableList(hasContainers); } @Override public void addHasContainer(final HasContainer original) { hasContainers.add(new GafferPopHasContainer(original)); } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy