- From: Aidan Hogan <aidhog@gmail.com>
- Date: Mon, 9 Aug 2021 02:46:04 -0400
- To: joylix <joylix@126.com>
- Cc: "public-sparql-dev@w3.org" <public-sparql-dev@w3.org>
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