All Downloads are FREE. Search and download functionalities are using the official Maven repository.
Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
graphql.execution.nextgen.BatchedExecutionStrategy Maven / Gradle / Ivy
package graphql.execution.nextgen;
import com.google.common.collect.ImmutableList;
import graphql.ExecutionResult;
import graphql.Internal;
import graphql.execution.Async;
import graphql.execution.ExecutionContext;
import graphql.execution.ExecutionStepInfo;
import graphql.execution.ExecutionStepInfoFactory;
import graphql.execution.FetchedValue;
import graphql.execution.MergedField;
import graphql.execution.MergedSelectionSet;
import graphql.execution.nextgen.result.ExecutionResultNode;
import graphql.execution.nextgen.result.ObjectExecutionResultNode;
import graphql.execution.nextgen.result.ResultNodesUtil;
import graphql.execution.nextgen.result.RootExecutionResultNode;
import graphql.execution.nextgen.result.UnresolvedObjectResultNode;
import graphql.util.FpKit;
import graphql.util.NodeMultiZipper;
import graphql.util.NodeZipper;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CompletableFuture;
import static graphql.Assert.assertNotEmpty;
import static graphql.Assert.assertTrue;
import static graphql.collect.ImmutableKit.map;
import static graphql.execution.nextgen.result.ResultNodeAdapter.RESULT_NODE_ADAPTER;
import static graphql.util.FpKit.flatList;
import static graphql.util.FpKit.mapEntries;
import static graphql.util.FpKit.transposeMatrix;
import static java.util.concurrent.CompletableFuture.completedFuture;
@Internal
public class BatchedExecutionStrategy implements ExecutionStrategy {
ExecutionStepInfoFactory executionInfoFactory = new ExecutionStepInfoFactory();
ValueFetcher valueFetcher = new ValueFetcher();
FetchedValueAnalyzer fetchedValueAnalyzer = new FetchedValueAnalyzer();
ExecutionStrategyUtil util = new ExecutionStrategyUtil();
ExecutionHelper executionHelper = new ExecutionHelper();
@Override
public CompletableFuture execute(ExecutionContext context) {
FieldSubSelection fieldSubSelection = executionHelper.getFieldSubSelection(context);
return executeImpl(context, fieldSubSelection)
.thenApply(ResultNodesUtil::toExecutionResult);
}
public CompletableFuture executeImpl(ExecutionContext executionContext, FieldSubSelection fieldSubSelection) {
CompletableFuture rootCF = Async.each(util.fetchSubSelection(executionContext, fieldSubSelection))
.thenApply(RootExecutionResultNode::new);
return rootCF.thenCompose(rootNode -> {
NodeMultiZipper unresolvedNodes = ResultNodesUtil.getUnresolvedNodes(rootNode);
return nextStep(executionContext, unresolvedNodes);
})
.thenApply(multiZipper -> multiZipper.toRootNode())
.thenApply(RootExecutionResultNode.class::cast);
}
private CompletableFuture> nextStep(ExecutionContext executionContext, NodeMultiZipper multizipper) {
NodeMultiZipper nextUnresolvedNodes = ResultNodesUtil.getUnresolvedNodes(multizipper.toRootNode());
if (nextUnresolvedNodes.getZippers().size() == 0) {
return completedFuture(nextUnresolvedNodes);
}
List> groups = groupNodesIntoBatches(nextUnresolvedNodes);
return resolveNodes(executionContext, groups).thenCompose(next -> nextStep(executionContext, next));
}
// all multizipper have the same root
private CompletableFuture> resolveNodes(ExecutionContext executionContext, List> unresolvedNodes) {
assertNotEmpty(unresolvedNodes, () -> "unresolvedNodes can't be empty");
ExecutionResultNode commonRoot = unresolvedNodes.get(0).getCommonRoot();
CompletableFuture>>> listListCF = Async.flatMap(unresolvedNodes,
executionResultMultiZipper -> fetchAndAnalyze(executionContext, executionResultMultiZipper.getZippers()));
return flatList(listListCF).thenApply(zippers -> new NodeMultiZipper(commonRoot, zippers, RESULT_NODE_ADAPTER));
}
private List> groupNodesIntoBatches(NodeMultiZipper unresolvedZipper) {
Map>> zipperBySubSelection = FpKit.groupingBy(unresolvedZipper.getZippers(),
(executionResultZipper -> executionResultZipper.getCurNode().getMergedField()));
return mapEntries(zipperBySubSelection, (key, value) -> new NodeMultiZipper(unresolvedZipper.getCommonRoot(), value, RESULT_NODE_ADAPTER));
}
private CompletableFuture>> fetchAndAnalyze(ExecutionContext executionContext, List> unresolvedNodes) {
assertTrue(unresolvedNodes.size() > 0, () -> "unresolvedNodes can't be empty");
List fieldSubSelections = map(unresolvedNodes,
node -> util.createFieldSubSelection(executionContext, node.getCurNode().getExecutionStepInfo(), node.getCurNode().getResolvedValue()));
//constrain: all fieldSubSelections have the same mergedSelectionSet
MergedSelectionSet mergedSelectionSet = fieldSubSelections.get(0).getMergedSelectionSet();
List>> fetchedValues = batchFetchForEachSubField(executionContext, fieldSubSelections, mergedSelectionSet);
return mapBatchedResultsBack(unresolvedNodes, fetchedValues);
}
private CompletableFuture>> mapBatchedResultsBack(List> unresolvedNodes, List>> fetchedValues) {
return Async.each(fetchedValues).thenApply(fetchedValuesMatrix -> {
List> result = new ArrayList<>();
List> newChildsPerNode = transposeMatrix(fetchedValuesMatrix);
for (int i = 0; i < newChildsPerNode.size(); i++) {
NodeZipper unresolvedNodeZipper = unresolvedNodes.get(i);
List fetchedValuesForNode = newChildsPerNode.get(i);
NodeZipper resolvedZipper = resolveZipper(unresolvedNodeZipper, fetchedValuesForNode);
result.add(resolvedZipper);
}
return result;
});
}
private List>> batchFetchForEachSubField(ExecutionContext executionContext,
List fieldSubSelections,
MergedSelectionSet mergedSelectionSet) {
List sources = map(fieldSubSelections, FieldSubSelection::getSource);
return mapEntries(mergedSelectionSet.getSubFields(), (name, mergedField) -> {
List newExecutionStepInfos = newExecutionInfos(executionContext, fieldSubSelections, mergedField);
return valueFetcher
.fetchBatchedValues(executionContext, sources, mergedField, newExecutionStepInfos)
.thenApply(fetchValue -> analyseValues(executionContext, fetchValue, newExecutionStepInfos));
});
}
private List newExecutionInfos(ExecutionContext executionContext, List fieldSubSelections, MergedField mergedField) {
return map(fieldSubSelections,
subSelection -> executionInfoFactory.newExecutionStepInfoForSubField(executionContext, mergedField, subSelection.getExecutionStepInfo()));
}
private NodeZipper resolveZipper(NodeZipper unresolvedNodeZipper, List fetchedValuesForNode) {
UnresolvedObjectResultNode unresolvedNode = (UnresolvedObjectResultNode) unresolvedNodeZipper.getCurNode();
List newChildren = util.fetchedValueAnalysisToNodes(fetchedValuesForNode);
ObjectExecutionResultNode newNode = unresolvedNode.withNewChildren(newChildren);
return unresolvedNodeZipper.withNewNode(newNode);
}
private List analyseValues(ExecutionContext executionContext, List fetchedValues, List executionInfos) {
List result = new ArrayList<>();
for (int i = 0; i < fetchedValues.size(); i++) {
FetchedValue fetchedValue = fetchedValues.get(i);
ExecutionStepInfo executionStepInfo = executionInfos.get(i);
FetchedValueAnalysis fetchedValueAnalysis = fetchedValueAnalyzer.analyzeFetchedValue(executionContext, fetchedValue, executionStepInfo);
result.add(fetchedValueAnalysis);
}
return result;
}
}