OWL Best Practices: Implementing N-ary Relations
In a previous post I demonstrated how to use enumerated classes to implement ordered values. In that example there were rules to test whether a customer listened to a specific artist more than sometimes and if so the result was the rule would suggest additional music by that artist to the customer. In that version of the ontology we kept track of how often a customer listened to a specific artist with a binary relation specific to that artist. So for example, we had relations such as listensToREM, listensToBritney, and listenstoMahler.
That design required us to write a rule for every artist we wanted to test. A better design would be to make a ternary relation that tracks each of a customer's listening preferences. E.g., triples such as <Kenneth, REM, Frequently>. The bad news is that OWL does not support ternary relations. The good news is that it is easy to use a design pattern to implement ternary (or n-ary) relations by creating additional classes. In this example, we can create a class called a Preference with object properties isPreferenceFor (maps a preference to the customer that it is a preference for) and preferenceForArtist (maps a preference to the artist it is a preference for). In this way we no longer need a rule for each artist, rather we can just create one rule that will work for all artists. This makes our ontology much more maintainable because when we add new data we don't need to change our rules.
Rather than rules such as:
listensToREM(?p, ?f) ^ isMoreFrequentThan(?f, Sometimes) -> suggestMusicBy(?p, REM) and
listensToMahler(?p, ?f) ^ isMoreFrequentThan(?f, Sometimes) -> suggestMusicBy(?p, Mahler)
we now only need one rule:
preferenceForArtist(?pr, ?a) ^ hasPreference(?p, ?pr) ^ preferenceFrequency(?pr, ?f) ^ isMoreFrequentThan(?f, Sometimes) -> suggestArtist(?p, ?a)