LDOM (Name and Acronym TBD) Templates

For the ongoing discussion about the potential name of this technology, see the W3C wiki page.

Template-based Constraints

TODO: This section needs work - templates are used but not introduced yet at this stage. The problem is that we want non-SPARQL experts to still be able to use those templates even if they don't understand how they work internally.

In addition to constraints defined in SPARQL, LDOM makes it possible to define constraints using higher-level elements called Templates. (How to define your own Templates is introduced later.) The following example demonstrates the use of the Template ldom:ShapeConstraint which links a property with a "shape" that it needs to fulfill:

ex:Rectangle
	ldom:constraint [
		a ldom:ShapeConstraint ;
		ldom:predicate ex:creator ;
		ldom:some [
			ldom:property [
				ldom:predicate schema:email ;
				ldom:minCount 1 ;
			] ;
		] ;
	] ;

The above means that at least one of the values of the property ex:creator must be an object that has at least one schema:email value. This constraint gets evaluated together with the constraints defined by the property declarations, which means that the ex:creator must also be an instance of schema:Person. The property ldom:some means "at least one" - use ldom:all to specify that all values of the property must match the given shape. Note that the matching values do not have to be instances of the given shape, i.e. no rdf:type triple is required. Shapes can be nested, e.g. using additional ldom:constraint values.

Templates can also be used as values of ldom:property, as long as the template is a subclass of the system template ldom:PropertyConstraint. The LDOM core includes two such templates: ldom:DerivedPropertyConstraint and ldom:PrimaryKeyProperty. Here is an example of the latter, declaring that the values of the property ex:isoCode must be unique for each instance of ex:Country.

ex:Country
	a rdfs:Class ;
	rdfs:subClassOf rdfs:Resource ;
	rdfs:label "Country" ;
  	ldom:property [
  		a ldom:PrimaryKeyPropertyConstraint ;
		ldom:predicate ex:isoCode ;
		ldom:valueType xsd:string ;
		ldom:uriStart "http://example.org/country-" ;
		rdfs:label "ISO code" ;
    ] ...

This type of constraint also specifies how the URIs of instances need to be constructed, by concatenating the provided ldom:uriStart with the value of the primary key. Tools can use this information to automatically create URIs for newly created instances, and for data imported from external non-RDF sources such as relational databases.

Another built-in template is ldom:OrConstraint which can be used to combine multiple other constraint templates to express that at least one of two conditions need to be satisfied. The following example expresses that a ex:RectangleWithArea must either have ex:width and ex:height, or a value for ex:area:

ex:RectangleWithArea
	a rdfs:Class ;
	rdfs:subClassOf ex:Rectangle ;
	ldom:constraint [
		a ldom:OrConstraint ;
		ldom:shape1 [
			ldom:property [
				ldom:predicate ex:width ;
				ldom:minCount 1 ;
			] ;
			ldom:property [
				ldom:predicate ex:height ;
				ldom:minCount 1 ;
			]
		] ;
		ldom:shape2 [
			ldom:property [
				ldom:predicate ex:area ;
				ldom:minCount 1 ;
			]
		]
	] ;
.

The SPARQL query behind the ldom:OrConstraint uses a built-in helper function ldom:violatesConstraints to recursively evaluate the nested shapes. Based on this function, similar templates (such as exclusive-or) could be defined.

Many other kinds of templates can be used from third-party template libraries that provide high-level vocabularies for common design patterns.

Templates

LDOM Templates are "boxed" queries that can be used as values of ldom:constraint and ldom:property (as well as ldom:rule if that gets included into the standard). The role of a template is to encapsulate a reusable piece of SPARQL logic so that users do not need to reinvent the wheel. Templates hide the complexity of the underlying SPARQL query and are therefore suitable for people who are not familiar with SPARQL. The following example declares a LDOM template that can be used for constraints to express that the values of a given property shall be greater than 0.

ex:PositivePropertyValueConstraint
	a ldom:ConstraintTemplate ;
	rdfs:subClassOf ldom:TemplateConstraint ;
	rdfs:label "Positive property value constraint" ;
	ldom:labelTemplate "Values of property {?property} must be > 0" ;
	rdfs:comment """
		A template that can be used to define a LDOM constraint on a given property
		(ex:property) to make sure that the values of that property are > 0.
		""" ;
	ldom:argument [
		ldom:predicate ex:property ;
		ldom:valueType rdf:Property ;
		rdfs:comment "The property to constrain (e.g. ex:width or ex:height)." ;
	] ;
	ldom:sparql """
		SELECT (?property AS ?path) ?message
		WHERE {
			?this ?property ?value .
			FILTER (?value <= 0) .
			BIND (CONCAT("Property ", ldom:label(?property), 
				" must only have positive values, but found ",
				xsd:string(?value)) AS ?message) .
		}
		""" ;
.

In the example above, the template takes an argument, as specified by the ldom:argument. This argument is represented by a value of the property ex:property which is mapped to the variable ?property when the query executes. Here is an example that instantiates, or "calls", the template to define constraints on the properties ex:width and ex:height for all instances of the class ex:Rectangle.

ex:Rectangle
	...
	ldom:constraint [
		a ex:PositivePropertyValueConstraint ;
		ex:property ex:height ;
	] ;
	ldom:constraint [
		a ex:PositivePropertyValueConstraint ;
		ex:property ex:width ;
	] .

When a LDOM engine encounters such a constraint definition, it will execute the ldom:sparql body of the template and pre-bind the declared argument variables with the values specified in the template call. In the case of ex:height, the template's body basically becomes the following, where every appearance of the variable ?property has been replaced with the constant ex:height.

