====== XL queries and operators ====== ===== Queries ===== Graph queries are used to analyse the actual structure (graph), which can be for example a plant structure. In XL, queries are enclosed in asterixed parentheses ''(* *)''. The elements in a query are given in their expected order, e.g. ''(* A A B *)'' searches for a subgraph which consists of a sequence of nodes of the types ''A'' ''A'' ''B'', connected by successor edges. To try out how queries work, we first create a simple plant model. ==== Test model ==== module Bud extends Sphere(0.1).(setShader(GREEN)); module Internode(int age, super.length) extends Cylinder(length, 0.1).(setShader(YELLOW)); module Leaf(int age, super.length, double width) extends Parallelogram(length, width).(setShader(GREEN)) { double area; { setArea(); } void setArea() { area = length*width; } } protected void init() [ Axiom ==> Bud; ] public void run() [ Bud ==> Internode(1, 1) [RL(60) Leaf(1, 1, 1)] RH(180) Bud ; i:Internode ::> { i[age]++; i[length] += 1; } l:Leaf ::> { l[age]++; l[length] += 1; l.setArea(); } ] We create a new method called ''analyse()''. The method will appear as a new button in the RGG toolbar. Before running the ''analyse()'' method, press the ''Reset'' button in the RGG Toolbar, to reset the graph structure. Inside this method, we first call the ''run()'' method for 3 steps. The result is a simple plant made of 3 internodes and 3 leaves. {{ :tutorials:testplant_crop.png?100|}} public void analyse() { for (apply(3)) { run(); } println("add here a query example"); } The underlying 2d graph of the generated structure given below: {{ :tutorials:graph_query_example_2.png?620|}} Beginning from //RGGRoot//, we have the sequence of three //Internodes// where to each is a //Leaf// linked by a branching edge. The last internode, additionally, has a Bud node successor. ==== Query examples ==== Replace the text (in red) inside the ''println()'' one by one by the queries below. Below each query, there is a result of the print command, for illustration. Note, that the ids will differ with different model execution. * Find all the internodes in the structure: (* Internode *) The query will return ids of all the internodes in the plant structure. Model.Internode[id=1911]@ecc95fb Model.Internode[id=1906]@7b431a06 Model.Internode[id=1901]@5753e950 __Note:__ Internode with the id=1901 is the first internode from the bottom (with the highest age: 3). Internode with the id=1911 is the top internode (with the lowest age: 1) Notice, that when you click with a mouse on an internode in the 3D View window, information about the selected internode will be displayed in the Attribute Editor. In the Attribute Editor, you can check the internode id (e.g. Model$Internode [Internode.1756]) and compare it with the one that was printed in the XL Console. Another option is to look at the node ids in the 2D Graph (Panels -> 2D -> Graph, in the graph window: View -> Redraw). * Find all newly created internode(s), with age == 1: (* i:Internode, (i[age] == 1) *) Model.Internode[id=1911]@ecc95fb * Find the internode below the bud: {{ :tutorials:testplant_graph.png?150|}} (* Bud < RH < Internode *) ''<'' represents a successor edge in the indicated direction Model.Internode[id=1911]@ecc95fb * Find all leaves connected to an internode: (* Internode +> RL > Leaf *) This is just to illustrate how to define different edge types inside a query. ''+>'' represents a branch edge. Model.Leaf[id=1913]@4b0f7bd Model.Leaf[id=1908]@1c6dbfa5 Model.Leaf[id=1903]@668c1f97 __Note:__ Leaf with the id=1903 is the first (and the oldest) leaf, leaf with the id=1913 is the last (and the yongest) leaf. ==== Aggregators ==== Aggregate operations are used to collect multiple values when traversing the graph structure and return a single value. Standard [[groimp-platform:xl-operators|aggregate operations]] are: ''count'', ''sum'', ''empty'', ''exist'', ''forall'', ''first'', ''last'', ''max'', ''min'', ''mean'', ''selectRandomly'', ''selectWhereMin'', ''selectWhereMax'', ... * Count all leaves: count((* Leaf *)) 3 * Sum up the area of all leaves: sum((* Leaf *)[area]) 6.0 * Sum up the length of internodes: sum((* Internode *)[length]) 6.0 In this particular example with internodes forming (only) the main stem, the query will return stem length. * Select the leaf with the highest leaf area: selectWhereMax((* l:Leaf *), (l[area])) Model.Leaf[id=1903]@668c1f97 * Count internodes that are older than 2 (time units): count((* i:Internode, (i[age] >= 2) *)) 2