Repository: marmotta Updated Branches: refs/heads/develop 7a73135e0 -> 4bc783a95
SPARQL: try preserving the type of string literals when applying functions that return string Project: http://git-wip-us.apache.org/repos/asf/marmotta/repo Commit: http://git-wip-us.apache.org/repos/asf/marmotta/commit/4bc783a9 Tree: http://git-wip-us.apache.org/repos/asf/marmotta/tree/4bc783a9 Diff: http://git-wip-us.apache.org/repos/asf/marmotta/diff/4bc783a9 Branch: refs/heads/develop Commit: 4bc783a95f2a7d0849bd00696cd49d1d3d0ead44 Parents: 7a73135 Author: Sebastian Schaffert <[email protected]> Authored: Tue Nov 4 17:00:56 2014 +0100 Committer: Sebastian Schaffert <[email protected]> Committed: Tue Nov 4 17:00:56 2014 +0100 ---------------------------------------------------------------------- .../builder/LiteralTypeExpressionFinder.java | 68 +++++++++++++++++ .../kiwi/sparql/builder/SQLBuilder.java | 43 ++++++++++- .../kiwi/sparql/builder/SQLVariable.java | 28 +++++++ .../persistence/KiWiSparqlConnection.java | 79 +++++++++++++------- 4 files changed, 189 insertions(+), 29 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/marmotta/blob/4bc783a9/libraries/kiwi/kiwi-sparql/src/main/java/org/apache/marmotta/kiwi/sparql/builder/LiteralTypeExpressionFinder.java ---------------------------------------------------------------------- diff --git a/libraries/kiwi/kiwi-sparql/src/main/java/org/apache/marmotta/kiwi/sparql/builder/LiteralTypeExpressionFinder.java b/libraries/kiwi/kiwi-sparql/src/main/java/org/apache/marmotta/kiwi/sparql/builder/LiteralTypeExpressionFinder.java new file mode 100644 index 0000000..020657b --- /dev/null +++ b/libraries/kiwi/kiwi-sparql/src/main/java/org/apache/marmotta/kiwi/sparql/builder/LiteralTypeExpressionFinder.java @@ -0,0 +1,68 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.marmotta.kiwi.sparql.builder; + +import org.apache.marmotta.kiwi.sparql.function.NativeFunction; +import org.apache.marmotta.kiwi.sparql.function.NativeFunctionRegistry; +import org.openrdf.query.algebra.FunctionCall; +import org.openrdf.query.algebra.QueryModelNode; +import org.openrdf.query.algebra.ValueExpr; +import org.openrdf.query.algebra.Var; +import org.openrdf.query.algebra.helpers.QueryModelVisitorBase; + +/** + * Functions that return a string literal do so with the string literal of the same kind as the first + * argument (simple literal, plain literal with same language tag, xsd:string). This visitor + * tries finding the relevant subexpression in a complex value expression. + * + * @author Sebastian Schaffert ([email protected]) + */ +public class LiteralTypeExpressionFinder extends QueryModelVisitorBase<RuntimeException> { + + protected Var expr = null; + + public LiteralTypeExpressionFinder(ValueExpr expr) { + expr.visit(this); + } + + @Override + public void meet(FunctionCall node) throws RuntimeException { + NativeFunction nf = NativeFunctionRegistry.getInstance().get(node.getURI()); + if(node.getArgs().size() > 0 && nf.getReturnType() == OPTypes.STRING) { + node.getArgs().get(0).visit(this); + } + // otherwise stop here, the function call hides the type and language anyways + } + + @Override + public void meet(Var node) throws RuntimeException { + expr = node; + } + + /** + * Method called by all of the other <tt>meet</tt> methods that are not + * overridden in subclasses. This method can be overridden in subclasses to + * define default behaviour when visiting nodes. The default behaviour of + * this method is to visit the node's children. + * + * @param node The node that is being visited. + */ + @Override + protected void meetNode(QueryModelNode node) throws RuntimeException { + } +} http://git-wip-us.apache.org/repos/asf/marmotta/blob/4bc783a9/libraries/kiwi/kiwi-sparql/src/main/java/org/apache/marmotta/kiwi/sparql/builder/SQLBuilder.java ---------------------------------------------------------------------- diff --git a/libraries/kiwi/kiwi-sparql/src/main/java/org/apache/marmotta/kiwi/sparql/builder/SQLBuilder.java b/libraries/kiwi/kiwi-sparql/src/main/java/org/apache/marmotta/kiwi/sparql/builder/SQLBuilder.java index 20899c2..fd01c1a 100644 --- a/libraries/kiwi/kiwi-sparql/src/main/java/org/apache/marmotta/kiwi/sparql/builder/SQLBuilder.java +++ b/libraries/kiwi/kiwi-sparql/src/main/java/org/apache/marmotta/kiwi/sparql/builder/SQLBuilder.java @@ -273,6 +273,11 @@ public class SQLBuilder { sv.setProjectionType(getProjectionType(ext.getExpr())); } + // Functions that return a string literal do so with the string literal of the same kind as the first + // argument (simple literal, plain literal with same language tag, xsd:string). + sv.setLiteralTypeExpression(getLiteralTypeExpression(ext.getExpr())); + sv.setLiteralLangExpression(getLiteralLangExpression(ext.getExpr())); + addVariable(sv); } @@ -400,6 +405,7 @@ public class SQLBuilder { } + private void prepareConditions() throws UnsatisfiableQueryException { // build the where clause as follows: // 1. iterate over all patterns and for each resource and literal field in subject, @@ -525,6 +531,13 @@ public class SQLBuilder { String fromName = v.getExpressions().get(0); projections.add(fromName + " AS " + projectedName); + + if(v.getLiteralTypeExpression() != null) { + projections.add(v.getLiteralTypeExpression() + " AS " + projectedName + "_TYPE"); + } + if(v.getLiteralLangExpression() != null) { + projections.add(v.getLiteralLangExpression() + " AS " + projectedName + "_LANG"); + } } } @@ -967,8 +980,7 @@ public class SQLBuilder { FunctionCall fc = (FunctionCall)expr; // special optimizations for frequent cases with variables - if((XMLSchema.DOUBLE.toString().equals(fc.getURI()) || XMLSchema.FLOAT.toString().equals(fc.getURI()) ) && - fc.getArgs().size() == 1) { + if((XMLSchema.DOUBLE.toString().equals(fc.getURI()) || XMLSchema.FLOAT.toString().equals(fc.getURI()) ) && fc.getArgs().size() == 1) { return evaluateExpression(fc.getArgs().get(0), OPTypes.DOUBLE); } else if((XMLSchema.INTEGER.toString().equals(fc.getURI()) || XMLSchema.INT.toString().equals(fc.getURI())) && fc.getArgs().size() == 1) { return evaluateExpression(fc.getArgs().get(0), OPTypes.INT); @@ -1204,6 +1216,33 @@ public class SQLBuilder { } } + + private String getLiteralLangExpression(ValueExpr expr) { + Var langVar = new LiteralTypeExpressionFinder(expr).expr; + + if(langVar != null) { + SQLVariable sqlVar = variables.get(langVar.getName()); + if(sqlVar != null) { + return sqlVar.getAlias() + ".lang"; + } + } + return null; + + } + + private String getLiteralTypeExpression(ValueExpr expr) { + Var typeVar = new LiteralTypeExpressionFinder(expr).expr; + + if(typeVar != null) { + SQLVariable sqlVar = variables.get(typeVar.getName()); + if(sqlVar != null) { + return sqlVar.getAlias() + ".ltype"; + } + } + return null; + } + + /** * Construct the SQL query for the given SPARQL query part. * http://git-wip-us.apache.org/repos/asf/marmotta/blob/4bc783a9/libraries/kiwi/kiwi-sparql/src/main/java/org/apache/marmotta/kiwi/sparql/builder/SQLVariable.java ---------------------------------------------------------------------- diff --git a/libraries/kiwi/kiwi-sparql/src/main/java/org/apache/marmotta/kiwi/sparql/builder/SQLVariable.java b/libraries/kiwi/kiwi-sparql/src/main/java/org/apache/marmotta/kiwi/sparql/builder/SQLVariable.java index bbcd153..921a2f1 100644 --- a/libraries/kiwi/kiwi-sparql/src/main/java/org/apache/marmotta/kiwi/sparql/builder/SQLVariable.java +++ b/libraries/kiwi/kiwi-sparql/src/main/java/org/apache/marmotta/kiwi/sparql/builder/SQLVariable.java @@ -68,6 +68,18 @@ public class SQLVariable implements Cloneable{ */ private ProjectionType projectionType = ProjectionType.NONE; + /** + * The expression to project the type for the literal that will be bound to this variable, e.g. in case + * the main expression is a function call and the type should be preserved. + */ + private String literalTypeExpression = null; + + /** + * The expression to project the language for the literal that will be bound to this variable, e.g. in case + * the main expression is a function call and the language should be preserved. + */ + private String literalLangExpression = null; + public SQLVariable(String name, String sparqlName) { this.name = name; this.sparqlName = sparqlName; @@ -112,6 +124,22 @@ public class SQLVariable implements Cloneable{ this.projectionType = projectionType; } + public String getLiteralTypeExpression() { + return literalTypeExpression; + } + + public void setLiteralTypeExpression(String literalTypeExpression) { + this.literalTypeExpression = literalTypeExpression; + } + + public String getLiteralLangExpression() { + return literalLangExpression; + } + + public void setLiteralLangExpression(String literalLangExpression) { + this.literalLangExpression = literalLangExpression; + } + @Override public boolean equals(Object o) { if (this == o) return true; http://git-wip-us.apache.org/repos/asf/marmotta/blob/4bc783a9/libraries/kiwi/kiwi-sparql/src/main/java/org/apache/marmotta/kiwi/sparql/persistence/KiWiSparqlConnection.java ---------------------------------------------------------------------- diff --git a/libraries/kiwi/kiwi-sparql/src/main/java/org/apache/marmotta/kiwi/sparql/persistence/KiWiSparqlConnection.java b/libraries/kiwi/kiwi-sparql/src/main/java/org/apache/marmotta/kiwi/sparql/persistence/KiWiSparqlConnection.java index 0c85daa..7d91d6b 100644 --- a/libraries/kiwi/kiwi-sparql/src/main/java/org/apache/marmotta/kiwi/sparql/persistence/KiWiSparqlConnection.java +++ b/libraries/kiwi/kiwi-sparql/src/main/java/org/apache/marmotta/kiwi/sparql/persistence/KiWiSparqlConnection.java @@ -32,6 +32,8 @@ import org.apache.marmotta.kiwi.sparql.builder.ProjectionType; import org.apache.marmotta.kiwi.sparql.builder.SQLBuilder; import org.apache.marmotta.kiwi.sparql.builder.SQLVariable; import org.apache.marmotta.kiwi.sparql.exception.UnsatisfiableQueryException; +import org.openrdf.model.Literal; +import org.openrdf.model.URI; import org.openrdf.model.impl.LiteralImpl; import org.openrdf.model.impl.URIImpl; import org.openrdf.query.Binding; @@ -138,35 +140,58 @@ public class KiWiSparqlConnection { } else if(sv.getProjectionType() != ProjectionType.NONE && (builder.getProjectedVars().isEmpty() || builder.getProjectedVars().contains(sv.getSparqlName()))) { // literal value String svalue; - switch (sv.getProjectionType()) { - case URI: - svalue = row.getString(sv.getName()); - if(svalue != null) - resultRow.addBinding(sv.getSparqlName(), new URIImpl(svalue)); - break; - case INT: - if(row.getObject(sv.getName()) != null) { - svalue = Integer.toString(row.getInt(sv.getName())); - resultRow.addBinding(sv.getSparqlName(), new LiteralImpl(svalue, XSD.Integer)); + switch (sv.getProjectionType()) { + case URI: + svalue = row.getString(sv.getName()); + if(svalue != null) + resultRow.addBinding(sv.getSparqlName(), new URIImpl(svalue)); + break; + case INT: + if(row.getObject(sv.getName()) != null) { + svalue = Integer.toString(row.getInt(sv.getName())); + resultRow.addBinding(sv.getSparqlName(), new LiteralImpl(svalue, XSD.Integer)); + } + break; + case DOUBLE: + if(row.getObject(sv.getName()) != null) { + svalue = Double.toString(row.getDouble(sv.getName())); + resultRow.addBinding(sv.getSparqlName(), new LiteralImpl(svalue, XSD.Double)); + } + break; + case STRING: + default: + svalue = row.getString(sv.getName()); + + if(svalue != null) { + + // retrieve optional type and language information + String lang = null; + URI type = null; + try { + lang = row.getString(sv.getName() + "_LANG"); + } catch (SQLException ex) { } - break; - case DOUBLE: - if(row.getObject(sv.getName()) != null) { - svalue = Double.toString(row.getDouble(sv.getName())); - resultRow.addBinding(sv.getSparqlName(), new LiteralImpl(svalue, XSD.Double)); + + try { + long typeId = row.getLong(sv.getName() + "_TYPE"); + if (typeId > 0) + type = (URI) parent.loadNodeById(typeId); + } catch (SQLException ex) { + } + + Literal v; + if(lang != null) { + v = new LiteralImpl(svalue,lang); + } else if(type != null) { + v = new LiteralImpl(svalue,type); + } else { + v = new LiteralImpl(svalue); } - break; - case STRING: - svalue = row.getString(sv.getName()); - if(svalue != null) - resultRow.addBinding(sv.getSparqlName(), new LiteralImpl(svalue)); - break; - default: - svalue = row.getString(sv.getName()); - if(svalue != null) - resultRow.addBinding(sv.getSparqlName(), new LiteralImpl(svalue)); - break; - } + + resultRow.addBinding(sv.getSparqlName(), v); + } + break; + } } }