SELECT (ex:height AS ?path) ?message
WHERE {
	?this ex:height ?value .
	FILTER (?value <= 0) .
	BIND (CONCAT("Property ", ldom:label(ex:height), 
		" must only have positive values, but found ", xsd:string(?value)) AS ?message) .
}

LDOM templates make it possible to create libraries of reusable constraints and rules (and other use cases), so that users do not need to learn SPARQL. The LDOM standard comes with several of such reusable constraints including ldom:PropertyConstraint and ldom:Argument which is used by LDOM itself to declare the arguments of templates.

The example above highlights that LDOM templates can be used to create higher-level modeling languages that introduce constructs such as the ex:PositivePropertyValueConstraint above together with semantics that are executable by any LDOM-compliant engine. At the same time, high-level elements such as the property declarations can also be used by other engines that do not necessarily rely on SPARQL.

Profiles

Some applications may only support certain templates as a controlled vocabulary. For example, a JavaScript client form engine may only support property definitions with value type and min/max cardinality. The class ldom:Profile can represent such sets of templates, as illustrated in the following example. Here, an application could use the profile ex:SimpleFormProfile to display warnings if an incoming LDOM model uses features outside of its profile.

ex:SimpleFormProfile
	a ldom:Profile ;
	ldom:member ldom:AbstractCountPropertyConstraint ; # defines ldom:min/maxCount
	ldom:member ldom:AbstractValueTypePropertyConstraint . # defines ldom:valueType

Functions

LDOM Functions are similar to Templates in their syntax, but they are used to declare new SPARQL functions based on an encapsulated, reusable query. The following snippet defines a function ex:computeArea that takes a Rectangle as its argument and returns an integer that is the result of multiplying the Rectangle's width with its height.

ex:computeArea
	a ldom:Function ;
	rdfs:subClassOf ldom:Functions ;
	rdfs:label "compute area" ;
	rdfs:comment "Computes the area of a given rectangle (?arg1) as the product of its width and height." ;
	ldom:argument [
		ldom:predicate ldom:arg1 ;
		ldom:valueType ex:Rectangle ;
		rdfs:comment "The rectangle whose area to compute." ;
	] ;
	ldom:sparql """
		SELECT ((?width * ?height) AS ?result)
		WHERE {
			?arg1 ex:width ?width .
			?arg1 ex:height ?height .
		}
		""" ;
	ldom:returnType xsd:integer ;
.

In LDOM-compliant SPARQL processors, this new function can be used such as in the following example:

SELECT *
WHERE {
    ?rectangle a ex:Rectangle .
    FILTER (ex:computeArea(?rectangle) >= 100) .
}

LDOM functions have a SELECT query as their body, and this query needs to have one result variable (here: ?result). The first binding of this variable will be used as result of the function execution. The execution mechanism is illustrated in the following diagram.

Derived Properties

LDOM includes a special kind of property constraints for properties that are computed (or derived or inferred) from other values. A good example of this is the ex:area property for rectangles:

ex:Rectangle
	...
	ldom:property [
		a ldom:DerivedPropertyConstraint ;
		ldom:predicate ex:area ;
		ldom:maxCount 1 ;
		ldom:sparql "ex:computeArea(?this)" ;
		ldom:valueType xsd:integer ;
		rdfs:label "area" ;
		rdfs:comment "The area of the Rectangle, defined as the product of width x height." ;
	] ;

The ldom:DerivedPropertyConstraint above will flag a constraint violation if the value of ex:area is not equal to the result of the provided SPARQL expression.

Some LDOM implementations may also chose to use constraints of this type in a constructive way, to automatically compute missing values (but this is not covered by the standard).

Rules

(Unclear whether the WG will include inferencing, so this feature is at risk)

LDOM makes it possible to attach executable rules to classes. Rules are represented as SPARQL CONSTRUCT queries that apply to all instances of the associated class and its subclasses. In those rules, the variable ?this refers to each instance of those classes. A LDOM execution engine will make sure that ?this has the correct values. The triples that are constructed by such a rule become "inferred" and are added to the RDF graph, so that other rules can "see" the new triples. In the following example, the value of ex:area gets computed by multiplying the values of ex:width and ex:height.

ex:Rectangle
	ldom:rule [
		ldom:sparql """
			CONSTRUCT {
				?this ex:area ?area .                 # Infer ?area as a value of ex:area
			}
			WHERE {
				?this ex:width ?width .               # Get the width of ?this Rectangle
				?this ex:height ?height .             # Get the height of ?this Rectangle
				BIND ((?width * ?height) AS ?area) .  # Compute area := width * height
			}
		""" ;
	] .

The property ldom:rule is used to link a class with a rule. The values of this property must be either CONSTRUCT queries or template calls that wrap a CONSTRUCT query.

Contexts

LDOM constraints can be grouped into contexts - URIs that have the type ldom:Context. This makes it possible to indicate that certain constraints shall not apply by default, but only for certain applications or user communities. The property ldom:context links an ldom:Constraint with a context:

ex:Rectangle
	ldom:property [
		ldom:predicate dct:publisher ;
		ldom:minCount 1 ;
		ldom:context <http://example.org/trackable>
	] .

In the example above, the class ex:Rectangle has been extended with a minimum cardinality constraint on the property dct:publisher. This constraint is applicable in the provided ldom:context only, which means that it does not apply by default. However, certain applications that process ex:Rectangle instances can trigger the constraint checking in a context that includes all default constraints plus the extra constraints from the http://example.org/trackable context.

The operation that triggers the constraint checking can take included and excluded contexts as argument. Contexts can be organized into hierarchies using ldom:subContextOf, which means that whenever a super-context is requested then all constraints from its sub-contexts also apply. The built-in URI ldom:DefaultContext can be used to specify the default context, and that is also the default value if ldom:context is unspecified.