@igea/oac_backend 1.0.34 → 1.0.35
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +4 -2
- package/src/controllers/ontology.js +43 -0
- package/src/index.js +3 -0
- package/src/models/Converter +173 -0
- package/src/ontology/config.shacl.ttl +251 -0
- package/src/ontology/schema_v1.rdf +628 -0
- package/src/ontology/schema_v1.shacl.ttl +2088 -0
- package/src/ontology/schema_v1.ttl +635 -0
- package/src/tools/rdfToShacl.js +7 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@igea/oac_backend",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.35",
|
|
4
4
|
"description": "Backend service for the OAC project",
|
|
5
5
|
"main": "src/index.js",
|
|
6
6
|
"bin": {
|
|
@@ -9,6 +9,7 @@
|
|
|
9
9
|
"scripts": {
|
|
10
10
|
"start": "cross-env NODE_ENV=production node src/index.js",
|
|
11
11
|
"dev": "cross-env NODE_ENV=development nodemon src/index.js",
|
|
12
|
+
"rdf2shacl": "cross-env NODE_ENV=development node src/tools/rdfToShacl.js",
|
|
12
13
|
"test": "mocha \"test/**/*.js\""
|
|
13
14
|
},
|
|
14
15
|
"repository": {
|
|
@@ -27,11 +28,12 @@
|
|
|
27
28
|
"cookie-parser": "1.4.7",
|
|
28
29
|
"crypto": "1.0.1",
|
|
29
30
|
"express": "5.1.0",
|
|
30
|
-
"express-rate-limit": "
|
|
31
|
+
"express-rate-limit": "8.1.0",
|
|
31
32
|
"get-port": "7.1.0",
|
|
32
33
|
"knex": "3.1.0",
|
|
33
34
|
"libxmljs2": "0.37.0",
|
|
34
35
|
"multer": "2.0.2",
|
|
36
|
+
"n3": "1.26.0",
|
|
35
37
|
"nodemailer": "7.0.6",
|
|
36
38
|
"pg": "8.16.3",
|
|
37
39
|
"strip-bom": "5.0.0"
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
const express = require('express');
|
|
2
|
+
const router = express.Router();
|
|
3
|
+
const path = require('path');
|
|
4
|
+
const ONTO_FOLDER = path.join(__dirname, '..', 'ontology'); ;
|
|
5
|
+
|
|
6
|
+
router.get('/schema/:format', (req, res) => {
|
|
7
|
+
console.log(`Requesting SHACL schema in format: ${req.params.format}`);
|
|
8
|
+
let format = req.params.format || 'ttl';
|
|
9
|
+
let filePath = null;
|
|
10
|
+
switch(format){
|
|
11
|
+
case 'ttl':
|
|
12
|
+
filePath = 'config.shacl.ttl'; //'schema_v1.shacl.ttl';
|
|
13
|
+
res.setHeader('Content-Type', 'text/turtle');
|
|
14
|
+
break;
|
|
15
|
+
case 'jsonld':
|
|
16
|
+
filePath = 'schema_v1.shacl.jsonld';
|
|
17
|
+
res.setHeader('Content-Type', 'application/ld+json');
|
|
18
|
+
break;
|
|
19
|
+
case 'xml':
|
|
20
|
+
filePath = 'schema_v1.shacl.rdf';
|
|
21
|
+
res.setHeader('Content-Type', 'application/rdf+xml');
|
|
22
|
+
break;
|
|
23
|
+
default:
|
|
24
|
+
res.status(400).json({
|
|
25
|
+
success: false,
|
|
26
|
+
data: null,
|
|
27
|
+
message: `Unsupported format: ${format}`
|
|
28
|
+
});
|
|
29
|
+
return;
|
|
30
|
+
}
|
|
31
|
+
res.sendFile(path.join(ONTO_FOLDER, filePath), (err) => {
|
|
32
|
+
if (err) {
|
|
33
|
+
res.status(500).json({
|
|
34
|
+
success: false,
|
|
35
|
+
data: null,
|
|
36
|
+
message: `Error sending file: ${err}`
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
});
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
module.exports = router
|
package/src/index.js
CHANGED
|
@@ -57,6 +57,9 @@ getPort.default({
|
|
|
57
57
|
const fusekiRouter = require('./controllers/fuseki.js')
|
|
58
58
|
app.use(`/${serviceName}/fuseki`, fusekiRouter)
|
|
59
59
|
|
|
60
|
+
const ontologyRouter = require('./controllers/ontology.js')
|
|
61
|
+
app.use(`/${serviceName}/ontology`, ontologyRouter)
|
|
62
|
+
|
|
60
63
|
app.listen(port, async () => {
|
|
61
64
|
console.log(`${serviceName} listening on port ${port}`)
|
|
62
65
|
await register()
|
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
const fs = require('fs');
|
|
2
|
+
const { Parser } = require('n3');
|
|
3
|
+
const { DataFactory, Store } = require('n3');
|
|
4
|
+
|
|
5
|
+
class Converter {
|
|
6
|
+
|
|
7
|
+
// Funzione per generare messaggi di errore multilingua
|
|
8
|
+
static generateErrorMessages(propertyName, datatype, isRelation) {
|
|
9
|
+
const messages = {
|
|
10
|
+
string: {
|
|
11
|
+
it: `Il campo ${propertyName} deve essere una stringa e non può essere vuoto.`,
|
|
12
|
+
en: `The field ${propertyName} must be a string and cannot be empty.`
|
|
13
|
+
},
|
|
14
|
+
integer: {
|
|
15
|
+
it: `Il campo ${propertyName} deve essere un numero intero.`,
|
|
16
|
+
en: `The field ${propertyName} must be an integer.`
|
|
17
|
+
},
|
|
18
|
+
relation: {
|
|
19
|
+
it: `Il campo ${propertyName} deve essere un'istanza valida.`,
|
|
20
|
+
en: `The field ${propertyName} must be a valid instance.`
|
|
21
|
+
}
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
if (isRelation) {
|
|
25
|
+
return messages.relation;
|
|
26
|
+
} else if (datatype.includes('integer')) {
|
|
27
|
+
return messages.integer;
|
|
28
|
+
} else {
|
|
29
|
+
return messages.string;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// Funzione per generare lo schema SHACL da un file RDF
|
|
34
|
+
static async generateShaclFromRdf(rdfFilePath, shaclFilePath) {
|
|
35
|
+
// Leggi il file RDF
|
|
36
|
+
const rdfData = fs.readFileSync(rdfFilePath, 'utf8');
|
|
37
|
+
|
|
38
|
+
// Crea un parser N3 e un store RDF
|
|
39
|
+
const parser = new Parser();
|
|
40
|
+
const quads = parser.parse(rdfData);
|
|
41
|
+
const store = new Store(quads);
|
|
42
|
+
|
|
43
|
+
// Estrai le classi, le proprietà e i tipi di dato
|
|
44
|
+
const classes = new Set();
|
|
45
|
+
const properties = new Map(); // Mappa: URI proprietà → { datatypes: Set, targetClasses: Set }
|
|
46
|
+
const classInstances = new Map(); // Mappa: URI classe → Set di istanze
|
|
47
|
+
|
|
48
|
+
// Analizza i tripli per trovare classi, proprietà e relazioni
|
|
49
|
+
for (const quad of store.getQuads(null, null, null, null)) {
|
|
50
|
+
const { subject, predicate, object } = quad;
|
|
51
|
+
|
|
52
|
+
// Trova le classi
|
|
53
|
+
if (predicate.value === 'http://www.w3.org/1999/02/22-rdf-syntax-ns#type') {
|
|
54
|
+
classes.add(object.value);
|
|
55
|
+
if (!classInstances.has(object.value)) {
|
|
56
|
+
classInstances.set(object.value, new Set());
|
|
57
|
+
}
|
|
58
|
+
classInstances.get(object.value).add(subject.value);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// Trova proprietà e tipi di dato
|
|
62
|
+
if (object.termType === 'Literal') {
|
|
63
|
+
const propertyUri = predicate.value;
|
|
64
|
+
const datatype = object.datatype ? object.datatype.value : 'http://www.w3.org/2001/XMLSchema#string';
|
|
65
|
+
|
|
66
|
+
if (!properties.has(propertyUri)) {
|
|
67
|
+
properties.set(propertyUri, { datatypes: new Set(), targetClasses: new Set() });
|
|
68
|
+
}
|
|
69
|
+
properties.get(propertyUri).datatypes.add(datatype);
|
|
70
|
+
} else if (object.termType === 'NamedNode') {
|
|
71
|
+
const propertyUri = predicate.value;
|
|
72
|
+
|
|
73
|
+
if (!properties.has(propertyUri)) {
|
|
74
|
+
properties.set(propertyUri, { datatypes: new Set(), targetClasses: new Set() });
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// Trova la classe dell'oggetto (se esiste)
|
|
78
|
+
for (const objQuad of store.getQuads(object, DataFactory.namedNode('http://www.w3.org/1999/02/22-rdf-syntax-ns#type'), null, null)) {
|
|
79
|
+
properties.get(propertyUri).targetClasses.add(objQuad.object.value);
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
// Genera lo schema SHACL
|
|
85
|
+
let shacl = `@prefix sh: <http://www.w3.org/ns/shacl#> .
|
|
86
|
+
@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
|
|
87
|
+
@prefix xsd: <http://www.w3.org/2001/XMLSchema#> .
|
|
88
|
+
@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
|
|
89
|
+
@prefix ex: <http://example.org/ns#> .
|
|
90
|
+
|
|
91
|
+
`;
|
|
92
|
+
|
|
93
|
+
// Aggiungi una shape per ogni classe
|
|
94
|
+
classes.forEach((classUri) => {
|
|
95
|
+
const className = classUri.split('#')[1] || classUri.split('/').pop();
|
|
96
|
+
const shapeName = `ex:${className}Shape`;
|
|
97
|
+
|
|
98
|
+
shacl += `${shapeName}
|
|
99
|
+
a sh:NodeShape ;
|
|
100
|
+
sh:targetClass <${classUri}> ;
|
|
101
|
+
`;
|
|
102
|
+
|
|
103
|
+
// Aggiungi le proprietà per questa classe
|
|
104
|
+
properties.forEach((propertyInfo, propertyUri) => {
|
|
105
|
+
// Verifica se questa proprietà è usata da istanze di questa classe
|
|
106
|
+
let usedByClass = false;
|
|
107
|
+
const propertyName = propertyUri.split('#')[1] || propertyUri.split('/').pop();
|
|
108
|
+
|
|
109
|
+
// Controlla se almeno un'istanza di questa classe ha questa proprietà
|
|
110
|
+
const classInstancesSet = classInstances.get(classUri) || new Set();
|
|
111
|
+
for (const instance of classInstancesSet) {
|
|
112
|
+
for (const quad of store.getQuads(
|
|
113
|
+
DataFactory.namedNode(instance),
|
|
114
|
+
DataFactory.namedNode(propertyUri),
|
|
115
|
+
null,
|
|
116
|
+
null
|
|
117
|
+
)) {
|
|
118
|
+
usedByClass = true;
|
|
119
|
+
break;
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
if (usedByClass) {
|
|
124
|
+
shacl += ` sh:property [
|
|
125
|
+
sh:name "${propertyName}" ;
|
|
126
|
+
sh:description "${className}.${propertyName}" ;
|
|
127
|
+
sh:path <${propertyUri}> ;
|
|
128
|
+
`;
|
|
129
|
+
|
|
130
|
+
// Aggiungi vincoli per tipi di dato
|
|
131
|
+
if (propertyInfo.datatypes.size > 0) {
|
|
132
|
+
const datatype = Array.from(propertyInfo.datatypes)[0]; // Prendi il primo tipo di dato
|
|
133
|
+
shacl += ` sh:datatype <${datatype}> ;
|
|
134
|
+
`;
|
|
135
|
+
// Aggiungi messaggi di errore per il tipo di dato
|
|
136
|
+
const messages = Converter.generateErrorMessages(propertyName, datatype, false);
|
|
137
|
+
shacl += ` sh:message "${messages.it}"@it ;
|
|
138
|
+
sh:message "${messages.en}"@en ;
|
|
139
|
+
`;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
// Aggiungi vincoli per classi target (relazioni)
|
|
143
|
+
if (propertyInfo.targetClasses.size > 0) {
|
|
144
|
+
const targetClasses = Array.from(propertyInfo.targetClasses).map(c => `<${c}>`).join(' ');
|
|
145
|
+
shacl += ` sh:class [ sh:in (${targetClasses}) ] ;
|
|
146
|
+
`;
|
|
147
|
+
// Aggiungi messaggi di errore per le relazioni
|
|
148
|
+
const messages = Converter.generateErrorMessages(propertyName, '', true);
|
|
149
|
+
shacl += ` sh:message "${messages.it}"@it ;
|
|
150
|
+
sh:message "${messages.en}"@en ;
|
|
151
|
+
`;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
// Aggiungi vincoli di cardinalità
|
|
155
|
+
shacl += ` sh:minCount 1 ;
|
|
156
|
+
sh:maxCount 1 ;
|
|
157
|
+
] ;
|
|
158
|
+
`;
|
|
159
|
+
}
|
|
160
|
+
});
|
|
161
|
+
|
|
162
|
+
shacl += ` .
|
|
163
|
+
`;
|
|
164
|
+
});
|
|
165
|
+
|
|
166
|
+
// Salva lo schema SHACL in un file
|
|
167
|
+
fs.writeFileSync(shaclFilePath, shacl);
|
|
168
|
+
console.log(`Schema SHACL generato e salvato in ${shaclFilePath}`);
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
module.exports = Converter
|
|
@@ -0,0 +1,251 @@
|
|
|
1
|
+
@prefix schema: <http://schema.org/> .
|
|
2
|
+
@prefix volipi: <http://data.sparna.fr/ontologies/volipi#> .
|
|
3
|
+
@prefix owl: <http://www.w3.org/2002/07/owl#> .
|
|
4
|
+
@prefix xsd: <http://www.w3.org/2001/XMLSchema#> .
|
|
5
|
+
@prefix skosthes: <http://purl.org/iso25964/skos-thes#> .
|
|
6
|
+
@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
|
|
7
|
+
@prefix geo: <http://www.opengis.net/ont/geosparql#> .
|
|
8
|
+
@prefix qb: <http://purl.org/linked-data/cube#> .
|
|
9
|
+
@prefix dct: <http://purl.org/dc/terms/> .
|
|
10
|
+
@prefix doap: <http://usefulinc.com/ns/doap#> .
|
|
11
|
+
@prefix sh: <http://www.w3.org/ns/shacl#> .
|
|
12
|
+
@prefix dcat: <http://www.w3.org/ns/dcat#> .
|
|
13
|
+
@prefix euvoc: <http://publications.europa.eu/ontology/euvoc#> .
|
|
14
|
+
@prefix prov: <http://www.w3.org/ns/prov#> .
|
|
15
|
+
@prefix foaf: <http://xmlns.com/foaf/0.1/> .
|
|
16
|
+
@prefix adms: <http://www.w3.org/ns/adms#> .
|
|
17
|
+
@prefix org: <http://www.w3.org/ns/org#> .
|
|
18
|
+
@prefix xls2rdf: <https://xls2rdf.sparna.fr/vocabulary#> .
|
|
19
|
+
@prefix this: <https://data.example.com/ontologies/sparnatural-config/> .
|
|
20
|
+
@prefix dbpedia: <http://dbpedia.org/ontology/> .
|
|
21
|
+
@prefix odb: <http://example.com/ontology/odb#> .
|
|
22
|
+
@prefix core: <http://data.sparna.fr/ontologies/sparnatural-config-core#> .
|
|
23
|
+
@prefix datasources: <http://data.sparna.fr/ontologies/sparnatural-config-datasources#> .
|
|
24
|
+
@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
|
|
25
|
+
@prefix dash: <http://datashapes.org/dash#> .
|
|
26
|
+
@prefix dc: <http://purl.org/dc/elements/1.1/> .
|
|
27
|
+
@prefix skos: <http://www.w3.org/2004/02/skos/core#> .
|
|
28
|
+
@prefix skosxl: <http://www.w3.org/2008/05/skos-xl#> .
|
|
29
|
+
|
|
30
|
+
<https://data.mydomain.com/ontologies/sparnatural-config> a owl:Ontology .
|
|
31
|
+
|
|
32
|
+
this:Vehicle a sh:NodeShape;
|
|
33
|
+
sh:order "1"^^xsd:integer;
|
|
34
|
+
volipi:iconName "fa-solid fa-car";
|
|
35
|
+
sh:targetClass odb:Vehicle;
|
|
36
|
+
rdfs:label "Vehicle"@en, "Véhicule"@fr;
|
|
37
|
+
sh:description "A vehicle is a car model for a specific brand."@en, "Un véhicule est un modèle de voiture pour une marque spécifique."@fr;
|
|
38
|
+
sh:property this:Vehicle_VIN, this:Vehicle_hasManufacturer .
|
|
39
|
+
|
|
40
|
+
this:Museum a sh:NodeShape;
|
|
41
|
+
sh:order "1"^^xsd:integer;
|
|
42
|
+
volipi:iconName "fas fa-university";
|
|
43
|
+
sh:targetClass dbpedia:Museum;
|
|
44
|
+
rdfs:label "Museum"@en, "Musée"@fr;
|
|
45
|
+
sh:description "A DBPedia Museum"@en, "Un Musée DBPedia"@fr;
|
|
46
|
+
sh:property this:Museum_country .
|
|
47
|
+
|
|
48
|
+
this:Country a sh:NodeShape;
|
|
49
|
+
sh:order "2"^^xsd:integer;
|
|
50
|
+
volipi:iconName "fas fa-globe-africa";
|
|
51
|
+
sh:targetClass dbpedia:Country;
|
|
52
|
+
rdfs:label "Country"@en, "Pays"@fr;
|
|
53
|
+
sh:description "A DBPedia Country"@en, "Un Pays DBPedia"@fr;
|
|
54
|
+
sh:property this:Country_countryOf, this:country_label .
|
|
55
|
+
|
|
56
|
+
this:Vehicle_VIN sh:path odb:VIN;
|
|
57
|
+
sh:order "1";
|
|
58
|
+
sh:name "has VIN"@en, "a pour VIN"@fr;
|
|
59
|
+
sh:description "Specifies the Vehicle Identification Number (VIN) of the vehicle."@en,
|
|
60
|
+
"Spécifie le numéro d'identification du véhicule (VIN)."@fr;
|
|
61
|
+
sh:minCount "1"^^xsd:integer;
|
|
62
|
+
sh:maxCount "1"^^xsd:integer;
|
|
63
|
+
sh:nodeKind sh:Literal;
|
|
64
|
+
sh:datatype xsd:string;
|
|
65
|
+
dash:searchWidget core:AutocompleteProperty;
|
|
66
|
+
dash:propertyRole dash:LabelRole .
|
|
67
|
+
|
|
68
|
+
this:Vehicle_hasManufacturer sh:path odb:hasManufacturer;
|
|
69
|
+
sh:order "2";
|
|
70
|
+
sh:name "has manufacturer"@en, "a pour constructeur"@fr;
|
|
71
|
+
sh:description "Specifies the manufacturer of the vehicle."@en, "Spécifie le constructeur d'un véhicule."@fr;
|
|
72
|
+
sh:minCount "1"^^xsd:integer;
|
|
73
|
+
sh:maxCount "1"^^xsd:integer;
|
|
74
|
+
sh:nodeKind sh:IRI;
|
|
75
|
+
sh:class odb:Manufacturer;
|
|
76
|
+
dash:searchWidget core:ListProperty;
|
|
77
|
+
core:enableNegation "true"^^xsd:boolean .
|
|
78
|
+
|
|
79
|
+
this:Museum_country sh:path dbpedia:country;
|
|
80
|
+
sh:order "1";
|
|
81
|
+
sh:name "country"@en, "pays"@fr;
|
|
82
|
+
sh:description "Specifies the country where the museum is located."@en, "Spécifie le pays où se trouve le musée."@fr;
|
|
83
|
+
sh:minCount "1"^^xsd:integer;
|
|
84
|
+
sh:nodeKind sh:IRI;
|
|
85
|
+
sh:class dbpedia:Country;
|
|
86
|
+
dash:searchWidget core:ListProperty;
|
|
87
|
+
core:enableOptional "true"^^xsd:boolean;
|
|
88
|
+
core:enableNegation "true"^^xsd:boolean .
|
|
89
|
+
|
|
90
|
+
this:Country_countryOf sh:path dbpedia:countryOf;
|
|
91
|
+
sh:order "1";
|
|
92
|
+
sh:name "country of"@en, "pays de"@fr;
|
|
93
|
+
sh:description "Specifies the museums located in this country."@en, "Spécifie les musées situés dans ce pays."@fr;
|
|
94
|
+
sh:minCount "1"^^xsd:integer;
|
|
95
|
+
sh:nodeKind sh:IRI;
|
|
96
|
+
sh:class dbpedia:Museum;
|
|
97
|
+
dash:searchWidget core:AutocompleteProperty;
|
|
98
|
+
core:enableOptional "true"^^xsd:boolean;
|
|
99
|
+
core:enableNegation "true"^^xsd:boolean .
|
|
100
|
+
|
|
101
|
+
this:country_label sh:path rdfs:label;
|
|
102
|
+
sh:name "label"@en, "libellé"@fr;
|
|
103
|
+
sh:minCount "1"^^xsd:integer;
|
|
104
|
+
sh:nodeKind sh:Literal;
|
|
105
|
+
sh:datatype rdf:langString;
|
|
106
|
+
dash:propertyRole dash:LabelRole .
|
|
107
|
+
|
|
108
|
+
this:search_VIN_strstarts a datasources:SparqlDatasource;
|
|
109
|
+
datasources:queryTemplate datasources:query_search_label_strstarts;
|
|
110
|
+
datasources:labelProperty odb:VIN .
|
|
111
|
+
|
|
112
|
+
this:list_componentCode_alpha a datasources:SparqlDatasource;
|
|
113
|
+
datasources:queryString """PREFIX odb: <http://example.com/ontology/odb#>
|
|
114
|
+
SELECT DISTINCT ?uri ?label
|
|
115
|
+
WHERE {
|
|
116
|
+
?domain $type $domain .
|
|
117
|
+
?domain $property ?uri .
|
|
118
|
+
# Note how the range criteria is not used in this query
|
|
119
|
+
FILTER(isIRI(?uri))
|
|
120
|
+
?uri rdfs:label ?libelleComposant .
|
|
121
|
+
FILTER(lang(?libelleComposant) = \"\" || lang(?libelleComposant) = $lang)
|
|
122
|
+
?uri odb:componentCode ?codeComposant .
|
|
123
|
+
# Concat component code + component label
|
|
124
|
+
BIND(CONCAT(STR(?codeComposant),\" - \",STR(?libelleComposant)) AS ?label)
|
|
125
|
+
}
|
|
126
|
+
ORDER BY UCASE(?label)
|
|
127
|
+
LIMIT 500""" .
|
|
128
|
+
|
|
129
|
+
this:tree_root_Component a datasources:SparqlDatasource;
|
|
130
|
+
datasources:queryString """PREFIX odb: <http://example.com/ontology/odb#>
|
|
131
|
+
PREFIX rdfs: <http://www.w3.org/2000/01/rdf-schema#label>
|
|
132
|
+
SELECT ?uri ?label ?hasChildren (COUNT(?x) AS ?count) WHERE {
|
|
133
|
+
?uri a odb:Component .
|
|
134
|
+
# Keep only roots, that do not have any parent
|
|
135
|
+
FILTER NOT EXISTS {
|
|
136
|
+
?uri odb:parentComponent ?parent .
|
|
137
|
+
}
|
|
138
|
+
?uri rdfs:label ?libelleComposant .
|
|
139
|
+
FILTER(lang(?libelleComposant) = \"\" || lang(?libelleComposant) = $lang)
|
|
140
|
+
?uri odb:componentCode ?codeComposant .
|
|
141
|
+
# Concat component code + component label
|
|
142
|
+
BIND(CONCAT(STR(?codeComposant),\" - \",STR(?libelleComposant)) AS ?label)
|
|
143
|
+
OPTIONAL { ?uri ^odb:parentComponent ?children }
|
|
144
|
+
BIND(IF(bound(?children),true,false) AS ?hasChildren)
|
|
145
|
+
OPTIONAL {
|
|
146
|
+
?x a $domain .
|
|
147
|
+
?x $property ?uri .
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
GROUP BY ?uri ?label ?hasChildren
|
|
151
|
+
ORDER BY ?label""" .
|
|
152
|
+
|
|
153
|
+
this:tree_children_Component a datasources:SparqlDatasource;
|
|
154
|
+
datasources:queryString """PREFIX odb: <http://example.com/ontology/odb#>
|
|
155
|
+
PREFIX rdfs: <http://www.w3.org/2000/01/rdf-schema#label>
|
|
156
|
+
SELECT DISTINCT ?uri ?label ?hasChildren (COUNT(?x) AS ?count) WHERE {
|
|
157
|
+
|
|
158
|
+
$node ^odb:parentComponent ?uri .
|
|
159
|
+
|
|
160
|
+
?uri rdfs:label ?libelleComposant .
|
|
161
|
+
FILTER(lang(?libelleComposant) = \"\" || lang(?libelleComposant) = $lang)
|
|
162
|
+
?uri odb:componentCode ?codeComposant .
|
|
163
|
+
# Concat component code + component label
|
|
164
|
+
BIND(CONCAT(STR(?codeComposant),\" - \",STR(?libelleComposant)) AS ?label)
|
|
165
|
+
|
|
166
|
+
OPTIONAL { ?uri ^odb:parentComponent ?children }
|
|
167
|
+
BIND(IF(bound(?children),true,false) AS ?hasChildren)
|
|
168
|
+
|
|
169
|
+
OPTIONAL {
|
|
170
|
+
?x a $domain .
|
|
171
|
+
?x $property ?uri .
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
GROUP BY ?uri ?label ?hasChildren
|
|
175
|
+
ORDER BY ?label""" .
|
|
176
|
+
|
|
177
|
+
this:query_list_label_alpha_with_count_langfr a datasources:SPARQLQuery;
|
|
178
|
+
datasources:queryString """SELECT DISTINCT ?uri ?count (CONCAT(STR(?theLabel), ' (', STR(?count), ')') AS ?label)
|
|
179
|
+
WHERE {
|
|
180
|
+
{
|
|
181
|
+
SELECT DISTINCT ?uri (COUNT(?domain) AS ?count)
|
|
182
|
+
WHERE {
|
|
183
|
+
?domain a $domain .
|
|
184
|
+
?domain $property ?uri .
|
|
185
|
+
FILTER(isIRI(?uri))
|
|
186
|
+
# Note how the range criteria is not used in this query
|
|
187
|
+
}
|
|
188
|
+
GROUP BY ?uri
|
|
189
|
+
}
|
|
190
|
+
OPTIONAL { ?uri $labelPath ?theLabelLang . FILTER(lang(?theLabelLang) = $lang) }
|
|
191
|
+
OPTIONAL { ?uri $labelPath ?theLabelNone . FILTER(lang(?theLabelNone) = \"\") }
|
|
192
|
+
OPTIONAL { ?uri $labelPath ?theLabelFr . FILTER(lang(?theLabelFr) = \"fr\") }
|
|
193
|
+
BIND(COALESCE(?theLabelLang, ?theLabelNone, ?theLabelFr, STR(?uri)) AS ?theLabel)
|
|
194
|
+
}
|
|
195
|
+
ORDER BY UCASE(?label)
|
|
196
|
+
LIMIT 500""";
|
|
197
|
+
rdfs:comment "A query that will list entries alphabetically with number of occurrences in parenthesis by fetching first in the user language but will default to French"@en .
|
|
198
|
+
|
|
199
|
+
this:query_list_label_count_langfr a datasources:SPARQLQuery;
|
|
200
|
+
datasources:queryString """SELECT ?uri ?count (CONCAT(STR(?theLabel), ' (', STR(?count), ')') AS ?label)
|
|
201
|
+
WHERE {
|
|
202
|
+
{
|
|
203
|
+
SELECT DISTINCT ?uri (COUNT(?domain) AS ?count)
|
|
204
|
+
WHERE {
|
|
205
|
+
?domain a $domain .
|
|
206
|
+
?domain $property ?uri .
|
|
207
|
+
FILTER(isIRI(?uri))
|
|
208
|
+
# Note how the range criteria is not used in this query
|
|
209
|
+
}
|
|
210
|
+
GROUP BY ?uri
|
|
211
|
+
}
|
|
212
|
+
OPTIONAL { ?uri $labelPath ?theLabelLang . FILTER(lang(?theLabelLang) = $lang) }
|
|
213
|
+
OPTIONAL { ?uri $labelPath ?theLabelNone . FILTER(lang(?theLabelNone) = \"\") }
|
|
214
|
+
OPTIONAL { ?uri $labelPath ?theLabelFr . FILTER(lang(?theLabelFr) = \"fr\") }
|
|
215
|
+
BIND(COALESCE(?theLabelLang, ?theLabelNone, ?theLabelFr) AS ?theLabel)
|
|
216
|
+
}
|
|
217
|
+
ORDER BY DESC(?count) UCASE(?label)
|
|
218
|
+
LIMIT 500""";
|
|
219
|
+
rdfs:comment "A query that will list entries by descending number of occurrences by fetching first in the user langauge but will default to French"@en .
|
|
220
|
+
|
|
221
|
+
this:query_list_label_alpha_langfr a datasources:SPARQLQuery;
|
|
222
|
+
datasources:queryString """SELECT DISTINCT ?uri ?label
|
|
223
|
+
WHERE {
|
|
224
|
+
?domain a $domain .
|
|
225
|
+
?domain $property ?uri .
|
|
226
|
+
# Note how the range criteria is not used in this query
|
|
227
|
+
FILTER(isIRI(?uri))
|
|
228
|
+
|
|
229
|
+
OPTIONAL { ?uri $labelPath ?theLabelLang . FILTER(lang(?theLabelLang) = $lang) }
|
|
230
|
+
OPTIONAL { ?uri $labelPath ?theLabelNone . FILTER(lang(?theLabelNone) = \"\") }
|
|
231
|
+
OPTIONAL { ?uri $labelPath ?theLabelFr . FILTER(lang(?theLabelFr) = \"fr\") }
|
|
232
|
+
BIND(COALESCE(?theLabelLang, ?theLabelNone, ?theLabelFr) AS ?label)
|
|
233
|
+
}
|
|
234
|
+
ORDER BY UCASE(?label)
|
|
235
|
+
LIMIT 500""";
|
|
236
|
+
rdfs:comment "A query that will list entries alphabetically by fetching first in the user language but will default to French"@en .
|
|
237
|
+
|
|
238
|
+
this:query_search_label_contains_langfr a datasources:SPARQLQuery;
|
|
239
|
+
datasources:queryString """SELECT DISTINCT ?uri ?label
|
|
240
|
+
WHERE {
|
|
241
|
+
?domain a $domain .
|
|
242
|
+
?domain $property ?uri .
|
|
243
|
+
?uri a $range .
|
|
244
|
+
?uri $labelPath ?label .
|
|
245
|
+
FILTER(isIRI(?uri))
|
|
246
|
+
FILTER(CONTAINS(LCASE(STR(?label)), LCASE(\"$key\")))
|
|
247
|
+
FILTER(lang(?label) = '' || lang(?label) = \"fr\")
|
|
248
|
+
}
|
|
249
|
+
ORDER BY UCASE(?label)
|
|
250
|
+
LIMIT 50""";
|
|
251
|
+
rdfs:comment "A query that will search in labels using contains function, first in the user language but will default to French."@en .
|