SQWRLCollections

Martin O'Connor edited this page Aug 12, 2016 · 12 revisions

Because of OWL and SWRL's open world assumption, certain queries on OWL ontologies can be difficult to express. Many types of questions require closure operations for correct formulation. The core SQWRL operators support some degree of closure when querying, and do so without violating OWL's open world assumption. For example, queries like List all patients in an ontology and the number of drugs that they are on can be expressed fairly directly. Similarly, a query like List the average age of all patients can also be expressed.

However, queries with more complex closure requirements can not be expressed using the core operators. For example, the query List all patients in an ontology that are on more than two drugs is not expressible. Basically, while the core operators support basic closure operations, no further operations can be performed on the results of these operators. Queries with negation or complex aggregation functionality are similarly not expressible using the core operators.

SQWRL provides collections to support the closure operations necessary for these functionalities. Note that SQWRL collections cannot be used in SWRL rules as their use would violate OWL's open world assumption.

Table of Contents

Basic Collections

Two types of collections are supported: sets and bags.

As might be inferred, sets do not allow duplicate elements whereas bags do.

A built-in called sqwrl:makeSet is provided to construct a set. Its basic form is:

 sqwrl:makeSet(<set>, <element>)

The first argument of this set construction operator specifies the set to be constructed and the second specifies the element to be added to the set. This built-in will construct a single set for a particular query and will place the supplied element into the set. If a variable is specified in the element position then all bindings for that variable in a query will be inserted into the set.

A built-in called sqwrl:makeBag is provided to construct a bag. Its basic form is:

 sqwrl:makeBag(<bag>, <element>)

The scope of each collection is limited to the query that contains it.

Collection operators, such as, for example, sqwrl:size, can then be applied to these collections. The results of these operators can be used by built-ins in the query thus allowing access to the results of the closure operation.

Two new SQWRL clauses are provided to contain these collection construction and manipulation operators. The collection construction clause comes after a standard SWRL pattern specification and is separated from it using the ˚ character. This clause is then followed by a collection operation clause that contains built-ins that operate on the collections, which is again separated from it by the ˚ character.

In outline, a SQWRL query with collections operators will look as follows:

SWRL Pattern Specification ˚ Collection Construction Clause ˚ Collection Operation ClauseSelect Clause

The collection construction clause can only contain SQWRL collection construction operators, such as sqwrl:makeSet and sqwrl:makeBag.

The collection operators clause can contain only collection operators, such as sqwrl:size, in addition to built-ins that operate on the results of these operations.

It may not contain any other SWRL atom types.

Using this approach, a SQWRL query to, say, list the number of persons in an ontology can be written:

 Person(?p) ˚ sqwrl:makeSet(?s, ?p) ˚ sqwrl:size(?size, ?s) 
 -> sqwrl:select(?size)

SQWRL also provides standard operators such as sqwrl:union, sqwrl:difference, sqwrl:intersection and so on.

These operators employ standard set semantics and generate sets.

An operator called sqwrl:append is also provided to append two collections to generate a bag.

Using these operators, queries can effectively examine the results of two or more closure operations, permitting the writing of far more complex queries.

Putting elements into collections effectively provides a closure mechanism.

Clearly, two phase processing is required for these queries - a query cannot, say, determine how many elements there are in a collection until the collection has been constructed.

As mentioned, the language restricts the atoms that are processed in the second phase to collection built-ins and other built-ins that operate on the results of these collection built-ins. That is, the first phase of query execution is analogous to standard rule execution. The second phase consists of operations on the collections constructed as a result of that execution.

The introduction of the new ˚ separator character does not prevent the use of the standard SWRL serialization for SQWRL queries that use it: the character itself does not have to be saved and its display position can be inferred by tools when reading serialized queries.

The ability to use the standard serialization mechanism means that queries can be stored in OWL ontologies along with rules, and can be shared between OWL tools even if those tools do not support the language.

Grouping

Collections support basic counting and aggregation operations over the entire ontology. However, the earlier query to list all patients in an ontology on more than two drugs is still not expressible using this approach. Additional collection construction operators are required to allow more complex queries that group related sets of entities. This additional expressivity is supplied by collections that are partitioned by a group of arguments.

A built-in called sqwrl:groupBy provides this functionality. The general form of this grouping is:

 sqwrl:makeSet(<set>, <element>) ^ sqwrl:groupBy(<set>, <group>)

or

 sqwrl:makeBag(<bag>, <element>) ^ sqwrl:groupBy(<bag>, <group>)

