@@ -393,16 +393,73 @@ aggregation in a simpler form:
393393Monotonic aggregates
394394====================
395395
396- In addition to the standard aggregates, QL also supports monotonic aggregates.
397- These are a slightly different way of computing aggregates which have some advantages.
398- For example, you can use monotonic aggregates :ref: `recursively <recursion >`.
399- You can't do this with normal aggregates.
396+ In addition to standard aggregates, QL also supports monotonic aggregates.
397+ Monotonic aggregates differ from standard aggregates in the way that they deal with the
398+ values generated by the ``<expression> `` part of the formula:
399+
400+ - Standard aggregates take the ``<expression> `` values for each ``<formula> `` value and
401+ flatten them into a list. A single aggregation function is applied to all the values.
402+ - Monotonic aggregates take an ``<expression> `` for each value given by the ``<formula> ``,
403+ and create combinations of all the possible values. The aggregation
404+ function is applied to each of the resulting combinations.
405+
406+ In general, if the ``<expression> `` is total and functional, then monotonic aggregates are
407+ equivalent to standard aggregates. Results differ when there is not precisely one ``<expression> ``
408+ value for each value generated by the ``<formula> ``:
409+
410+ - If there are missing ``<expression> `` values (that is, there is no
411+ ``<expression> `` value for a value generated by the ``<formula> ``), monotonic aggregates
412+ won't compute a result, as you cannot create combinations of values
413+ including exactly one ``<expression> `` value for each value generated by the ``<formula> ``.
414+
415+ - If there is more than one ``<expression> `` per ``<formula> `` result, you can create multiple
416+ combinations of values including exactly one ``<expression> `` value for each
417+ value generated by the ``<formula> ``. Here, the aggregation function is applied to each of the
418+ resulting combinations.
419+
420+ Recursive monotonic aggregates
421+ ------------------------------
422+
423+ Monotonic aggregates may be used :ref: `recursively <recursion >`, but the recursive call may only appear in the
424+ expression, and not in the range. The recursive semantics for aggregates are the same as the
425+ recursive semantics for the rest of QL. For example, we might define a predicate to calculate
426+ the distance of a node in a graph from the leaves as follows:
400427
401- For more information and examples, see `Monotonic aggregates in QL
402- <https://help.semmle.com/QL/learn-ql/advanced/monotonic-aggregates.html> `_.
428+ .. code-block :: ql
429+
430+ int depth(Node n) {
431+ if not exists(n.getAChild())
432+ then result = 0
433+ else result = 1 + max(Node child | child = n.getAChild() | depth(child))
434+ }
435+
436+ Here the recursive call is in the expression, which is legal. The recursive semantics for aggregates
437+ are the same as the recursive semantics for the rest of QL. If you understand how aggregates work in
438+ the non-recursive case then you should not find it difficult to use them recursively. However, it is
439+ worth seeing how the evaluation of a recursive aggregation proceeds.
440+
441+ Consider the depth example we just saw with the following graph as input (arrows point from children to parents):
442+
443+ .. |image0 | image :: ../images/monotonic-aggregates-graph.png
444+
445+ |image0 |
446+
447+ Then the evaluation of the ``depth `` predicate proceeds as follows:
448+
449+ +-----------+--------------------------------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
450+ | **Stage ** | **depth ** | **Comments ** |
451+ +===========+============================================+==========================================================================================================================================================================+
452+ | 0 | | We always begin with the empty set. |
453+ +-----------+--------------------------------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
454+ | 1 | ``(0, b), (0, d), (0, e) `` | The nodes with no children have depth 0. The recursive step for **a ** and **c ** fails to produce a value, since some of their children do not have values for ``depth ``. |
455+ +-----------+--------------------------------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
456+ | 2 | ``(0, b), (0, d), (0, e), (1, c) `` | The recursive step for **c ** succeeds, since ``depth `` now has a value for all its children (**d ** and **e **). The recursive step for **a ** still fails. |
457+ +-----------+--------------------------------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
458+ | 3 | ``(0, b), (0, d), (0, e), (1, c), (2, a) `` | The recursive step for **a ** succeeds, since ``depth `` now has a value for all its children (**b ** and **c **). |
459+ +-----------+--------------------------------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
403460
404- .. TODO: Eventually replace this link with just the relevant examples.
405- (Some of the content is a duplicate of the above discussion on aggregates.)
461+ Here, we can see that at the intermediate stages it is very important for the aggregate to
462+ fail if some of the children lack a value - this prevents erroneous values being added.
406463
407464.. index :: any
408465
0 commit comments