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

Oops! The name of the function is simply "bound", not "isBound".

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(
     (!bound(?minInc) || (?score >= ?minInc)) &&
     (!bound(?minExc) || (?score > ?minExc)) &&
     (!bound(?maxInc) || (?score <= ?maxInc)) &&
     (!bound(?maxExc) || (?score < ?maxExc))
    )
}

This time I tested it. :)

Regarding other ways to specify the rule, there have been quite a lot of 
proposals for this on the Semantic Web:

Notation3: https://www.w3.org/TeamSubmission/n3/
SWRL: http://www.daml.org/rules/proposal/
SPIN: https://spinrdf.org/
RIF: https://www.w3.org/TR/rif-overview/
SHACL Rules (as you saw)

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 {
    ?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 06:47:19 UTC