Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Multiple top concepts / dct:subject of concept schemes #663

Merged
merged 5 commits into from
Dec 11, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 38 additions & 2 deletions controller/RestController.php
Original file line number Diff line number Diff line change
Expand Up @@ -734,6 +734,30 @@ public function hierarchy($request)
if (empty($results)) {
return $this->returnError('404', 'Not Found', "Could not find concept <{$request->getUri()}>");
}


// set the "top" key from the "tops" key
foreach ($results as $value) {
$uri = $value['uri'];
if (isset($value['tops'])) {
if ($request->getVocab()->getConfig()->getMainConceptSchemeURI() != null) {
foreach ($results[$uri]['tops'] as $top) {
// if a value in 'tops' matches the main concept scheme of the vocabulary, take it
if ($top == $request->getVocab()->getConfig()->getMainConceptSchemeURI()) {
$results[$uri]['top'] = $top;
break;
}
}
// if the main concept scheme was not found, set 'top' to the first 'tops' (sorted alphabetically on the URIs)
if (! isset($results[$uri]['top'])) {
$results[$uri]['top'] = $results[$uri]['tops'][0];
}
} else {
// no main concept scheme set on the vocab, take the first value of 'tops' (sorted alphabetically)
$results[$uri]['top'] = $results[$uri]['tops'][0];
}
}
}

