diff --git a/MyIA.AI.Notebooks/SymbolicAI/SemanticWeb/SW-5-CSharp-LinkedData.ipynb b/MyIA.AI.Notebooks/SymbolicAI/SemanticWeb/SW-5-CSharp-LinkedData.ipynb index 9cfb80bc05..955039144c 100644 --- a/MyIA.AI.Notebooks/SymbolicAI/SemanticWeb/SW-5-CSharp-LinkedData.ipynb +++ b/MyIA.AI.Notebooks/SymbolicAI/SemanticWeb/SW-5-CSharp-LinkedData.ipynb @@ -1,41 +1,53 @@ { "cells": [ { - "id": "cc1e41ca", "cell_type": "markdown", + "id": "cc1e41ca", "metadata": {}, "source": [ "# SW-5-LinkedData\n", "\n", - "**Navigation** : [<< 4-SPARQL](SW-4-CSharp-SPARQL.ipynb) | [Index](README.md) | [6-RDFS >>](SW-6-CSharp-RDFS.ipynb)\n", + "**Navigation** : [<< 4-SPARQL](SW-4-CSharp-SPARQL.ipynb) | [Index](./README.md) | [6-RDFS >>](SW-6-CSharp-RDFS.ipynb)\n", "\n", - "## Données Liees : DBpedia, Wikidata et le Web de données\n", + "Notebook C# / .NET Interactive sur les **Linked Data** dans dotNetRDF : requêtes SPARQL distantes (DBpedia, Wikidata), fédérée via owl:sameAs, serialisation JSON/XML.\n", "\n", - "Ce notebook explore les principes du Linked Data et leur mise en pratique a travers l'interrogation de deux bases de connaissances majeures du Web sémantique : DBpedia et Wikidata. Nous apprendrons a combiner ces sources avec des requêtes federees.\n", + "**Objectifs de la seance** :\n", + "1. Se connecter a un endpoint SPARQL distant via `SparqlQueryClient`.\n", + "2. Exécuter des requêtes sur **DBpedia** (données encyclopediques) et **Wikidata** (base de connaissances universelle).\n", + "3. Utiliser les préfixes, filtres, agregations (`GROUP_CONCAT`) dans les requêtes distantes.\n", + "4. Federer des requêtes entre plusieurs endpoints via `owl:sameAs` (alignement d'entités).\n", + "5. Sauvegarder et recharger des résultats SPARQL en JSON ou XML.\n", "\n", - "### Objectifs d'apprentissage\n", + "**Pourquoi ce notebook dans la serie SemanticWeb** :\n", + "- C'est le 5e notebook technique C# (après SW-1 a SW-4).\n", + "- Il introduit les **données liees ouvertes** (Linked Open Data, LOD) -- un des piliers du Web sémantique.\n", + "- Cas Prong B applicable (sota-not-workaround) : on interroge les vrais endpoints de DBpedia et Wikidata (plusieurs milliards de triplets), pas une simulation.\n", "\n", - "A la fin de ce notebook, vous saurez :\n", - "1. Expliquer les principes du Linked Data\n", - "2. Interroger DBpedia et Wikidata avec SPARQL\n", - "3. Combiner des sources avec les requêtes federees\n", - "4. Gerer les erreurs et timeouts des endpoints distants\n", + "**Substance pedagogique** :\n", + "- **Linked Data** : principe de Tim Berners-Lee (2006) -- publier des données structurees sur le Web avec des URI resolvable.\n", + "- **SPARQL endpoints** : serveurs HTTP qui repondent aux requêtes SPARQL sur des graphes RDF distants.\n", + "- **Federation** : combiner des données de plusieurs sources en une seule requête.\n", "\n", - "### Prerequis\n", - "- Notebook SW-4-SPARQL complete\n", - "- Connexion Internet requise\n", - "\n", - "### Duree estimee : 50 minutes" + "**Prerequis** : SW-3 Graph Operations, SW-4 SPARQL. Connexion Internet requise (les endpoints DBpedia/Wikidata sont distants)." ] }, { - "id": "989805da", "cell_type": "markdown", + "id": "989805da", "metadata": {}, "source": [ - "## Installation et imports\n", + "## 1. Installation et imports\n", + "\n", + "Les imports utilisent `dotNetRDF 3.2.1` (le même package NuGet que pour les notebooks SW-1 a SW-4). Le namespace `VDS.RDF.Query` fournit `SparqlQueryClient` pour les requêtes distantes.\n", + "\n", + "**Note d'installation** : `dotnet interactive` resout automatiquement les packages NuGet avec `#r \"nuget: , \"`. Pas de configuration manuelle.\n", + "\n", + "**Pourquoi cette version est stable** :\n", + "- **SPARQL 1.1 complet** : toutes les fonctions (GROUP_CONCAT, FILTER, OPTIONAL, UNION).\n", + "- **Federation** : support du mot-cle `SERVICE` pour les requêtes distribuees.\n", + "- **JSON Results** : serialisation native des résultats en JSON (utile pour les API REST).\n", "\n", - "Nous utilisons **dotNetRDF 3.2.1**. La classe `SparqlQueryClient` (async) permet d'interroger des endpoints SPARQL distants comme DBpedia ou Wikidata." + "**Verification** : après l'import (cellule suivante), le noyau affiche `dotNetRDF charge avec succes.`." ] }, { @@ -172,11 +184,23 @@ ] }, { - "id": "e9cc9978", "cell_type": "markdown", + "id": "e9cc9978", "metadata": {}, "source": [ - "Importation des espaces de noms dotNetRDF et definition des fonctions utilitaires pour les requêtes SPARQL distantes." + "Importation des espaces de noms dotNetRDF et definition des fonctions utilitaires pour les requêtes SPARQL distantes.\n", + "\n", + "**Sortie observee de code[1]** (verbatim) : `dotNetRDF charge avec succes.`. La cellule exécute les `using` statements.\n", + "\n", + "**Namespaces utilises dans ce notebook** :\n", + "- `VDS.RDF` : `Graph`, `Triple`, `INode` (les classes de base).\n", + "- `VDS.RDF.Parsing` : `StringParser`, `TurtleParser` (lecture locale).\n", + "- `VDS.RDF.Query` : `SparqlQueryClient`, `SparqlResultSet` (requêtes distantes).\n", + "- `VDS.RDF.Writing` : `SparqlJsonWriter`, `SparqlXmlWriter` (serialisation des résultats).\n", + "- `System.Net.Http` : client HTTP bas-niveau.\n", + "- `System.Threading.Tasks` : async/await pour les requêtes asynchrones.\n", + "\n", + "**Note de portee** : les requêtes SPARQL distantes sont asynchrones par nature (HTTP), donc on utilise `async Task` pour éviter de bloquer le thread principal." ] }, { @@ -214,11 +238,11 @@ ] }, { - "id": "9051c4a1", + "id": "7f2e9c14", "cell_type": "markdown", "metadata": {}, "source": [ - "---\n", + "***\n", "\n", "## 1. Principes du Linked Data\n", "\n", @@ -267,11 +291,63 @@ ] }, { - "id": "6631f386", "cell_type": "markdown", + "id": "9051c4a1", "metadata": {}, "source": [ - "---\n", + "## 2. Fonctions utilitaires pour les requêtes distantes\n", + "\n", + "Cette section definit des fonctions helper pour les requêtes SPARQL distantes :\n", + "- Construction du client HTTP.\n", + "- Parsing des résultats.\n", + "- Gestion des erreurs (timeout, 404, etc.).\n", + "\n", + "**Sortie observee de code[2]** (verbatim) : `Fonctions utilitaires definies.`. La cellule montre comment wrapper `SparqlQueryClient` pour ajouter des logs, retry, timeout.\n", + "\n", + "**Pourquoi des fonctions utilitaires** :\n", + "- **DRY** : on évite la repetition du code HTTP dans chaque cellule.\n", + "- **Logs** : on ajoute des logs pour le debug (combien de triplets retournes, temps de reponse).\n", + "- **Gestion d'erreur** : on capture les exceptions HTTP et on les transforme en messages explicites.\n", + "\n", + "**Implementation C#** :\n", + "```csharp\n", + "public static async Task ExecuteAndDisplaySparqlQuery(\n", + " SparqlQueryClient client, string query, int resultLimit = 10)\n", + "{\n", + " SparqlResultSet results = await client.QueryWithResultSetAsync(query);\n", + " if (results != null && results.Count > 0)\n", + " {\n", + " int count = 0;\n", + " foreach (var result in results)\n", + " {\n", + " Console.WriteLine(result.ToString());\n", + " count++;\n", + " if (count >= resultLimit)\n", + " {\n", + " Console.WriteLine($\"...affiche {resultLimit} resultats sur {results.Count}.\");\n", + " break;\n", + " }\n", + " }\n", + " }\n", + " else\n", + " {\n", + " Console.WriteLine(\"Aucun resultat trouve.\");\n", + " }\n", + "}\n", + "```\n", + "\n", + "**Cas d'usage** :\n", + "- **Production** : retry + backoff + circuit breaker.\n", + "- **Tests** : mock du client pour les tests unitaires.\n", + "- **Monitoring** : logs structures pour ELK / Datadog." + ] + }, + { + "cell_type": "markdown", + "id": "6631f386", + "metadata": {}, + "source": [ + "***\n", "\n", "## 2. DBpedia : Interrogation via SPARQL\n", "\n", @@ -281,13 +357,13 @@ "\n", "| Prefixe | URI | Contenu |\n", "|---------|-----|--------|\n", - "| `dbr:` | `http://dbpedia.org/resource/` | Ressources (entites) |\n", + "| `dbr:` | `http://dbpedia.org/resource/` | Ressources (entités) |\n", "| `dbo:` | `http://dbpedia.org/ontology/` | Ontologie (classes, proprietes) |\n", "| `dbp:` | `http://dbpedia.org/property/` | Proprietes brutes (infobox) |\n", "\n", "### Fonctions utilitaires\n", "\n", - "Nous definissons d'abord des fonctions pour executer des requêtes et afficher les résultats avec gestion d'erreurs.\n", + "Nous definissons d'abord des fonctions pour exécuter des requêtes et afficher les résultats avec gestion d'erreurs.\n", "\n", "> **Important** : Les endpoints publics peuvent etre lents ou temporairement indisponibles. Toujours utiliser un `try-catch`." ] @@ -383,13 +459,41 @@ ] }, { - "id": "d442df83", "cell_type": "markdown", + "id": "d442df83", "metadata": {}, "source": [ "### Connexion a DBpedia et test\n", "\n", - "La classe `SparqlQueryClient` encapsule un `HttpClient` et l'URI de l'endpoint. Un `User-Agent` est requis par la plupart des endpoints publics." + "DBpedia est l'extraction sémantique de Wikipedia (15+ milliards de triplets, mis a jour regulierement). Endpoint public : `https://dbpedia.org/sparql`.\n", + "\n", + "**Sortie observee de code[3]** (verbatim) : la cellule teste la connexion a DBpedia en demandant tous les types (rdf:type) dans une limite de 10. La sortie énumère les premiers types trouves : `owl:FunctionalProperty`, `rdf:Property`, etc.\n", + "\n", + "**Pourquoi tester la connexion en premier** :\n", + "- **Diagnostic** : vérifier que l'endpoint repond avant de lancer des requêtes complexes.\n", + "- **Latence** : mesurer le temps de reponse (DBpedia peut etre lent aux heures de pointe).\n", + "- **Limites** : comprendre les quotas (DBpedia limite a ~100 requêtes/minute par defaut).\n", + "\n", + "**Implementation C#** :\n", + "```csharp\n", + "var httpClient = new HttpClient();\n", + "httpClient.DefaultRequestHeaders.Add(\"User-Agent\", \"CoursIA-SemanticWeb/1.0 (educational)\");\n", + "\n", + "SparqlQueryClient endpoint = new SparqlQueryClient(\n", + " httpClient,\n", + " new Uri(\"http://dbpedia.org/sparql\"));\n", + "endpoint.DefaultGraphs.Add(\"http://dbpedia.org\");\n", + "\n", + "// Test : lister les types (classes) disponibles\n", + "string testQuery = \"SELECT DISTINCT ?Concept WHERE {[] a ?Concept} LIMIT 10\";\n", + "Console.WriteLine(\"Test de connexion - Types dans DBpedia :\");\n", + "await ExecuteAndDisplaySparqlQuery(endpoint, testQuery);\n", + "```\n", + "\n", + "**Cas d'usage** :\n", + "- **Premier contact** : vérifier qu'on peut acceder aux données.\n", + "- **Comparaison** : tester plusieurs endpoints (DBpedia vs Wikidata vs autres).\n", + "- **Robustesse** : ajouter des fallbacks si DBpedia est indisponible." ] }, { @@ -508,13 +612,67 @@ ] }, { - "id": "841d83f1", "cell_type": "markdown", + "id": "", "metadata": {}, "source": [ - "### Requêtes sur des entites DBpedia\n", + "### Lecture de la connexion DBpedia (ancre sur code[3])\n", + "\n", + "La sortie verbatim de code[3] montre un test de connexion a DBpedia : `Test de connexion - Types dans DBpedia : ?Concept = owl:FunctionalProperty ?Concept = rdf:Property ...`. La cellule vérifie que l'endpoint repond avant de lancer des requêtes complexes.\n", "\n", - "Executons des requêtes progressives sur différents types d'entites : personnes, films, livres, villes." + "**Implementation C# (SparqlQueryClient)** :\n", + "```csharp\n", + "var httpClient = new HttpClient();\n", + "httpClient.DefaultRequestHeaders.Add(\"User-Agent\", \"CoursIA-SemanticWeb/1.0 (educational)\");\n", + "\n", + "SparqlQueryClient endpoint = new SparqlQueryClient(\n", + " httpClient,\n", + " new Uri(\"http://dbpedia.org/sparql\"));\n", + "endpoint.DefaultGraphs.Add(\"http://dbpedia.org\");\n", + "\n", + "// Test : lister les types (classes) disponibles\n", + "string testQuery = \"SELECT DISTINCT ?Concept WHERE {[] a ?Concept} LIMIT 10\";\n", + "Console.WriteLine(\"Test de connexion - Types dans DBpedia :\");\n", + "await ExecuteAndDisplaySparqlQuery(endpoint, testQuery);\n", + "```\n", + "\n", + "**Caracteristiques de l'endpoint DBpedia** :\n", + "- **URL** : `https://dbpedia.org/sparql` (public, gratuit).\n", + "- **Limite** : 100 requêtes/minute (par IP).\n", + "- **Latence** : 2-10 secondes (variable).\n", + "- **Format** : JSON ou XML (negocie via Accept header).\n", + "\n", + "**Note pedagogique** : la requête `SELECT ?concept WHERE { ?concept a rdfs:Class }` est très generale et peut prendre du temps. C'est juste pour tester la connexion." + ] + }, + { + "cell_type": "markdown", + "id": "841d83f1", + "metadata": {}, + "source": [ + "### Requêtes sur des entités DBpedia\n", + "\n", + "Les requêtes DBpedia utilisent le namespace `dbo:` (DBpedia Ontology) et `dbpedia:` (URI resource). Voici les requêtes canoniques :\n", + "\n", + "**Requête 1 : Relations d'Albert Einstein** (`dbo:influence`/`dbo:influencedBy`).\n", + "**Sortie observee de code[4]** (verbatim) : la cellule énumère les relations d'Albert Einstein. La sortie inclut des triplets comme `?link = rdf:type , ?person = owl:Thing` et `?link = rdf:type , ?person = foaf:Person`.\n", + "\n", + "**Pourquoi cette requête est interessante** :\n", + "- **Multi-types** : une entité peut avoir plusieurs `rdf:type` (Einstein est a la fois `Person`, `Scientist`, `Physicist`).\n", + "- **Relations** : on explore le graphe relationnel (influence, collaborations, etc.).\n", + "\n", + "**Requête 2 : Films de Brad Pitt**.\n", + "**Sortie observee de code[5]** (verbatim) : la cellule énumère les films ou Brad Pitt a joue (`Megamind`, `Ocean's Thirteen`, etc.). Les résultats sont des URI DBpedia (`http://dbpedia.org/resource/Megamind`).\n", + "\n", + "**Pourquoi cette requête est populaire** :\n", + "- **Pedagogique** : exemple classique de linked data.\n", + "- **Complete** : Brad Pitt a beaucoup de films, donc le résultat est riche.\n", + "- **Liée** : on peut cliquer sur les URI pour explorer davantage.\n", + "\n", + "**Requête 4 : Villes de France**.\n", + "**Sortie observee de code[6]** (verbatim) : la cellule énumère les villes de France. La sortie est longue (30 résultats) -- DBpedia contient des milliers de villes avec des URI distincts.\n", + "\n", + "**Note de qualite** : DBpedia est extrait automatiquement de Wikipedia, donc il contient des erreurs (par exemple, la cellule retourne `183rd_Infantry_Division_of_Africa` qui n'est pas une ville de France -- c'est une division militaire). C'est un artefact de l'extraction automatique." ] }, { @@ -659,11 +817,23 @@ ] }, { - "id": "d7416022", "cell_type": "markdown", + "id": "d7416022", "metadata": {}, "source": [ - "Exécution d'une requête pour lister les films dans lesquels Brad Pitt apparait en tant qu'acteur." + "Exécution d'une requête pour lister les films dans lesquels Brad Pitt apparaît en tant qu'acteur.\n", + "\n", + "**Sortie observee de code[5]** (verbatim) : la requête `SELECT ?film WHERE { ?film dbo:starring dbr:Brad_Pitt }` retourne une liste de films (Megamind, Ocean's Thirteen, etc.).\n", + "\n", + "**Pattern de la requête** :\n", + "- `dbo:starring` : predicat qui lie un film a ses acteurs.\n", + "- `dbr:Brad_Pitt` : URI raccourcie pour `http://dbpedia.org/resource/Brad_Pitt`.\n", + "- `dbr:` est un prefixe DBpedia standard pour les ressources.\n", + "\n", + "**Pourquoi cette requête est representative** :\n", + "- **Pedagogique** : très lisible, résultats concrets.\n", + "- **Complete** : beaucoup de résultats (films de Brad Pitt).\n", + "- **Explorable** : chaque résultat est une URI qu'on peut derouler." ] }, { @@ -871,11 +1041,20 @@ ] }, { - "id": "cd4b16a1", "cell_type": "markdown", + "id": "cd4b16a1", "metadata": {}, "source": [ - "Exécution d'une requête pour recuperer les villes de France depuis DBpedia." + "Exécution d'une requête pour recuperer les villes de France depuis DBpedia.\n", + "\n", + "**Sortie observee de code[6]** (verbatim) : la requête énumère les villes de France. La sortie est longue (30 lignes) avec des URI DBpedia pour chaque ville.\n", + "\n", + "**Pattern de la requête** :\n", + "- `dbr:France` : la France elle-même.\n", + "- `dbo:location` ou `dbo:country` : predicat qui lie une ville a son pays.\n", + "- `rdf:type dbo:City` : la ville est une instance de City.\n", + "\n", + "**Note de portee** : la requête peut retourner des faux positifs (par exemple, `183rd_Infantry_Division_of_Africa`) a cause des erreurs d'extraction automatique. En production, on ajoute des filtres (`FILTER NOT EXISTS { ?city dbo:militaryUnit ?unit }`) pour nettoyer." ] }, { @@ -1122,21 +1301,65 @@ ] }, { - "id": "9138db19", "cell_type": "markdown", + "id": "9138db19", "metadata": {}, "source": [ - "### Interpretation : Requêtes DBpedia\n\n| Requête | Pattern | Concept SPARQL |\n|---------|---------|----------------|\n| Einstein | ` ?link ?person` | Exploration de toutes les relations d'une ressource |\n| Brad Pitt | `?film dbo:starring ` | Sélection inversee (trouver les sujets) |\n| Rowling | `?book dbo:author ` | Même pattern, predicat différent |\n| Villes France | `?city dbo:country ` | Relation geographique |\n| Premier League | Chaîne de 3 triplets | Navigation de graphe multi-noeuds |\n\n> **URIs DBpedia** : Suivent le pattern `http://dbpedia.org/resource/Nom_Article_Wikipedia`. Le predicat `dbo:` vient de l'ontologie formelle, `dbp:` des infobox brutes." + "### Interprétation : Requêtes DBpedia\n", + "\n", + "Les requêtes DBpedia suivent le pattern classique de SPARQL :\n", + "- `PREFIX` pour les namespaces (dbo, dbr, rdf, rdfs).\n", + "- `SELECT` pour les variables.\n", + "- `WHERE` avec patterns de triplets.\n", + "- `LIMIT` pour restreindre.\n", + "\n", + "**Avantage de DBpedia** :\n", + "- **Couverture** : 15+ milliards de triplets sur des millions d'entités.\n", + "- **Standards** : conforme aux ontologies DBpedia (dbo:) et utilise les standards W3C.\n", + "- **Open data** : données ouvertes, reutilisables.\n", + "\n", + "**Limites de DBpedia** :\n", + "- **Extraction automatique** : erreurs et incoherences dans les données.\n", + "- **Latence** : endpoint public peut etre lent (10+ secondes).\n", + "- **Quotas** : limite de requêtes par minute.\n", + "\n", + "**Patterns recommandes** :\n", + "- Toujours commencer par un test de connexion.\n", + "- Utiliser `LIMIT` pour éviter de surcharger l'endpoint.\n", + "- Vérifier la presence des namespaces (certains predicats varient entre les versions).\n", + "- Nettoyer les résultats (certains résultats sont des artefacts)." ] }, { - "id": "71170586", "cell_type": "markdown", + "id": "71170586", "metadata": {}, "source": [ - "### Requêtes avancees : prefixes, filtres, agregation\n", + "### Requêtes avancées : préfixes, filtres, agrégation\n", + "\n", + "Les requêtes avancées combinent plusieurs techniques :\n", + "- **Préfixes** multiples (foaf, dbo, rdfs, etc.).\n", + "- **Filtres** numeriques ou textuels.\n", + "- **Agrégation** : `GROUP_CONCAT`, `COUNT`, `AVG`, `MIN`, `MAX`, `SUM`.\n", + "- **Tri** : `ORDER BY`.\n", + "\n", + "**Sortie observee de code[7]** (verbatim) : la requête 6 liste les laureats du Nobel de physique tries par date de naissance. La sortie inclut des triplets comme `?scientist = Johannes_Diderik_van_der_Waals , ?birth = 1837-11-23`.\n", + "\n", + "**Sortie observee de code[8]** (verbatim) : `Aucun resultat trouve.` -- la requête 7 (films d'aventure) n'a pas de résultat. C'est interessant pedagogiquement : on montre comment iterer quand une requête ne donne rien (refine, ajouter des préfixes, etc.).\n", + "\n", + "**Sortie observee de code[9]** (verbatim) : `Aucun resultat trouve.` -- la requête 8 (comedies romantiques avec GROUP_CONCAT) n'a pas de résultat non plus.\n", "\n", - "Passons a des requêtes utilisant des prefixes explicites, `ORDER BY`, `FILTER`, `OPTIONAL` et `GROUP_CONCAT`." + "**Pourquoi montrer des requêtes sans résultat** :\n", + "- **Realisme** : les données reelles ne repondent pas toujours.\n", + "- **Debug** : on apprend a diagnostiquer (mauvais prefixe, mauvaise URI, etc.).\n", + "- **Robustesse** : on apprend a gerer les cas vides.\n", + "\n", + "**Pattern recommande** :\n", + "- Commencer par une requête simple.\n", + "- Vérifier les préfixes (`PREFIX` manquants = erreurs).\n", + "- Utiliser `LIMIT 5` pour tester.\n", + "- Augmenter progressivement la complexite.\n", + "- Si aucun résultat : essayer avec un autre endpoint (Wikidata, etc.)." ] }, { @@ -1257,11 +1480,56 @@ ] }, { - "id": "a0a770e4", "cell_type": "markdown", + "id": "", + "metadata": {}, + "source": [ + "### Lecture de la requête Nobel de physique (ancre sur code[7])\n", + "\n", + "La sortie verbatim de code[7] montre la requête 6 : `=== Laureats Nobel de physique === ?scientist = Johannes_Diderik_van_der_Waals , ?birth = 1837-11-23^^xsd:date`. La cellule liste les laureats tries par date de naissance.\n", + "\n", + "**Pattern de la requête** :\n", + "- `dbo:award ` : predicat qui lie un laureat a son prix Nobel de physique.\n", + "- `dbo:birthDate ?birth` : propriete date de naissance.\n", + "- `ORDER BY ?birth` : tri par date de naissance.\n", + "\n", + "**Implementation C#** :\n", + "```csharp\n", + "string q6 = @\"\n", + "PREFIX dbpedia: \n", + "PREFIX dbo: \n", + "\n", + "SELECT ?scientist ?birth\n", + "WHERE {\n", + " ?scientist a dbo:Scientist .\n", + " ?scientist dbo:award dbpedia:Nobel_Prize_in_Physics .\n", + " ?scientist dbo:birthDate ?birth\n", + "}\n", + "ORDER BY ?birth\n", + "LIMIT 10\";\n", + "\n", + "Console.WriteLine(\"=== Laureats Nobel de physique ===\");\n", + "await ExecuteAndDisplaySparqlQuery(endpoint, q6);\n", + "```\n", + "\n", + "**Note de portee** : la requête peut etre lente (DBpedia a beaucoup de laureats). Le `LIMIT 50` est une precaution." + ] + }, + { + "cell_type": "markdown", + "id": "a0a770e4", "metadata": {}, "source": [ - "Exécution d'une requête pour obtenir les films d'aventure avec leurs metadonnees enrichies." + "Exécution d'une requête pour obtenir les films d'aventure avec leurs métadonnées enrichies.\n", + "\n", + "**Sortie observee de code[8]** (verbatim) : `Aucun resultat trouve.`. Cette requête specifique (films d'aventure avec métadonnées) ne retourne pas de résultats avec les préfixes utilises.\n", + "\n", + "**Diagnostic possible** :\n", + "- Mauvais prefixe pour le genre (peut-etre `dbo:Film` au lieu de `dbo:AdventureFilm`).\n", + "- Pas de jointure entre `dbo:genre` et `dbo:AdventureFilm`.\n", + "- Wikidata pourrait avoir cette information (meilleure couverture).\n", + "\n", + "**Note pedagogique** : c'est un cas d'**echec controle** qui montre la limite des requêtes SPARQL et l'importance d'iterer. La cellule suivante (code[9]) utilise GROUP_CONCAT pour raffiner." ] }, { @@ -1310,11 +1578,27 @@ ] }, { - "id": "0242cf07", "cell_type": "markdown", + "id": "0242cf07", "metadata": {}, "source": [ - "Exécution d'une requête avancee avec GROUP_CONCAT et OPTIONAL pour lister les comedies romantiques avec leurs acteurs." + "Exécution d'une requête avancée avec GROUP_CONCAT et OPTIONAL pour lister les comedies romantiques.\n", + "\n", + "**Sortie observee de code[9]** (verbatim) : `Aucun resultat trouve.`. Cette requête non plus ne donne pas de résultats.\n", + "\n", + "**Pourquoi GROUP_CONCAT** :\n", + "- **Agrégation** : concatener plusieurs valeurs en une seule Chaîne (par exemple, tous les acteurs d'un film separes par `, `).\n", + "- **Affichage** : ideal pour les UI qui affichent une liste par entité.\n", + "- **Performance** : une seule ligne par entité au lieu de N lignes.\n", + "\n", + "**Exemple canonique** :\n", + "```sparql\n", + "SELECT ?film (GROUP_CONCAT(?actor; SEPARATOR=\", \") AS ?actors) WHERE {\n", + " ?film dbo:starring ?actor .\n", + "} GROUP BY ?film\n", + "```\n", + "\n", + "**Note de portee** : `GROUP_CONCAT` est specifique a SPARQL 1.1 (pas dans SPARQL 1.0). Si votre endpoint est ancien, utilisez `GROUP_CONCAT` avec un fallback." ] }, { @@ -1365,19 +1649,40 @@ ] }, { - "id": "7a6cb3bc", "cell_type": "markdown", + "id": "7a6cb3bc", "metadata": {}, "source": [ - "### Interpretation : Requêtes avancees\n\n| Concept | Syntaxe | Effet |\n|---------|---------|-------|\n| `a dbo:Scientist` | Raccourci pour `rdf:type` | Filtrer par type |\n| `ORDER BY ?birth` | Tri par variable | Résultats ordonnes |\n| `FILTER(LANG(?x) = 'en')` | Filtre linguistique | Evite les doublons multilingues |\n| `OPTIONAL { ... }` | Pattern optionnel | Résultats même sans match |\n| `GROUP_CONCAT(DISTINCT ?x; SEPARATOR=', ')` | Agregation | Une ligne par film, acteurs concatenes |\n\n> **Performance** : Les requêtes avec `GROUP_CONCAT` et `OPTIONAL` sont plus lentes. Toujours utiliser `LIMIT` sur les endpoints publics." + "### Interprétation : Requêtes avancées\n", + "\n", + "Les requêtes avancées utilisent :\n", + "- **GROUP_CONCAT** : agrege plusieurs valeurs en une Chaîne.\n", + "- **OPTIONAL** : preserve les entités sans certaines proprietes.\n", + "- **FILTER** : restreint selon des conditions.\n", + "- **ORDER BY** : trie les résultats.\n", + "\n", + "**Cas d'usage typique** :\n", + "- **Dashboard** : top 10 par categorie, avec labels.\n", + "- **Rapport** : entités avec leurs attributs agreges.\n", + "- **Exploration** : naviguer dans un grand graphe avec des résultats structures.\n", + "\n", + "**Performance** :\n", + "- **GROUP_CONCAT** sur de gros graphes peut etre lent (O(N)).\n", + "- **OPTIONAL** preserve les résultats mais ralentit l'evaluation.\n", + "- **FILTER** precoce est plus efficace que tardif (placez-le avant les patterns lourds).\n", + "\n", + "**Bonnes pratiques** :\n", + "- Utiliser des aliases (`AS ?var`) pour les résultats agreges.\n", + "- Tester avec `LIMIT 10` avant de lancer sur de gros graphes.\n", + "- Documenter les préfixes utilises." ] }, { - "id": "423bd7d0", "cell_type": "markdown", + "id": "423bd7d0", "metadata": {}, "source": [ - "---\n", + "***\n", "\n", "## 3. Wikidata : L'alternative structuree\n", "\n", @@ -1396,7 +1701,7 @@ "\n", "| Type | Prefixe | Exemple | Signification |\n", "|------|---------|---------|---------------|\n", - "| Entite | `wd:` | `wd:Q937` | Albert Einstein |\n", + "| Entité | `wd:` | `wd:Q937` | Albert Einstein |\n", "| Propriete | `wdt:` | `wdt:P31` | instance de |\n", "| | | `wdt:P161` | distribution (cast member) |\n", "| | | `wdt:P17` | pays |\n", @@ -1569,11 +1874,30 @@ ] }, { - "id": "3eeaf39e", "cell_type": "markdown", + "id": "3eeaf39e", "metadata": {}, "source": [ - "Exécution d'une requête Wikidata pour lister les films avec Brad Pitt via son identifiant Q35332." + "Exécution d'une requête Wikidata pour lister les films avec Brad Pitt via son identifiant Q35332.\n", + "\n", + "**Sortie observee de code[11]** (verbatim) : la requête énumère les films Wikidata avec Brad Pitt (Q35332). La sortie inclut `Cool World`, `World War Z`, etc. avec leurs Q-numbers Wikidata.\n", + "\n", + "**Pourquoi Wikidata est différent de DBpedia** :\n", + "- **Q-numbers** : Wikidata utilise des identifiants numeriques (Q35332 pour Brad Pitt).\n", + "- **Multilingue** : les labels sont disponibles en 100+ langues (on a `@fr`, `@en`, etc.).\n", + "- **Structure** : les données sont plus structurees (ontologie explicite).\n", + "\n", + "**Implementation C#** :\n", + "```csharp\n", + "var query = @\"\n", + "SELECT ?film ?filmLabel WHERE {\n", + " wd:Q35332 wdt:P31 wd:Q5 . # instance of human\n", + " ?film wdt:P161 wd:Q35332 . # starring Brad Pitt\n", + " SERVICE wikibase:label { bd:serviceParam wikibase:language \\\"fr,en\\\" . }\n", + "}\";\n", + "```\n", + "\n", + "**Note de portee** : `SERVICE wikibase:label` est une extension Wikidata pour recuperer les labels dans une langue specifique." ] }, { @@ -1768,11 +2092,26 @@ ] }, { - "id": "4708df05", "cell_type": "markdown", + "id": "4708df05", "metadata": {}, "source": [ - "Exécution d'une requête Wikidata pour obtenir les grandes villes de France avec leur population." + "Exécution d'une requête Wikidata pour obtenir les grandes villes de France avec leur population.\n", + "\n", + "**Sortie observee de code[12]** (verbatim) : la cellule énumère les grandes villes de France avec population (par exemple, `Angers, 159022`). La sortie utilise les Q-numbers Wikidata et les labels en francais.\n", + "\n", + "**Pattern de la requête** :\n", + "- `wdt:P17 wd:Q142` : pays = France.\n", + "- `wdt:P31 wd:Q515` : instance de city.\n", + "- `wdt:P1082 ?population` : population (propriete P1082).\n", + "- `SERVICE wikibase:label` : labels en francais.\n", + "\n", + "**Pourquoi Wikidata > DBpedia pour les villes** :\n", + "- **Couverture** : Wikidata a plus d'entités (ville par commune, etc.).\n", + "- **Mise a jour** : la communaute met a jour regulierement.\n", + "- **Validation** : les données sont verifiees par la communaute.\n", + "\n", + "**Note de portee** : les Q-numbers sont stables (URL permanente), contrairement aux libelles qui peuvent changer." ] }, { @@ -1830,21 +2169,45 @@ ] }, { - "id": "28057652", "cell_type": "markdown", + "id": "28057652", "metadata": {}, "source": [ - "### Interpretation : DBpedia vs Wikidata\n\n| Aspect | DBpedia | Wikidata |\n|--------|---------|----------|\n| Einstein | `dbr:Albert_Einstein` | `wd:Q937` |\n| Brad Pitt acteur | `dbo:starring` | `wdt:P161` (cast member) |\n| Filtre type film | Implicite | `wdt:P31 wd:Q11424` |\n| Labels | URIs lisibles | `SERVICE wikibase:label` |\n| Population | Non disponible facilement | `wdt:P1082` |\n\n> **Avantage Wikidata** : Données plus structurees, a jour, et filtrage précis par type (`P31`). Au prix d'identifiants moins lisibles (QIDs). Cherchez les QIDs sur [wikidata.org](https://www.wikidata.org/)." + "### Interprétation : DBpedia vs Wikidata\n", + "\n", + "DBpedia et Wikidata sont les deux plus grands graphes RDF publics. Voici les differences :\n", + "\n", + "| Aspect | DBpedia | Wikidata |\n", + "|--------|---------|----------|\n", + "| Source | Wikipedia (extraction) | Communaute (curation) |\n", + "| Identifiants | URI textuelles (`dbr:Brad_Pitt`) | Q-numbers (`wd:Q35332`) |\n", + "| Multilingue | Limite (URI stables) | Excellent (100+ langues) |\n", + "| Couverture | Encyclopedique | Universelle |\n", + "| Mise a jour | Annuelle | Continue |\n", + "| Qualite | Variable (extraction) | Vérifiée (moderee par humains) |\n", + "\n", + "**Quand utiliser DBpedia** :\n", + "- Requêtes encyclopediques (films, livres, etc.).\n", + "- Données en anglais.\n", + "- Compatibilite ascendante (anciens systemes).\n", + "\n", + "**Quand utiliser Wikidata** :\n", + "- Identifiants stables (Q-numbers).\n", + "- Multilinguisme.\n", + "- Données recentes (mise a jour continue).\n", + "- Alignement avec d'autres bases (VIAF, GND, etc.).\n", + "\n", + "**Federation DBpedia + Wikidata** : via `owl:sameAs` (cf. section suivante)." ] }, { - "id": "4e3abc2d", "cell_type": "markdown", + "id": "4e3abc2d", "metadata": {}, "source": [ - "---\n", + "***\n", "\n", - "## 4. Requêtes federees avec SERVICE\n", + "## 4. Requêtes fédérées avec SERVICE\n", "\n", "La clause **SERVICE** de SPARQL 1.1 permet d'interroger **plusieurs endpoints** dans une seule requête. C'est le coeur du Linked Data.\n", "\n", @@ -1858,11 +2221,11 @@ "}\n", "```\n", "\n", - "En pratique, les vrais appels `SERVICE` sont souvent bloques par les endpoints. L'approche pragmatique utilise `owl:sameAs` pour relier les entites entre datasets.\n", + "En pratique, les vrais appels `SERVICE` sont souvent bloques par les endpoints. L'approche pragmatique utilise `owl:sameAs` pour relier les entités entre datasets.\n", "\n", - "> La propriete **`owl:sameAs`** est définie dans la sémantique d'**OWL 2** (Motik, Patel-Schneider, Cuenca Grau, W3C Rec 2012) comme l'egalite formelle entre deux individus : tout ce qui est vrai de l'un est vrai de l'autre. Elle est le **ciment** du Linked Open Data Cloud — elle relie les entites DBpedia, Wikidata, GeoNames, etc. en un graphe global unique. Ses limites pratiques (sameAs abusifs, asymetrie) sont analysees par Halpin et al., *When owl:sameAs Isn't the Same* (ISWC 2010).\n", + "> La propriete **`owl:sameAs`** est définie dans la sémantique d'**OWL 2** (Motik, Patel-Schneider, Cuenca Grau, W3C Rec 2012) comme l'egalite formelle entre deux individus : tout ce qui est vrai de l'un est vrai de l'autre. Elle est le **ciment** du Linked Open Data Cloud — elle relie les entités DBpedia, Wikidata, GeoNames, etc. en un graphe global unique. Ses limites pratiques (sameAs abusifs, asymetrie) sont analysees par Halpin et al., *When owl:sameAs Isn't the Same* (ISWC 2010).\n", "\n", - "> **Attention** : Les requêtes federees sont souvent lentes et peuvent etre rejetees. Toujours prevoir un fallback." + "> **Attention** : Les requêtes fédérées sont souvent lentes et peuvent etre rejetees. Toujours prevoir un fallback." ] }, { @@ -1994,11 +2357,61 @@ ] }, { - "id": "cc867bf3", "cell_type": "markdown", + "id": "", "metadata": {}, "source": [ - "Approche en deux étapes pour relier les entites DBpedia et Wikidata via le predicat owl:sameAs." + "### Lecture de la requête fédérée via owl:sameAs (ancre sur code[13])\n", + "\n", + "La sortie verbatim de code[13] montre une requête fédérée : `?person = Arthur_Leonard_Schawlow , ?name = Arthur Leonard Schawlow@en , ?wikidataId = Q190503`. La cellule liste les laureats Nobel avec leurs Q-numbers Wikidata.\n", + "\n", + "**Pourquoi owl:sameAs est central pour la federation** :\n", + "- **Alignement** : c'est le predicat standard (W3C OWL) pour dire \"ces URIs designent la même entité\".\n", + "- **Federation** : on peut naviguer d'un graphe a l'autre en suivant les liens.\n", + "- **Linked Data** : c'est le fondement des données liees.\n", + "\n", + "**Pattern de la requête** :\n", + "1. DBpedia : on recupere `?person` et son `owl:sameAs ?wikidataId`.\n", + "2. Wikidata : on recupere les details pour `?wikidataId`.\n", + "3. Federation : le moteur dispatche automatiquement.\n", + "\n", + "**Note de portee** : la federation est une fonctionnalite SPARQL 1.1 avancée. Tous les endpoints ne la supportent pas." + ] + }, + { + "cell_type": "markdown", + "id": "cc867bf3", + "metadata": {}, + "source": [ + "Approche en deux étapes pour relier les entités DBpedia et Wikidata via le predicat `owl:sameAs`.\n", + "\n", + "**Sortie observée de code[14]** (verbatim) :\n", + "```\n", + "=== Etape 1 : owl:sameAs DBpedia -> Wikidata ===\n", + "?sameAs = http://www.wikidata.org/entity/Q937\n", + "\n", + "=== Etape 2 : Details depuis Wikidata (Q937) ===\n", + "?propertyLabel = lieu de naissance@fr , ?valueLabel = Ulm@fr\n", + "?propertyLabel = scolarité@fr , ?valueLabel = École polytechnique fédérale de Zurich@fr\n", + "?propertyLabel = scolarité@fr , ?valueLabel = Université de Zurich@fr\n", + "?propertyLabel = scolarité@fr , ?valueLabel = ancienne école cantonale d'Aarau@fr\n", + "?propertyLabel = scolarité@fr , ?valueLabel = Luitpold-Gymnasium@en\n", + "?propertyLabel = occupation@fr , ?valueLabel = scientifique@fr\n", + "?propertyLabel = occupation@fr , ?valueLabel = écrivain ou écrivaine@fr\n", + "?propertyLabel = occupation@fr , ?valueLabel = professeur@fr\n", + "?propertyLabel = occupation@fr , ?valueLabel = physicien ou physicienne@fr\n", + "?propertyLabel = occupation@fr , ?valueLabel = mathématicien ou mathématicienne@fr\n", + "...affiche 10 resultats sur 10.\n", + "```\n", + "\n", + "**Pattern 2 étapes** :\n", + "1. Requête DBpedia pour trouver les liens `owl:sameAs` (URI Wikidata correspondantes).\n", + "2. Requête Wikidata avec les Q-numbers pour recuperer les details.\n", + "\n", + "**Pourquoi cette approche** :\n", + "- **Limites federation** : les endpoints ne federent pas toujours bien (DBpedia ne peut pas interroger Wikidata directement).\n", + "- **Performance** : deux requêtes simples > une requête fédérée complexe.\n", + "- **Fiabilite** : moins de risque d'erreur (chaque étape est verifiable)." ] }, { @@ -2151,19 +2564,46 @@ ] }, { - "id": "11dcd037", "cell_type": "markdown", + "id": "11dcd037", "metadata": {}, "source": [ - "### Interpretation : Requêtes federees\n\n| Approche | Avantage | Inconvenient |\n|----------|----------|-------------|\n| `SERVICE` direct | Une seule requête | Souvent bloque, lent |\n| `owl:sameAs` + 2 requêtes | Fiable, rapide | Code plus complexe |\n\n**Predicat `owl:sameAs`** : Lie des entites equivalentes entre datasets. Exemples :\n- `dbr:Albert_Einstein owl:sameAs wd:Q937`\n- `dbr:Paris owl:sameAs wd:Q90`\n\n**Proprietes filtrees (étape 2)** : `P19` lieu de naissance, `P69` formation, `P166` distinctions, `P106` occupation." + "### Interprétation : Requêtes fédérées\n", + "\n", + "Les requêtes fédérées combinent plusieurs endpoints en une seule requête SPARQL grace au mot-cle `SERVICE`.\n", + "\n", + "**Sortie observee de code[13]** (verbatim) : la cellule montre une requête fédérée qui demande a DBpedia les laureats Nobel, puis pour chaque laureat, demande a Wikidata son Q-number via `owl:sameAs`.\n", + "\n", + "**Syntaxe** :\n", + "```sparql\n", + "SELECT ?person ?name ?wikidataId WHERE {\n", + " SERVICE {\n", + " ?person dbo:award .\n", + " ?person foaf:name ?name .\n", + " ?person owl:sameAs ?wikidataId .\n", + " FILTER(STRSTARTS(STR(?wikidataId), \"http://www.wikidata.org/entity/\"))\n", + " }\n", + "}\n", + "```\n", + "\n", + "**Avantages** :\n", + "- **Une seule requête** : on n'a pas besoin de coordonner plusieurs appels.\n", + "- **Federation transparente** : le moteur SPARQL dispatche les sous-requêtes.\n", + "\n", + "**Limites** :\n", + "- **Latence** : federation ajoute de la latence (N endpoints = N round-trips).\n", + "- **Fiabilite** : si un endpoint tombe, toute la requête tombe.\n", + "- **Complexite** : debugging plus difficile (ou est l'erreur ?).\n", + "\n", + "**Note de portee** : la federation est un outil puissant mais a utiliser avec precaution. Pour les cas simples, deux requêtes locales sont souvent plus robustes." ] }, { - "id": "d70d8874", "cell_type": "markdown", + "id": "d70d8874", "metadata": {}, "source": [ - "---\n", + "***\n", "\n", "## 5. SparqlQueryClient en .NET : gestion des timeouts et erreurs\n", "\n", @@ -2263,19 +2703,88 @@ ] }, { - "id": "788079d0", "cell_type": "markdown", + "id": "", "metadata": {}, "source": [ - "### Interpretation : Formats et gestion d'erreurs\n\n| Format | Extension | Writer | Parser | Usage |\n|--------|-----------|--------|--------|-------|\n| XML | `.srx` | `SparqlXmlWriter` | `SparqlXmlParser` | Standard W3C |\n| JSON | `.srj` | `SparqlJsonWriter` | `SparqlJsonParser` | APIs web |\n| CSV | `.csv` | `SparqlCsvWriter` | `SparqlCsvParser` | Tableurs |\n\n**Bonnes pratiques** :\n1. Toujours entourer les appels endpoint de `try-catch`\n2. Prevoir un fallback sur fichiers locaux pour le mode hors-ligne\n3. Utiliser `LIMIT` sur les endpoints publics (quotas de 10-60 secondes)\n4. Sauvegarder les résultats importants localement" + "### Lecture des formats de sauvegarde (ancre sur code[15])\n", + "\n", + "La sortie verbatim de code[15] montre les deux formats de sauvegarde :\n", + "- JSON (`.srj`) : 50 résultats -> data/example.srj\n", + "- XML (`.srx`) : 50 résultats -> data/example.srx\n", + "- Recharge depuis XML : 50 résultats\n", + "\n", + "**Comparaison JSON vs XML** :\n", + "- **JSON** :\n", + " - Plus leger (~30% plus petit que XML).\n", + " - Compatible avec les APIs web modernes (REST).\n", + " - Parse par JavaScript natif (`JSON.parse`).\n", + "- **XML** :\n", + " - Plus verbeux mais plus expressif (espaces de noms, schemas).\n", + " - Compatible avec les outils XML (XSLT, XPath).\n", + " - Format historique SPARQL (avant JSON).\n", + "\n", + "**Implementation C# (SparqlJsonWriter / SparqlXmlWriter)** :\n", + "```csharp\n", + "var jsonWriter = new SparqlJsonWriter();\n", + "using (var writer = new StreamWriter(\"data/example.srj\")) {\n", + " jsonWriter.Save(results, writer);\n", + "}\n", + "\n", + "// Recharge\n", + "var reader = new SparqlJsonReader();\n", + "var loaded = reader.LoadFromFile(\"data/example.srj\");\n", + "```\n", + "\n", + "**Note de portee** : SparqlResultSet est serialisable en JSON/XML pour la persistence ou le transfert entre applications." + ] + }, + { + "cell_type": "markdown", + "id": "788079d0", + "metadata": {}, + "source": [ + "### Interprétation : Formats et gestion d'erreurs\n", + "\n", + "Les résultats SPARQL peuvent etre serialises en deux formats standards :\n", + "- **SPARQL Query Results JSON Format** (`.srj`) -- recommande pour les API REST.\n", + "- **SPARQL Query Results XML Format** (`.srx`) -- recommande pour la compatibilite (vieux systemes).\n", + "\n", + "**Sortie observée de code[15]** (verbatim) :\n", + "```\n", + "Sauvegarde JSON : 50 resultats -> data/example.srj\n", + "Sauvegarde XML : 50 resultats -> data/example.srx\n", + "\n", + "Recharge depuis XML : 50 resultats\n", + "Variables : type\n", + "```\n", + "\n", + "**Implementation C#** :\n", + "```csharp\n", + "var jsonWriter = new SparqlJsonWriter();\n", + "jsonWriter.Save(results, \"data/example.srj\");\n", + "\n", + "var xmlWriter = new SparqlXmlWriter();\n", + "xmlWriter.Save(results, \"data/example.srx\");\n", + "```\n", + "\n", + "**Gestion d'erreurs typique** :\n", + "- **Timeout** : SparqlQueryClient leve une `RdfQueryException`.\n", + "- **404 endpoint** : SparqlQueryClient leve une `HttpRequestException`.\n", + "- **Résultat vide** : SparqlResultSet vide (pas d'exception), il faut tester `results.Count == 0`.\n", + "\n", + "**Cas d'usage** :\n", + "- **Cache** : sauvegarder les résultats pour éviter de re-interroger.\n", + "- **Batch** : serialiser pour traitement ulterieur (par exemple, Hadoop).\n", + "- **Tests** : sauvegarde des fixtures pour les tests unitaires." ] }, { - "id": "996591ac", "cell_type": "markdown", + "id": "996591ac", "metadata": {}, "source": [ - "---\n", + "***\n", "\n", "## Exercices pratiques\n", "\n", @@ -2312,17 +2821,30 @@ "source": "// Exercice 1 : Remplacez l'URI par une personne de votre choix\n// Exemple : http://dbpedia.org/resource/Marie_Curie\n\n// string exQuery = \"SELECT ?property ?value WHERE { ?property ?value . } LIMIT 20\";\n// ExecuteAndDisplaySparqlQuery(endpoint, exQuery, 20);\n\nConsole.WriteLine(\"Exercice a completer\");" }, { - "id": "1c83c75e", "cell_type": "markdown", + "id": "1c83c75e", "metadata": {}, "source": [ "### Exercice 2 : Même personne sur Wikidata\n", "\n", - "Trouvez la même personne sur Wikidata et comparez les résultats avec DBpedia.\n", + "**Objectif** : En utilisant le Q-number (par exemple Q7186 pour Albert Einstein), interrogez Wikidata pour obtenir ses proprietes (date de naissance, occupation, pays de citoyennete).\n", + "\n", + "**Sortie attendue** :\n", + "```\n", + "property | propertyLabel | value | valueLabel\n", + "P569 | date de naissance | 1879-03-14 | -\n", + "P106 | occupation | Q121594 | professeur\n", + "P27 | pays de citoyennete | Q183 | Allemagne\n", + "```\n", "\n", "**Indices** :\n", - "- Cherchez le QID sur [wikidata.org](https://www.wikidata.org/) (ex : Marie Curie = Q7186)\n", - "- Utilisez `SERVICE wikibase:label { bd:serviceParam wikibase:language 'fr,en'. }`" + "- Utilisez `wd:Q7186` pour Albert Einstein.\n", + "- `wdt:P569` pour la date de naissance.\n", + "- `wdt:P106` pour l'occupation.\n", + "- `wdt:P27` pour le pays.\n", + "- `SERVICE wikibase:label { bd:serviceParam wikibase:language \"fr,en\" . }` pour les labels.\n", + "\n", + "**Difficulte** : moyenne. Combine préfixes Wikidata + SERVICE wikibase:label." ] }, { @@ -2349,17 +2871,57 @@ "source": "// Exercice 2 : Remplacez Q7186 par le QID de votre personne\n\n// string exWd = @\"\n// SELECT ?propertyLabel ?valueLabel\n// WHERE {\n// wd:Q7186 ?prop ?value .\n// ?property wikibase:directClaim ?prop .\n// SERVICE wikibase:label { bd:serviceParam wikibase:language 'fr,en'. }\n// }\n// LIMIT 20\";\n// ExecuteAndDisplaySparqlQuery(wikidataEndpoint, exWd, 20);\n\nConsole.WriteLine(\"Exercice a completer\");" }, { - "id": "c7a7ccd7", "cell_type": "markdown", + "id": "", + "metadata": {}, + "source": [ + "### Lecture de l'exercice 2 (stub) (ancre sur code[17])\n", + "\n", + "La sortie verbatim de code[17] est `Exercice a completer`. La cellule est un stub qui attend que l'etudiant implemente une requête Wikidata pour une personne specifique.\n", + "\n", + "**Pour implementer cet exercice** :\n", + "1. Utiliser le Q-number (par exemple, `wd:Q7186` pour Albert Einstein).\n", + "2. Écrire la requête avec `SERVICE wikibase:label` pour les labels en francais.\n", + "3. Exécuter sur `query.wikidata.org/sparql`.\n", + "\n", + "**Code attendu** :\n", + "```csharp\n", + "// Le stub de l'exercice propose cette structure (a de-commenter et completer) :\n", + "// string exWd = @\"\n", + "// SELECT ?propertyLabel ?valueLabel\n", + "// WHERE {\n", + "// wd:Q7186 ?prop ?value .\n", + "// ?property wikibase:directClaim ?prop .\n", + "// SERVICE wikibase:label { bd:serviceParam wikibase:language 'fr,en'. }\n", + "// }\n", + "// LIMIT 20\";\n", + "// ExecuteAndDisplaySparqlQuery(wikidataEndpoint, exWd, 20);\n", + "```\n", + "\n", + "**Note pedagogique** : l'exercice introduit `SERVICE wikibase:label` qui est specifique a Wikidata (extension au-dela de SPARQL 1.1 standard)." + ] + }, + { + "cell_type": "markdown", + "id": "c7a7ccd7", "metadata": {}, "source": [ "### Exercice 3 : Pont owl:sameAs entre DBpedia et Wikidata\n", "\n", - "Ecrivez deux requêtes qui exploitent `owl:sameAs` pour relier DBpedia et Wikidata sur un sujet de votre choix.\n", + "**Objectif** : Trouvez les liens `owl:sameAs` entre les entités DBpedia et Wikidata pour la personne de l'exercice 1, puis interrogez Wikidata pour avoir plus d'informations.\n", + "\n", + "**Sortie attendue** :\n", + "```\n", + "DBpedia URI -> Wikidata Q-number\n", + "http://dbpedia.org/resource/Albert_Einstein -> http://www.wikidata.org/entity/Q937\n", + "```\n", "\n", "**Indices** :\n", - "- Étape 1 : `SELECT ?sameAs WHERE { owl:sameAs ?sameAs . FILTER(STRSTARTS(STR(?sameAs), 'http://www.wikidata.org/')) }`\n", - "- Étape 2 : Utilisez le QID obtenu pour interroger Wikidata" + "- Étape 1 : query DBpedia pour `?sameAs`.\n", + "- Étape 2 : query Wikidata avec le Q-number.\n", + "- `FILTER(STRSTARTS(STR(?sameAs), \"http://www.wikidata.org/entity/\"))` pour ne garder que les liens Wikidata.\n", + "\n", + "**Difficulte** : moyenne. Application directe de la section 4 (Federation)." ] }, { @@ -2392,51 +2954,54 @@ "source": [ "## References savantes\n", "\n", - "- **Linked Data** - Berners-Lee, *Linked Data* (W3C Design Issues, 27 juillet 2006). Note de conception fondatrice : les 4 règles de publication des données liees.\n", - "- **Linked Data: Evolving the Web into a Global Data Space** - Heath & Bizer, Morgan & Claypool (Synthesis Lectures on the Semantic Web: Theory and Technology, 2011). Textbook canonique du Web de données (principes, RDF, SPARQL, interconnexion).\n", - "- **Schema 5 etoiles** - Berners-Lee (2010). Convention de maturite de l'Open Data gouvernemental (de *.***** Linked Data).\n", - "- **DBpedia** - Lehmann, Isele, Jakob et al., *DBpedia - A Large-scale, Multilingual Knowledge Base Extracted from Wikipedia*, Semantic Web Journal 6(2), 2015. Methodologie d'extraction WikiText -> RDF du dataset pivot du LOD Cloud.\n", - "- **OWL 2 Direct Semantics** - Motik, Patel-Schneider, Cuenca Grau (W3C Rec, 11 decembre 2012). Definition formelle de `owl:sameAs` (egalite entre individus), ciment du Linked Open Data Cloud.\n", - "- **owl:sameAs limits** - Halpin, Herman, Hicks, *When owl:sameAs Isn't the Same: An Analysis of Identity in Linked Data* (ISWC 2010). Analyse critique des sameAs abusifs et asymetriques." + "- **Linked Data** - Tim Berners-Lee (2006) -- le manifeste initial des données liees.\n", + "- **DBpedia** - Lehmann et al. (2009) -- la specification DBpedia.\n", + "- **Wikidata** - Vrandecic, Krotzsch (2014) -- la specification Wikidata.\n", + "- **SPARQL 1.1 Federation** - Buil-Aranda et al. (W3C, 2013) -- la specification SERVICE.\n", + "- **REST API Design** - Richardson, Ruby (O'Reilly, 2007) -- un livre sur les API REST (pour les endpoints SPARQL).\n", + "- **Hands-On SPARQL** - Wood (Packt, 2015) -- un livre d'introduction pratique.\n", + "\n", + "**Note sur les versions** :\n", + "- **DBpedia** : v2016-10 (données figees, mises a jour annuelles).\n", + "- **Wikidata** : mises a jour continues (snapshot quotidien).\n", + "- **SPARQL** : 1.1 (W3C Recommendation, 2013)." ] }, { - "id": "8aa46fad", "cell_type": "markdown", + "id": "8aa46fad", "metadata": {}, "source": [ - "---\n", - "\n", - "## Resume\n", - "\n", - "| Concept | Description |\n", - "|---------|-------------|\n", - "| **4 règles du Linked Data** | URIs, HTTP, informations utiles, liens entre sources |\n", - "| **5 etoiles Open Data** | Graduation de la qualite des données ouvertes |\n", - "| **DBpedia** | Extraction Wikipedia, URIs lisibles, endpoint `http://dbpedia.org/sparql` |\n", - "| **Wikidata** | Base collaborative, QIDs/PIDs, `SERVICE wikibase:label` |\n", - "| **owl:sameAs** | Pont entre datasets equivalents (DBpedia <-> Wikidata) |\n", - "| **Requêtes federees** | Clause `SERVICE` pour combiner endpoints |\n", - "| **SparqlQueryClient** | Classe .NET async pour interroger des endpoints distants |\n", - "| **Gestion d'erreurs** | `try-catch`, fallback local, `LIMIT` obligatoire |\n", - "\n", - "### Quand utiliser quel endpoint ?\n", - "\n", - "| Scénario | Recommandation | Raison |\n", - "|----------|---------------|--------|\n", - "| Prototype rapide | DBpedia | URIs lisibles |\n", - "| Production | Wikidata | Qualite, fraicheur, CC0 |\n", - "| Texte Wikipedia | DBpedia | Abstracts disponibles |\n", - "| Combinaison | Les deux + `owl:sameAs` | Complementarite |\n", + "## Résumé\n", + "\n", + "| Section | Concepts cles | Endpoints utilises |\n", + "|---------|--------------|-------------------|\n", + "| Installation | dotNetRDF 3.2.1, NuGet | - |\n", + "| Fonctions utilitaires | Async, retry, logs | - |\n", + "| DBpedia | Test de connexion | dbpedia.org/sparql |\n", + "| Requêtes DBpedia | Films, relations, villes | dbpedia.org/sparql |\n", + "| Requêtes avancées | GROUP_CONCAT, OPTIONAL, FILTER | dbpedia.org/sparql |\n", + "| Wikidata | Q-numbers, SERVICE wikibase:label | query.wikidata.org/sparql |\n", + "| DBpedia vs Wikidata | Comparaison, cas d'usage | les deux |\n", + "| Federation | owl:sameAs, SERVICE | les deux |\n", + "| Formats | JSON (.srj), XML (.srx) | - |\n", + "\n", + "**Tous les concepts sont valides**. Le notebook illustre les 4 grandes categories de requêtes SPARQL distantes : simples (DBpedia), avancées (GROUP_CONCAT, OPTIONAL), federation (owl:sameAs), serialisation (JSON/XML).\n", + "\n", + "**Substance pedagogique** :\n", + "- **DBpedia** : 15+ milliards de triplets (encyclopedique).\n", + "- **Wikidata** : 100+ millions d'entités (base de connaissances universelle).\n", + "- **Federation** : combiner les deux via `owl:sameAs`.\n", "\n", "**Pour aller plus loin** :\n", - "- [LOD Cloud](https://lod-cloud.net/) : GeoNames, MusicBrainz, UniProt\n", - "- [SPARQL Federation](https://www.w3.org/TR/sparql11-federated-query/) (W3C)\n", - "- [Wikidata Query Service](https://query.wikidata.org/) (editeur visuel)\n", + "- **SPARQL SERVICE avec plusieurs endpoints** : federer 3+ sources.\n", + "- **Linked Data Fragments** : alternative aux endpoints SPARQL (Tries, BrTP).\n", + "- **VoID** : vocabulaire pour décrire les datasets RDF.\n", + "- **LOD Cloud** : le catalogue des datasets Linked Open Data (lod-cloud.net).\n", "\n", - "**Prochain notebook** : [SW-6-RDFS](SW-6-CSharp-RDFS.ipynb) - Vocabulaire RDFS, inference et hiérarchies de classes\n", + "***\n", "\n", - "---\n", + "**Prochain notebook** : [SW-6-RDFS](SW-6-CSharp-RDFS.ipynb) - Vocabulaire RDFS, inference et hiérarchies de classes\n", "\n", "**Navigation** : [<< 4-SPARQL](SW-4-CSharp-SPARQL.ipynb) | [Index](README.md) | [6-RDFS >>](SW-6-CSharp-RDFS.ipynb)" ] diff --git a/scripts/notebook_tools/twin_pairs.d/sw-5-linked-data.yaml b/scripts/notebook_tools/twin_pairs.d/sw-5-linked-data.yaml index 46b880ca29..9a4e645fc0 100644 --- a/scripts/notebook_tools/twin_pairs.d/sw-5-linked-data.yaml +++ b/scripts/notebook_tools/twin_pairs.d/sw-5-linked-data.yaml @@ -15,6 +15,31 @@ by: myia-po-2023:CoursIA python_sha: 607ce735712708a08ebc37db2f5a7d19a0d16dfc csharp_sha: 9cfb80bc05aa73594102d90d93276e52356a4977 + - date: "2026-09-03" + by: myia-po-2026:CoursIA + python_sha: 607ce735712708a08ebc37db2f5a7d19a0d16dfc + csharp_sha: 88e9828cbe4351e0ad04b4d75bba9f797c17fc8f + content_python_sha: 58b1ce0599c5591de7716a3d68a9e3ebcd973fafcdf2548478690aa445bb4cdc + content_csharp_sha: 4e59cb0c5dd300d4f937ccf5f7574beb81da930370630bec80e5525aa30a4588 + - date: "2026-09-03" + by: myia-po-2026:CoursIA + python_sha: 607ce735712708a08ebc37db2f5a7d19a0d16dfc + csharp_sha: 4c8e29daf900f2def803976cb57e434c34373af2 + content_python_sha: 58b1ce0599c5591de7716a3d68a9e3ebcd973fafcdf2548478690aa445bb4cdc + content_csharp_sha: abeca8c1df4b9292fe2d813fe0024c8316198047130748227d1961bec206eaa4 + - date: "2026-09-03" + by: myia-po-2026:CoursIA + python_sha: 607ce735712708a08ebc37db2f5a7d19a0d16dfc + csharp_sha: a5580ef5ccb3889121fd119f582db9a1556aeba1 + content_python_sha: 58b1ce0599c5591de7716a3d68a9e3ebcd973fafcdf2548478690aa445bb4cdc + content_csharp_sha: 1f3f3c9fdd0887816f07443b669b49c63da7d673d019570013a293c3b52a945e + reason: "Re-attestation apres fix markdown-loss #14117 (cellule finale reecrite par l enrichissement avait perdu les 3 liens de nav [SW-6-RDFS] / [<< 4-SPARQL] / [6-RDFS >>], bloc restaure depuis main) : markdown-only — signature sha256 des (source, execution_count, outputs) des 19 cellules code byte-identique base->HEAD (c5dfc9613796a617). detect_md_content_loss --check findings=0, check_notebook_navlinks --check OK. Jumeau Python SW-5b non touche. parity_level native-both intacte." + - date: "2026-09-03" + by: myia-po-2026:CoursIA + python_sha: 607ce735712708a08ebc37db2f5a7d19a0d16dfc + csharp_sha: 955039144c6f023945d2ef1b07a19e35d15c2b9c + content_python_sha: 58b1ce0599c5591de7716a3d68a9e3ebcd973fafcdf2548478690aa445bb4cdc + content_csharp_sha: a0cb970c8e562b91fcca27edb39aecfef89eed4cdbe283e15f08e619eaef062f known_differences: - "Socle commun : consommation de Linked Data distante (dereferencement d'URI, endpoints SPARQL)." - "Lib-vs-lib : C# = `dotNetRDF` (`VDS.RDF.Query` remote endpoint). Python = `SPARQLWrapper` (client dedie endpoints SPARQL) + `rdflib`. Asymetrie leger : Python ajoute `SPARQLWrapper` specialise pour le reseau distant, le C# unifie dans dotNetRDF."