This group can contain one or more entities. The first argument to the sqwrl:groupBy built-in is the collection and the second and (optional) subsequent arguments are the entities to group by. Only one grouping can be applied to each collection. This grouping mechanism is analogous to SQL's GROUP BY clause.

Using this grouping mechanism, the construction of a set of drugs for each patient can be written:

 Patient(?p) ^ hasDrug(?p,?d) ˚ sqwrl:makeSet(?s, ?d) ^ sqwrl:groupBy(?s, ?p) 

Here, a new set is built for each patient matched in the query and all the drugs for each patient are added to their set.

Using this grouped set, the query to list all patients on more than two drugs can then be written:

 Patient(?p) ^ hasDrug(?p,?d) ˚ 
 sqwrl:makeSet(?s, ?d) ^ sqwrl:groupBy(?s, ?p) ˚ 
 sqwrl:size(?n, ?s) ^ swrlb:greaterThan(?n, 2)
 -> sqwrl:select(?p) 

Here, the sqwrl:size operator will apply to each individual grouped set.

In general, operators applied to grouped collections will automatically consider the grouping.

More complex groupings will require multiple grouping entities.

For example, to build bags that contain the doses of each drug taken by each patient where each drug is stored in a treatment together with its dose the bags must be grouped by both patients and treatments:

 Patient(?p) ^ hasTreatment(?p,?t) ^ hasDrug(?t, ?d) ^ hasDose(?t,?dose) ˚
 sqwrl:makeBag(?b, ?dose) ^ sqwrl:groupBy(?b, ?p, ?d) 

Here, bags will be constructed for each patient and drug combination and the all doses for that combination will be added to them.

Using this approach, a query to return the number of doses of each drug taken by each patient can be written:

 Patient(?p) ^ hasTreatment(?p,?t) ^ hasDrug(?t, ?d) ^ hasDose(?t,?dose) ˚
 sqwrl:makeBag(?b, ?dose) ^ sqwrl:groupBy(?b, ?p, ?d) ˚
 sqwrl:size(?n, ?b) 
 -> sqwrl:select(?p, ?d, ?n) 

SQWRL's grouping mechanism dramatically expands the power of the language.

This mechanism effectively allows queries to perform closure by selectively partitioning OWL entities into collections.

It then supports an array of standard collection operations on these partitioned entities, which allow it to answer very complex queries.

Aggregation

SQWRL supports standard aggregation operators on collections if the elements have a natural ordering.

For example, a query to return the average age of all patients can be written:

 Patient(?p) ^ hasAge(?p, ?age) ˚ 
 sqwrl:makeBag(?b, ?age) ˚
 sqwrl:avg(?avg, ?b) 
 -> sqwrl:select(?avg) 

A query to list all patients less than the average age in an ontology can be written:

 Patient(?p) ^ hasAge(?p, ?age) ˚
 sqwrl:makeBag(?b, ?age) ˚
 sqwrl:avg(?avg, ?b) ^ swrlb:lessThan(?age, ?avg)
 -> sqwrl:select(?p, ?age) 

More complex queries can be constructed by combining SQWRL's grouping mechanism with aggregation operators.

For example, a query to return the average dose of each drug taken by each patient can be written:

 Patient(?p) ^ hasTreatment(?p,?t) ^ hasDrug(?t, ?d) ^ hasDose(?t,?dose) ˚
 sqwrl:makeBag(?b, ?dose) ^ sqwrl:groupBy(?b, ?p, ?d) ˚
 sqwrl:avg(?avg, ?b) 
 -> sqwrl:select(?p, ?d, ?avg) 

A similar query to return the maximum dose of each drug for each patient can be written:

 Patient(?p) ^ hasTreatment(?p, ?t) ^ hasDrug(?t, ?d) ^ hasDose(?t, ?dose) ˚
 sqwrl:makeBag(?b, ?dose) ^ sqwrl:groupBy(?b, ?p, ?d) ˚
 sqwrl:max(?max, ?b) 
 -> sqwrl:select(?p, ?d, ?max) 

Grouped and non grouped collection can also be used in the same query.

For example, a query to list patients with less than the average DDI dose can be written:

 Patient(?p) ^ hasTreatment(?p, ?t) ^ hasDrug(?t, DDI) ^ hasDose(?t, ?d) ˚
 sqwrl:makeBag(?spd, ?d) ^ sqwrl:groupBy(?spd, ?p) ^ sqwrl:makeBag(?sddi, ?d) ˚
 sqwrl:avg(?avgPD, ?spd) ^ sqwrl:avg(?avgDDI, ?sddi) ^
 swrlb:lessThan(?avgPD, ?avgDDI)
 -> sqwrl:select(?p) 

