Re: How to store rule-related data in RDF and query with Sparql?

Excellent suggestions from Aidan!  One minor refinement I'd suggest . . .

On 8/9/21 2:46 AM, Aidan Hogan wrote:
> . . . 
> As well as SPARQL. If you wanted to turn the query into a rule that 
> infers new RDF data, you could use construct:
> 
> PREFIX : <http://example.org/#>
> CONSTRUCT {

Instead of CONSTRUCT, I generally prefer to use "INSERT ... WHERE ..." 
for this same purpose, because it allows you to directly insert the new 
triples into your SPARQL server without the extra round trip of sending 
them back to the client first.  I have often used SPARQL INSERT this way 
as a rules language.  But I generally debug those rules first using 
CONSTRUCT.

Hope that helps,
David Booth


>     ?test :grade ?grade .
> }
> WHERE {
>     ?student :tookTest ?test .
>     ?test :score ?score .
>     ?range a :range .
>     ?range :grade ?grade .
>     { ?range :maxInclusive ?maxInc }
>     UNION
>     { ?range :maxExclusive ?maxExc }
>     { ?range :minInclusive ?minInc }
>     UNION
>     { ?range :minExclusive ?minExc }
>     FILTER(
>      (!bound(?minInc) || (?score >= ?minInc)) &&
>      (!bound(?minExc) || (?score > ?minExc)) &&
>      (!bound(?maxInc) || (?score <= ?maxInc)) &&
>      (!bound(?maxExc) || (?score < ?maxExc))
>     )
> }
> 
> You could also define similar logic using ontologies rather than rules. 
> OWL 2 has datatype facets that could be used (with some additional 
> definitions) to infer the grades for your example:
> 
> https://www.w3.org/TR/owl2-quick-reference/#Built-in_Datatypes_and_Facets
> 
> Best,
> Aidan
> 
> On 2021-08-08 21:14, joylix wrote:
>> Hi, Aidan,  The first Sparql statement about integer types works 
>> perfectly!  It's amazing.
>> The operation on the decimal query appears to have failed:
>> /          ---------------------------------------------------/
>>
>>     /@base <http://example.org/> ./
>>     /@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> ./
>>     /@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> ./
>>     /@prefix : <http://example.org/#> ./
>>     /:range1 a :range;/
>>     /          :grade "A";/
>>     /          :minInclusive 90.0;/
>>     /          :maxInclusive 100.0 ./
>>     /:range2 a :range;/
>>     /          :grade "B";/
>>     /          :minInclusive 75.0;/
>>     /          :maxExclusive 90.0 ./
>>     /:range3 a :range;/
>>     /          :grade "C";/
>>     /          :minInclusive 60.0;/
>>     /          :maxExclusive 75.0 ./
>>
>>     /:range4 a :range;/
>>     /          :grade "D";/
>>     /          :minInclusive 0;/
>>     /          :maxExclusive 60.0 ./
>>     /:Bob a :Student;/
>>     /     :tookTest :Test0,:Test1,:Test2, :Test3 ./
>>
>>     /:Test0 :score 91.2 ./
>>     /:Test1 :score 75.5 ./
>>     /:Test2 :score 73.2 ./
>>     /:Test3 :score 59.5 ./
>>     /---------------------------------------------------/
>>     /
>>     /
>>
>> But it gave me a clear direction and a great idea.
>> I'm also trying to do this with shACL, which might be simpler.  But 
>> I'm not familiar with SH :rule yet, I don't know if that's possible:
>> https://w3c.github.io/shacl/shacl-af/#rules 
>> <https://w3c.github.io/shacl/shacl-af/#rules>
>> Another approach I think might be useful:
>> https://henrietteharmse.com/category/inferencing/ 
>> <https://henrietteharmse.com/category/inferencing/>
>>
>> Thank you for your prompt reply and kind help.
>>
>> Kind regards,
>> Joylix
>>
>>
>>
>>
>>
>>
>>
>>
>> At 2021-08-09 08:39:38, "Aidan Hogan" <aidhog@gmail.com> wrote:
>>> Hi Joylix,
>>>
>>> I think that the solution gets a bit more difficult, but if you are 
>>> working with integers, you could try to query the values like this:
>>>
>>> PREFIX : <http://example.org/#>
>>> SELECT ?student ?test ?grade
>>> WHERE {
>>>   ?student :tookTest ?test .
>>>   ?test :score ?score .
>>>   ?range a :range .
>>>   ?range :grade ?grade .
>>>   { ?range :maxInclusive ?max }
>>>   UNION
>>>   { ?range :maxExclusive ?maxExc . BIND(?maxExc - 1 AS ?max) }
>>>   { ?range :minInclusive ?min }
>>>   UNION
>>>   { ?range :minExclusive ?minExc . BIND(?minExc + 1 AS ?min) }
>>>   FILTER(?score >= ?min && ?score <= ?max)
>>> }
>>>
>>> Another option (that would work also for non-integer values) would be 
>>> to expand the FILTER.
>>>
>>> PREFIX : <http://example.org/#>
>>> SELECT ?student ?test ?grade
>>> WHERE {
>>>   ?student :tookTest ?test .
>>>   ?test :score ?score .
>>>   ?range a :range .
>>>   ?range :grade ?grade .
>>>   { ?range :maxInclusive ?maxInc }
>>>   UNION
>>>   { ?range :maxExclusive ?maxExc }
>>>   { ?range :minInclusive ?minInc }
>>>   UNION
>>>   { ?range :minExclusive ?minExc }
>>>   FILTER(
>>>    (!isBound(?minInc) || (?score >= ?minInc)) &&
>>>    (!isBound(?minExc) || (?score > ?minExc)) &&
>>>    (!isBound(?maxInc) || (?score <= ?maxInc)) &&
>>>    (!isBound(?maxExc) || (?score < ?maxExc))
>>>   )
>>> }
>>>
>>> Best,
>>> Aidan
>>>
>>> On 2021-08-08 11:42, joylix wrote:
>>>> Dear Aidan, Thank you very much for the solution.
>>>> Also, in this case, I still don't know how to deal with the limit of 
>>>> boundary.
>>>> If I need to consider the inclusive and exclusive values of limit as:
>>>> :range1 a :range ;
>>>>           :grade "A" ;
>>>>           :minInclusive 90 ;
>>>>           :maxInclusive 100.
>>>> :range2 a :range ;
>>>>           :grade "B" ;
>>>>           :minInclusive 75 ;
>>>>           :maxExclusive 90 .
>>>> :range3 a :range ;
>>>>           :grade "C" ;
>>>>           :minInclusive 60 ;
>>>>           :maxExclusive 75 .
>>>> :range4 a :range ;
>>>>           :grade "D" ;
>>>>           :minInclusive 0 ;
>>>>           :maxExclusive 60 .
>>>> How can I write logical operators in A SPARQL statement based on 
>>>> different predicates (inclusive or exclusive)?
>>>> FILTER(?score >= ?lower && ?score <= ?upper)
>>>> So this part of the SPARQ statement ">=" is sometimes ">",  "<="  
>>>> can also be  "<". Is there an easy way to deal with this situation?
>>>> I thought it might be easy to implement this example with shacl, but 
>>>> I didn't know how.
>>>> Thanks again for your reply and help.
>>>>
>>>> Kind regards,
>>>> Joylix
>>>>
>>>>
>>>>
>>>>
>>>>
>>>> At 2021-08-07 06:11:22, "Aidan Hogan" <aidhog@gmail.com> wrote:
>>>>> Hi Joylix,
>>>>>
>>>>> You could try something like:
>>>>>
>>>>> PREFIX : <http://example.org/#>
>>>>> SELECT ?student ?test ?grade
>>>>> WHERE {
>>>>>   ?student :tookTest ?test .
>>>>>   ?test :score ?score .
>>>>>   ?range a :range .
>>>>>   ?range :grade ?grade .
>>>>>   ?range :lowerLimit ?lower .
>>>>>   ?range :upperLimit ?upper .
>>>>>   FILTER(?score >= ?lower && ?score <= ?upper)
>>>>> }
>>>>>
>>>>> Not tested, but I think this should work.
>>>>>
>>>>> Best,
>>>>> Aidan
>>>>>
>>>>> P.S., if you want to express rules over RDF (in RDF) and like to 
>>>>> use SPARQL for that, you might be interested in SPIN:
>>>>>
>>>>> https://www.w3.org/Submission/spin-sparql/
>>>>>
>>>>>
>>>>> On 2021-08-06 4:23, joylix wrote:
>>>>>> Dear all, I have some data on students' test scores as follows:
>>>>>>
>>>>>>
>>>>>>     /@prefix : <http://example.org/#> ./
>>>>>>
>>>>>>     /:Bob a :Student;/
>>>>>>
>>>>>>     /     :tookTest :Test0,:Test1,:Test2 ,:Test3 ./
>>>>>>
>>>>>>     /:Test0 :score 90 ./
>>>>>>
>>>>>>     /:Test1 :score 81 ./
>>>>>>
>>>>>>     /:Test2 :score 62 ./
>>>>>>
>>>>>>     /:Test3 :score 32 ./
>>>>>>
>>>>>> The rules for grading based on scores are as follows:
>>>>>>
>>>>>>      [90-100]  --> A
>>>>>>
>>>>>>      [75-90)   --> B
>>>>>>
>>>>>>      [60-75)   --> C
>>>>>>
>>>>>>      [0-60)    --> D
>>>>>>
>>>>>> So I used the following SPARQL query to get the grade of the 
>>>>>> student's Test:
>>>>>>
>>>>>>
>>>>>>     /prefix : <http://example.org/#>/
>>>>>>
>>>>>>     /select ?student ?test ?grade/
>>>>>>
>>>>>>     /    WHERE { /
>>>>>>
>>>>>>     /      ?student :tookTest ?test ./
>>>>>>
>>>>>>     /      ?test :score ?score ./
>>>>>>
>>>>>>     /      BIND ( IF ( ?score >= 90 , "A", IF ( ?score>=75, "B", IF (
>>>>>>     ?score>=60, "C", "D" ) ) ) AS ?grade )/
>>>>>>
>>>>>>     /      }/
>>>>>>
>>>>>> And I got the right result as such:
>>>>>>
>>>>>>
>>>>>>     student,                  test,                       grade
>>>>>>
>>>>>>     http://example.org/#Bob,  http://example.org/#Test3,  D
>>>>>>
>>>>>>     http://example.org/#Bob,  http://example.org/#Test2,  C
>>>>>>
>>>>>>     http://example.org/#Bob,  http://example.org/#Test1,  B
>>>>>>
>>>>>>     http://example.org/#Bob,  http://example.org/#Test0,  A
>>>>>>
>>>>>>      Now my question is how to store this rule data in RDF (along 
>>>>>> with the score) rather than in SPARQL, so that the rule data can 
>>>>>> be defined and modified by the user, The server side reads the 
>>>>>> rule data to generate the appropriate SPARQL query.
>>>>>>
>>>>>> My initial thoughts are as follows:
>>>>>>
>>>>>>
>>>>>>     / :range1 a :range ;/
>>>>>>
>>>>>>     /         :grade "A" ;/
>>>>>>
>>>>>>     /         :lowerLimit 90 ;/
>>>>>>
>>>>>>     /         :upperLimit 100./
>>>>>>
>>>>>>     /:range2 a :range ;/
>>>>>>
>>>>>>     /         :grade "B" ;/
>>>>>>
>>>>>>     /         :lowerLimit 75 ;/
>>>>>>
>>>>>>     /         :upperLimit 89 ./
>>>>>>
>>>>>>     /:range3 a :range ;/
>>>>>>
>>>>>>     /         :grade "C" ;/
>>>>>>
>>>>>>     /         :lowerLimit 60 ;/
>>>>>>
>>>>>>     /         :upperLimit 74 ./
>>>>>>
>>>>>>     /:range4 a :range ;/
>>>>>>
>>>>>>     /         :grade "D" ;/
>>>>>>
>>>>>>     /         :lowerLimit 0 ;/
>>>>>>
>>>>>>     /         :upperLimit 59 ./
>>>>>>
>>>>>>     /:rule1 :hasRange :range1,:range2, :range3, :range4 ./
>>>>>>
>>>>>>
>>>>>> But how do I now write the right SPARQL to read the rule and 
>>>>>> complete the grade transformation?
>>>>>>
>>>>>> And how to handle with inclusive and exclusive of the boundary of 
>>>>>> limit?
>>>>>> Thank you for any suggestion.
>>>>>>
>>>>>> Kind regards,
>>>>>> Joylix
>>>>>>
>>>>>>
>>>>>>
>>>>>>
>>>>
>>>>
>>>>
>>
>>
>>
> 

Received on Monday, 9 August 2021 12:16:21 UTC