1

I am working on a personal project using PHP and RDF4J Workbench Service and I have a repository with 3 contexts, one for robot categories, one for those robots that belongs to these categories and another one in which I store the owl:sameAs property to wikidata identifiers for the initial data.

The problem is that a user can insert robots and the new inserted ones will not be linked to wikidata identifiers but instead will have unique URI's resulted by just removing the extra-space and concatenating the name that user had inserted. Now, when I query for the data I need, I want all these robots to be shown, but if my query is as it follows:

PREFIX : <http://example.com#> 
PREFIX rdfs: <http://www.w3.org/2000/01/rdf-schema#> 
PREFIX wdt: <http://www.wikidata.org/prop/direct/> 
PREFIX owl: <http://www.w3.org/2002/07/owl#> 
PREFIX wd: <http://www.wikidata.org/entity/> 
SELECT ?name ?image ?releaseDate ?skills ?manufacturer ?mass
 WHERE { 
         { SELECT ?robot ?image ?releaseDate ?skills ?manufacturer ?mass 
              WHERE {
                     <http://example.com#EducationalRobots> a :Category; :hasRobots [:idRobot ?x] .      
                     ?x owl:sameAs ?robot . 
                     ?x :hasImage ?image . 
                     ?x :releaseDate ?releaseDate . 
                     ?x :hasSkills ?skills . 
                     ?x :manufacturer ?manufacturer. 
                     ?x :hasMass ?mass
                    } 
             } 
    SERVICE <https://query.wikidata.org/sparql> { 
             ?robot rdfs:label ?name .
             FILTER(lang(?name)="en") 
        } 
}

in the end I will only get the initial statements I inserted, those that do have a wikidata identifier linked. So I was thinking of maybe making the difference in the backend, using an if statement where I can ASK if the items from the requested category have an identifier and if so do a specific query, the one from above, else just replace the query and retrieve the local URI. But isn't there a way of doing this directly in the query, so the code will look much cleaner ? Such as SELECT IF statement or so. I am not familiar with advanced query practices and I would appreciate any advice.

Snippet of the graph:

enter image description here

Request:

function getDetails(id) {
  //console.log(id);
  let url = "query.php";
  $.ajax({
    type: "POST",
    url:url,
    data: {"id": id },
    success: function (response){
      let final = JSON.parse(response);
      console.log(final.results);
      $('#tableId').find('tbody')
          .empty();

      final.results.bindings.forEach((item, index)=>{   
        console.log(item);
        if (item.name) { 
          s = item.name;
          id = s.split(" ").join("");  

             $('#tableId').find('tbody')
                .append($(`<tr id=${id} class="border_bottom">`)
                  .append($('<td class="mt-3 text-center">')
                    .append(`<p class='mt-4'><b><i>${item.name.value} </i></b></p><br><button class='btn btn-danger' id='${item.name.value}' onclick="deleteMovie(this.id, '${id}')">Delete</button></div>`)
                  )
                  .append($('<td class="text-center">')
                    .append($('<img height="150" width="250">')
                      .attr('src', 'images/'+item.image.value)
                    )
                  )
                  .append($('<td>')
                    .append($('<small> Manufacturer: </small><p>'+ item.manufacturer.value+ '</p><small>Release date: </small><p>' + item.releaseDate.value +'</p><small>Skills: </small><p>'+item.skills.value +'</p><small> Mass: </small><p>'+item.mass.value + ' kg</p>'))
                  )
                )     
        }
    })   
    }
  });
}

And in the backend I'm working with EasyRdf\Sparql\Client:

<?php

$id = $_POST['id'];

require 'vendor/autoload.php';

$address = "http://localhost:8080/rdf4j-server/repositories/graph?query=";
 