Any number of collections can be used in the same query.

For example, a query to list the average doses of drugs taken by patients that are on more than two drugs and where none of those drugs is a beta blocker or an anti-hypertensive can be written:

 Patient(?p) ^ hasDrug(?p,?drug) ^ hasDose(?drug,?dose) ^ 
 BetaBlocker(?bb) ^ AntiHypertensive(?ahtn) ˚
 sqwrl:makeSet(?s1, ?dose) ^ sqwrl:groupBy(?s1, ?p, ?drug) ^
 sqwrl:makeSet(?s2, ?drug) ^ sqwrl:groupBy(?s2, ?p) ^
 sqwrl:makeSet(?s3, ?bb, ?ahtn) ˚
 sqwrl:avg(?avg, ?s1) ^ sqwrl:size(?n, ?s2) ^ swrlb:greaterThan(?n, 2) ^
 sqwrl:intersection(?s4, ?s2, ?s3) ^ sqwrl:isEmpty(?s4)
 -> sqwrl:select(?p, ?drug, ?avg)  

This query illustrates the use of grouping, counting, aggregation, negation as failure (see below) and disjunction.

As can be seen from this query, set operations can be applied to grouped and non grouped collections simultaneously and both types of collections can be used in the same operator.

Retrieving All Elements from a Collection

The sqwrl:element built-in can be used to determine if a specific element is in a collection or it can be used to select all elements from collections. It takes two arguments: the first is the element and the second is the collection. If the first element argument is unbound, it will be bound to all elements in the specified collection.

For example, a query to find patients that have more than two treatments and at least one of those is a DDI treatment can be written:

 Patient(?p) ^ hasDrug(?p,?d) ˚
 sqwrl:makeSet(?sd, ?d) ^ sqwrl:groupBy(?sd, ?p) ˚
 sqwrl:size(?sd, ?size) ^ swrlb:greaterThan(?size, 2) ^
 sqwrl:element(DDI, ?sd) 
 -> sqwrl:select(?p) 

A similar query to list the drugs taken by patients with more than two treatments can be written:

 Patient(?p) ^ hasDrug(?p,?d) ˚
 sqwrl:makeSet(?sd, ?d) ^ sqwrl:groupBy(?sd, ?p) ˚
 sqwrl:size(?sd, ?size) ^ swrlb:greaterThan(?size, 2) ^
 sqwrl:element(?e, ?sd) 
 -> sqwrl:select(?p, ?e) 

The negative sqwrl:notElement variant is also provided.

Retrieving Specific Elements from a Collection

By default, collections are unordered. However, if all the elements in a collection have a natural ordering SQWRL supports selection operators on these collections. These operators take at least two arguments: the first is the element to be retrieved and the second is the collection. These operators include sqwrl:least, sqwrl:greatest, sqwrl:nth, and sqwrl:nthLast. The collections are automatically ordered when these operators are applied.

For example, using the sqwrl:least built-in, a query to return the lowest DDI dose for each patient can be written:

 Patient(?p) ^ hasTreatment(?p, ?tr) ^ hasDrug(?tr, DDI) ^ hasDose(?tr, ?dose) ˚
 sqwrl:makeBag(?b, ?dose) ^ sqwrl:groupBy(?b, ?p) ˚
 sqwrl:least(?leastDose, ?b) ^ swrlb:equal(?leastDose, ?dose)
 -> sqwrl:select(?p, ?leastDose) 

SQWRL provides aliases for the sqwrl:least and sqwrl:greatest operators. These are sqwrl:first and sqwrl:last, respectively.

A built-in called sqwrl:nth allows the selection of an arbitrary result row. It take a third integer parameter indicating the row index.

Using this operator, a query to, for example, return the third lowest DDI doses for each patient can be written:

 Patient(?p) ^ hasTreatment(?p, ?tr) ^ hasDrug(?tr, DDI) ^ hasDose(?tr, ?dose) ˚
 sqwrl:makeBag(?b, ?dose) ^ sqwrl:groupBy(?b, ?p) ˚
 sqwrl:nth(?third, ?b, 3) ^ swrlb:equal(?third, ?dose)
 -> sqwrl:select(?p, ?third) 

A variant called sqwrl:nthLast can be used to select relative to the end of a collection.

