core.pure.graphFetch.graphExtension.pure Maven / Gradle / Ivy
// Copyright 2020 Goldman Sachs
//
// 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.
import meta::pure::graphFetch::*;
import meta::pure::graphFetch::routing::*;
import meta::pure::lineage::scanProperties::*;
import meta::pure::lineage::scanProperties::propertyTree::*;
import meta::pure::mapping::*;
import meta::external::store::model::*;
import meta::pure::mapping::modelToModel::contract::*;
import meta::pure::mapping::modelToModel::inMemory::*;
import meta::pure::metamodel::constraint::*;
import meta::pure::metamodel::serialization::grammar::*;
import meta::pure::milestoning::*;
import meta::pure::extension::*;
Class meta::pure::graphFetch::ExtendedRootGraphFetchTree extends RootGraphFetchTree
{
requiredQualifiedProperties: QualifiedProperty[*];
constraintsExclusions: String[*];
}
Class meta::pure::graphFetch::ExtendedPropertyGraphFetchTree extends PropertyGraphFetchTree
{
requiredQualifiedProperties: QualifiedProperty[*];
constraintsExclusions: String[*];
}
Class {doc.doc = 'Nodes that were added by the system to meet e.g. constraints requirements'} meta::pure::graphFetch::SystemGeneratedPropertyGraphFetchTree extends PropertyGraphFetchTree
{
}
function meta::pure::graphFetch::switchToMilestoneProperties(tree:GraphFetchTree[1], parameters:ValueSpecification[*]): GraphFetchTree[1]
{
if($tree->instanceOf(PropertyGraphFetchTree) && $tree->cast(@PropertyGraphFetchTree).property->hasGeneratedMilestoningPropertyStereotype() && !$tree->cast(@PropertyGraphFetchTree).property->instanceOf(QualifiedProperty),
{|
let pgft = $tree->cast(@PropertyGraphFetchTree);
^$pgft(
property = $pgft.property->getMilestonedGeneratedQualifiedPropertiesForEdgePointProperty()->filter(x|!$x->isNoArgMilestonedGeneratedQualifiedProperty())->at(0),
parameters = $parameters,
subTrees = $tree.subTrees->map(gft| $gft->switchToMilestoneProperties($parameters))
);
},
| ^$tree(subTrees = $tree.subTrees->map(gft| $gft->switchToMilestoneProperties($parameters)))
);
}
function meta::pure::graphFetch::addSubTrees(onto:GraphFetchTree[1], subTrees:PropertyGraphFetchTree[*]): GraphFetchTree[1]
{
$subTrees->fold({st, node|$node->addSubTree($st)}, $onto);
}
function <> meta::pure::graphFetch::addSubTree(onto:GraphFetchTree[1], subTree:PropertyGraphFetchTree[1]): GraphFetchTree[1]
{
let existingSubTrees = $onto.subTrees->filter(st|$st->cast(@PropertyGraphFetchTree).property == $subTree.property && $st->cast(@PropertyGraphFetchTree).subType == $subTree.subType);
assert($existingSubTrees->size() < 2, 'Too many existing');
$existingSubTrees->match([
stree0: PropertyGraphFetchTree[0] | ^$onto(subTrees=$onto.subTrees->concatenate($subTree)),
stree : PropertyGraphFetchTree[1] | ^$onto(subTrees=$onto.subTrees->cast(@PropertyGraphFetchTree)->filter(st|!$st->forSameProperty($stree))->concatenate($stree->addSubTrees($subTree.subTrees->cast(@PropertyGraphFetchTree))))
]);
}
function <> meta::pure::graphFetch::forSameProperty(t1:PropertyGraphFetchTree[1], t2:PropertyGraphFetchTree[1]): Boolean[1]
{
$t1.property == $t2.property && $t1.parameters == $t2.parameters && $t1.alias == $t2.alias && $t1.subType == $t2.subType;
}
function meta::pure::graphFetch::addSubTypeTrees(onto:GraphFetchTree[1], subTypeTrees:SubTypeGraphFetchTree[*]): GraphFetchTree[1]
{
$subTypeTrees->fold({st, node|$node->addSubTypeTree($st)}, $onto);
}
function <> meta::pure::graphFetch::addSubTypeTree(onto:GraphFetchTree[1], subTree:SubTypeGraphFetchTree[1]): GraphFetchTree[1]
{
let existingSubTrees = $onto.subTypeTrees->filter(st| $st->forSameSubTypeClass($subTree));
assert($existingSubTrees->size() < 2, 'Too many existing');
let treeWithSubTypeTrees= $existingSubTrees->match([
stree0: SubTypeGraphFetchTree[0] | ^$onto(subTypeTrees = $onto.subTypeTrees->concatenate($subTree)),
stree : SubTypeGraphFetchTree[1] | ^$onto(subTrees = $onto.subTypeTrees->cast(@SubTypeGraphFetchTree)->filter(st|!$st->forSameSubTypeClass($stree))
->concatenate($stree->addSubTypeTrees($subTree.subTypeTrees->cast(@SubTypeGraphFetchTree)))) // recursively add subtype trees if already existing subtype tree found
]);
let treeWithBasePropertiesCopiedToSubTypeTrees = $treeWithSubTypeTrees->meta::pure::graphFetch::copyPropertiesFromBaseTreeToSubTypeTrees();
}
function <> meta::pure::graphFetch::copyPropertiesFromBaseTreeToSubTypeTrees(subTypeTrees:SubTypeGraphFetchTree[*], baseTree:GraphFetchTree[1]): SubTypeGraphFetchTree[*]
{
$subTypeTrees->map(st|$st->addSubTrees($baseTree.subTrees->cast(@PropertyGraphFetchTree)))->cast(@SubTypeGraphFetchTree);
}
function <> meta::pure::graphFetch::copyPropertiesFromBaseTreeToSubTypeTrees(baseTree:GraphFetchTree[1]): GraphFetchTree[1]
{
^$baseTree(subTypeTrees = meta::pure::graphFetch::copyPropertiesFromBaseTreeToSubTypeTrees($baseTree.subTypeTrees, $baseTree));
}
function <> meta::pure::graphFetch::forSameSubTypeClass(t1:SubTypeGraphFetchTree[1], t2:SubTypeGraphFetchTree[1]): Boolean[1]
{
$t1.subTypeClass == $t2.subTypeClass;
}
function meta::pure::graphFetch::allClasses(root:GraphFetchTree[1]): Class[*]
{
$root->extractClasses()->removeDuplicates();
}
function <> meta::pure::graphFetch::extractClasses(tree:GraphFetchTree[1]): Class[*]
{
let class = $tree->match([
r: RootGraphFetchTree[1] | $r.class,
a: Any[1] | []->cast(@Class)
]);
let propertyClasses = $tree.subTrees->cast(@PropertyGraphFetchTree)
->map(pgft| $pgft.property.genericType.rawType)
->filter(t | $t->instanceOf(Class))
->cast(@Class);
$class->concatenate($propertyClasses)->concatenate($tree.subTrees->map(st| $st->extractClasses()));
}
function meta::pure::graphFetch::printTree(root:GraphFetchTree[1]): Any[*]
{
$root->treeToString()->println();
}
function meta::pure::graphFetch::treeToString(root:GraphFetchTree[1]): String[1]
{
$root->nodeToString('');
}
function meta::pure::graphFetch::treeToString(root:GraphFetchTree[1], debug:DebugContext[1]): String[1]
{
$root->nodeToString(if($debug.debug,|$debug.space, |''));
}
function <> meta::pure::graphFetch::nodeToString(node:GraphFetchTree[1], indent: String[1]): String[1]
{
let indentSep = ' ';
let requiredQP = $node->match([
r: ExtendedRootGraphFetchTree[1] | $r.requiredQualifiedProperties,
p: ExtendedPropertyGraphFetchTree[1] | $p.requiredQualifiedProperties,
r: RoutedRootGraphFetchTree[1] | $r.requiredQualifiedProperties,
p: RoutedPropertyGraphFetchTree[1] | $p.requiredQualifiedProperties,
a: GraphFetchTree[1] | []
]);
let requiredQPText = if($requiredQP->isEmpty(), |'', |' [requires: '+$requiredQP.name->joinStrings(',')+ ']');
let constraintsExclusions = $node->match([
r: ExtendedRootGraphFetchTree[1] | $r.constraintsExclusions,
p: ExtendedPropertyGraphFetchTree[1] | $p.constraintsExclusions,
r: RoutedRootGraphFetchTree[1] | $r.constraintsExclusions,
p: RoutedPropertyGraphFetchTree[1] | $p.constraintsExclusions,
a: GraphFetchTree[1] | []
]);
let constraintsExclusionsText = if($constraintsExclusions->isEmpty(), |'', |' [exclude constraints: '+ $constraintsExclusions->joinStrings(',')+ ']');
let nodeText = $node->match([
rr: RoutedRootGraphFetchTree[1] | $indent + $rr.class.name->toOne() + $rr.sets.id->map(id|$id->toString())->joinStrings('[',',',']'),
rp: RoutedPropertyGraphFetchTree[1] | $indent + $rp.property.name->toOne()+if($rp.property->instanceOf(QualifiedProperty), |'('+$rp.parameters->evaluateAndDeactivate()->map(p|$p->printValueSpecification(''))->joinStrings(', ')+')', |'') + $rp.sets.id->map(id|$id->toString())->joinStrings(' sets: [',',',']') + $rp.propertyMapping->map(pm|$pm.targetSetImplementationId)->joinStrings(' targetSets: [',',',']'),
r: RootGraphFetchTree[1] | $indent + $r.class.name->toOne(),
p: PropertyGraphFetchTree[1] | $indent + $p.property.name->toOne()+if($p.property->instanceOf(QualifiedProperty), |'('+$p.parameters->evaluateAndDeactivate()->map(p|$p->printValueSpecification(''))->joinStrings(', ')+')', |'')+if($p.subType->isNotEmpty(),| '->subType(' + $p.subType->toOne().name->toOne() + ')', | '')
]) + $requiredQPText + $constraintsExclusionsText;
$nodeText +
if($node.subTypeTrees->isEmpty() && $node.subTrees->isEmpty(),
| '',
| '\n' + $indent + '(' +
if($node.subTrees->isEmpty(), |'', | '\n' + $node.subTrees->map(st|$st->nodeToString($indent+$indentSep))->joinStrings( '\n')) +
if($node.subTypeTrees->isEmpty(), |'', | '\n'+ $node.subTypeTrees->map(rst|$rst->subTypeNodeToString($indent+$indentSep))->joinStrings('\n')) +
'\n'+ $indent + ')'
);
}
function <> meta::pure::graphFetch::subTypeNodeToString(stgft:SubTypeGraphFetchTree[1], indent: String[1]): String[1]
{
let indentSep = ' ';
$indent + '->SubType(' + $stgft.subTypeClass->toOne().name->toOne() + ')'
+ if($stgft.subTrees->isEmpty(),
|'',
|'\n' + $indent + '(' + $stgft.subTrees->map(st|$st->nodeToString( $indent + $indentSep))->joinStrings('\n', '\n', '\n') + $indent + ')';
);
}
function meta::pure::graphFetch::getNodesOrdered(tree: GraphFetchTree[1]):GraphFetchTree[*]
{
$tree->match([
cls : ClusteredGraphFetchTree[1] | $cls.tree->getNodesOrdered(),
g : GraphFetchTree[1] | $tree->concatenate($tree.subTrees->map(x | $x->getNodesOrdered()))
])
}
function meta::pure::graphFetch::getPathsOrdered(tree:GraphFetchTree[1]):String[*]
{
$tree->match([
cls : ClusteredGraphFetchTree[1] | $cls.tree->getPathsOrdered(),
{g : GraphFetchTree[1]|
let name = $g->nodePathName();
$name->concatenate($tree.subTrees->map(x | $x->getPathsOrdered())->map(subPath| $name+'.'+$subPath));
}
])
}
function meta::pure::graphFetch::getPathTo(tree:GraphFetchTree[1], node:GraphFetchTree[1]):String[0..1]
{
if($tree == $node,
| [],
| $tree->match([
cls : ClusteredGraphFetchTree[1] | $cls.tree->getPathTo($node),
g : GraphFetchTree[1] | $g.subTrees->map(x | getPathTo($g->nodePathName(), $x, $node));
])->first()
);
}
function <> meta::pure::graphFetch::getPathTo(path:String[1], tree:GraphFetchTree[1], node:GraphFetchTree[1]):String[0..1]
{
if($tree == $node,
| $path,
| $tree->match([
cls : ClusteredGraphFetchTree[1] | getPathTo($path, $cls.tree, $node),
g : GraphFetchTree[1] | $g.subTrees->map(x | getPathTo($path+'.'+$g->nodePathName(), $x, $node));
])->first()
);
}
function meta::pure::graphFetch::nodePathName(tree:GraphFetchTree[1]): String[1]
{
$tree->match([
r:RootGraphFetchTree[1] | 'root',
p:PropertyGraphFetchTree[1] | if($p.alias->isEmpty(), |$p.property.name->toOne() + if($p.property->instanceOf(QualifiedProperty), | $p.parameters->evaluateAndDeactivate()->map(x | $x->meta::pure::router::printer::asString())->joinStrings('(', ', ', ')'), | ''), |$p.alias)->toOne() +
if($p->instanceOf(RoutedPropertyGraphFetchTree) && $p->cast(@RoutedPropertyGraphFetchTree).sets->isNotEmpty(), | $p->cast(@RoutedPropertyGraphFetchTree).sets->map(s | $s.id)->joinStrings('@', '+', ''), | '')
]);
}
function meta::pure::graphFetch::typeFromGraphFetchTree(g: GraphFetchTree[1]):Type[1]
{
$g->match([
r : RootGraphFetchTree[1] | $r.class,
p : PropertyGraphFetchTree[1] | if($p.subType->isNotEmpty(), | $p.subType->toOne(), | $p.property->functionReturnType().rawType->toOne())
])
}
function <> meta::pure::graphFetch::isUnitFunctionReturnType(tree:PropertyGraphFetchTree[1]): Boolean[1]
{
$tree.property->functionReturnType().rawType->toOne()->instanceOf(Unit)
}
function meta::pure::graphFetch::sortTree(tree:GraphFetchTree[1]): GraphFetchTree[1]
{
let sortedPropertyTrees = $tree.subTrees->cast(@PropertyGraphFetchTree)
->sortBy(pgft| $pgft->getSubTreeName())
->map(pgft| $pgft->sortTree());
let sortedSubTypeTrees = $tree.subTypeTrees
->sortBy(stgft| $stgft->getSubTreeName())
->map(stgft| $stgft->sortTree());
^$tree
(
subTrees = $sortedPropertyTrees,
subTypeTrees = $sortedSubTypeTrees->cast(@SubTypeGraphFetchTree)
);
}
function meta::pure::graphFetch::getSubTreeName(tree:GraphFetchTree[1]): String[1]
{
$tree->match([
pgft:PropertyGraphFetchTree[1]|
let subTypeName = if($pgft.subType->isEmpty(),|'',|$pgft.subType->toOne().name->toOne());
$pgft.property.name->toOne() + $subTypeName;,
stgft:SubTypeGraphFetchTree[1] |
$stgft.subTypeClass->toOne().name->toOne();
]);
}
function meta::pure::graphFetch::pathsForConstraintFunctions(class:Class[1], functions:FunctionDefinition[*]): List[*]
{
let generalizations = $class->getAllTypeGeneralisations();
let functionsPaths = $functions->map(c| $c.expressionSequence->at(0)->evaluateAndDeactivate()->scanProperties());
$functionsPaths.result->filter(path| $path.values->isNotEmpty() && $path.values->at(0).class->in($generalizations));
}
function meta::pure::graphFetch::ensureConstraintsRequirements(treeRoot:RootGraphFetchTree[1]): RootGraphFetchTree[1]
{
let constraintsForClass = {c:Class[1] |
let allConstraints = $c->getAllTypeGeneralisations()->filter(x| $x->instanceOf(ElementWithConstraints))->cast(@ElementWithConstraints).constraints;
$allConstraints.functionDefinition->concatenate($allConstraints.messageFunction);
};
ensureFunctionRequirements($treeRoot, $treeRoot.class, $constraintsForClass, [], true)->cast(@RootGraphFetchTree);
}
function meta::pure::graphFetch::ensureFunctionRequirementsFromLambda(node:GraphFetchTree[1], class:Class[1], f:FunctionDefinition[1], processed:Class[*], ensureConstraintsForSubTrees:Boolean[1]): GraphFetchTree[1]
{
let paths = meta::pure::graphFetch::pathsForConstraintFunctions($class, $f);
let qualifiedPropertyPaths = $paths->filter(path| $path.values->exists(x| $x.property->instanceOf(QualifiedProperty)));
let inlinedPropertyTree = $paths->buildPropertyTree()->inlineQualifiedPropertyNodes();
let inlinedGraphTree = $inlinedPropertyTree->propertyTreeToGraphFetchTree($class);
let inlinedPropertyGraphTrees = $inlinedGraphTree.subTrees->cast(@PropertyGraphFetchTree);
let withFoundProperties = $node->addSubTrees($inlinedPropertyGraphTrees);
let updatedForClass = $qualifiedPropertyPaths->fold({path, gt| $gt->recordQualifiedProperties($path)}, $withFoundProperties);
if($ensureConstraintsForSubTrees,
{|
let updatedProcessed = $processed->add($class);
let newSubTrees = $updatedForClass.subTrees->map({st|
let returns = if($st->cast(@PropertyGraphFetchTree).subType->isEmpty(),
| $st->cast(@PropertyGraphFetchTree).property->functionReturnType().rawType->toOne(),
| $st->cast(@PropertyGraphFetchTree).subType->toOne()
);
if($returns->instanceOf(Class) && !$updatedProcessed->contains($returns),
| $st->ensureFunctionRequirementsFromLambda($returns->cast(@Class), $f, $updatedProcessed, $ensureConstraintsForSubTrees),
| $st
);
});
^$updatedForClass(subTrees=$newSubTrees);
},
| $updatedForClass
);
}
function meta::pure::graphFetch::ensureFunctionRequirements(node:GraphFetchTree[1], class:Class[1], f:Function<{Class[1]->FunctionDefinition[*]}>[1], processed:Class[*], ensureConstraintsForSubTrees:Boolean[1]): GraphFetchTree[1]
{
let constraintResult = pathsForConstraintFunctions($class, $f->eval($class));
let qualifiedPropertyPaths = $constraintResult->filter(path| $path.values->exists(x| $x.property->instanceOf(QualifiedProperty)));
let inlinedPropertyTree = $constraintResult->buildPropertyTree()->inlineQualifiedPropertyNodes();
let inlinedGraphTree = $inlinedPropertyTree->propertyTreeToGraphFetchTree($class);
let inlinedPropertyGraphTrees = $inlinedGraphTree.subTrees->cast(@PropertyGraphFetchTree);
let withFoundProperties = $node->addSubTrees($inlinedPropertyGraphTrees);
let updatedForClass = $qualifiedPropertyPaths->fold({path, gt| $gt->recordQualifiedProperties($path)}, $withFoundProperties);
let updatedProcessed = $processed->add($class);
if($ensureConstraintsForSubTrees,
{|
let newSubTrees = $updatedForClass.subTrees->map({st|
let returns = if($st->cast(@PropertyGraphFetchTree).subType->isEmpty(),
| $st->cast(@PropertyGraphFetchTree).property->functionReturnType().rawType->toOne(),
| $st->cast(@PropertyGraphFetchTree).subType->toOne()
);
if($returns->instanceOf(Class) && !$updatedProcessed->contains($returns),
| $st->ensureFunctionRequirements($returns->cast(@Class), $f, $updatedProcessed, $ensureConstraintsForSubTrees),
| $st
);
});
^$updatedForClass(subTrees=$newSubTrees);
},
| $updatedForClass
);
}
function meta::pure::graphFetch::canEvaluateForTree(pTree:GraphFetchTree[1], gfTree:GraphFetchTree[1]): Boolean[1]
{
$pTree->match([
r:RootGraphFetchTree[1] | if(!$gfTree->instanceOf(RootGraphFetchTree),
| false,
| let subTreesIndexedByProperty = $gfTree.subTrees->groupBy(st | $st->cast(@PropertyGraphFetchTree).property);
($gfTree->cast(@RootGraphFetchTree).class == $r.class || $gfTree->cast(@RootGraphFetchTree).class->_subTypeOf($r.class))
&& $r.subTrees->forAll(st | let reqSubTrees = $subTreesIndexedByProperty->get($st->cast(@PropertyGraphFetchTree).property).values;
$reqSubTrees->isNotEmpty() && $reqSubTrees->exists(reqSubTree | $st->canEvaluateForTree($reqSubTree));););,
p:PropertyGraphFetchTree[1] | if(!$gfTree->instanceOf(PropertyGraphFetchTree),
| false,
| let subTreesIndexedByProperty = $gfTree.subTrees->groupBy(st | $st->cast(@PropertyGraphFetchTree).property);
$gfTree->cast(@PropertyGraphFetchTree).property == $p.property
&& $p.subTrees->forAll(st | let reqSubTrees = $subTreesIndexedByProperty->get($st->cast(@PropertyGraphFetchTree).property).values;
$reqSubTrees->isNotEmpty() && $reqSubTrees->exists(reqSubTree | $st->canEvaluateForTree($reqSubTree));););
])
}
function meta::pure::graphFetch::calculateSourceTree(tree:RootGraphFetchTree[1], mapping: Mapping[1], extensions:meta::pure::extension::Extension[*]): RootGraphFetchTree[1]
{
let replaced = $tree->replaceQualifiedPropertiesWithRequiredProperties();
let rootSetImplementation = $tree->match([r:RoutedRootGraphFetchTree[1]|$r.sets->toOne(),
r:RootGraphFetchTree[1]|$mapping->rootClassMappingByClass($replaced.class)]);
assert($rootSetImplementation->isNotEmpty(), |'Mapping ' + $mapping->elementToPath() + ' does not map class from tree: ' + $replaced.class->elementToPath());
$rootSetImplementation->toOne()->match($extensions.graphExtension_calculateSourceTree->map(ext | $ext->eval($replaced))->concatenate([
{pisi: PureInstanceSetImplementation[1] |
assert($pisi.srcClass->isNotEmpty() && $pisi.srcClass->toOne()->instanceOf(Class), 'Pure mapping does not have a class as ~src for root of tree');
let srcClass = $pisi.srcClass->toOne()->cast(@Class);
let root = ^RootGraphFetchTree(class=$srcClass);
let withChildren = $root->enrichSourceTreeNode($pisi, $replaced, $extensions)->mergeSubTrees()->sortTree()->cast(@RootGraphFetchTree);
}
])->toOneMany());
}
function meta::pure::graphFetch::mergeSubTrees(tree:GraphFetchTree[1]): GraphFetchTree[1]
{
^$tree(subTrees = $tree.subTrees->cast(@PropertyGraphFetchTree)->mergeSubTreesRecur());
}
function meta::pure::graphFetch::mergeSubTreesRecur(subTrees:PropertyGraphFetchTree[*]): PropertyGraphFetchTree[*]
{
let subTreesGroupedByProperty = $subTrees->groupBy(c|$c->cast(@PropertyGraphFetchTree).property);
let updatedSubTrees = $subTreesGroupedByProperty->keyValues()->map(p|mergePropertiesFromBaseSubTreeToSubTypedSubTree($p.second.values->cast(@PropertyGraphFetchTree)));
}
function <> meta::pure::graphFetch::mergePropertiesFromBaseSubTreeToSubTypedSubTree(subTrees:PropertyGraphFetchTree[*]): PropertyGraphFetchTree[*]
{
let subTreeWithBaseClass = $subTrees->filter(st|$st->cast(@PropertyGraphFetchTree).subType->isEmpty())->cast(@PropertyGraphFetchTree);
let subTreesWithSubTypes = $subTrees->removeAll($subTreeWithBaseClass);
let updatedSubTreeWithBaseClass = $subTreeWithBaseClass->first()->map(st| ^$st(subTrees = $subTreeWithBaseClass->first().subTrees->concatenate($subTreeWithBaseClass->tail().subTrees)->cast(@PropertyGraphFetchTree)->mergeSubTreesRecur()));
$updatedSubTreeWithBaseClass->concatenate($subTreesWithSubTypes->map(st|^$st(subTrees = $st.subTrees->concatenate($subTreeWithBaseClass->cast(@PropertyGraphFetchTree).subTrees)->cast(@PropertyGraphFetchTree)->mergeSubTreesRecur())));
}
function <> meta::pure::graphFetch::enrichSourceTreeNodeForProperty(srcNode:GraphFetchTree[1], setImplementation: PureInstanceSetImplementation[1], tgtPgft:PropertyGraphFetchTree[1], extensions:meta::pure::extension::Extension[*], visitedFunctions:Map[1]): Pair>[1]
{
let srcNodeOwner = $srcNode->match([
r:RootGraphFetchTree[1] | $r.class,
p:PropertyGraphFetchTree[1] | if ($p.subType->isNotEmpty(), |$p.subType->toOne(), | $p.property->functionReturnType().rawType->toOne());
]);
let isPropertyTemporalMilestoned = $tgtPgft.property->hasGeneratedMilestoningPropertyStereotype();
let requiredProperty = if($isPropertyTemporalMilestoned, |$setImplementation.class->propertyByName($tgtPgft.property->edgePointPropertyName()->toOne()), |$tgtPgft.property);
let propertyMappings = $setImplementation.propertyMappings->filter(pm|$pm.property == $requiredProperty)->cast(@PurePropertyMapping);
if($srcNodeOwner->instanceOf(Class) && ($propertyMappings->isNotEmpty() || ($requiredProperty->isNotEmpty() && ($requiredProperty->toOne()->isPropertyAutoMapped($setImplementation) || $setImplementation->isNoMappingDefaultToEmpty($requiredProperty->toOne()) || $setImplementation->isPartOfMerge()))),
{|
let owner = $srcNodeOwner->cast(@Class);
let returnType = $tgtPgft.property->functionReturnType().rawType->toOne();
let childSetImpls = $returnType->match([
{c:Class[1]|
let childSIs = $tgtPgft->match([
r:RoutedPropertyGraphFetchTree[1] | if($r.sets->isNotEmpty(), |$r.sets, |$setImplementation.parent->rootClassMappingByClass($c)),
r:PropertyGraphFetchTree[1] | $setImplementation.parent->rootClassMappingByClass($c)
]);
let childSIsResolved = $childSIs->map(childSI|
$childSI->match([
{o:OperationSetImplementation[1] |
assert($o.parameters.setImplementation->forAll(s|$s->instanceOf(PureInstanceSetImplementation)), |'Unsupported types: ' + $o.parameters.setImplementation->filter(i|!$i->instanceOf(PureInstanceSetImplementation))->type()->elementToPath());
$o->meta::pure::router::clustering::resolveInstanceSetImplementations();
},
{e:EmbeddedSetImplementation[1] |
[];
},
{i:InstanceSetImplementation[1] |
assert($i->instanceOf(PureInstanceSetImplementation), 'Unsupported type: ' + $childSI->type()->elementToPath());
$i;
}
])->cast(@PureInstanceSetImplementation)
);
let allowComplexPropertyMappingWithoutChildSI = $requiredProperty->toOne()->isPropertyAutoMapped($setImplementation);
assert($propertyMappings->isEmpty() || $childSIsResolved->isNotEmpty() || $allowComplexPropertyMappingWithoutChildSI , |'Mapping ' + $setImplementation.parent->elementToPath() + ' does not map class from tree: ' + $c->elementToPath());
$childSIsResolved;
},
{a:Any[1]| []}
]);
let propertyPaths = $propertyMappings->match([
{none:PurePropertyMapping[0] |
if($requiredProperty->toOne()->isPropertyAutoMapped($setImplementation),
{|
let path = ^PropertyPathNode(
class = $setImplementation.srcClass->toOne()->cast(@Class),
property = $setImplementation->noMappingPassThruSourceProperty($requiredProperty->toOne())->toOne()
);
^ScanPropertiesState(current=list($path), result=list($path), visitedFunctions = ^Map());
},
{|
assert($setImplementation->isNoMappingDefaultToEmpty($requiredProperty->toOne()) || $setImplementation->isPartOfMerge(), |'No mapping found for property \'' + $tgtPgft.property.name->toOne() + '\'');
[];
}
);
},
{pms:PurePropertyMapping[*] |
let result = $pms->fold({pm, prevRes | let result = $pm.transform.expressionSequence->at(0)->evaluateAndDeactivate()->scanProperties(noDebug(), ^ScanConfig(scanClasses=true, explodeMilestonedProperties=false), $prevRes->last()->getVisitedFunctions());
$prevRes->concatenate($result);
},
[^ScanPropertiesState(current = emptyPath(), result = [], visitedFunctions = $visitedFunctions)]
);
$result->slice(1, $result->size());
}
]);
let inlinedPropertyTree = $propertyPaths.result->buildPropertyTree()->inlineQualifiedPropertyNodes();
// Property tree root node may not start at owner (e.g. when the source of a mapping is wrapped in a new class and passed to a property mapping).
// Here we try and find the part of the tree that starts at the owner - in usual case we will return the same tree but now also handle case mentioned above.
// Due to subTypes we could end up returning multiple trees hence we check if there is only one and start there, otherwise continue with old behaviour.
let treeStartingAtOwner = findSubTreeWithOwner($inlinedPropertyTree, $owner);
let inlinedGraphTree = if($treeStartingAtOwner->size() == 1,
| $treeStartingAtOwner->toOne()->propertyTreeToGraphFetchTree($owner)->removeDummyProperties(),
| $inlinedPropertyTree->propertyTreeToGraphFetchTree($owner)->removeDummyProperties()
);
// copy common properties from base type tree to subtype trees at property level
// TODO - remove when propertySubTypes are embedded in propertyTrees
let setClasses = $childSetImpls->map(s | $s.srcClass);
let inlinedPropertyGraphTrees = $inlinedGraphTree.subTrees->cast(@PropertyGraphFetchTree);
let withSubTypes = $inlinedPropertyGraphTrees->concatenate($inlinedPropertyGraphTrees->map(pgft | let appendAtPath = $propertyPaths.current->filter(path| $path.values->size() > 1)->cast(@List);
if($appendAtPath->isEmpty(),
| $setClasses->map(sc | if($sc->getAllTypeGeneralisationsExcluded()->contains($pgft.property.genericType.rawType->toOne()), | ^$pgft(subType=$sc->cast(@Class)), | [])),
| let val = list($appendAtPath->map(x|$x.values)->cast(@PropertyPathNode)->tail());
$pgft->getSubTreeNodesAtPath($val, $setClasses->cast(@Class), $extensions);
);
));
let srcNodeProperties = $withSubTypes->filter(p | $owner->getAllTypeGeneralisations()->contains($p.property->meta::pure::functions::meta::ownerClass()));
let withFoundProperties = $srcNode->addSubTrees($srcNodeProperties);
// copy common properties from base type tree to subtype trees at root level
let withRootSubTypes= if($srcNode->instanceOf(RootGraphFetchTree), //TODO : will be handled properly when property subtype gfts are moved within property access gfts
| let sourceNodeWithSubTypes = $srcNode->addSubTypeTrees($inlinedGraphTree.subTypeTrees);
^$withFoundProperties(subTypeTrees=$sourceNodeWithSubTypes.subTypeTrees);, // copy properties except current one
| $withFoundProperties
);
let withNextSetImpl = $childSetImpls->fold({setImpl, tree |
let appendAtPaths = $propertyPaths.current->removeDuplicates();
if($appendAtPaths->isEmpty(),
| $tree->enrichSourceTreeNode($setImpl, $tgtPgft, $extensions),
| $appendAtPaths->fold({path,tree |
if($path.values->isEmpty(),
| $tree->enrichSourceTreeNode($setImpl, $tgtPgft, $extensions),
| $tree->enrichSourceTreeNodeAtPath($path, $setImpl, $tgtPgft, $extensions))
}, $tree)
);
},
$withRootSubTypes
);
let withPassThruSubTrees = if($childSetImpls->isEmpty() && $returnType->instanceOf(Class) && $requiredProperty->toOne()->isPropertyAutoMapped($setImplementation),
|let appendAtPaths = $propertyPaths.current->filter(path| $path.values->isNotEmpty());
if($appendAtPaths->isEmpty(),
| $withNextSetImpl->addPassThroughSubTrees($tgtPgft, $extensions),
| $appendAtPaths->fold({path,tree | $tree->addPassThroughSubTreesAtPath($path, $tgtPgft, $extensions)}, $withNextSetImpl)
);,
|$withNextSetImpl);
let withQPsRecorded = $propertyPaths.result->filter(path| $path.values->exists(x| $x.property->instanceOf(QualifiedProperty)))
->fold({path, gt| $gt->recordQualifiedProperties($path)}, $withPassThruSubTrees);
let lastVisitedFunctions = $propertyPaths->last()->getVisitedFunctions();
if($isPropertyTemporalMilestoned,
| pair(^$withQPsRecorded(subTrees=$withQPsRecorded.subTrees->map(st| $st->switchToMilestoneProperties($tgtPgft.parameters->evaluateAndDeactivate()))), $lastVisitedFunctions),
| pair($withQPsRecorded, $lastVisitedFunctions)
);
},
| pair($srcNode, ^Map())
);
}
function <> meta::pure::graphFetch::getSubTreeNodesAtPath(srcNode:PropertyGraphFetchTree[1], path:List[1], setClasses: Class[*], extensions:meta::pure::extension::Extension[*]): PropertyGraphFetchTree[*]
{
let head = $path.values->at(0);
let tail = $path.values->tail();
let requiredSubTree = $srcNode.subTrees->cast(@PropertyGraphFetchTree)->filter(st|$st.property == $head.property);
if($tail->isEmpty(),
| let updatedSubTrees = $requiredSubTree->map(rst | $setClasses->map(sc | if($sc->getAllTypeGeneralisationsExcluded()->contains($rst.property.genericType.rawType->toOne()), | ^$rst(subType=$sc->cast(@Class)), | [])));
$updatedSubTrees->map(ust | ^$srcNode(subTrees=$srcNode.subTrees->cast(@PropertyGraphFetchTree)->map(st|if($st.property == $head.property, |$ust, |$st))));,
| let updatedSubTrees = $requiredSubTree->map(rst | $rst->getSubTreeNodesAtPath(list($tail), $setClasses, $extensions));
$updatedSubTrees->map(ust | ^$srcNode(subTrees=$srcNode.subTrees->cast(@PropertyGraphFetchTree)->map(st|if($st.property == $head.property, |$ust, |$st))));
);
}
function <> meta::pure::graphFetch::enrichSourceTreeNodeForExpression(srcNode:GraphFetchTree[1], exprThisClass:Class[1], expression:ValueSpecification[1]): GraphFetchTree[1]
{
let propertyPath = $expression->evaluateAndDeactivate()->scanProperties(^List(), [], [], noDebug());
$srcNode->enrichSourceTreeNodeForPropertyPath($exprThisClass, $propertyPath);
}
function <> meta::pure::graphFetch::enrichSourceTreeNodeForPropertyPath(srcNode:GraphFetchTree[1], baseClass:Class[1], propertyPath:ScanPropertiesState[0..1]): GraphFetchTree[1]
{
let inlinedPropertyTree = $propertyPath.result->buildPropertyTree()->inlineQualifiedPropertyNodes();
let inlinedPropertyGraphTrees = $inlinedPropertyTree->propertyTreeToGraphFetchTree($baseClass).subTrees->cast(@PropertyGraphFetchTree);
let srcNodeProperties = $inlinedPropertyGraphTrees->filter(p | $baseClass->getAllTypeGeneralisations()->contains($p.property->meta::pure::functions::meta::ownerClass()));
let withFoundProperties = $srcNode->addSubTrees($srcNodeProperties);
let withQPsRecorded = $propertyPath.result->filter(path| $path.values->exists(x| $x.property->instanceOf(QualifiedProperty)))
->fold({path, gt| $gt->recordQualifiedProperties($path)}, $withFoundProperties);
}
function <> meta::pure::graphFetch::enrichSourceTreeNode(srcNode:GraphFetchTree[1], setImplementation: PureInstanceSetImplementation[1], tgtNode:GraphFetchTree[1], extensions:meta::pure::extension::Extension[*]): GraphFetchTree[1]
{
let setContainsExplodeProperty = $setImplementation.propertyMappings->filter(pm|if($pm->instanceOf(PurePropertyMapping),| $pm->cast(@PurePropertyMapping).explodeProperty->isTrue(), |false))->isNotEmpty();
let subTreeProperties = $tgtNode.subTrees->cast(@PropertyGraphFetchTree).property.name;
let requiredTgtSubTrees = $tgtNode.subTrees->cast(@PropertyGraphFetchTree)->concatenate(if($setContainsExplodeProperty,
|$setImplementation.propertyMappings->filter(pm | $pm->instanceOf(PurePropertyMapping)).property->map(p | if(!$p.name->in($subTreeProperties),
|^PropertyGraphFetchTree(property = $p),
|[])),
|[]));
let subTrees = $requiredTgtSubTrees->fold({st,n | $n.first->enrichSourceTreeNodeForProperty($setImplementation, $st, $extensions, $n.second)},
pair($srcNode, ^Map())
).first;
let srcClass = $setImplementation.srcClass->toOne();
let srcNodeOwner = $srcNode->typeFromGraphFetchTree();
if($srcClass->instanceOf(Class) && $setImplementation.filter->isNotEmpty() && $srcClass == $srcNodeOwner,
| $subTrees->enrichSourceTreeNodeForExpression($srcClass->cast(@Class), $setImplementation.filter.expressionSequence->at(0)),
| $subTrees
);
}
function <> meta::pure::graphFetch::enrichSourceTreeNodeAtPath(srcNode:GraphFetchTree[1], path:List[1], setImplementation: PureInstanceSetImplementation[1], tgtPgft:PropertyGraphFetchTree[1], extensions:meta::pure::extension::Extension[*]): GraphFetchTree[1]
{
// If path is empty then we are enriching the source node rather than the subTrees
if($path.values->isEmpty(),
| let enrichedSourceNode = enrichSourceTreeNode($srcNode, $setImplementation, $tgtPgft, $extensions);
^$enrichedSourceNode(subTrees=$enrichedSourceNode.subTrees->concatenate($srcNode.subTrees)->removeDuplicates());,
| let head = $path.values->at(0);
let tail = $path.values->tail();
if($tail->isEmpty(),
| ^$srcNode(subTrees=$srcNode.subTrees->cast(@PropertyGraphFetchTree)->map(st|if($st.property == $head.property, |$st->enrichSourceTreeNode($setImplementation, $tgtPgft, $extensions), |$st))),
| ^$srcNode(subTrees=$srcNode.subTrees->cast(@PropertyGraphFetchTree)->map(st|if($st.property == $head.property, |$st->enrichSourceTreeNodeAtPath(list($tail), $setImplementation, $tgtPgft, $extensions), |$st)))
);
);
}
function <> meta::pure::graphFetch::addPassThroughSubTrees(srcNode:GraphFetchTree[1], tgtNode:GraphFetchTree[1], extensions:meta::pure::extension::Extension[*]): GraphFetchTree[1]
{
^$srcNode(subTrees += $tgtNode.subTrees);
}
function <> meta::pure::graphFetch::addPassThroughSubTreesAtPath(srcNode:GraphFetchTree[1], path:List[1], tgtPgft:PropertyGraphFetchTree[1], extensions:meta::pure::extension::Extension[*]): GraphFetchTree[1]
{
let head = $path.values->at(0);
let tail = $path.values->tail();
if($tail->isEmpty(),
| ^$srcNode(subTrees=$srcNode.subTrees->cast(@PropertyGraphFetchTree)->map(st|if($st.property == $head.property, |$st->addPassThroughSubTrees($tgtPgft, $extensions), |$st))),
| ^$srcNode(subTrees=$srcNode.subTrees->cast(@PropertyGraphFetchTree)->map(st|if($st.property == $head.property, |$st->addPassThroughSubTreesAtPath(list($tail), $tgtPgft, $extensions), |$st)))
);
}
function meta::pure::graphFetch::findSubTreeWithOwner(pTree:PropertyPathTree[1], ownerClass:Class[1]) : PropertyPathTree[*]
{
$pTree.value->match([
node:PropertyPathNode[1] | if($ownerClass == $node.class || $node.class->isStrictSubType($ownerClass),
| $pTree,
| $pTree.children->map(ch | findSubTreeWithOwner($ch, $ownerClass))
),
clz:Class[1] | if($clz == $ownerClass || $ownerClass->isStrictSubType($clz),
| $pTree,
| $pTree.children->map(ch | findSubTreeWithOwner($ch, $ownerClass))
),
any:Any[1] | $pTree.children->map(ch | findSubTreeWithOwner($ch, $ownerClass))
]);
}
function meta::pure::graphFetch::propertyTreeToGraphFetchTree(pTree:PropertyPathTree[1], ownerClass:Class[1]): RootGraphFetchTree[1]
{
let root = ^RootGraphFetchTree(class = $ownerClass);
$root->addPropertyGraphFetchTrees($pTree, $ownerClass)->copyPropertiesFromBaseTreeToSubTypeTrees()->cast(@RootGraphFetchTree);
}
function meta::pure::graphFetch::propertyGraphFetchTreeToRootGraphFetchTree(pTree:PropertyGraphFetchTree[1]): RootGraphFetchTree[1]
{
let ownerClass = if($pTree.subType->isNotEmpty(), | $pTree.subType, | $pTree.property->functionReturnType().rawType)->toOne();
assert($ownerClass->instanceOf(Class), | 'Primitive property tree cannot be converted to RootGraphFetchTree');
^RootGraphFetchTree(class = $ownerClass->cast(@Class), subTrees = $pTree.subTrees);
}
function <> meta::pure::graphFetch::addPropertyGraphFetchTrees(onto:GraphFetchTree[1], pTree:PropertyPathTree[1], ownerClass:Class[1]): GraphFetchTree[1]
{
$pTree.value->match([
node: PropertyPathNode[1] | if($ownerClass == $node.class || $node.class->isStrictSubType($ownerClass),
| $onto->addPropertyGraphFetchTrees($pTree, $ownerClass, $node),
| $onto
),
clz: Class[1] | if($clz->isStrictSubType($ownerClass),
|
let pTreesInBaseType=$pTree.children->map(c|$c.value->match([
node: PropertyPathNode[1] | if($ownerClass->allProperties()->contains($node.property), |$c, |[]),
a:Any[1] | []
]));
let processedBaseTree= $pTreesInBaseType->fold({child, gft|$gft->addPropertyGraphFetchTrees($child, $ownerClass)}, $onto);
// add subtype trees also
let rootSubTypeTree= ^SubTypeGraphFetchTree(subTypeClass=$clz);
let rootSubTypeTreeWithProperties= $pTree.children->fold({child, gft|
$gft->addPropertyGraphFetchTrees($child, $clz)->cast(@SubTypeGraphFetchTree)
}, $rootSubTypeTree);
let withSubTypeTree = $processedBaseTree->addSubTypeTrees($rootSubTypeTreeWithProperties);,
| if($clz == $ownerClass || $ownerClass->isStrictSubType($clz), // get properties from superclass also ,
| $pTree.children->fold({child, gft|$gft->addPropertyGraphFetchTrees($child, $clz) }, $onto),
| $onto // dont process further
)
);,
any: Any[1] | $pTree.children->fold({child, gft| $gft->addPropertyGraphFetchTrees($child, $ownerClass)}, $onto)
]);
}
function meta::pure::functions::meta::isStrictSubType(subType:Type[1], superType:Type[1]):Boolean[1]
{
if($subType == Nil,
|true,
|$subType->getAllTypeGeneralisations()->map(st|$st->elementToPath())->contains($superType->elementToPath()) && $subType->elementToPath()!=$superType->elementToPath() // object comparison might not work due to lazy evalution of some duplicate objects
);
}
function <> meta::pure::graphFetch::addPropertyGraphFetchTrees(onto:GraphFetchTree[1], pTree:PropertyPathTree[1], ownerClass:Class[1], node: PropertyPathNode[1]): GraphFetchTree[1]
{
let pgft = ^SystemGeneratedPropertyGraphFetchTree(property = $node.property,
parameters = if($node.property->hasGeneratedMilestoningPropertyStereotype(),|$node.parameters,|[])
);
if($pTree.children->size() == 0,
| $onto->addSubTree($pgft),
{|
let rtnClass = $node.property.genericType.rawType->cast(@Class)->toOne();
let childrenByClass = $pTree.children->groupBy(c|$c.value->cast(@PropertyPathNode).class);
$childrenByClass->keyValues()->fold(
{kv, gft|
let childrenOwner = $kv.first;
let children = $kv.second.values;
if($childrenOwner == $rtnClass || $childrenOwner->_subTypeOf($rtnClass) || $rtnClass->_subTypeOf($childrenOwner),
{|
let withSubtype = if($childrenOwner == $rtnClass || $rtnClass->_subTypeOf($childrenOwner),
| $pgft,
| ^$pgft(subType = $childrenOwner)
);
let withChildren = $children->fold({child, gft2| $gft2->addPropertyGraphFetchTrees($child, $childrenOwner)->cast(@PropertyGraphFetchTree)}, $withSubtype->cast(@PropertyGraphFetchTree));
$gft->addSubTree($withChildren);
},
| $gft
);
},
$onto
);
}
);
}
function <> meta::pure::graphFetch::replaceQualifiedPropertiesWithRequiredProperties(pgft:PropertyGraphFetchTree[1], class:Class[1]):GraphFetchTree[*]
{
let copy = ^$pgft(subTrees=[]);
if($pgft.property->hasGeneratedMilestoningPropertyStereotype(),
|
let rtns = $pgft.property->functionReturnType().rawType->toOne();
let subTrees = $pgft.subTrees->cast(@PropertyGraphFetchTree)->map(st| $st->replaceQualifiedPropertiesWithRequiredProperties($rtns->cast(@Class)))->cast(@PropertyGraphFetchTree);
$copy->addSubTrees($subTrees);,
|
$pgft.property->match([
{p:Property[1]|
let rtns = $p->functionReturnType().rawType->toOne();
let subTrees = if($rtns->instanceOf(Class),
|$pgft.subTrees->cast(@PropertyGraphFetchTree)->map(st| $st->replaceQualifiedPropertiesWithRequiredProperties($rtns->cast(@Class))),
|[]
)->cast(@PropertyGraphFetchTree);
$copy->addSubTrees($subTrees);
},
{qp:QualifiedProperty[1]|
let propertyPaths = $qp.expressionSequence->evaluateAndDeactivate()->map(e| $e->scanProperties());
let inlinedPropertyTree = $propertyPaths.result->buildPropertyTree()->inlineQualifiedPropertyNodes();
$inlinedPropertyTree->propertyTreeToGraphFetchTree($class).subTrees;
}
]);
);
}
function <> meta::pure::graphFetch::replaceQualifiedPropertiesWithRequiredProperties(rgft:RootGraphFetchTree[1]):RootGraphFetchTree[1]
{
let copy = ^$rgft(subTrees=[]);
let subTrees = $rgft.subTrees->cast(@PropertyGraphFetchTree)->map(st| $st->replaceQualifiedPropertiesWithRequiredProperties($rgft.class))->cast(@PropertyGraphFetchTree);
$copy->addSubTrees($subTrees)->cast(@RootGraphFetchTree);
}
function <> meta::pure::graphFetch::recordQualifiedProperties(tree:GraphFetchTree[1], path:List[1]): GraphFetchTree[1]
{
if($path.values->isEmpty(),
| $tree,
{|
let head = $path.values->at(0);
let tail = if($head.property->instanceOf(QualifiedProperty),
| list($head.nestedQualifierReturn.values->tail()->concatenate($path.values->tail())),
| list($path.values->tail())
);
let nextProperty = if($head.property->instanceOf(QualifiedProperty),
| $head.nestedQualifierReturn.values->first().property,
| $head.property
);
let withQp = if($head.property->instanceOf(QualifiedProperty),
{|
let qp = $head.property->cast(@QualifiedProperty);
$tree->match([
ergft: ExtendedRootGraphFetchTree[1] | ^$ergft(requiredQualifiedProperties=$ergft.requiredQualifiedProperties->concatenate($qp)->removeDuplicates()),
epgft: ExtendedPropertyGraphFetchTree[1] | ^$epgft(requiredQualifiedProperties=$epgft.requiredQualifiedProperties->concatenate($qp)->removeDuplicates()),
rgft : RootGraphFetchTree[1] | ^ExtendedRootGraphFetchTree(requiredQualifiedProperties=$qp, class=$rgft.class, subTrees=$rgft.subTrees),
pgft : PropertyGraphFetchTree[1] | ^ExtendedPropertyGraphFetchTree(requiredQualifiedProperties=$qp, property=$pgft.property, subTrees=$pgft.subTrees)
]);
},
| $tree;
);
^$withQp(subTrees=$tree.subTrees->cast(@PropertyGraphFetchTree)->map(st|if($st.property == $nextProperty, |$st->recordQualifiedProperties($tail), |$st)));
}
);
}
function <> meta::pure::graphFetch::removeDummyProperties(tree:GraphFetchTree[1]): GraphFetchTree[0..1]
{
$tree->match([
pgft:PropertyGraphFetchTree[1] |
if($pgft.property == getDummyProperty(),
| [],
| $pgft
);,
t:GraphFetchTree[1] | ^$t(subTrees= $t.subTrees->map(st|$st->removeDummyProperties()),
subTypeTrees = $t.subTypeTrees->map(st|$st->removeDummyProperties())->cast(@SubTypeGraphFetchTree)
);
])
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy