Skip to content

Commit 2aed6fb

Browse files
authored
Merge pull request github#2878 from jf205/monotonic-aggregates-123
QL handbook: updates for rc/1.23
2 parents c5d8aac + fc3d30c commit 2aed6fb

File tree

1 file changed

+65
-8
lines changed

1 file changed

+65
-8
lines changed

docs/language/ql-handbook/expressions.rst

Lines changed: 65 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -393,16 +393,73 @@ aggregation in a simpler form:
393393
Monotonic 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

Comments
 (0)