Using this operator, a query to, for example, return the third highest DDI doses for each patient can be written:

 Patient(?p) ^ hasTreatment(?p, ?tr) ^ hasDrug(?tr, DDI) ^ hasDose(?tr, ?dose) ˚
 sqwrl:makeBag(?b, ?dose) ^ sqwrl:groupBy(?b, ?p) ˚
 sqwrl:nthLast(?thirdLast, ?b, 3) ^ swrlb:equal(?thirdLast, ?dose)
 -> sqwrl:select(?p, ?thirdLast) 

This built-in also has an alias called sqwrl:nthGreatest.

If the element specified by the built-in does not exist, it evaluates to false. For example, if the sqwrl:nth built-in is used to retrieve the 5th element of a collection with three elements, it will return false.

A bound argument can also be supplied in the element position.

For example, a query to retrieve all patients that have a lowest dose of 3.0 can be written:

 Patient(?p) ^ hasTreatment(?p, ?tr) ^ hasDrug(?tr, DDI) ^ hasDose(?tr, ?dose) ˚
 sqwrl:makeBag(?b, ?dose) ^ sqwrl:groupBy(?b, ?p) ˚
 sqwrl:least(3.0, ?b) 
 -> sqwrl:select(?p) 

A more complex variant to return patients that have the same lowest dose of DDI and AZT can be written:

 Person(?p) ^
 hasTreatment(?p, ?trDDI) ^ hasDrug(?trDDI, DDI) ^ hasDose(?trDDI, ?doseDDI) ^
 hasTreatment(?p, ?trAZT) ^ hasDrug(?trAZT, AZT) ^ hasDose(?trAZT, ?doseAZT) ˚
 sqwrl:makeBag(?bDDI, ?doseDDI) ^ sqwrl:groupBy(?bDDI, ?p) ^
 sqwrl:makeBag(?bAZT, ?doseAZT) ^ sqwrl:groupBy(?bAZT, ?p) ˚
 sqwrl:least(?lowestDose, ?bDDI) &#8743; sqwrl:least(?lowestDose, ?bAZT)
 -> sqwrl:select(?p, ?lowestDose) 

Retrieving Sub-Collections from a Collection

SQWRL supports the selection of sub-collections. These operators take at least three arguments: the first argument is the result collection, the second is the collection to be operated on, and the final argument indicates the index of the selected row.

Like the individual element selection operators, these operators order the source collection (be it a set or a bag) and return ordered collections, or bags, as a result.

The sqwrl:nthSlice operator provides the most basic form of this selection. It supports the selection of collection of a specified size starting at an arbitrary result row.

Using this operator, a query to calculate the average of the third through the fifth lowest DDI doses for each patient can be written:

 Patient(?p) ^ hasTreatment(?p, ?tr) ^ hasDrug(?tr, DDI) ^ hasDose(?tr, ?dose) ˚
 sqwrl:makeBag(?b, ?dose) ^ sqwrl:groupBy(?b, ?p) ˚
 sqwrl:nth(?thirdToFifthB, ?b, 3, 2) ^ swrlb:avg(?avg, ?thirdToFifthB)
 -> sqwrl:select(?p, ?avg) 

SQWRL also supports the operators sqwrl:greatestN and sqwrl:leastN, which can be used to select a sequence of values from a collection. These built-ins also take a third argument that specifies the size of the slice.

Aliases for the sqwrl:firstN and sqwrl:lastN operators are also provided. These are sqwrl:leastN and sqwrl:greatestN, respectively.

For example, a query to get the average of the lowest three DDI doses for each patient can be written:

 Patient(?p) ^ hasTreatment(?p, ?tr) ^ hasDrug(?tr, DDI) ^ hasDose(?tr, ?dose) ˚
 sqwrl:makeBag(?b, ?dose) ^ sqwrl:groupBy(?b, ?p) ˚
 sqwrl:leastN(?lowest3DosesB, ?b, 3) ^ swrlb:avg(?avg, ?lowest3DosesB)
 -> sqwrl:select(?p, ?avg) 

A more complex variant to calculate the averages of the lowest two and the highest two DDI doses can be written:

 Person(?p) ^ hasTreatment(?p, ?t) ^ hasDose(?t, ?dose) ^ hasDrug(?t, DDI) ˚
 sqwrl:makeBag(?s, ?dose) ^ sqwrl:groupBy(?s, ?p) ˚
 sqwrl:leastN(?lowest2B, ?s, 2) ^ sqwrl:greatestN(?greatest2B, ?s, 2) ^
 sqwrl:avg(?avgL2, ?lowest2B) ^ sqwrl:avg(?avgG2, ?greatest2B)
 -> sqwrl:select(?p, ?avgL2, ?avgG2) 

Collections generated by these operators can also be combined using the standard sqwrl:union, sqwrl:difference, sqwrl:intersection and sqwrl:append operators

For example, the above query can be modified to calculate the combined average of the lowest two and the highest two DDI doses:

 Person(?p) ^ hasTreatment(?p, ?t) ^ hasDose(?t, ?dose) ^ hasDrug(?t, DDI) ˚
 sqwrl:makeBag(?s, ?dose) ^ sqwrl:groupBy(?s, ?p) ˚
 sqwrl:leastN(?lowest2B, ?s, 2) ^ sqwrl:greatestN(?greatest2B, ?s, 2) ^
 sqwrl:avg(?avgL2, ?lowest2B) ^ sqwrl:avg(?avgG2, ?greatest2B) ^
 sqwrl:append(?rB, ?lowest2B, ?greatest2B) ^ sqwrl:avg(?avg, ?rB)
 -> sqwrl:select(?p, ?avg) 

The sqwrl:element operator can be used to extract the elements from the collection returned by these operators.

For example, a query to list the lowest two DDI doses for each patient can be written:

 Person(?p) ^ hasTreatment(?p, ?t) ^ hasDose(?t, ?dose) ^ hasDrug(?t, DDI) ˚
 sqwrl:makeBag(?s, ?dose) ^ sqwrl:groupBy(?s, ?p) ˚
 sqwrl:leastN(?lowest2B, ?s, 2) ^ sqwrl:element(?lowest2, ?lowest2B)
 -> sqwrl:select(?p, ?lowest2) 

If the elements specified by one of these built-ins does not exist, an empty bag is returned. For example, if the sqwrl:nthSlice built-in is used to retrieve the 2nd through the 4th the fourth element of a collection with only one element, the built-in will evaluate to true and the first argument will be assigned an empty bag. This behavior differs from the built-ins that retrieve individual elements from a collection - these built-ins evaluate to false if an element does not exist that satisfies its condition .

A bound argument representing a bag can also be supplied in the return argument position.

For example, a query to retrieve all patients that have the same two lowest doses of AZT and DDI can be written:

 Person(?p) ^
 hasTreatment(?p, ?trDDI) ^ hasDrug(?trDDI, DDI) ^ hasDose(?trDDI, ?doseDDI) ^
 hasTreatment(?p, ?trAZT) ^ hasDrug(?trAZT, AZT) ^ hasDose(?trAZT, ?doseAZT) ˚
 sqwrl:makeBag(?bDDI, ?doseDDI) ^ sqwrl:groupBy(?bDDI, ?p) ^
 sqwrl:makeBag(?bAZT, ?doseAZT) ^ sqwrl:groupBy(?bAZT, ?p) ˚
 sqwrl:leastN(?lowest2DosesB, ?bDDI, 2) ^ sqwrl:leastN(?lowest2DosesB, ?bAZT, 2)
 -> sqwrl:select(?p, ?e) 

Alternatively, separate collections can be created by each sqwrl:least operator and then compared using the sqwrl:equal operator.

Negative form of Selection Operators

All SQWRL collection selection operators have a negative form. These operators return collections made up from elements in a source collection that do not meet the criteria specified in their positive equivalent.

For example, a built-in called sqwrl:notNth provides the negative form of the sqwrl:nth operator.

Using it, a query to return, for example, all but the third lowest DDI doses for each patient can be written:

 Patient(?p) ^ hasTreatment(?p, ?tr) ^ hasDrug(?tr, DDI) ^ hasDose(?tr, ?dose) ˚
 sqwrl:makeBag(?b, ?dose) ^ sqwrl:groupBy(?b, ?p) ˚
 sqwrl:notNth(?notThirdB, ?b, 3) ^ swrlb:contains(?notThirdB, ?e)
 -> sqwrl:select(?p, ?e) 

The sqwrl:notNthLast form is also supported.

SQWRL also supports the operators sqwrl:notGreatestN, sqwrl:notLeastN, sqwrl:notNthSlice, and sqwrl:notNthLastSlice to select elements that are not in a particular range.

