Queries to lists
The query language allows simple querying of lists (including nested lists) for aggregates of values. To do this, it uses a syntax similar to that available in C# - Language Integrated Query.
The general syntax is of the form:
from iterator in list
[where condition]
[skip n]
[take n]
select aggregation (iterator::element)
from Clients where [IsActive] select first (Name)
from cl in Clients where cl::[IsActive] select first::(cl::Name)
Individual syntax elements:
Iterator- the name of a new data tree declared at the query level, accessible only internally, needed to ensure the unambiguity of data references.
There is also a simplified syntax (from list ...), but then it is not possible to simply refer to the current data tree, which is the situation that probably occurs most often.
2. List - an expression that returns some list of records, usually in the form of a path starting from an unnamed local tree, such as Client, or in a more advanced variant, multiple nested: Clients\Relationships\Products\Transactions. All elements in the list built this way will be processed by the rest of the expression.
3. Condition - an optional expression returning the required logical value true/false, filtering the elements processed by the rest of the query.
4. Skip n - optionally the number of records to skip from the beginning of the constructed list.
5. Take n - optionally the number of elements to keep at the beginning of the built list (in that order). Any subsequent elements will not be taken into account.
6. Aggregation (iterator::element) - calling an aggregating function on all elements of the constructed list. If no aggregation function is specified, the result of the query will be the first element of this list (aggregate function first()). In the simplified syntax (without naming the iterator), the name of the field under the list element is enough.
If the result of the query will be a structure (e.g., an entire iterator record denoted by a dot .), the aggregating function is not used, and the expression returns a record or a list of records. It is also possible to create an in-line record yourself based on a fragment of the queried data structure (see the next section: Creating an in-line record).
Examples:
The most simplified syntax: from Clients select ID - selects the first ID from the list of clients, operation identical to first(Clients\ID)
The usefulness of the query syntax is revealed when using conditions and aggregation, e.g. from Clients where Vip select sum(Income)
The simplified form is the most commonly used, but we also have the full form of the query.
The full form gives much more possibilities because it calls iterators, so we can reach both them and the external context. In this case, the iterator looks like a data tree named cl, as an abbreviation for the iterated Clients record. In turn, the OpeningDate path leads to the main record from the data context. Together, this allows us to correlate different data.

There is no obligation to return aggregated values from individual fields from such a query - for example, you can return the entire first record from the iterator.
from Clients where [IsActive] select first (.)
You can also return all records, without any aggregation, but filtered instead.
from Clients where [IsActive] select .
Or, as in SQL, you can build yourself and return a completely new record, in this case containing the client ID, the minimum and maximum date of the relationship (from the nested list of relationship histories in the client), and, for example, the first operation code from the operation history.
This is what the snippet of the newly added data structure in the tree will then look like.
from Clients
where [IsActive]
select
{
id = ClientId,
minRelation = min(RelationHistory\Timestamp),
maxRelation = max(RelationHistory\Timestamp),
firstOpCode = first(...\OperationHistory\OperationCode)
}

In this type of queries we also have SKIP and TAKE operators available, for skipping the first and limiting the result count, just as in various SQL variants we have OFFSET, TOP, FETCH FIRST, etc. clauses.
from cl in Clients
where cl::[IsActive]
skip alreadyProcessedCount
take 5
select first (cl::.)
These types of expressions do not cause any database queries, they operate only on values and lists loaded earlier.
The grammar allows you to very simply apply a scalar function to a list to get a list of the function's values for all arguments. We call the function on the list even if we do not have such a function variant defined. This is very convenient and does not require the use of, for example, lambda calculus, closures or other such things.
See also
Special cases: no value vs. empty value