Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

[#184] Refactor out spin #251

Open
wants to merge 5 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,8 @@
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 org.apache.jena.sparql.function.FunctionBase2;

import java.time.LocalDate;
import java.time.format.DateTimeParseException;
Expand All @@ -19,7 +17,7 @@
* Extend specified `date` by number of `days`. Return typed literal with same datatype.
* Currently, supports only xsd:date datatype.
*/
public class AddDays extends AbstractFunction2 implements ValueFunction {
public class AddDays extends FunctionBase2 implements ValueFunction {


private static final String TYPE_IRI = KBSS_TIMEF.uri + "add-days";
Expand All @@ -29,37 +27,37 @@ public String getTypeURI() {
return TYPE_IRI;
}

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

@Override
public NodeValue exec(NodeValue date, NodeValue days) {
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);
String newDate = LocalDate.parse(date.asNode().getLiteral().getValue().toString()).plusDays(daysToAdd).toString();
return NodeValue.makeNode(newDate, datatype);
}
} catch (DateTimeParseException e){
}

return null;
}

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

RDFDatatype getDatatype(Node date) {
RDFDatatype getDatatype(NodeValue date) {

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

Expand Down
105 changes: 91 additions & 14 deletions s-pipes-core/src/test/java/cz/cvut/spin/SpinIntegrationTest.java
Original file line number Diff line number Diff line change
@@ -1,35 +1,70 @@
package cz.cvut.spin;

import cz.cvut.spipes.engine.PipelineFactory;
import org.apache.jena.ontology.OntModelSpec;
import org.apache.jena.query.QuerySolutionMap;
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.io.UnsupportedEncodingException;
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 {


@Test
public void executeSPINExpressionWithCustomSpinFunction() throws UnsupportedEncodingException {
public void executeCustomSPINRDFFunctionWithinQuery() {
// load custom function definition from RDF
Model funcDefModel = getCustomSPINRDFFunctionModel();

// load custom function definition
Model funcDefModel = ModelFactory.createOntologyModel(OntModelSpec.OWL_MEM);
// Model funcDefModel = ModelFactory.createDefaultModel(); // TODO this does not work
// register custom function
//SPINModuleRegistry.get().init();
SPINModuleRegistry.get().registerAll(funcDefModel, null);

final InputStream funcDefIs = this.getClass().getResourceAsStream("/spin/spin-function.spin.ttl");
String repositoryUrl = "http://repository.org";
String graphId = "http://graphid.org";

funcDefModel.read(funcDefIs, null, FileUtils.langTurtle);
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();

Query query = QueryFactory.create(queryString);

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

assertTrue(results.hasNext(), "No results found");

QuerySolution soln = results.nextSolution();
assertEquals(
soln.getResource("sparqlServiceUrl").getURI(),
constructServiceUrl(repositoryUrl, graphId)
);

}

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

// register custom function
//SPINModuleRegistry.get().init();
Expand All @@ -49,14 +84,56 @@ 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(), constructServiceUrl(repositoryUrl, graphId));
}

@Test
public void executeSPINQueryWithCustomJavaFunction() {

PipelineFactory.registerFunctionsOnClassPath();

String queryString = """
PREFIX kbss-timef: <http://onto.fel.cvut.cz/ontologies/lib/function/time/>
PREFIX xsd: <http://www.w3.org/2001/XMLSchema#>
SELECT ?nextDay
WHERE {
BIND(kbss-timef:add-days("2022-01-01"^^xsd:date, 1) AS ?nextDay)
}
""";
Model model = ModelFactory.createDefaultModel();

Query query = QueryFactory.create(queryString);

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

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;
}

assertEquals(node.toString(), repositoryUrl + "?default-graph-uri=" + URLEncoder.encode(reportGraphId, StandardCharsets.UTF_8) );
@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
Expand Up @@ -2,7 +2,6 @@

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;

Expand All @@ -16,25 +15,25 @@ public class AddDaysTest {
public void execReturnsTimeFromPast() {

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

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

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

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

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

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

NodeValue expectedDate = getNode("2022-01-01", dt);
assertEquals(expectedDate, returnedDate);
Expand Down
4 changes: 2 additions & 2 deletions s-pipes-core/src/test/resources/spin/spin-function-call.ttl
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,10 @@
[
rdf:type kbss-spif:create-sparql-service-url ;
sp:arg1 [
sp:varName "repositoryUrl" ;
sp:varName "sparqlEndpoint" ;
] ;
sp:arg2 [
sp:varName "reportGraphId" ;
sp:varName "defaultGraphUri" ;
] ;
]
.
38 changes: 1 addition & 37 deletions s-pipes-core/src/test/resources/spin/spin-function.spin.ttl
Original file line number Diff line number Diff line change
Expand Up @@ -19,41 +19,10 @@ kbss-spif:create-sparql-service-url
rdf:type spin:Function ;
spin:body [
rdf:type sp:Select ;
sp:resultVariables (
[
sp:varName "serviceUrl"^^xsd:string ;
]
) ;
sp:text """SELECT ?serviceUrl
WHERE {
BIND (IRI(CONCAT(str(?arg1), \"?default-graph-uri=\", ENCODE_FOR_URI(str(?arg2)))) AS ?serviceUrl) .
}"""^^xsd:string ;
sp:where (
[
rdf:type sp:Bind ;
sp:expression [
rdf:type sp:iri ;
sp:arg1 [
rdf:type sp:concat ;
sp:arg1 [
rdf:type sp:str ;
sp:arg1 spin:_arg1 ;
] ;
sp:arg2 "?default-graph-uri=" ;
sp:arg3 [
rdf:type sp:encode_for_uri ;
sp:arg1 [
rdf:type sp:str ;
sp:arg1 spin:_arg2 ;
] ;
] ;
] ;
] ;
sp:variable [
sp:varName "serviceUrl"^^xsd:string ;
] ;
]
) ;
}""" ;
] ;
spin:constraint [
rdf:type spl:Argument ;
Expand All @@ -67,9 +36,4 @@ WHERE {
rdfs:comment "URI of named graph (e.g. http://example.org/my-ontology)."^^xsd:string ;
] ;
rdfs:comment "Construct sparql service url from ?sparqlEndpointUrl and ?namedGraphURI."^^xsd:string ;
rdfs:subClassOf spin:Functions ;
.
arg:namedGraphUri
rdf:type rdf:Property ;
rdfs:subPropertyOf sp:arg ;
.
Loading