1

I will use generic terms for classes here.

I have a set of Objects, each of those has multiple Properties linking them to Subjects. Subject has a integer value associated with it through another property.

For each Object I want to retrieve its Subject with maximum value.


Retrieving a Subject with maximum value for one Object is easy, based on this: How do I make a SPARQL query to find the highest value for a property? . Since I want the Subject itself (and not only its value), I used the solution of "get all subjects, order them, LIMIT 1".

However, I cannot get this approach working for multiple Objects. I tried:

SELECT ?object ?subject ?subMaxVal
WHERE {
  ?object a :Object.

  {
    SELECT ?subject (?value as ?subMaxVal)
    WHERE {
      ?object :Predicate ?subject.
      ?subject :value ?value.  
    }
    ORDER BY DESC(?value)
    LIMIT 1
  }
}

This returns incorrect results though. It finds a value for ?subMaxVal, but then returns this value with all Objects in the dataset (even though this value is only correct for one of them).

I found a mention saying that SPARQL sub-queries are executed inner-first. I cannot wrap my head around how to form the query to perform the sub-query for each object though.

Martin Melka
  • 7,177
  • 16
  • 79
  • 138
  • You want the subject but you're not selecting the subject - that's something I don't understand. The idea is to get the max value in the sub-SELECT and then get in the outer query the subject that has the given max value – UninformedUser Dec 29 '17 at 16:35
  • You are right and this was my re-writing mistake. I corrected the question, but this query still returns wrong values. I have edited the question with more details. – Martin Melka Dec 29 '17 at 22:03

1 Answers1

4

The idea is to use a sub-SELECT first to get the maximum value for each object. Then in the outer query, this max value is used to get the subject:

Sample data

@prefix : <http://ex.org/> .
:o1 :Predicate :s1 ; a :Object . :s1 :value 1 .
:o1 :Predicate :s2 ; a :Object . :s2 :value 2 .
:o1 :Predicate :s3 ; a :Object . :s3 :value 3 .
:o2 :Predicate :s2 ; a :Object . :s2 :value 2 .
:o2 :Predicate :s3 ; a :Object . :s3 :value 3 .
:o3 :Predicate :s2 ; a :Object . :s2 :value 2 .
:o3 :Predicate :s3 ; a :Object . :s3 :value 3 .
:o3 :Predicate :s4 ; a :Object . :s4 :value 3 .

Query

PREFIX : <http://ex.org/>
SELECT ?object ?subject ?subMaxVal
WHERE {
  ?object a :Object.
  ?object :Predicate ?subject.
  ?subject :value ?subMaxVal.  

  # find the max value per object
  {
    SELECT ?object (MAX(?value) as ?subMaxVal)
    WHERE {
      ?object :Predicate ?subject.
      ?subject :value ?value.  
    }
    GROUP BY ?object
  }
}

Output

--------------------------------
| object | subject | subMaxVal |
================================
| :o2    | :s3     | 3         |
| :o3    | :s4     | 3         |
| :o3    | :s3     | 3         |
| :o1    | :s3     | 3         |
--------------------------------

As you can see, this query indeed returns multiple subjects for a single object if those subjects share the max. value. In that case, you could get a sample of those subjects:

Query

PREFIX : <http://ex.org/>
SELECT ?object (MIN(?subj) as ?subject) ?subMaxVal
WHERE {
  ?object a :Object.
  ?object :Predicate ?subj.
  ?subj :value ?subMaxVal.  

  # find the max value per object
  {
    SELECT ?object (MAX(?value) as ?subMaxVal)
    WHERE {
      ?object :Predicate ?subject.
      ?subject :value ?value.  
    }
    GROUP BY ?object
  }
}
GROUP BY ?object ?subMaxVal

Output

--------------------------------
| object | subject | subMaxVal |
================================
| :o1    | :s3     | 3         |
| :o3    | :s3     | 3         |
| :o2    | :s3     | 3         |
--------------------------------
UninformedUser
  • 8,397
  • 1
  • 14
  • 23