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

Processing tabular data from select queries #292

Merged
merged 2 commits into from
Dec 26, 2024
Merged
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 @@ -46,6 +46,11 @@ public static String nextResultsToValuesClause(ResultSet resultSet, int rowsCoun
}

public static String serializeToSparql(RDFNode rdfNode) {

if (rdfNode == null) {
return "UNDEF";
}

ParameterizedSparqlString pss= new ParameterizedSparqlString();
pss.appendNode(rdfNode);
return pss.toString();
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
package cz.cvut.spipes.util.query;

import org.apache.jena.query.QuerySolution;
import org.apache.jena.query.QuerySolutionMap;
import org.apache.jena.query.ResultSet;
import org.apache.jena.rdf.model.Model;
import org.apache.jena.sparql.engine.binding.Binding;

import java.util.ArrayList;
import java.util.List;

/**
* ResultSet that extends the original result set by adding bindings from the previous query solution.
* The previous query solution is available under the same variable name with suffix "__previous".
*
* <p>Example use:</p>
* <pre>
* ResultSet resultSet = ...;
* ResultSet extendedResultSet = new OneStepBackExtendedResultSet(resultSet);
* while (extendedResultSet.hasNext()) {
* QuerySolution querySolution = extendedResultSet.next();
* RDFNode currentResource = querySolution.get("resource");
* RDFNode previousResource = querySolution.get("resource__previous");
* }
* </pre>
*/
public class OneStepBackExtendedResultSet implements ResultSet {

private static final String PREVIOUS_BINDING_SUFFIX = "__previous";

private final ResultSet resultSet;
private final List<String> resultVars;

private QuerySolution previousQuerySolution;

public OneStepBackExtendedResultSet(ResultSet resultSet) {
this.resultSet = resultSet;
this.previousQuerySolution = new QuerySolutionMap();
this.resultVars = getExtendedResultVars(resultSet.getResultVars());
}

private static List<String> getExtendedResultVars(List<String> originalResultVars) {
originalResultVars.stream()
.filter(s -> s.endsWith(PREVIOUS_BINDING_SUFFIX))
.findAny()
.ifPresent(s -> {
throw new IllegalArgumentException(
"The result set already contains a variable with suffix " + PREVIOUS_BINDING_SUFFIX
);
});
List<String> joinedList = new ArrayList<String>();
joinedList.addAll(originalResultVars);
joinedList.addAll(originalResultVars.stream()
.map(v -> v + PREVIOUS_BINDING_SUFFIX)
.toList());
return joinedList;
}

@Override
public boolean hasNext() {
return resultSet.hasNext();
}

@Override
public QuerySolution next() {
QuerySolution nextQuerySolution = resultSet.next();

QuerySolutionMap querySolution = new QuerySolutionMap();
querySolution.addAll(nextQuerySolution);
previousQuerySolution.varNames().
forEachRemaining(
varName -> querySolution.add(varName + PREVIOUS_BINDING_SUFFIX, previousQuerySolution.get(varName))
);
previousQuerySolution = nextQuerySolution;
return querySolution;
}

@Override
public QuerySolution nextSolution() {
return this.next();
}

@Override
public Binding nextBinding() {
throw new UnsupportedOperationException("Not implemented");
}

@Override
public int getRowNumber() {
return resultSet.getRowNumber();
}

@Override
public List<String> getResultVars() {
return resultVars;
}

@Override
public Model getResourceModel() {
return resultSet.getResourceModel();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,10 @@
import org.topbraid.spin.model.Select;

/**
* Apply construct query with chunked values and scrollable cursor.
* See {@link ApplyConstructWithChunkedValuesModule} and {@link ApplyConstructWithScrollableCursorModule}
* for more details.
*
* TODO Order of queries is not enforced.
* TODO issue with redundant call {@link ScrollableCursorProvider}
* TODO supports only one CONSTRUCT query
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@

import cz.cvut.spipes.constants.KBSS_MODULE;
import cz.cvut.spipes.constants.SML;
import cz.cvut.spipes.engine.VariablesBinding;
import cz.cvut.spipes.modules.annotations.SPipesModule;
import cz.cvut.spipes.util.QueryUtils;
import cz.cvut.spipes.util.query.OneStepBackExtendedResultSet;
import lombok.extern.slf4j.Slf4j;
import org.apache.jena.query.*;
import org.apache.jena.rdf.model.Model;
Expand All @@ -16,13 +16,41 @@
import java.util.Objects;

/**
* TODO Order of queries is not enforced.
* Apply construct query with chunked values.
* The construct query is provided as a template with marker `#${VALUES}`.
* The marker is substituted with chunked values from a select query.
*
* <p>Example select query:</p>
* <pre>
* SELECT ?person ?lastName
* WHERE {
* ?person a foaf:Person .
* ?person foaf:lastName ?lastName .
* }
* </pre>
*
* <p>Example construct query:</p>
* <pre>
* CONSTRUCT {
* ?person a :User .
* ?person :has-last-name ?lastName .
* }
* WHERE {
* #${VALUES}
* FILTER(strlen(?lastName) < 20)
* }
* </pre>
*
* <p>TODOs:</p>
* <ul>
* <li>Order of queries is not enforced.</li>
* </ul>
*/
@Slf4j
@SPipesModule(label = "apply construct with chunked values", comment = "Apply construct with chunked values.")
public class ApplyConstructWithChunkedValuesModule extends ApplyConstructAbstractModule {

private static final String TYPE_URI = KBSS_MODULE.uri;
private static final String TYPE_URI = KBSS_MODULE.uri + "apply-construct-with-chunked-values";
private static final String TYPE_PREFIX = TYPE_URI + "/";
private static final int DEFAULT_CHUNK_SIZE = 10;
private static final String VALUES_CLAUSE_MARKER_NAME = "VALUES";
Expand Down Expand Up @@ -68,9 +96,7 @@ public void initializeQuery() {

log.debug("Executing query of chunk provider ...");

selectResultSet = execution.execSelect();

VariablesBinding variablesBinding = new VariablesBinding();
selectResultSet = new OneStepBackExtendedResultSet(execution.execSelect());

if (! selectResultSet.hasNext()) {
log.debug("\"{}\" query did not return any values.", getLabel());
Expand Down Expand Up @@ -103,7 +129,7 @@ protected String substituteQueryMarkers(int currentIteration, String queryStr) {

String markerValue = QueryUtils.nextResultsToValuesClause(getCurrentResultSetInstance(), chunkSize);

log.debug("Creating query with values clause: \n{}.", markerValue );
log.trace("Creating query with values clause: \n{}.", markerValue );

return QueryUtils
.substituteMarkers(VALUES_CLAUSE_MARKER_NAME,
Expand Down
Loading