# Cardinality​

The number of items in a set is known as its cardinality. A set with a cardinality of zero is referred to as an empty set. A set with a cardinality of one is known as a singleton.

The term cardinality is used to refer to both the exact number of elements in a given set or a range of possible values. Internally, EdgeDB tracks 5 different cardinality ranges: `Empty` (zero elements), `One` (a singleton set), `AtMostOne` (zero or one elements), `AtLeastOne` (one or more elements), and `Many` (any number of elements).

EdgeDB uses this information to statically check queries for validity. For instance, when assigning to a `required multi` link, the value being assigned in question must have a cardinality of `One` or `AtLeastOne` (as empty sets are not permitted).

It’s often useful to think of EdgeDB functions/operators as either element-wise or aggregate. Element-wise operations are applied to each item in a set. Aggregate operations operate on sets as a whole.

This is a simplification, but it’s a useful mental model when getting started with EdgeDB.

An example of an aggregate function is `count()`. It returns the number of elements in a given set. Regardless of the size of the input set, the result is a singleton integer.

Copy
`db> `
`select count('hello');`
`{1}`
Copy
`db> `
`select count({'this', 'is', 'a', 'set'});`
`{4}`
Copy
`db> `
`select count(<str>{});`
`{0}`

Another example is `array_agg()`, which converts a set of elements into a singleton array.

Copy
`db> `
`select array_agg({1,2,3});`
`{[1, 2, 3]}`

By contrast, the `len()` function is element-wise; it computes the length of each string inside a set of strings; as such, it converts a set of `str` into an equally-sized set of `int64`.

Copy
`db> `
`select len('hello');`
`{5}`
Copy
`db> `
`select len({'hello', 'world'});`
`{5, 5}`

In case of element-wise operations that accept multiple arguments, the operation is applied to a cartesian product of all the input sets.

Copy
`db> `
`select {'aaa', 'bbb'} ++ {'ccc', 'ddd'};`
`{'aaaccc', 'aaaddd', 'bbbccc', 'bbbddd'}`
Copy
`db> `
`select {true, false} or {true, false};`
`{true, true, true, false}`

By extension, if any of the input sets are empty, the result of applying an element-wise function is also empty. In effect, when EdgeDB detects an empty set, it “short-circuits” and returns an empty set without applying the operation.

Copy
`db> `
`select {} ++ {'ccc', 'ddd'};`
`{}`
Copy
`db> `
`select {} or {true, false};`
`{}`

Certain functions and operators avoid this “short-circuit” behavior by marking their inputs as optional. A notable example of an operator with optional inputs is the `??` operator.

Copy
`db> `
`select <str>{} ?? 'default';`
`{'default'}`

Ultimately, the distinction between “aggregate vs element-wise” operations is a false one. Consider the `in` operation.

Copy
`db> `
`select {1, 4} in {1, 2, 3};`
`{true, false}`

This operator takes two inputs. If it was “element-wise” we would expect the cardinality of the above operation to the cartesian product of the input cardinalities: `2 x 3 = 6`. It it was aggregate, we’d expect a singleton output.

Instead, the cardinality is `2`. This operator is element-wise with respect to the first input and aggregate with respect to the second. The “element-wise vs aggregate” concept isn’t determined on a per-function/per-operator basis; it determined on a per-input basis.

When defining functions, all inputs are element-wise by default. The `set of` type qualifier is used to designate an input as aggregate. Currently this modifier is not supported for user-defined functions, but it is used by certain standard library functions.

Similarly the `optional` qualifier marks the input as optional; an operation will be executed is an optional input is empty, whereas passing an empty set for a “standard” (non-optional) element-wise input will always result in an empty set.

Similarly, the output of a function can be annotated with `set of` and `optional` qualifiers.

To compute the number of times a function/operator will be invoked, take the cardinality of each input and apply the following transformations, based on the type qualifier (or lack thereof) for each:

```element-wise:  N -> N
optional:      N -> max(1, N)
aggregate:     N -> 1```

The ultimate cardinality of the result is the union of the results of each invokation; as such, it depends on the values returned by each invokation.