80 Query Examples

This chapter gives a small tour of Predica query notation using the familiar Supplier/Parts model. The examples focus on how the notation reads. The Relations part gives the fuller correspondence with relational-model operators, and the Semantics part gives the logic-frame denotation.

Example Model


entity Supplier
{
    Integer id;
    String name;
    Integer status;
    String city;
    Shipment shipment* where shipment.supplier = this;
    key {id};
}

entity Part
{
    Integer id;
    String name;
    Color color;
    Real weight;
    String city;
    Shipment shipment* where shipment.part = this;
    key {id};
    //constraints { shipment.part ⊆ { this }; }
}

entity Shipment
{
    Supplier supplier;
    Part part;
    Integer qty;
    key {supplier,part};
}

entity ShipmentsSystem
{
    Supplier supplier*;
    Part part*;
    → Shipment shipment* = either supplier.shipment, part.shipment;
}

Assume a variable s whose type is ShipmentsSystem. Paths starting at s navigate through suppliers, parts, and shipments.

Navigation and Projection

Question Predica Reading
All suppliers
s.supplier
Navigate from the system to its supplier values.
Supplier names
s.supplier.name
For each supplier reached, observe its name.
Supplier names and cities
s.supplier.(name, city)
Reshape each supplier into a result with two exposed labels.
Shipment rows with names
s.shipment.(
    supplier.name as supplier_name,
    part.name as part_name,
    qty)
Use the shipment as the witness and expose values reached through it.

Filtering

Question Predica Reading
Suppliers in London
s.supplier.[city = "London"]
Keep the current suppliers whose city satisfies the filter.
Names of suppliers in London
s.supplier.[city = "London"].name
Filter suppliers first, then observe their names.
Parts heavier than 15
s.part.[weight > 15]
Use a numeric condition in the context of each part.
Shipments of red parts
s.shipment.[part.color = Color.red]
The filter may navigate from the current shipment to related values.

Inverse Navigation

If Supplier defines → shipment* = inverse shipment.supplier, then a supplier can be queried directly for the shipments that refer to it.

Question Predica Reading
Shipments for each supplier
s.supplier.shipment
Navigate backwards from supplier to referring shipments.
Part names shipped by each supplier
s.supplier.(name, shipment.part.name as part_name)
Combine forward and inverse navigation in one result shape.

Folds and Set Observations

Question Predica Reading
Suppliers with shipments
s.supplier.[shipment.Σexists]
Fold the supplier's shipment path to a Boolean condition.
Suppliers with no shipments
s.supplier.[shipment.Σempty]
Keep suppliers whose inverse shipment path is empty.
Number of shipments per supplier
s.supplier.(
    name,
    shipment.Σcount as shipment_count)
Fold the reached shipments while preserving the supplier context.
Total quantity shipped by supplier
s.supplier.(
    name,
    shipment.qty.Σsum as total_qty)
Navigate to quantities and aggregate them for each supplier.
Parts shipped by exactly one supplier
s.part.[shipment.supplier.Σcount = 1]
Use a folded path inside a filter condition.

Cardinality-Aware Assignment

Queries often reach more than one value. Assigning such a result to a single-valued variable is invalid; the target must permit the cardinality of the query result.


var id1 = 0;
id1 := s.supplier.id;     // static cardinality constraint check failure

var Integer id2* = {};
id2 := s.supplier.id;     // ok

Value Sharing in Literals

The .. notation resolves an existing value through a query while constructing a literal. This lets a literal share values already present in the surrounding object graph.


Shipment {
    supplier = ..supplier[id = 1],
    part = ..part[id = 2],
    qty = 200
}

The resolved values are still ordinary Predica values. Any internal handles or indexes used to implement this lookup are not part of the user-visible semantics.