$query = "PREFIX : <http://example.com#> PREFIX rdfs: <http://www.w3.org/2000/01/rdf-schema#> 
PREFIX wdt: <http://www.wikidata.org/prop/direct/> 
PREFIX owl: <http://www.w3.org/2002/07/owl#> 
PREFIX wd: <http://www.wikidata.org/entity/> 
SELECT ?name ?image ?releaseDate ?skills ?manufacturer ?mass
 WHERE { { SELECT ?robot ?image ?releaseDate ?skills ?manufacturer ?mass WHERE {<".$id."> a :Category; :hasRobots [:idRobot ?x] . ?x owl:sameAs ?robot . ?x :hasImage ?image . ?x :releaseDate ?releaseDate . ?x :hasSkills ?skills . ?x :manufacturer ?manufacturer. ?x :hasMass ?mass} } SERVICE <https://query.wikidata.org/sparql> { ?robot rdfs:label ?name . FILTER(lang(?name)=\"en\") } }";


$qry = urlencode($query);
//  print $qry;

$client = new EasyRdf\Http\Client($address.$qry);

$client->setHeaders("Accept","application/sparql-results+json");

$resultJSON = $client->request()->getBody();

// print $resultJSON;

$finalResult = new EasyRdf\Sparql\Result($rezultatJSON,"application/sparql-results+json");
// print $finalResult;

return $finalResult;

Thank you !

theospace
  • 77
  • 3
  • 10
  • it's possible to some extent, yes. Can you share the sample data as text please such that I can try it locally? – UninformedUser May 02 '21 at 11:55
  • 1
    Yes, you can have it here: https://github.com/mirasoil/MoviesKnowledgeGraph/blob/main/robograph.ttl – theospace May 02 '21 at 13:01
  • 1
    `PREFIX : PREFIX rdfs: PREFIX wdt: PREFIX owl: PREFIX wd: SELECT ?name ?image ?releaseDate ?skills ?manufacturer ?mass FROM :robots FROM :samenessgraph FROM :categories WHERE {` – UninformedUser May 02 '21 at 15:53
  • 1
    `:EducationalRobots a :Category; :hasRobots [:idRobot ?x] . ?x :hasImage ?image . ?x :releaseDate ?releaseDate . ?x :hasSkills ?skills . ?x :manufacturer ?manufacturer. ?x :hasMass ?mass . OPTIONAL { ?x owl:sameAs ?robot } BIND(COALESCE(?robot, ) as ?input) OPTIONAL { SERVICE { ?input rdfs:label ?wdName . FILTER(lang(?name)="en") } } OPTIONAL { ?x rdfs:label ?label } BIND(COALESCE(?wdName, ?label, strafter(str(?x), str(:))) as ?name) }` – UninformedUser May 02 '21 at 15:54
  • 1
    this query takes Wikidata name first, then an optional label in your data and last, the local name of the URI. Maybe this is what you want, if not please clarify. – UninformedUser May 02 '21 at 15:55
  • 1
    By the way, some of your date literals are invalid. The schema is `YYYY-MM-DD`, you mixed up month and day position. – UninformedUser May 02 '21 at 15:57
  • This is exactly what I was looking for, I'll have a deep look into COALESCE operator but I get the main idea of the whole process. It doesn't work with as absoulte uri but instead I used "unknown" and it works really smooth. Also, I noticed that, I will double check, thanks for noticing :) – theospace May 02 '21 at 16:25
  • 1
    glad it works. `COALESCE` here is basically a shortcut for `IF(bound(?robot), ?robot, "unknown")`, i.e. it takes the first bound value. The main difference is that `COALESCE` is also able to recover from an evaluation error. Just another comment as I did work on your sample data, the file extension should be `.trig` as it is a Trig file containing graphs in comparison to just triples in a Turtle file – UninformedUser May 03 '21 at 06:54
  • I understand, you definitely spared me a lot of time since I didn't know I can let's say "join" the graphs using `FROM`, I used to have `GRAPH` operator before every query and as you can imagine it's not such a good practice so I really appreciate your help. Yes indeed, I worked with simple turtle syntax before and I forgot to change the extension after inserting graphs but again, thanks a lot for letting me know :) – theospace May 03 '21 at 07:13

1 Answers1

1

The final query:

PREFIX : <http://example.com#>  
PREFIX rdfs: <http://www.w3.org/2000/01/rdf-schema#>  
PREFIX wdt: <http://www.wikidata.org/prop/direct/>  
PREFIX owl: <http://www.w3.org/2002/07/owl#>  
PREFIX wd: <http://www.wikidata.org/entity/>  

SELECT ?name ?image ?releaseDate ?skills ?manufacturer ?mass 
FROM :robots FROM :samenessgraph FROM :categories  WHERE {
  :EducationalRobots a :Category; :hasRobots [:idRobot ?x] .                            
  ?x :hasImage ?image .   
  ?x :releaseDate ?releaseDate .  
  ?x :hasSkills ?skills .   
  ?x :manufacturer ?manufacturer .   
    ?x :hasMass ?mass . 
  OPTIONAL { ?x owl:sameAs ?robot } 
  BIND(COALESCE(?robot, "unknown") as ?input) OPTIONAL {     
    SERVICE <https://query.wikidata.org/sparql> {               
      ?input rdfs:label ?wdName .              
      FILTER(lang(?name)="en")          
    }          
  }     
  OPTIONAL { ?x rdfs:label ?label }     
  BIND(COALESCE(?wdName, ?label, strafter(str(?x), str(:))) as ?name) }

as mentioned in the comments.

theospace
  • 77
  • 3
  • 10