Skip to content

Commit

Permalink
[#184] Add SPINRDF custom function call from query test
Browse files Browse the repository at this point in the history
  • Loading branch information
blcham committed Aug 14, 2024
1 parent 651c154 commit afa3e82
Show file tree
Hide file tree
Showing 4 changed files with 217 additions and 40 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
package cz.cvut.spipes.function.time;

import cz.cvut.spipes.constants.KBSS_TIMEF;
import cz.cvut.spipes.function.ValueFunction;
import org.apache.jena.datatypes.RDFDatatype;
import org.apache.jena.datatypes.xsd.XSDDatatype;
import org.apache.jena.datatypes.xsd.impl.XSDBaseStringType;
import org.apache.jena.datatypes.xsd.impl.XSDDateType;
import org.apache.jena.graph.Node;
import org.apache.jena.sparql.expr.NodeValue;
import org.apache.jena.sparql.function.FunctionEnv;
import org.topbraid.spin.arq.AbstractFunction2;

import java.time.LocalDate;
import java.time.format.DateTimeParseException;
import java.util.Optional;

/**
* Extend specified `date` by number of `days`. Return typed literal with same datatype.
* Currently, supports only xsd:date datatype.
*/
public class AddDaysDeprecated extends AbstractFunction2 implements ValueFunction {


private static final String TYPE_IRI = KBSS_TIMEF.uri + "add-days-deprecated";

@Override
public String getTypeURI() {
return TYPE_IRI;
}

@Override
protected NodeValue exec(Node date, Node days, FunctionEnv env) {

Long daysToAdd = getDays(days);
RDFDatatype datatype = getDatatype(date);

try {
if (datatype != null && daysToAdd != null) {
//TODO quite slow to parse everytime
String newDate = LocalDate.parse(date.getLiteral().getValue().toString()).plusDays(daysToAdd).toString();
return NodeValue.makeNode(newDate, datatype);
}
} catch (DateTimeParseException e){
}

return null;
}

private Long getDays(Node days) {
return Optional.of(days)
.filter(Node::isLiteral)
.filter(n -> n.getLiteralValue() instanceof Integer)
.map(n -> ((Integer) n.getLiteralValue()).longValue())
.orElse(null);
}

RDFDatatype getDatatype(Node date) {

return Optional.of(date)
.filter(Node::isLiteral)
.map(n -> getNewDatatype(n.getLiteralDatatype()))
.orElse(null);
}

RDFDatatype getNewDatatype(RDFDatatype datatype){
if (datatype instanceof XSDDateType) {
return XSDDatatype.XSDdate;
}
if (datatype instanceof XSDBaseStringType) {
return XSDDatatype.XSDstring;
}
return null;
}
}
120 changes: 83 additions & 37 deletions s-pipes-core/src/test/java/cz/cvut/spin/SpinIntegrationTest.java
Original file line number Diff line number Diff line change
@@ -1,42 +1,74 @@
package cz.cvut.spin;

import cz.cvut.spipes.engine.PipelineFactory;
import org.apache.jena.ontology.OntModelSpec;
import org.apache.jena.query.*;
import org.apache.jena.rdf.model.*;
import org.apache.jena.util.FileUtils;
import org.junit.jupiter.api.Test;
import org.topbraid.spin.model.SPINFactory;
import org.topbraid.spin.system.SPINModuleRegistry;
import org.topbraid.spin.util.SPINExpressions;
import org.topbraid.spin.vocabulary.SP;
import org.apache.jena.ontology.OntModelSpec;
import org.apache.jena.query.*;
import org.apache.jena.rdf.model.*;
import org.apache.jena.util.FileUtils;
import org.jetbrains.annotations.NotNull;
import org.junit.jupiter.api.Test;
import org.topbraid.spin.model.SPINFactory;
import org.topbraid.spin.system.SPINModuleRegistry;
import org.topbraid.spin.util.SPINExpressions;
import org.topbraid.spin.vocabulary.SP;

import java.io.InputStream;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertTrue;

public class SpinIntegrationTest {

import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
@Test
public void executeCustomSPINRDFFunctionWithinQuery() {
// load custom function definition from RDF
Model funcDefModel = getCustomSPINRDFFunctionModel();

import static org.junit.jupiter.api.Assertions.assertEquals;
// register custom function
//SPINModuleRegistry.get().init();
SPINModuleRegistry.get().registerAll(funcDefModel, null);

public class SpinIntegrationTest {
String repositoryUrl = "http://repository.org";
String graphId = "http://graphid.org";

String queryString = String.format("""
PREFIX kbss-spif: <http://onto.fel.cvut.cz/ontologies/lib/spin-function/>
SELECT ?sparqlServiceUrl
WHERE {
BIND(kbss-spif:create-sparql-service-url(
"%s",
"%s"
) AS ?sparqlServiceUrl)
}
""", repositoryUrl, graphId);

Model model = ModelFactory.createDefaultModel();

@Test
public void executeSPINExpressionWithCustomSpinFunction() throws UnsupportedEncodingException {
Query query = QueryFactory.create(queryString);

QueryExecution qexec = QueryExecutionFactory.create(query, model);
ResultSet results = qexec.execSelect();

// load custom function definition
Model funcDefModel = ModelFactory.createOntologyModel(OntModelSpec.OWL_MEM);
// Model funcDefModel = ModelFactory.createDefaultModel(); // TODO this does not work
assertTrue(results.hasNext(), "No results found");

final InputStream funcDefIs = this.getClass().getResourceAsStream("/spin/spin-function.spin.ttl");
QuerySolution soln = results.nextSolution();
assertEquals(
soln.getResource("sparqlServiceUrl").getURI(),
constructServiceUrl(repositoryUrl, graphId)
);

funcDefModel.read(funcDefIs, null, FileUtils.langTurtle);
}

@Test
public void executeSPINExpressionWithCustomSPINRDFFunction() {
// load custom function definition from RDF
Model funcDefModel = getCustomSPINRDFFunctionModel();

// register custom function
//SPINModuleRegistry.get().init();
SPINModuleRegistry.get().registerAll(funcDefModel, null);



// load custom function call
Model funcCallModel = ModelFactory.createDefaultModel();

Expand All @@ -51,20 +83,17 @@ public void executeSPINExpressionWithCustomSpinFunction() throws UnsupportedEnco
// evaluate SPIN expression
QuerySolutionMap bindings = new QuerySolutionMap();
String repositoryUrl = "http://repository.org";
String reportGraphId = "http://graphid.org";
bindings.add("repositoryUrl", ResourceFactory.createPlainLiteral(repositoryUrl));
bindings.add("reportGraphId", ResourceFactory.createPlainLiteral(reportGraphId));

String graphId = "http://graphid.org";
bindings.add("sparqlEndpoint", ResourceFactory.createPlainLiteral(repositoryUrl));
bindings.add("defaultGraphUri", ResourceFactory.createPlainLiteral(graphId));

RDFNode node = SPINExpressions.evaluate(callExpr, callExpr.getModel(), bindings); //TODO resource.getModel() should be part o context


assertEquals(node.toString(), repositoryUrl + "?default-graph-uri=" + URLEncoder.encode(reportGraphId, StandardCharsets.UTF_8) );
assertEquals(node.toString(), constructServiceUrl(repositoryUrl, graphId));
}

@Test
public void executeSPINQueryWithCustomJavaFunction() {
PipelineFactory pipelineFactory = new PipelineFactory();

String queryString = """
PREFIX kbss-timef: <http://onto.fel.cvut.cz/ontologies/lib/function/time/>
Expand All @@ -78,13 +107,30 @@ public void executeSPINQueryWithCustomJavaFunction() {

Query query = QueryFactory.create(queryString);


QueryExecution qexec = QueryExecutionFactory.create(query, model);
ResultSet results = qexec.execSelect();

if (results.hasNext()) {
QuerySolution soln = results.nextSolution();
assertEquals(soln.getLiteral("nextDay").getString(), "2022-01-02");
}
assertTrue(results.hasNext(), "No results found");

QuerySolution soln = results.nextSolution();
assertEquals(soln.getLiteral("nextDay").getString(), "2022-01-02");
}

@NotNull
private Model getCustomSPINRDFFunctionModel() {
// load custom function definition
Model funcDefModel = ModelFactory.createOntologyModel(OntModelSpec.OWL_MEM);
// Model funcDefModel = ModelFactory.createDefaultModel(); // TODO this does not work

final InputStream funcDefIs = this.getClass().getResourceAsStream("/spin/spin-function.spin.ttl");

funcDefModel.read(funcDefIs, null, FileUtils.langTurtle);

return funcDefModel;
}

@NotNull
private String constructServiceUrl(String repositoryUrl, String graphId) {
return String.format("%s?default-graph-uri=%s", repositoryUrl, URLEncoder.encode(graphId, StandardCharsets.UTF_8));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
package cz.cvut.spipes.function.time;

import org.apache.jena.datatypes.RDFDatatype;
import org.apache.jena.datatypes.xsd.XSDDatatype;
import org.apache.jena.graph.Node;
import org.apache.jena.sparql.expr.NodeValue;
import org.junit.jupiter.api.Test;

import java.util.stream.Stream;

import static org.junit.jupiter.api.Assertions.assertEquals;

public class AddDaysDeprecatedTest {

@Test
public void execReturnsTimeFromPast() {

AddDaysDeprecated addDays = new AddDaysDeprecated();
Node date = getDateNode("2022-01-01").asNode();
Node days = NodeValue.makeNodeDecimal("-1").asNode();

NodeValue returnedDate = addDays.exec(date, days, null);

NodeValue expectedDate = getDateNode("2021-12-31");
assertEquals(expectedDate, returnedDate);
}

@Test
public void execReturnsDatatypeOfInputLiteral() {
Node days = NodeValue.makeNodeDecimal("1").asNode();

Stream.of(XSDDatatype.XSDdate, XSDDatatype.XSDstring).forEach(
dt -> {
Node date = getNode("2021-12-31", dt).asNode();

AddDaysDeprecated addDays = new AddDaysDeprecated();
NodeValue returnedDate = addDays.exec(date, days, null);

NodeValue expectedDate = getNode("2022-01-01", dt);
assertEquals(expectedDate, returnedDate);
});
}


private NodeValue getDateNode(String date){
return getNode(date, XSDDatatype.XSDdate);
}

private NodeValue getNode(String date, RDFDatatype datatype) {
return NodeValue.makeNode(
date,
null,
datatype.getURI()
);
}
}
6 changes: 3 additions & 3 deletions s-pipes-core/src/test/resources/spin/spin-function-call.ttl
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,12 @@
.

[
rdf:type kbss-spif:create-sparql-service-url ;
rdf:type kbss-spif:create-sparql-service-url-deprecated ;
sp:arg1 [
sp:varName "repositoryUrl" ;
sp:varName "sparqlEndpoint" ;
] ;
sp:arg2 [
sp:varName "reportGraphId" ;
sp:varName "defaultGraphUri" ;
] ;
]
.

0 comments on commit afa3e82

Please sign in to comment.