where ("M" Keywords)
[This content is no longer valid. For the latest information on "M", "Quadrant", SQL Server Modeling Services, and the Repository, see the Model Citizen blog.]
The where keyword precedes a Boolean expression known as a constraint, which is a condition that must be true for a value or extent to belong to a type. Constraints may be stated in type declarations or at the point of use. Constraints can be applied to any type: members of an entity, items within a collection, and collections that an entity is a member of. For query expressions, the where clause expresses a constraint on the collection returned by the query.
Constraints on Types
A constraint can be applied to a type using the where operator. The following example shows an [Order Detail]
that uses a constraint to enforce the business rule that if Quantity
is less than 10, then Discount
must be less than 10%.
module calcs
{
type OrderDetail
{
UnitPrice => 0 : Integer32;
Quantity => 1 : Integer32;
Discount => 0 : Number;
} where (value.Quantity < 10 ? value.Discount < 0.10 : true);
}
Constraints can enforce membership in other types. For example, the following type is required to be in OrderDetail
.
type ExpensiveItem
{
ExtendedPrice : Integer32;
} where value in OrderDetail && value.ExtendedPrice > 1000000;
Because it is a common desire to factor member declarations into smaller pieces that can be easily composed, “M” provides explicit syntax support for this.
Consider the following type declaration.
type RushItem
{
DeliveryTime : Integer32;
} where value in OrderDetail && value.DeliveryTime < 2;
You can see how to define an ExpensiveRushItem
by composing the prior definitions, as seen in the following code.
type ExpensiveRushItem : ExpensiveItem, RushItem {
}
This definition is equivalent to this longer definition.
type ExpensiveRushItem
{
UnitPrice => 0 : Integer32;
Quantity => 1 : Integer32;
Discount => 0 : Number;
ExtendedPrice : Integer32;
DeliveryTime : Integer32;
} where value.ExtendedPrice > 1000000 && value.DeliveryTime < 2;
The names of the types are just ways to refer to types. The values themselves have no record of the type names used to describe them. This is a difference from an object-oriented language, where an instance of a class "knows" what class it belongs to.
Constraints on Extents
Constraints can be applied to extents. The following constraint states that the Name
field of every element of Categories
must be less than 20 characters long.
module constraints
{
type Category
{
Name : Text;
}
Categories : {Category*} where value.Name.Count <= 20;
}
You can also constrain the extent itself. Of course you can easily constrain the number of items in a collection by means of the suffix operations, without using the where key word, as shown in the following code.
Categories : Category#1..6;
You can also apply a variety of constraints to an extent, as shown in the following example.
module constraints
{
type Employee {
Id : Integer32;
LastName : Text;
FirstName : Text;
ReportsTo : Employee?;
DirectReports : SmallBusinessEmployees*;
City : Text;
State : Text#2;
} where identity Id;
SmallBusinessEmployees : (Employee where identity Id,
value.State == "WA",
value.City == "Tacoma",
value.ReportsTo in SmallBusinessEmployees,
value.ReportsTo != value)#0..100;
}
See Also
Concepts
in ("M" Keywords)
identity ("M" Keywords)
unique ("M" Keywords)