Bob Dudan
Bob Dudan

Reputation: 97

Rank results of a sparql query

I have a hierarchy of skos concepts and some individuals indexed by those concepts (for example ex:Regina dct:subject ex:Pizza).

I want to find every individuals indexed by some concepts (or more specific ones). The query below retrieves every meal that is a pizza or homemade. (I'm using the concat and group_concat to easily get the concepts of the meals in one row)

select ?ind ?name (group_concat(concat(str(?concept),':',?conceptName)) as ?concepts){
  ?ind a ex:Individual.
  ?ind skos:prefLabel ?name.
  ?ind dct:subject ?concept.
  ?concept skos:prefLabel ?conceptName.
  optional{?ind dct:subject [skos:broaderTransitive* ex:Pizza].}
  optional{?ind dct:subject [skos:broaderTransitive* ex:Homemade].}
}
group by ?ind ?name

This is great, but now I would like to rank those results according to the numbers of concepts they have in common with what I want. So in my example I would like the homemade pizzas first, then only pizzas or homemade meals.
I would also like to get rid of results having none of the concepts I'm interested in.
Ideally, I would also like that meals directly indexed by the desired concept have a higher score than the one being indexed by more specific concepts.

I could postprocess the results of course, but I'm pretty sure there is a clever and efficient way to do it in sparql. Any idea ?

EDIT : Sample data as per request

@prefix : <http://www.semanticweb.org/owl/owlapi/turtle#> .
@prefix owl: <http://www.w3.org/2002/07/owl#> .
@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
@prefix xml: <http://www.w3.org/XML/1998/namespace> .
@prefix xsd: <http://www.w3.org/2001/XMLSchema#> .
@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
@prefix skos: <http://www.w3.org/2004/02/skos/core#> .
@prefix dct: <http://purl.org/dc/terms/>  .
@prefix ex: <http://example.com#> .

ex:FoodScheme a skos:ConceptScheme ;
    skos:prefLabel "The food schema"@en ;
    skos:hasTopConcept ex:Food .

ex:Food a skos:Concept ;
    skos:prefLabel "Food"@en ;
    skos:inScheme ex:FoodScheme .

    ex:Italian a skos:Concept ;
        skos:prefLabel "Italian food"@en ;
        skos:broaderTransitive ex:Food ;
        skos:inScheme ex:FoodScheme .

        ex:Pizza a skos:Concept ;
            skos:prefLabel "Pizza"@en ;
            skos:broaderTransitive ex:Italian ;
            skos:inScheme ex:FoodScheme .

        ex:Pasta a skos:Concept ;
            skos:prefLabel "Pasta"@en ;
            skos:broaderTransitive ex:italian ;
            skos:inScheme ex:FoodScheme .

    ex:Homemade a skos:Concept ;
        skos:prefLabel "Homemade food"@en ;
        skos:broaderTransitive ex:Food ;
        skos:inScheme ex:FoodScheme .

ex:Regina a ex:Meal ;
    skos:prefLabel "Regina"@en ;
    dct:subject ex:Homemade ;
    dct:subject ex:Pizza.

ex:PeppyPaneer a ex:Meal ;
    skos:prefLabel "From Domino's"@en ;
    dct:subject ex:Pizza.

ex:Carbonara a ex:Meal ;
    skos:prefLabel "Pasta a la carbonara"@en ;
    dct:subject ex:Homemade ;
    dct:subject ex:Pasta.

ex:Lasagna a ex:Meal ;
    skos:prefLabel "Lasagna"@en ;
    dct:subject ex:Italian.

Expected results for homemade pizza (Regina first, because it's homemade and a pizza, carbonara not included beacause it's neither homemade nor a pizza. The order of PeppyPaneer and Carbonara does not matter, they should have the same "score"):

ex:Regina
ex:PeppyPaneer
ex:Carbonara

Expected results for Italian (Lasagna first because it is directly indexed as Italian while the others are not):

ex:Lasagna
ex:Carbonara
ex:PeppyPaneer
ex:Regina

Upvotes: 1

Views: 363

Answers (1)

UninformedUser
UninformedUser

Reputation: 8465

Given a set of relevant subjects { ex:Pizza ex:Homemade }, we can do:

Query

PREFIX  ex: <http://example.com#> 
PREFIX  dct:  <http://purl.org/dc/terms/>
PREFIX  skos: <http://www.w3.org/2004/02/skos/core#>

SELECT  ?ind ?name (GROUP_CONCAT(DISTINCT ?conceptStr) AS ?concepts) (COUNT(DISTINCT ?cls) AS ?score)
WHERE
  { ?ind      skos:prefLabel  ?name ;
              dct:subject     ?concept .
    ?concept  skos:prefLabel  ?conceptName
    BIND(concat(str(?concept), ":", ?conceptName) AS ?conceptStr)
    OPTIONAL
      { VALUES ?cls { ex:Pizza ex:Homemade }
        ?ind dct:subject/(skos:broaderTransitive)* ?cls
      }
  }
GROUP BY ?ind ?name
HAVING ( ?score > 0 )
ORDER BY DESC(?score)

Output:

+-----------------+-------------------------+--------------------------------------------------------------------------+-------+
|       ind       |          name           |                                 concepts                                 | score |
+-----------------+-------------------------+--------------------------------------------------------------------------+-------+
|  ex:Regina      | Regina en               | http://example.com#Pizza:Pizza http://example.com#Homemade:Homemade food |     2 |
|  ex:Carbonara   | Pasta a la carbonara en | http://example.com#Pasta:Pasta http://example.com#Homemade:Homemade food |     1 |
|  ex:PeppyPaneer | From Domino's en        | http://example.com#Pizza:Pizza                                           |     1 |
+-----------------+-------------------------+--------------------------------------------------------------------------+-------+

Upvotes: 3

Related Questions