From f29ca3da69ae793691a84a6689260f4d37a340f9 Mon Sep 17 00:00:00 2001 From: Hannah Bast Date: Fri, 18 Oct 2024 23:40:50 +0200 Subject: [PATCH] Add function `geof:isWktPoint` The function returns true if and only if the argument is a WKT point. This can be checked efficiently by checking the datatype bits of the ID. Note that this function is not part of the GeoSPARQL standard. We add it because we need it for https://github.com/ad-freiburg/qlever-petrimaps to fetch alll WKT literals that are not points efficiently. --- .../sparqlExpressions/IsSomethingExpressions.cpp | 14 ++++++++++---- src/engine/sparqlExpressions/NaryExpression.h | 1 + .../SparqlExpressionValueGetters.h | 12 ++++++++++++ src/parser/sparqlParser/SparqlQleverVisitor.cpp | 3 +++ test/SparqlAntlrParserTest.cpp | 2 ++ test/SparqlExpressionTest.cpp | 3 +++ 6 files changed, 31 insertions(+), 4 deletions(-) diff --git a/src/engine/sparqlExpressions/IsSomethingExpressions.cpp b/src/engine/sparqlExpressions/IsSomethingExpressions.cpp index 0d1780b756..cdfa18364d 100644 --- a/src/engine/sparqlExpressions/IsSomethingExpressions.cpp +++ b/src/engine/sparqlExpressions/IsSomethingExpressions.cpp @@ -24,14 +24,17 @@ namespace detail { // `...Expression` (`std::move` the arguments into the constructor). The // function should be declared in `NaryExpression.h`. -// Expressions for `isIRI`, `isBlank`, `isLiteral`, and `isNumeric`. Note that -// the value getters already return the correct `Id`, hence `std::identity`. +// Expressions for the builtin functions `isIRI`, `isBlank`, `isLiteral`, +// `isNumeric`, and the custom function `isWktPoint`. Note that the value +// getters already return the correct `Id`, hence `std::identity`. using isIriExpression = NARY<1, FV>; using isBlankExpression = NARY<1, FV>; using isLiteralExpression = NARY<1, FV>; using isNumericExpression = NARY<1, FV>; -// The expression for `bound` is slightly different because -// `IsValidValueGetter` returns a `bool` and not an `Id`. +using isWktPointExpression = NARY<1, FV>; + +// The expression for `bound` is slightly different as `IsValidValueGetter` +// returns a `bool` and not an `Id`. inline auto boolToId = [](bool b) { return Id::makeFromBool(b); }; using boundExpression = NARY<1, FV>; @@ -49,6 +52,9 @@ SparqlExpression::Ptr makeIsLiteralExpression(SparqlExpression::Ptr arg) { SparqlExpression::Ptr makeIsNumericExpression(SparqlExpression::Ptr arg) { return std::make_unique(std::move(arg)); } +SparqlExpression::Ptr makeIsWktPointExpression(SparqlExpression::Ptr arg) { + return std::make_unique(std::move(arg)); +} SparqlExpression::Ptr makeBoundExpression(SparqlExpression::Ptr arg) { return std::make_unique(std::move(arg)); } diff --git a/src/engine/sparqlExpressions/NaryExpression.h b/src/engine/sparqlExpressions/NaryExpression.h index 5237a2283a..c82e1b22ff 100644 --- a/src/engine/sparqlExpressions/NaryExpression.h +++ b/src/engine/sparqlExpressions/NaryExpression.h @@ -119,6 +119,7 @@ SparqlExpression::Ptr makeIsIriExpression(SparqlExpression::Ptr child); SparqlExpression::Ptr makeIsBlankExpression(SparqlExpression::Ptr child); SparqlExpression::Ptr makeIsLiteralExpression(SparqlExpression::Ptr child); SparqlExpression::Ptr makeIsNumericExpression(SparqlExpression::Ptr child); +SparqlExpression::Ptr makeIsWktPointExpression(SparqlExpression::Ptr child); SparqlExpression::Ptr makeBoundExpression(SparqlExpression::Ptr child); // For a `function` that takes `std::vector` (size only diff --git a/src/engine/sparqlExpressions/SparqlExpressionValueGetters.h b/src/engine/sparqlExpressions/SparqlExpressionValueGetters.h index 6e7cd310ec..194c6c4b43 100644 --- a/src/engine/sparqlExpressions/SparqlExpressionValueGetters.h +++ b/src/engine/sparqlExpressions/SparqlExpressionValueGetters.h @@ -190,6 +190,18 @@ struct IsNumericValueGetter : Mixin { } }; +// Value getter for `isWktPoint`. +struct IsWktPointValueGetter : Mixin { + using Mixin::operator(); + Id operator()(ValueId id, const EvaluationContext*) const { + return Id::makeFromBool(id.getDatatype() == Datatype::GeoPoint); + } + + Id operator()(const LiteralOrIri&, const EvaluationContext*) const { + return Id::makeFromBool(false); + } +}; + /// This class can be used as the `ValueGetter` argument of Expression /// templates. It produces a `std::optional`. struct DateValueGetter : Mixin { diff --git a/src/parser/sparqlParser/SparqlQleverVisitor.cpp b/src/parser/sparqlParser/SparqlQleverVisitor.cpp index 777f29714d..6cfb5831de 100644 --- a/src/parser/sparqlParser/SparqlQleverVisitor.cpp +++ b/src/parser/sparqlParser/SparqlQleverVisitor.cpp @@ -112,6 +112,9 @@ ExpressionPtr Visitor::processIriFunctionCall( } else if (functionName == "latitude") { checkNumArgs(1); return sparqlExpression::makeLatitudeExpression(std::move(argList[0])); + } else if (functionName == "iswktpoint") { + checkNumArgs(1); + return sparqlExpression::makeIsWktPointExpression(std::move(argList[0])); } } else if (checkPrefix(MATH_PREFIX)) { if (functionName == "log") { diff --git a/test/SparqlAntlrParserTest.cpp b/test/SparqlAntlrParserTest.cpp index 0ee1705f46..598809e1ba 100644 --- a/test/SparqlAntlrParserTest.cpp +++ b/test/SparqlAntlrParserTest.cpp @@ -1655,6 +1655,8 @@ TEST(SparqlParser, FunctionCall) { matchUnary(&makeLatitudeExpression)); expectFunctionCall(absl::StrCat(geof, "longitude>(?x)"), matchUnary(&makeLongitudeExpression)); + expectFunctionCall(absl::StrCat(geof, "iswktpoint>(?x)"), + matchUnary(&makeIsWktPointExpression)); expectFunctionCall( absl::StrCat(geof, "distance>(?a, ?b)"), matchNary(&makeDistExpression, Variable{"?a"}, Variable{"?b"})); diff --git a/test/SparqlExpressionTest.cpp b/test/SparqlExpressionTest.cpp index ab5b3add68..da9083e23b 100644 --- a/test/SparqlExpressionTest.cpp +++ b/test/SparqlExpressionTest.cpp @@ -1117,6 +1117,7 @@ TEST(SparqlExpression, testToNumericExpression) { TEST(SparqlExpression, geoSparqlExpressions) { auto checkLat = testUnaryExpression<&makeLatitudeExpression>; auto checkLong = testUnaryExpression<&makeLongitudeExpression>; + auto checkIsWktPoint = testUnaryExpression<&makeIsWktPointExpression>; auto checkDist = std::bind_front(testNaryExpression, &makeDistExpression); auto p = GeoPoint(26.8, 24.3); @@ -1136,9 +1137,11 @@ TEST(SparqlExpression, geoSparqlExpressions) { checkLat(v, vLat); checkLong(v, vLng); + checkIsWktPoint(v, B(true)); checkDist(D(0.0), v, v); checkLat(idOrLitOrStringVec({"NotAPoint", I(12)}), Ids{U, U}); checkLong(idOrLitOrStringVec({D(4.2), "NotAPoint"}), Ids{U, U}); + checkIsWktPoint(IdOrLiteralOrIri{lit("NotAPoint")}, B(false)); checkDist(U, v, IdOrLiteralOrIri{I(12)}); checkDist(U, IdOrLiteralOrIri{I(12)}, v); checkDist(U, v, IdOrLiteralOrIri{lit("NotAPoint")});