Using the negative form of sqwrl:notGreatestN, a query to return all DDI doses except the lowest three doses for each patient can be written:

 Patient(?p) ^ hasTreatment(?p, ?tr) ^ hasDrug(?tr, DDI) ^ hasDose(?tr, ?dose) ˚
 sqwrl:makeBag(?b, ?dose) ^ sqwrl:groupBy(?b, ?p) ˚
 sqwrl:notLeastN(?notLeast3DosesC, ?b, 3) ^ swrlb:contains(?notLeast3DosesC, ?e)
 -> sqwrl:select(?p, ?e) 

SQWRL provides aliases for the sqwrl:notFirstN, sqwrl:notLastN, and sqwrl:notNthLastSlice operators. These are sqwrl:notLeastN, sqwrl:notGreatestN, and sqwrl:notNthGreatestSlice respectively.

Comparing Collections

SQWRL provides a number of operators to compare collections. The basic operator is sqwrl:equal, which takes two collections and compares them.

Using this operator, a query to return patients that are on exactly the drugs DDI and AZT (and no other known drugs) can be written:

 Patient(?p) ^ hasTreatment(?p, ?tr) ^ hasDrug(?tr, ?d) ˚
 sqwrl:makeSet(?pds, ?d) ^ sqwrl:groupBy(?pds, ?p) ^
 sqwrl:makeSet(?ds, DDI) ^ sqwrl:makeSet(?ds, AZT) ˚
 sqwrl:equal(?pds, ?ds)
 -> sqwrl:select(?p) 

The negative sqwrl:notEqual variant is also provided.

A sqwrl:contains operator can be used to see if one collection contains all the elements of another connection.

Using this operator, the above query can be modified to return patients that are drugs DDI and AZT (and possibly other drugs) as follows:

 Patient(?p) ^ hasTreatment(?p, ?tr) ^ hasDrug(?tr, ?d) ˚
 sqwrl:makeSet(?pds, ?d) ^ sqwrl:groupBy(?pds, ?p) ^
 sqwrl:makeSet(?ds, DDI) ^ sqwrl:makeSet(?ds, AZT) ˚
 sqwrl:contains(?pds, ?ds)
 -> sqwrl:select(?p) 

Again, the negative sqwrl:notContians variant is provided.

Interoperation with Built-in Libraries

As mentioned, only built-ins can be used in SQWRL's collection operation clause. However, there is no restriction on the these built-ins. Built-ins from any SWRL library can be used.

For example, using the swrlm:eval built-in from the mathematical expressions built-in library, a query to list patients with DDI doses greater than 10% of the average DDI dose can be written:

 Patient(?p) ^ hasTreatment(?p, ?t) ^ hasDrug(?t, DDI) ^ hasDose(?t, ?d) ˚
 sqwrl:makeBag(?bpd, ?d) ^ sqwrl:groupBy(?bpd, ?p) ^ sqwrl:makeBag(?bddi, ?d) ˚
 sqwrl:avg(?avgP, ?bpd) ^ sqwrl:avg(?avgDDI, ?bddi) ^
 swrlm:eval(?r, "(avgP - avgDDI) / avgDDI * 100", ?avgP, ?avgDDI) ^
 swrlb:greaterThan(?r, 10)
 -> sqwrl:select(?p, ?avgP, ?avgDDI) 

Collection operators can also work with arguments bound by built-ins.

Negation As Failure

Queries that require negation as failure semantics can effectively be provided using collection operators.

For example, in an ontology with a class Drug and various subclasses, including the class BetaBlocker, a query to list the number of drugs that are not individuals of type beta-blocker can be written:

 Drug(?d) ^ BetaBlocker(?bbd) ˚
 sqwrl:makeSet(?s1, ?d) ^ sqwrl:makeSet(?s2, ?bbd) ˚
 sqwrl:difference(?s3, ?s1, ?s2) ^ sqwrl:size(?n, ?s3)
 -> sqwrl:select(?n)

Using this set difference approach, SQWRL can effectively provide negation as failure in queries.

Again, the results of these operators can not be used in rules so monotonicity is ensured.

Disjunction

Disjunction can be supported using the set union operator.

For example, a query to list the number of beta-blocker or anti-hypertensive drugs can be written:

 AntiHypertensive(?htnd) ^ BetaBlocker(?bbd) ˚
 sqwrl:makeSet(?s1, ?htnd) ^ sqwrl:makeSet(?s2, ?bbd) ˚
 sqwrl:union(?s3, ?s1, ?s2) ^ sqwrl:size(?n, ?s3)
 -> sqwrl:select(?n)  

This example assumes that the class AntiHypertensive is also a subclass of Drug.