# 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.

We use ChatGPT with additional context from our documentation to answer your questions. Not all answers will be accurate. Please join our Discord if you need more help.