if ($request->getVocab()->getConfig()->getShowHierarchy()) {
$schemes = $request->getVocab()->getConceptSchemes($request->getLang());
Expand All @@ -748,7 +772,7 @@ public function hierarchy($request)
$topconcepts = $request->getVocab()->getTopConcepts(array_keys($schemes), $request->getLang());
foreach ($topconcepts as $top) {
if (!isset($results[$top['uri']])) {
$results[$top['uri']] = array('uri' => $top['uri'], 'top' => $top['topConceptOf'], 'prefLabel' => $top['label'], 'hasChildren' => $top['hasChildren']);
$results[$top['uri']] = array('uri' => $top['uri'], 'top'=>$top['topConceptOf'], 'tops'=>array($top['topConceptOf']), 'prefLabel' => $top['label'], 'hasChildren' => $top['hasChildren']);
if (isset($top['notation'])) {
$results[$top['uri']]['notation'] = $top['notation'];
}
Expand All @@ -758,7 +782,19 @@ public function hierarchy($request)
}

$ret = array_merge_recursive($this->context, array(
'@context' => array('onki' => 'http://schema.onki.fi/onki#', 'prefLabel' => 'skos:prefLabel', 'notation' => 'skos:notation', 'narrower' => array('@id' => 'skos:narrower', '@type' => '@id'), 'broader' => array('@id' => 'skos:broader', '@type' => '@id'), 'broaderTransitive' => array('@id' => 'skos:broaderTransitive', '@container' => '@index'), 'top' => array('@id' => 'skos:topConceptOf', '@type' => '@id'), 'hasChildren' => 'onki:hasChildren', '@language' => $request->getLang()),
'@context' => array(
'onki' => 'http://schema.onki.fi/onki#',
'prefLabel' => 'skos:prefLabel',
'notation' => 'skos:notation',
'narrower' => array('@id' => 'skos:narrower', '@type' => '@id'),
'broader' => array('@id' => 'skos:broader', '@type' => '@id'),
'broaderTransitive' => array('@id' => 'skos:broaderTransitive', '@container' => '@index'),
'top' => array('@id' => 'skos:topConceptOf', '@type' => '@id'),
// the tops key will contain all the concept scheme values, while top (singular) contains a single value
'tops' => array('@id' => 'skos:topConceptOf', '@type' => '@id'),
'hasChildren' => 'onki:hasChildren',
'@language' => $request->getLang()
),
'uri' => $request->getUri(),
'broaderTransitive' => $results)
);
Expand Down
4 changes: 2 additions & 2 deletions model/Vocabulary.php
Original file line number Diff line number Diff line change
Expand Up @@ -217,9 +217,9 @@ public function getConceptSchemes($lang = '')

public function getDefaultConceptScheme()
{
$conceptScheme = $this->resource->get("skosmos:mainConceptScheme");
$conceptScheme = $this->config->getMainConceptSchemeURI();
if ($conceptScheme) {
return $conceptScheme->getUri();
return $conceptScheme;
}

// mainConceptScheme not explicitly set, guess it
Expand Down
18 changes: 18 additions & 0 deletions model/VocabularyConfig.php
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,24 @@ public function getDataURLs()
return $ret;
}

/**
* Returns the main Concept Scheme URI of that Vocabulary,
* or null if not set.
* @return string concept scheme URI or null
*/

public function getMainConceptSchemeURI()
{
$val = $this->resource->getResource("skosmos:mainConceptScheme");
if ($val) {
return $val->getURI();
}

return null;
}



/**
* Returns the class URI used for concept groups in this vocabulary,
* or null if not set.
Expand Down
27 changes: 22 additions & 5 deletions model/sparql/GenericSparql.php
Original file line number Diff line number Diff line change
Expand Up @@ -595,9 +595,14 @@ public function queryConceptScheme($conceptscheme) {
private function generateQueryConceptSchemesQuery($lang) {
$fcl = $this->generateFromClause();
$query = <<<EOQ
SELECT ?cs ?label ?preflabel ?title $fcl
SELECT ?cs ?label ?preflabel ?title ?domain ?domainLabel $fcl
WHERE {
?cs a skos:ConceptScheme .
OPTIONAL{
?cs dcterms:subject ?domain.
?domain skos:prefLabel ?domainLabel.
FILTER(langMatches(lang(?domainLabel), '$lang'))
}
OPTIONAL {
?cs rdfs:label ?label .
FILTER(langMatches(lang(?label), '$lang'))
Expand All @@ -612,7 +617,8 @@ private function generateQueryConceptSchemesQuery($lang) {
{ ?cs dc:title ?title }
FILTER(langMatches(lang(?title), '$lang'))
}
} ORDER BY ?cs
}
ORDER BY ?cs
EOQ;
return $query;
}
Expand All @@ -637,6 +643,11 @@ private function transformQueryConceptSchemesResults($result) {
if (isset($row->title)) {
$conceptscheme['title'] = $row->title->getValue();
}
// add dct:subject and their labels in the result
if(isset($row->domain) && isset($row->domainLabel)){
$conceptscheme['subject']['uri']=$row->domain->getURI();
$conceptscheme['subject']['prefLabel']=$row->domainLabel->getValue();
}

$ret[$row->cs->getURI()] = $conceptscheme;
}
Expand Down Expand Up @@ -1733,6 +1744,7 @@ public function queryTopConcepts($conceptSchemes, $lang, $fallback) {

/**
* Generates a sparql query for finding the hierarchy for a concept.
* A concept may be a top concept in multiple schemes, returned as a single whitespace-separated literal.
* @param string $uri concept uri.
* @param string $lang
* @param string $fallback language to use if label is not available in the preferred language
Expand All @@ -1743,7 +1755,8 @@ private function generateParentListQuery($uri, $lang, $fallback, $props) {
$propertyClause = implode('|', $props);
$query = <<<EOQ
SELECT ?broad ?parent ?member ?children ?grandchildren
(SAMPLE(?lab) as ?label) (SAMPLE(?childlab) as ?childlabel) (SAMPLE(?topcs) AS ?top) (SAMPLE(?nota) as ?notation) (SAMPLE(?childnota) as ?childnotation) $fcl
(SAMPLE(?lab) as ?label) (SAMPLE(?childlab) as ?childlabel) (GROUP_CONCAT(?topcs; separator=" ") as ?tops)
(SAMPLE(?nota) as ?notation) (SAMPLE(?childnota) as ?childnotation) $fcl
WHERE {
<$uri> a skos:Concept .
OPTIONAL {
Expand Down Expand Up @@ -1807,8 +1820,12 @@ private function transformParentListResults($result, $lang)
if (isset($row->exact)) {
$ret[$uri]['exact'] = $row->exact->getUri();
}
if (isset($row->top)) {
$ret[$uri]['top'] = $row->top->getUri();
if (isset($row->tops)) {
$topConceptsList=array();
$topConceptsList=explode(" ", $row->tops->getValue());
// sort to garantee an alphabetical ordering of the URI
sort($topConceptsList);
$ret[$uri]['tops'] = $topConceptsList;
}
if (isset($row->children)) {
if (!isset($ret[$uri]['narrower'])) {
Expand Down
45 changes: 44 additions & 1 deletion tests/GenericSparqlTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -350,6 +350,49 @@ public function testQueryConceptSchemes()
$this->assertEquals('Test conceptscheme', $label['label']);
}
}

/**
* @covers GenericSparql::queryConceptSchemes
* @covers GenericSparql::generateQueryConceptSchemesQuery
* @covers GenericSparql::transformQueryConceptSchemesResults
*/
public function testQueryConceptSchemesSubject()
{
$sparql = new GenericSparql('http://localhost:3030/ds/sparql', 'http://www.skosmos.skos/test-concept-schemes/', $this->model);

$actual = $sparql->queryConceptSchemes('en');
$expected = array(
'http://exemple.fr/domains' => array(
'prefLabel' => 'Special Domains Concept Scheme'
),
'http://exemple.fr/mt1' => array(
'prefLabel' => 'Micro-Thesaurus 1',
'subject' => array(
'uri' => 'http://exemple.fr/d1',
'prefLabel' => 'Domain 1'
)
),
'http://exemple.fr/mt2' => array(
'prefLabel' => 'Micro-Thesaurus 2',
'subject' => array(
'uri' => 'http://exemple.fr/d1',
'prefLabel' => 'Domain 1'
)
),
'http://exemple.fr/mt3' => array(
'prefLabel' => 'Micro-Thesaurus 3',
'subject' => array(
'uri' => 'http://exemple.fr/d2',
'prefLabel' => 'Domain 2'
)
),
'http://exemple.fr/thesaurus' => array(
'prefLabel' => 'The Thesaurus'
),
);

$this->assertEquals($expected, $actual);
}

/**
* @covers GenericSparql::queryConcepts
Expand Down Expand Up @@ -738,7 +781,7 @@ public function testQueryParentList()
);
$props = array (
'uri' => 'http://www.skosmos.skos/test/ta1',
'top' => 'http://www.skosmos.skos/test/conceptscheme',
'tops' => array('http://www.skosmos.skos/test/conceptscheme'),
'prefLabel' => 'Fish',
);
$narrowers = array (
Expand Down
56 changes: 56 additions & 0 deletions tests/test-vocab-data/test-concept-schemes.ttl
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
@prefix skos: <http://www.w3.org/2004/02/skos/core#>.
@prefix ex: <http://exemple.fr/>.
@prefix dcterms: <http://purl.org/dc/terms/>.

ex:thesaurus a skos:ConceptScheme ;
skos:prefLabel "The Thesaurus"@en .

ex:mt1 a skos:ConceptScheme ;
skos:prefLabel "Micro-Thesaurus 1"@en ;
dcterms:subject ex:d1 .

ex:mt2 a skos:ConceptScheme ;
skos:prefLabel "Micro-Thesaurus 2"@en ;
dcterms:subject ex:d1 .

ex:mt3 a skos:ConceptScheme ;
skos:prefLabel "Micro-Thesaurus 3"@en ;
dcterms:subject ex:d2 .

### Begin Domains

ex:domains a skos:ConceptScheme ;
skos:prefLabel "Special Domains Concept Scheme"@en .

ex:d1 a skos:Concept ;
skos:inScheme ex:domains ;
skos:topConceptOf ex:domains ;
skos:prefLabel "Domain 1"@en .

ex:d2 a skos:Concept ;
skos:inScheme ex:domains ;
skos:topConceptOf ex:domains ;
skos:prefLabel "Domain 2"@en .

#### End Domains

ex:c1 a skos:Concept ;
skos:prefLabel "Concept 1"@en ;
skos:inScheme ex:thesaurus , ex:mt1 ;
skos:topConceptOf ex:thesaurus , ex:mt1 ;
skos:narrower ex:c1.1 .

ex:c1.1 a skos:Concept ;
skos:prefLabel "Concept 1.1"@en ;
skos:inScheme ex:thesaurus , ex:mt1 ;
skos:broader ex:c1 .

ex:c2 a skos:Concept ;
skos:prefLabel "Concept 2"@en ;
skos:inScheme ex:thesaurus , ex:mt2 ;
skos:topConceptOf ex:thesaurus , ex:mt2 .

ex:c3 a skos:Concept ;
skos:prefLabel "Concept 3"@en ;
skos:inScheme ex:thesaurus , ex:mt3 ;
skos:topConceptOf ex:thesaurus , ex:mt3 .
13 changes: 13 additions & 0 deletions tests/testvocabularies.ttl
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,19 @@
skosmos:sparqlGraph <http://www.skosmos.skos/groups/> .


:test-concept-schemes a skosmos:Vocabulary, void:Dataset ;
dc11:title "Test concept schemes"@en ;
dc:subject :cat_general ;
void:dataDump <http://skosmos.skos/dump/test/test-concept-schemes.ttl>,
<http://skosmos.skos/dump/test/test-concept-schemes> ;
void:uriSpace "http://www.exemple.fr/";
skosmos:arrayClass isothes:ThesaurusArray ;
skosmos:groupClass skos:Collection ;
void:sparqlEndpoint <http://localhost:3030/ds/sparql> ;
skosmos:language "en";
skosmos:defaultLanguage "en";
skosmos:sparqlGraph <http://www.skosmos.skos/test-concept-schemes/> .

:showDeprecated a skosmos:Vocabulary, void:Dataset ;
dc11:title "Show deprecated test vocabulary"@en ;
dc:subject :cat_general ;
Expand Down