From 99da434b3dab7035b93d2eac03045ae3086faeb5 Mon Sep 17 00:00:00 2001 From: realHannes Date: Thu, 25 Apr 2024 19:07:46 +0200 Subject: [PATCH 01/50] Added conversion str to int --- .../sparqlExpressions/StringExpressions.cpp | 23 +++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/src/engine/sparqlExpressions/StringExpressions.cpp b/src/engine/sparqlExpressions/StringExpressions.cpp index 7e48871a94..d468784d00 100644 --- a/src/engine/sparqlExpressions/StringExpressions.cpp +++ b/src/engine/sparqlExpressions/StringExpressions.cpp @@ -399,6 +399,25 @@ class ConcatExpression : public detail::VariadicExpression { using EncodeForUriExpression = StringExpressionImpl<1, decltype(encodeForUriImpl)>; +// TO_INT +auto InvalidArg = std::errc::invalid_argument; +auto OutOfRange = std::errc::result_out_of_range; + +[[maybe_used]] auto strToIntImpl = [] (std::optional input) { + if (!input.has_value()) { + return Id::makeUndefined(); + } else { + int res{}; + auto str = boost::lexical_cast(*input); + auto conv = std::from_chars(str.data(), str.data() + str.size(), res); + if (conv.ec == InvalidArg || conv.ec == OutOfRange) { + return Id::makeUndefined(); + } + return Id::makeFromInt(res); + } +}; +using MakeStrToInt = StringExpressionImpl<1, decltype(strToIntImpl)>; + } // namespace detail::string_expressions using namespace detail::string_expressions; using std::make_unique; @@ -452,4 +471,8 @@ Expr makeEncodeForUriExpression(Expr child) { return make(child); } +Expr makeStrToIntExpression(Expr child) { + return make(child); +} + } // namespace sparqlExpression From 424d023ac6b6ec2fb4c439e8c1b949bcfa7a541a Mon Sep 17 00:00:00 2001 From: realHannes Date: Thu, 25 Apr 2024 19:45:06 +0200 Subject: [PATCH 02/50] templated function for toNumeric, add declaration to NaryExpression.h --- src/engine/sparqlExpressions/NaryExpression.h | 3 +++ .../sparqlExpressions/StringExpressions.cpp | 21 ++++++++++++------- 2 files changed, 17 insertions(+), 7 deletions(-) diff --git a/src/engine/sparqlExpressions/NaryExpression.h b/src/engine/sparqlExpressions/NaryExpression.h index 154e039f7b..fc34baae18 100644 --- a/src/engine/sparqlExpressions/NaryExpression.h +++ b/src/engine/sparqlExpressions/NaryExpression.h @@ -82,6 +82,9 @@ SparqlExpression::Ptr makeIfExpression(SparqlExpression::Ptr child1, SparqlExpression::Ptr child2, SparqlExpression::Ptr child3); +SparqlExpression::Ptr makeStrToIntExpression(SparqlExpression::Ptr child); +// SparqlExpression::Ptr makeStrToDoubleExpression(SparqlExpression::Ptr child); + SparqlExpression::Ptr makeEncodeForUriExpression(SparqlExpression::Ptr child); SparqlExpression::Ptr makeIsIriExpression(SparqlExpression::Ptr child); diff --git a/src/engine/sparqlExpressions/StringExpressions.cpp b/src/engine/sparqlExpressions/StringExpressions.cpp index d468784d00..d09e7cb3e7 100644 --- a/src/engine/sparqlExpressions/StringExpressions.cpp +++ b/src/engine/sparqlExpressions/StringExpressions.cpp @@ -403,20 +403,23 @@ using EncodeForUriExpression = auto InvalidArg = std::errc::invalid_argument; auto OutOfRange = std::errc::result_out_of_range; -[[maybe_used]] auto strToIntImpl = [] (std::optional input) { +template +[[maybe_used]] auto toNumeric = [] (std::optional input) { if (!input.has_value()) { return Id::makeUndefined(); } else { - int res{}; + T res{}; auto str = boost::lexical_cast(*input); auto conv = std::from_chars(str.data(), str.data() + str.size(), res); if (conv.ec == InvalidArg || conv.ec == OutOfRange) { return Id::makeUndefined(); } - return Id::makeFromInt(res); + if (std::is_same_v) { return Id::makeFromDouble(res); } + else { return Id::makeFromInt(res); } } }; -using MakeStrToInt = StringExpressionImpl<1, decltype(strToIntImpl)>; +using MakeStrToInt = StringExpressionImpl<1, decltype(toNumeric)>; +using MakeDoubleToInt = StringExpressionImpl<1, decltype(toNumeric)>; } // namespace detail::string_expressions using namespace detail::string_expressions; @@ -471,8 +474,12 @@ Expr makeEncodeForUriExpression(Expr child) { return make(child); } -Expr makeStrToIntExpression(Expr child) { - return make(child); +Expr makeStrToIntExpression(Expr child) { + return make(child); +} + +Expr makeStrToDoubleExpression(Expr child) { + return make(child); } -} // namespace sparqlExpression +} // namespace sparqlExpression \ No newline at end of file From 0117e8277565a5865c786817758ce2c9004c1316 Mon Sep 17 00:00:00 2001 From: realHannes Date: Thu, 25 Apr 2024 20:58:01 +0200 Subject: [PATCH 03/50] str to num for SparqlExpression implemented + added test --- src/engine/sparqlExpressions/NaryExpression.h | 2 +- src/engine/sparqlExpressions/StringExpressions.cpp | 2 +- src/parser/sparqlParser/SparqlQleverVisitor.cpp | 4 ++++ test/SparqlExpressionTest.cpp | 13 +++++++++++++ 4 files changed, 19 insertions(+), 2 deletions(-) diff --git a/src/engine/sparqlExpressions/NaryExpression.h b/src/engine/sparqlExpressions/NaryExpression.h index fc34baae18..40c4726597 100644 --- a/src/engine/sparqlExpressions/NaryExpression.h +++ b/src/engine/sparqlExpressions/NaryExpression.h @@ -83,7 +83,7 @@ SparqlExpression::Ptr makeIfExpression(SparqlExpression::Ptr child1, SparqlExpression::Ptr child3); SparqlExpression::Ptr makeStrToIntExpression(SparqlExpression::Ptr child); -// SparqlExpression::Ptr makeStrToDoubleExpression(SparqlExpression::Ptr child); +SparqlExpression::Ptr makeStrToDoubleExpression(SparqlExpression::Ptr child); SparqlExpression::Ptr makeEncodeForUriExpression(SparqlExpression::Ptr child); diff --git a/src/engine/sparqlExpressions/StringExpressions.cpp b/src/engine/sparqlExpressions/StringExpressions.cpp index d09e7cb3e7..9c1d0c1d09 100644 --- a/src/engine/sparqlExpressions/StringExpressions.cpp +++ b/src/engine/sparqlExpressions/StringExpressions.cpp @@ -399,7 +399,7 @@ class ConcatExpression : public detail::VariadicExpression { using EncodeForUriExpression = StringExpressionImpl<1, decltype(encodeForUriImpl)>; -// TO_INT +// TO_NUMERIC (int or double) auto InvalidArg = std::errc::invalid_argument; auto OutOfRange = std::errc::result_out_of_range; diff --git a/src/parser/sparqlParser/SparqlQleverVisitor.cpp b/src/parser/sparqlParser/SparqlQleverVisitor.cpp index 1c8d3fec47..24b8bc1b6e 100644 --- a/src/parser/sparqlParser/SparqlQleverVisitor.cpp +++ b/src/parser/sparqlParser/SparqlQleverVisitor.cpp @@ -1801,6 +1801,10 @@ ExpressionPtr Visitor::visit([[maybe_unused]] Parser::BuiltInCallContext* ctx) { return createUnary(&makeIsLiteralExpression); } else if (functionName == "isnumeric") { return createUnary(&makeIsNumericExpression); + } else if (functionName == "INT()") { + return createUnary(&makeStrToIntExpression); + } else if (functionName == "DOUBLE()") { + return createUnary(&makeStrToDoubleExpression); } else if (functionName == "bound") { return makeBoundExpression( std::make_unique(visit(ctx->var()))); diff --git a/test/SparqlExpressionTest.cpp b/test/SparqlExpressionTest.cpp index 5dee81b2e3..4288d0c480 100644 --- a/test/SparqlExpressionTest.cpp +++ b/test/SparqlExpressionTest.cpp @@ -805,6 +805,19 @@ TEST(SparqlExpression, isSomethingFunctions) { Ids{T, T, T, T, T, T, T, T, T, F}); } +// ____________________________________________________________________________ +TEST(SparqlExpression, testToNumericExpression) { + auto checkGetInt = testUnaryExpression<&makeStrToIntExpression>; + auto checkGetDouble = testUnaryExpression<&makeStrToDoubleExpression>; + + checkGetInt(idOrLitOrStringVec( + {U, "5.97", "-78.97", "-57BoB36", "11111111111111", "FreBurg1", ""}), + Ids{U, I(5), I(-78), I(-57), U, U, U}); + checkGetDouble(idOrLitOrStringVec( + {U, "-122.2", "19,96", "-125878930300334.345","UniFr2", "-ab", "144.86375"}), + Ids{U, D(-122.2), D(19), D(-125878930300334.345), U, U, D(144.86375)}); +} + // ____________________________________________________________________________ TEST(SparqlExpression, geoSparqlExpressions) { auto checkLat = testUnaryExpression<&makeLatitudeExpression>; From 94356c2a25861f4c5150553637c2648c93abba63 Mon Sep 17 00:00:00 2001 From: Hannes Baumann <116301375+realHannes@users.noreply.github.com> Date: Fri, 26 Apr 2024 10:16:05 +0200 Subject: [PATCH 04/50] Update src/engine/sparqlExpressions/StringExpressions.cpp Co-authored-by: Johannes Kalmbach --- src/engine/sparqlExpressions/StringExpressions.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/engine/sparqlExpressions/StringExpressions.cpp b/src/engine/sparqlExpressions/StringExpressions.cpp index 9c1d0c1d09..5daead1893 100644 --- a/src/engine/sparqlExpressions/StringExpressions.cpp +++ b/src/engine/sparqlExpressions/StringExpressions.cpp @@ -399,7 +399,7 @@ class ConcatExpression : public detail::VariadicExpression { using EncodeForUriExpression = StringExpressionImpl<1, decltype(encodeForUriImpl)>; -// TO_NUMERIC (int or double) +// Expressions that convert a string to a number (int or double). auto InvalidArg = std::errc::invalid_argument; auto OutOfRange = std::errc::result_out_of_range; From decc8baca480fac89aca5dfdea689cb11bc37e24 Mon Sep 17 00:00:00 2001 From: Hannes Baumann <116301375+realHannes@users.noreply.github.com> Date: Fri, 26 Apr 2024 10:19:03 +0200 Subject: [PATCH 05/50] Update src/engine/sparqlExpressions/StringExpressions.cpp Co-authored-by: Johannes Kalmbach --- src/engine/sparqlExpressions/StringExpressions.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/engine/sparqlExpressions/StringExpressions.cpp b/src/engine/sparqlExpressions/StringExpressions.cpp index 5daead1893..d2a78f4066 100644 --- a/src/engine/sparqlExpressions/StringExpressions.cpp +++ b/src/engine/sparqlExpressions/StringExpressions.cpp @@ -409,7 +409,7 @@ template return Id::makeUndefined(); } else { T res{}; - auto str = boost::lexical_cast(*input); + const auto& str = input.value(); auto conv = std::from_chars(str.data(), str.data() + str.size(), res); if (conv.ec == InvalidArg || conv.ec == OutOfRange) { return Id::makeUndefined(); From 850152cd3f797acfdb639eb7b83041886b3469ef Mon Sep 17 00:00:00 2001 From: Hannes Baumann <116301375+realHannes@users.noreply.github.com> Date: Fri, 26 Apr 2024 10:22:33 +0200 Subject: [PATCH 06/50] Update src/engine/sparqlExpressions/StringExpressions.cpp Co-authored-by: Johannes Kalmbach --- src/engine/sparqlExpressions/StringExpressions.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/engine/sparqlExpressions/StringExpressions.cpp b/src/engine/sparqlExpressions/StringExpressions.cpp index d2a78f4066..5e1a895804 100644 --- a/src/engine/sparqlExpressions/StringExpressions.cpp +++ b/src/engine/sparqlExpressions/StringExpressions.cpp @@ -411,7 +411,7 @@ template T res{}; const auto& str = input.value(); auto conv = std::from_chars(str.data(), str.data() + str.size(), res); - if (conv.ec == InvalidArg || conv.ec == OutOfRange) { + if (conv.ec != std::error_code{} || conv.ptr != str.data() + str.size()) { return Id::makeUndefined(); } if (std::is_same_v) { return Id::makeFromDouble(res); } From d650d678217c1227203aba29e013dc708b22b1f6 Mon Sep 17 00:00:00 2001 From: Hannes Baumann <116301375+realHannes@users.noreply.github.com> Date: Fri, 26 Apr 2024 10:24:55 +0200 Subject: [PATCH 07/50] Update src/engine/sparqlExpressions/StringExpressions.cpp Co-authored-by: Johannes Kalmbach --- src/engine/sparqlExpressions/StringExpressions.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/engine/sparqlExpressions/StringExpressions.cpp b/src/engine/sparqlExpressions/StringExpressions.cpp index 5e1a895804..c9f2df976e 100644 --- a/src/engine/sparqlExpressions/StringExpressions.cpp +++ b/src/engine/sparqlExpressions/StringExpressions.cpp @@ -418,7 +418,7 @@ template else { return Id::makeFromInt(res); } } }; -using MakeStrToInt = StringExpressionImpl<1, decltype(toNumeric)>; +using MakeStrToInt = StringExpressionImpl<1, decltype(toNumeric)>; using MakeDoubleToInt = StringExpressionImpl<1, decltype(toNumeric)>; } // namespace detail::string_expressions From 46cc69735256da80d5f878c0eb6ad0b439743ae1 Mon Sep 17 00:00:00 2001 From: realHannes Date: Fri, 26 Apr 2024 13:32:20 +0200 Subject: [PATCH 08/50] using now absl::from_chars() and stripping whitespaces for string to number conv. --- .../sparqlExpressions/NaryExpressionImpl.h | 2 ++ .../sparqlExpressions/StringExpressions.cpp | 16 ++++++++-------- test/SparqlExpressionTest.cpp | 8 ++++---- 3 files changed, 14 insertions(+), 12 deletions(-) diff --git a/src/engine/sparqlExpressions/NaryExpressionImpl.h b/src/engine/sparqlExpressions/NaryExpressionImpl.h index f099f3eee6..5637223158 100644 --- a/src/engine/sparqlExpressions/NaryExpressionImpl.h +++ b/src/engine/sparqlExpressions/NaryExpressionImpl.h @@ -6,6 +6,8 @@ #include +#include "absl/strings/charconv.h" +#include "absl/strings/ascii.h" #include "engine/sparqlExpressions/NaryExpression.h" #include "engine/sparqlExpressions/SparqlExpressionGenerators.h" #include "engine/sparqlExpressions/SparqlExpressionValueGetters.h" diff --git a/src/engine/sparqlExpressions/StringExpressions.cpp b/src/engine/sparqlExpressions/StringExpressions.cpp index c9f2df976e..a426d489e5 100644 --- a/src/engine/sparqlExpressions/StringExpressions.cpp +++ b/src/engine/sparqlExpressions/StringExpressions.cpp @@ -400,22 +400,22 @@ using EncodeForUriExpression = StringExpressionImpl<1, decltype(encodeForUriImpl)>; // Expressions that convert a string to a number (int or double). -auto InvalidArg = std::errc::invalid_argument; -auto OutOfRange = std::errc::result_out_of_range; - template [[maybe_used]] auto toNumeric = [] (std::optional input) { if (!input.has_value()) { return Id::makeUndefined(); } else { - T res{}; - const auto& str = input.value(); - auto conv = std::from_chars(str.data(), str.data() + str.size(), res); + auto str = absl::StripAsciiWhitespace(input.value()); + double resD{}; + auto conv = absl::from_chars(str.data(), str.data() + str.size(), resD); if (conv.ec != std::error_code{} || conv.ptr != str.data() + str.size()) { return Id::makeUndefined(); } - if (std::is_same_v) { return Id::makeFromDouble(res); } - else { return Id::makeFromInt(res); } + if (std::is_same_v) { + auto resT = static_cast(resD); + return Id::makeFromInt(resT); + } + else { return Id::makeFromDouble(resD); } } }; using MakeStrToInt = StringExpressionImpl<1, decltype(toNumeric)>; diff --git a/test/SparqlExpressionTest.cpp b/test/SparqlExpressionTest.cpp index 4288d0c480..8ed86fce07 100644 --- a/test/SparqlExpressionTest.cpp +++ b/test/SparqlExpressionTest.cpp @@ -811,11 +811,11 @@ TEST(SparqlExpression, testToNumericExpression) { auto checkGetDouble = testUnaryExpression<&makeStrToDoubleExpression>; checkGetInt(idOrLitOrStringVec( - {U, "5.97", "-78.97", "-57BoB36", "11111111111111", "FreBurg1", ""}), - Ids{U, I(5), I(-78), I(-57), U, U, U}); + {U, "5.97", "-78.97", "-5BoB6", "FreBurg1", "", " .", " 42\n", " 0.01 "}), + Ids{U, I(5), I(-78), U, U, U, U, I(42), I(0)}); checkGetDouble(idOrLitOrStringVec( - {U, "-122.2", "19,96", "-125878930300334.345","UniFr2", "-ab", "144.86375"}), - Ids{U, D(-122.2), D(19), D(-125878930300334.345), U, U, D(144.86375)}); + {U, "-122.2", "19,96", " 128789334.345 ", "-0.f" , " 0.007 "," -14.75 "}), + Ids{U, D(-122.2), U, D(128789334.345), U, D(0.007), D(-14.75)}); } // ____________________________________________________________________________ From 7fc5c2805e2dafa662969d2fc866bf9c872491d9 Mon Sep 17 00:00:00 2001 From: realHannes Date: Fri, 26 Apr 2024 16:51:13 +0200 Subject: [PATCH 09/50] added new functions to processIriFuntionCall() (for string to number) --- src/global/Constants.h | 2 ++ .../sparqlParser/SparqlQleverVisitor.cpp | 18 +++++++++++++----- 2 files changed, 15 insertions(+), 5 deletions(-) diff --git a/src/global/Constants.h b/src/global/Constants.h index ad5a2ff99d..97f7d8d3f5 100644 --- a/src/global/Constants.h +++ b/src/global/Constants.h @@ -60,6 +60,8 @@ static constexpr std::pair GEOF_PREFIX = { "geof:", "http://www.opengis.net/def/function/geosparql/"}; static constexpr std::pair MATH_PREFIX = { "math:", "http://www.w3.org/2005/xpath-functions/math#"}; +static constexpr std::pair SCHEMA_TYPE = { + "xml_schema", "http://www.w3.org/2001/XMLSchema#"}; static const std::string INTERNAL_VARIABLE_PREFIX = "?_QLever_internal_variable_"; diff --git a/src/parser/sparqlParser/SparqlQleverVisitor.cpp b/src/parser/sparqlParser/SparqlQleverVisitor.cpp index 0f6809cdec..6c3ff08432 100644 --- a/src/parser/sparqlParser/SparqlQleverVisitor.cpp +++ b/src/parser/sparqlParser/SparqlQleverVisitor.cpp @@ -121,7 +121,19 @@ ExpressionPtr Visitor::processIriFunctionCall( checkNumArgs(1); return sparqlExpression::makeTanExpression(std::move(argList[0])); } - } + } else if (checkPrefix(SCHEMA_TYPE)) { + if (functionName == "integer") { + checkNumArgs(1); + return sparqlExpression::makeStrToIntExpression( + std::move(argList[0]) + ); + } else if (functionName == "double") { + checkNumArgs(1); + return sparqlExpression::makeStrToDoubleExpression( + std::move(argList[0]) + ); + } + } reportNotSupported(ctx, "Function \""s + iri.toStringRepresentation() + "\" is"); } @@ -1801,10 +1813,6 @@ ExpressionPtr Visitor::visit([[maybe_unused]] Parser::BuiltInCallContext* ctx) { return createUnary(&makeIsLiteralExpression); } else if (functionName == "isnumeric") { return createUnary(&makeIsNumericExpression); - } else if (functionName == "INT()") { - return createUnary(&makeStrToIntExpression); - } else if (functionName == "DOUBLE()") { - return createUnary(&makeStrToDoubleExpression); } else if (functionName == "bound") { return makeBoundExpression( std::make_unique(visit(ctx->var()))); From efb0e24427c8c939dc8fd3cabf285a96c64f7739 Mon Sep 17 00:00:00 2001 From: realHannes Date: Fri, 26 Apr 2024 17:24:43 +0200 Subject: [PATCH 10/50] renaming to: toIntExpression and toDoubleExpression for later more general implementation --- src/engine/sparqlExpressions/NaryExpression.h | 4 ++-- src/engine/sparqlExpressions/StringExpressions.cpp | 14 +++++++------- src/parser/sparqlParser/SparqlQleverVisitor.cpp | 8 ++------ test/SparqlExpressionTest.cpp | 4 ++-- 4 files changed, 13 insertions(+), 17 deletions(-) diff --git a/src/engine/sparqlExpressions/NaryExpression.h b/src/engine/sparqlExpressions/NaryExpression.h index 40c4726597..64e74675d8 100644 --- a/src/engine/sparqlExpressions/NaryExpression.h +++ b/src/engine/sparqlExpressions/NaryExpression.h @@ -82,8 +82,8 @@ SparqlExpression::Ptr makeIfExpression(SparqlExpression::Ptr child1, SparqlExpression::Ptr child2, SparqlExpression::Ptr child3); -SparqlExpression::Ptr makeStrToIntExpression(SparqlExpression::Ptr child); -SparqlExpression::Ptr makeStrToDoubleExpression(SparqlExpression::Ptr child); +SparqlExpression::Ptr toIntExpression(SparqlExpression::Ptr child); +SparqlExpression::Ptr toDoubleExpression(SparqlExpression::Ptr child); SparqlExpression::Ptr makeEncodeForUriExpression(SparqlExpression::Ptr child); diff --git a/src/engine/sparqlExpressions/StringExpressions.cpp b/src/engine/sparqlExpressions/StringExpressions.cpp index a426d489e5..eb4aa6d94b 100644 --- a/src/engine/sparqlExpressions/StringExpressions.cpp +++ b/src/engine/sparqlExpressions/StringExpressions.cpp @@ -411,15 +411,15 @@ template if (conv.ec != std::error_code{} || conv.ptr != str.data() + str.size()) { return Id::makeUndefined(); } - if (std::is_same_v) { + if constexpr (std::is_same_v) { auto resT = static_cast(resD); return Id::makeFromInt(resT); } else { return Id::makeFromDouble(resD); } } }; -using MakeStrToInt = StringExpressionImpl<1, decltype(toNumeric)>; -using MakeDoubleToInt = StringExpressionImpl<1, decltype(toNumeric)>; +using ToInt = StringExpressionImpl<1, decltype(toNumeric)>; +using ToDouble = StringExpressionImpl<1, decltype(toNumeric)>; } // namespace detail::string_expressions using namespace detail::string_expressions; @@ -474,12 +474,12 @@ Expr makeEncodeForUriExpression(Expr child) { return make(child); } -Expr makeStrToIntExpression(Expr child) { - return make(child); +Expr toIntExpression(Expr child) { + return make(child); } -Expr makeStrToDoubleExpression(Expr child) { - return make(child); +Expr toDoubleExpression(Expr child) { + return make(child); } } // namespace sparqlExpression \ No newline at end of file diff --git a/src/parser/sparqlParser/SparqlQleverVisitor.cpp b/src/parser/sparqlParser/SparqlQleverVisitor.cpp index 6c3ff08432..cc4a34785d 100644 --- a/src/parser/sparqlParser/SparqlQleverVisitor.cpp +++ b/src/parser/sparqlParser/SparqlQleverVisitor.cpp @@ -124,14 +124,10 @@ ExpressionPtr Visitor::processIriFunctionCall( } else if (checkPrefix(SCHEMA_TYPE)) { if (functionName == "integer") { checkNumArgs(1); - return sparqlExpression::makeStrToIntExpression( - std::move(argList[0]) - ); + return sparqlExpression::toIntExpression(std::move(argList[0])); } else if (functionName == "double") { checkNumArgs(1); - return sparqlExpression::makeStrToDoubleExpression( - std::move(argList[0]) - ); + return sparqlExpression::toDoubleExpression(std::move(argList[0])); } } reportNotSupported(ctx, diff --git a/test/SparqlExpressionTest.cpp b/test/SparqlExpressionTest.cpp index 8ed86fce07..39ecc8102b 100644 --- a/test/SparqlExpressionTest.cpp +++ b/test/SparqlExpressionTest.cpp @@ -807,8 +807,8 @@ TEST(SparqlExpression, isSomethingFunctions) { // ____________________________________________________________________________ TEST(SparqlExpression, testToNumericExpression) { - auto checkGetInt = testUnaryExpression<&makeStrToIntExpression>; - auto checkGetDouble = testUnaryExpression<&makeStrToDoubleExpression>; + auto checkGetInt = testUnaryExpression<&toIntExpression>; + auto checkGetDouble = testUnaryExpression<&toDoubleExpression>; checkGetInt(idOrLitOrStringVec( {U, "5.97", "-78.97", "-5BoB6", "FreBurg1", "", " .", " 42\n", " 0.01 "}), From a88537c227cf2cef4d64319d5e8e8003c9308681 Mon Sep 17 00:00:00 2001 From: realHannes Date: Fri, 26 Apr 2024 19:24:25 +0200 Subject: [PATCH 11/50] made format (clang-format-16) --- .../sparqlExpressions/StringExpressions.cpp | 17 +++++++---------- src/parser/sparqlParser/SparqlQleverVisitor.cpp | 2 +- test/SparqlExpressionTest.cpp | 15 ++++++++------- 3 files changed, 16 insertions(+), 18 deletions(-) diff --git a/src/engine/sparqlExpressions/StringExpressions.cpp b/src/engine/sparqlExpressions/StringExpressions.cpp index eb4aa6d94b..b6adde3985 100644 --- a/src/engine/sparqlExpressions/StringExpressions.cpp +++ b/src/engine/sparqlExpressions/StringExpressions.cpp @@ -401,7 +401,7 @@ using EncodeForUriExpression = // Expressions that convert a string to a number (int or double). template -[[maybe_used]] auto toNumeric = [] (std::optional input) { +[[maybe_used]] auto toNumeric = [](std::optional input) { if (!input.has_value()) { return Id::makeUndefined(); } else { @@ -411,11 +411,12 @@ template if (conv.ec != std::error_code{} || conv.ptr != str.data() + str.size()) { return Id::makeUndefined(); } - if constexpr (std::is_same_v) { + if constexpr (std::is_same_v) { auto resT = static_cast(resD); return Id::makeFromInt(resT); + } else { + return Id::makeFromDouble(resD); } - else { return Id::makeFromDouble(resD); } } }; using ToInt = StringExpressionImpl<1, decltype(toNumeric)>; @@ -474,12 +475,8 @@ Expr makeEncodeForUriExpression(Expr child) { return make(child); } -Expr toIntExpression(Expr child) { - return make(child); -} +Expr toIntExpression(Expr child) { return make(child); } -Expr toDoubleExpression(Expr child) { - return make(child); -} +Expr toDoubleExpression(Expr child) { return make(child); } -} // namespace sparqlExpression \ No newline at end of file +} // namespace sparqlExpression diff --git a/src/parser/sparqlParser/SparqlQleverVisitor.cpp b/src/parser/sparqlParser/SparqlQleverVisitor.cpp index cc4a34785d..5e0bd41a57 100644 --- a/src/parser/sparqlParser/SparqlQleverVisitor.cpp +++ b/src/parser/sparqlParser/SparqlQleverVisitor.cpp @@ -129,7 +129,7 @@ ExpressionPtr Visitor::processIriFunctionCall( checkNumArgs(1); return sparqlExpression::toDoubleExpression(std::move(argList[0])); } - } + } reportNotSupported(ctx, "Function \""s + iri.toStringRepresentation() + "\" is"); } diff --git a/test/SparqlExpressionTest.cpp b/test/SparqlExpressionTest.cpp index 39ecc8102b..492816f48c 100644 --- a/test/SparqlExpressionTest.cpp +++ b/test/SparqlExpressionTest.cpp @@ -809,13 +809,14 @@ TEST(SparqlExpression, isSomethingFunctions) { TEST(SparqlExpression, testToNumericExpression) { auto checkGetInt = testUnaryExpression<&toIntExpression>; auto checkGetDouble = testUnaryExpression<&toDoubleExpression>; - - checkGetInt(idOrLitOrStringVec( - {U, "5.97", "-78.97", "-5BoB6", "FreBurg1", "", " .", " 42\n", " 0.01 "}), - Ids{U, I(5), I(-78), U, U, U, U, I(42), I(0)}); - checkGetDouble(idOrLitOrStringVec( - {U, "-122.2", "19,96", " 128789334.345 ", "-0.f" , " 0.007 "," -14.75 "}), - Ids{U, D(-122.2), U, D(128789334.345), U, D(0.007), D(-14.75)}); + + checkGetInt(idOrLitOrStringVec({U, "5.97", "-78.97", "-5BoB6", "FreBurg1", "", + " .", " 42\n", " 0.01 "}), + Ids{U, I(5), I(-78), U, U, U, U, I(42), I(0)}); + checkGetDouble( + idOrLitOrStringVec({U, "-122.2", "19,96", " 128789334.345 ", "-0.f", + " 0.007 ", " -14.75 "}), + Ids{U, D(-122.2), U, D(128789334.345), U, D(0.007), D(-14.75)}); } // ____________________________________________________________________________ From ca1e2e05cfd15d51d103c843ad1c40d3c77861dc Mon Sep 17 00:00:00 2001 From: Hannes Baumann <116301375+realHannes@users.noreply.github.com> Date: Mon, 29 Apr 2024 09:10:33 +0200 Subject: [PATCH 12/50] Update src/parser/sparqlParser/SparqlQleverVisitor.cpp Co-authored-by: Johannes Kalmbach --- src/parser/sparqlParser/SparqlQleverVisitor.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/parser/sparqlParser/SparqlQleverVisitor.cpp b/src/parser/sparqlParser/SparqlQleverVisitor.cpp index 5e0bd41a57..a6a2692613 100644 --- a/src/parser/sparqlParser/SparqlQleverVisitor.cpp +++ b/src/parser/sparqlParser/SparqlQleverVisitor.cpp @@ -125,7 +125,7 @@ ExpressionPtr Visitor::processIriFunctionCall( if (functionName == "integer") { checkNumArgs(1); return sparqlExpression::toIntExpression(std::move(argList[0])); - } else if (functionName == "double") { + } else if (functionName == "double" || functionName == "decimal") { checkNumArgs(1); return sparqlExpression::toDoubleExpression(std::move(argList[0])); } From 4adc831fd9e97f8527feeeb61a9afcf975b7ec47 Mon Sep 17 00:00:00 2001 From: Hannes Baumann <116301375+realHannes@users.noreply.github.com> Date: Mon, 29 Apr 2024 09:10:48 +0200 Subject: [PATCH 13/50] Update src/parser/sparqlParser/SparqlQleverVisitor.cpp Co-authored-by: Johannes Kalmbach --- src/parser/sparqlParser/SparqlQleverVisitor.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/parser/sparqlParser/SparqlQleverVisitor.cpp b/src/parser/sparqlParser/SparqlQleverVisitor.cpp index a6a2692613..9b0cbf7706 100644 --- a/src/parser/sparqlParser/SparqlQleverVisitor.cpp +++ b/src/parser/sparqlParser/SparqlQleverVisitor.cpp @@ -122,7 +122,7 @@ ExpressionPtr Visitor::processIriFunctionCall( return sparqlExpression::makeTanExpression(std::move(argList[0])); } } else if (checkPrefix(SCHEMA_TYPE)) { - if (functionName == "integer") { + if (functionName == "integer" || functionName == "int") { checkNumArgs(1); return sparqlExpression::toIntExpression(std::move(argList[0])); } else if (functionName == "double" || functionName == "decimal") { From d0f0d632f29ae9a0c097174c18043791d1a70b05 Mon Sep 17 00:00:00 2001 From: realHannes Date: Mon, 29 Apr 2024 10:19:10 +0200 Subject: [PATCH 14/50] renaming in NaryExpression.h for accordance with other function, adding correct prefix in Constants.h --- src/engine/sparqlExpressions/NaryExpression.h | 4 ++-- src/engine/sparqlExpressions/StringExpressions.cpp | 9 +++++---- src/global/Constants.h | 4 ++-- src/parser/sparqlParser/SparqlQleverVisitor.cpp | 6 +++--- test/SparqlExpressionTest.cpp | 4 ++-- 5 files changed, 14 insertions(+), 13 deletions(-) diff --git a/src/engine/sparqlExpressions/NaryExpression.h b/src/engine/sparqlExpressions/NaryExpression.h index 64e74675d8..992fbb10e4 100644 --- a/src/engine/sparqlExpressions/NaryExpression.h +++ b/src/engine/sparqlExpressions/NaryExpression.h @@ -82,8 +82,8 @@ SparqlExpression::Ptr makeIfExpression(SparqlExpression::Ptr child1, SparqlExpression::Ptr child2, SparqlExpression::Ptr child3); -SparqlExpression::Ptr toIntExpression(SparqlExpression::Ptr child); -SparqlExpression::Ptr toDoubleExpression(SparqlExpression::Ptr child); +SparqlExpression::Ptr makeIntExpression(SparqlExpression::Ptr child); +SparqlExpression::Ptr makeDoubleExpression(SparqlExpression::Ptr child); SparqlExpression::Ptr makeEncodeForUriExpression(SparqlExpression::Ptr child); diff --git a/src/engine/sparqlExpressions/StringExpressions.cpp b/src/engine/sparqlExpressions/StringExpressions.cpp index b6adde3985..f1f1ef4dd1 100644 --- a/src/engine/sparqlExpressions/StringExpressions.cpp +++ b/src/engine/sparqlExpressions/StringExpressions.cpp @@ -406,9 +406,10 @@ template return Id::makeUndefined(); } else { auto str = absl::StripAsciiWhitespace(input.value()); + auto strEnd = str.data() + str.size(); double resD{}; - auto conv = absl::from_chars(str.data(), str.data() + str.size(), resD); - if (conv.ec != std::error_code{} || conv.ptr != str.data() + str.size()) { + auto conv = absl::from_chars(str.data(), strEnd, resD); + if (conv.ec != std::error_code{} || conv.ptr != strEnd) { return Id::makeUndefined(); } if constexpr (std::is_same_v) { @@ -475,8 +476,8 @@ Expr makeEncodeForUriExpression(Expr child) { return make(child); } -Expr toIntExpression(Expr child) { return make(child); } +Expr makeIntExpression(Expr child) { return make(child); } -Expr toDoubleExpression(Expr child) { return make(child); } +Expr makeDoubleExpression(Expr child) { return make(child); } } // namespace sparqlExpression diff --git a/src/global/Constants.h b/src/global/Constants.h index 97f7d8d3f5..c7b68d5735 100644 --- a/src/global/Constants.h +++ b/src/global/Constants.h @@ -60,8 +60,8 @@ static constexpr std::pair GEOF_PREFIX = { "geof:", "http://www.opengis.net/def/function/geosparql/"}; static constexpr std::pair MATH_PREFIX = { "math:", "http://www.w3.org/2005/xpath-functions/math#"}; -static constexpr std::pair SCHEMA_TYPE = { - "xml_schema", "http://www.w3.org/2001/XMLSchema#"}; +static constexpr std::pair XSD_PREFIX = { + "xsd", "http://www.w3.org/2001/XMLSchema#"}; static const std::string INTERNAL_VARIABLE_PREFIX = "?_QLever_internal_variable_"; diff --git a/src/parser/sparqlParser/SparqlQleverVisitor.cpp b/src/parser/sparqlParser/SparqlQleverVisitor.cpp index 9b0cbf7706..02bbde7e26 100644 --- a/src/parser/sparqlParser/SparqlQleverVisitor.cpp +++ b/src/parser/sparqlParser/SparqlQleverVisitor.cpp @@ -121,13 +121,13 @@ ExpressionPtr Visitor::processIriFunctionCall( checkNumArgs(1); return sparqlExpression::makeTanExpression(std::move(argList[0])); } - } else if (checkPrefix(SCHEMA_TYPE)) { + } else if (checkPrefix(XSD_PREFIX)) { if (functionName == "integer" || functionName == "int") { checkNumArgs(1); - return sparqlExpression::toIntExpression(std::move(argList[0])); + return sparqlExpression::makeIntExpression(std::move(argList[0])); } else if (functionName == "double" || functionName == "decimal") { checkNumArgs(1); - return sparqlExpression::toDoubleExpression(std::move(argList[0])); + return sparqlExpression::makeDoubleExpression(std::move(argList[0])); } } reportNotSupported(ctx, diff --git a/test/SparqlExpressionTest.cpp b/test/SparqlExpressionTest.cpp index 492816f48c..f2102cb7e0 100644 --- a/test/SparqlExpressionTest.cpp +++ b/test/SparqlExpressionTest.cpp @@ -807,8 +807,8 @@ TEST(SparqlExpression, isSomethingFunctions) { // ____________________________________________________________________________ TEST(SparqlExpression, testToNumericExpression) { - auto checkGetInt = testUnaryExpression<&toIntExpression>; - auto checkGetDouble = testUnaryExpression<&toDoubleExpression>; + auto checkGetInt = testUnaryExpression<&makeIntExpression>; + auto checkGetDouble = testUnaryExpression<&makeDoubleExpression>; checkGetInt(idOrLitOrStringVec({U, "5.97", "-78.97", "-5BoB6", "FreBurg1", "", " .", " 42\n", " 0.01 "}), From a1186098b8fefcca27c8ebe7da20a9971027f100 Mon Sep 17 00:00:00 2001 From: realHannes Date: Mon, 29 Apr 2024 12:20:14 +0200 Subject: [PATCH 15/50] added test coverage for function calls makeIntExpression and make DoubleExpression --- src/engine/sparqlExpressions/StringExpressions.cpp | 10 ++++++---- test/SparqlAntlrParserTest.cpp | 9 +++++++++ 2 files changed, 15 insertions(+), 4 deletions(-) diff --git a/src/engine/sparqlExpressions/StringExpressions.cpp b/src/engine/sparqlExpressions/StringExpressions.cpp index f1f1ef4dd1..16c7613940 100644 --- a/src/engine/sparqlExpressions/StringExpressions.cpp +++ b/src/engine/sparqlExpressions/StringExpressions.cpp @@ -420,8 +420,8 @@ template } } }; -using ToInt = StringExpressionImpl<1, decltype(toNumeric)>; -using ToDouble = StringExpressionImpl<1, decltype(toNumeric)>; +using ToIntExpression = StringExpressionImpl<1, decltype(toNumeric)>; +using ToDoubleExpression = StringExpressionImpl<1, decltype(toNumeric)>; } // namespace detail::string_expressions using namespace detail::string_expressions; @@ -476,8 +476,10 @@ Expr makeEncodeForUriExpression(Expr child) { return make(child); } -Expr makeIntExpression(Expr child) { return make(child); } +Expr makeIntExpression(Expr child) { return make(child); } -Expr makeDoubleExpression(Expr child) { return make(child); } +Expr makeDoubleExpression(Expr child) { + return make(child); +} } // namespace sparqlExpression diff --git a/test/SparqlAntlrParserTest.cpp b/test/SparqlAntlrParserTest.cpp index a11d54f865..4de7c953b7 100644 --- a/test/SparqlAntlrParserTest.cpp +++ b/test/SparqlAntlrParserTest.cpp @@ -1534,6 +1534,7 @@ TEST(SparqlParser, FunctionCall) { // manually add it when constructing parser inputs. auto geof = absl::StrCat("<", GEOF_PREFIX.second); auto math = absl::StrCat("<", MATH_PREFIX.second); + auto xsd = absl::StrCat("<", XSD_PREFIX.second); // Correct function calls. Check that the parser picks the correct expression. expectFunctionCall(absl::StrCat(geof, "latitude>(?x)"), @@ -1555,6 +1556,14 @@ TEST(SparqlParser, FunctionCall) { matchUnary(&makeCosExpression)); expectFunctionCall(absl::StrCat(math, "tan>(?x)"), matchUnary(&makeTanExpression)); + expectFunctionCall(absl::StrCat(xsd, "int>(?x)"), + matchUnary(&makeIntExpression)); + expectFunctionCall(absl::StrCat(xsd, "integer>(?x)"), + matchUnary(&makeIntExpression)); + expectFunctionCall(absl::StrCat(xsd, "double>(?x)"), + matchUnary(&makeDoubleExpression)); + expectFunctionCall(absl::StrCat(xsd, "decimal>(?x)"), + matchUnary(&makeDoubleExpression)); // Wrong number of arguments. expectFunctionCallFails( From 062052e142380cb1a39e8216048824c24d1ee59f Mon Sep 17 00:00:00 2001 From: realHannes Date: Mon, 29 Apr 2024 15:46:59 +0200 Subject: [PATCH 16/50] toNumeric has now correct behavior and uses absl::from_chars() and std::from_chars() --- .../sparqlExpressions/StringExpressions.cpp | 19 +++++++++++-------- test/SparqlExpressionTest.cpp | 6 +++--- 2 files changed, 14 insertions(+), 11 deletions(-) diff --git a/src/engine/sparqlExpressions/StringExpressions.cpp b/src/engine/sparqlExpressions/StringExpressions.cpp index 16c7613940..fb6f3dc0b7 100644 --- a/src/engine/sparqlExpressions/StringExpressions.cpp +++ b/src/engine/sparqlExpressions/StringExpressions.cpp @@ -407,17 +407,20 @@ template } else { auto str = absl::StripAsciiWhitespace(input.value()); auto strEnd = str.data() + str.size(); - double resD{}; - auto conv = absl::from_chars(str.data(), strEnd, resD); - if (conv.ec != std::error_code{} || conv.ptr != strEnd) { - return Id::makeUndefined(); - } + auto strStart = str.data(); + T resD{}; if constexpr (std::is_same_v) { - auto resT = static_cast(resD); - return Id::makeFromInt(resT); + auto conv = std::from_chars(strStart, strEnd, resD); + if (conv.ec == std::error_code{} && conv.ptr == strEnd) { + return Id::makeFromInt(resD); + } } else { - return Id::makeFromDouble(resD); + auto conv = absl::from_chars(strStart, strEnd, resD); + if (conv.ec == std::error_code{} && conv.ptr == strEnd) { + return Id::makeFromDouble(resD); + } } + return Id::makeUndefined(); } }; using ToIntExpression = StringExpressionImpl<1, decltype(toNumeric)>; diff --git a/test/SparqlExpressionTest.cpp b/test/SparqlExpressionTest.cpp index f2102cb7e0..d010d9a94a 100644 --- a/test/SparqlExpressionTest.cpp +++ b/test/SparqlExpressionTest.cpp @@ -810,9 +810,9 @@ TEST(SparqlExpression, testToNumericExpression) { auto checkGetInt = testUnaryExpression<&makeIntExpression>; auto checkGetDouble = testUnaryExpression<&makeDoubleExpression>; - checkGetInt(idOrLitOrStringVec({U, "5.97", "-78.97", "-5BoB6", "FreBurg1", "", - " .", " 42\n", " 0.01 "}), - Ids{U, I(5), I(-78), U, U, U, U, I(42), I(0)}); + checkGetInt(idOrLitOrStringVec({U, " -1275", "5.97", "-78.97", "-5BoB6", + "FreBurg1", "", " .", " 42\n", " 0.01 ", ""}), + Ids{U, I(-1275), U, U, U, U, U, U, I(42), U, U}); checkGetDouble( idOrLitOrStringVec({U, "-122.2", "19,96", " 128789334.345 ", "-0.f", " 0.007 ", " -14.75 "}), From 6d0f42a8b4c70bc906135e9cc54478d4c43136e2 Mon Sep 17 00:00:00 2001 From: realHannes Date: Mon, 29 Apr 2024 16:07:40 +0200 Subject: [PATCH 17/50] made clang-format for NaryExpressionImpl.h --- src/engine/sparqlExpressions/NaryExpressionImpl.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/engine/sparqlExpressions/NaryExpressionImpl.h b/src/engine/sparqlExpressions/NaryExpressionImpl.h index 5637223158..1a5d89c315 100644 --- a/src/engine/sparqlExpressions/NaryExpressionImpl.h +++ b/src/engine/sparqlExpressions/NaryExpressionImpl.h @@ -6,8 +6,8 @@ #include -#include "absl/strings/charconv.h" #include "absl/strings/ascii.h" +#include "absl/strings/charconv.h" #include "engine/sparqlExpressions/NaryExpression.h" #include "engine/sparqlExpressions/SparqlExpressionGenerators.h" #include "engine/sparqlExpressions/SparqlExpressionValueGetters.h" From edc974a5572d3c254222320182d388aa85976e7e Mon Sep 17 00:00:00 2001 From: realHannes Date: Wed, 18 Sep 2024 09:29:42 +0200 Subject: [PATCH 18/50] Add implementation for pre-filtering blocks on their metadata --- src/index/CMakeLists.txt | 2 +- src/index/CompressedBlockPrefiltering.cpp | 161 ++++++++ src/index/CompressedBlockPrefiltering.h | 129 ++++++ test/CMakeLists.txt | 2 + test/CompressedBlockPrefilteringTest.cpp | 482 ++++++++++++++++++++++ test/SparqlExpressionTestHelpers.h | 32 ++ 6 files changed, 807 insertions(+), 1 deletion(-) create mode 100644 src/index/CompressedBlockPrefiltering.cpp create mode 100644 src/index/CompressedBlockPrefiltering.h create mode 100644 test/CompressedBlockPrefilteringTest.cpp diff --git a/src/index/CMakeLists.txt b/src/index/CMakeLists.txt index e41fd2471f..ac795a0fd4 100644 --- a/src/index/CMakeLists.txt +++ b/src/index/CMakeLists.txt @@ -5,5 +5,5 @@ add_library(index LocatedTriples.cpp Permutation.cpp TextMetaData.cpp DocsDB.cpp FTSAlgorithms.cpp PrefixHeuristic.cpp CompressedRelation.cpp - PatternCreator.cpp ScanSpecification.cpp) + PatternCreator.cpp ScanSpecification.cpp CompressedBlockPrefiltering.cpp) qlever_target_link_libraries(index util parser vocabulary compilationInfo ${STXXL_LIBRARIES}) diff --git a/src/index/CompressedBlockPrefiltering.cpp b/src/index/CompressedBlockPrefiltering.cpp new file mode 100644 index 0000000000..564d768127 --- /dev/null +++ b/src/index/CompressedBlockPrefiltering.cpp @@ -0,0 +1,161 @@ +// Copyright 2024, University of Freiburg, +// Chair of Algorithms and Data Structures +// Author: Hannes Baumann + +#include "index/CompressedBlockPrefiltering.h" + +// SECTION RELATIONAL OPERATIONS +//______________________________________________________________________________ +template +bool RelationalExpressions::compare(const ValueId& firstId, + const ValueId& secondId) { + // For compactness we combine LE and GT here. In evaluate (see below), for + // LE we select all blocks at a smaller (lower_bound) index position, and + // for GT all the respective blocks at larger or equal indices. + if constexpr (Comparison == CompOp::LE || Comparison == CompOp::GT) { + return compareIds(firstId, secondId, CompOp::LE) == + valueIdComparators::ComparisonResult::True; + } else { + // Follows the same logic for evaluation as explained above. + // Used for CompOp::LT, CompOp::GE, CompOp::EQ and CompOp::NE. + // For CompOp::EQ we also use the LT operation: This is possible because in + // evaluate we calculate a lower_bound and upper_bound over this LT + // operation, and those bounds act as the plausible range for equality + // (evaluation of CompOp::NE follows a similar approach). + return compareIds(firstId, secondId, CompOp::LT) == + valueIdComparators::ComparisonResult::True; + } +} + +//______________________________________________________________________________ +template +auto RelationalExpressions::lowerIndex( + std::vector& input, size_t evaluationColumn) { + // Custom compare function for binary search (lower bound). + auto makeCompare = [this, evaluationColumn]( + const BlockMetadata& blockMetadata, + const ValueId& referenceId) -> bool { + ValueId blockId = + Comparison == CompOp::LT || Comparison == CompOp::LE || + Comparison == CompOp::NE + ? getIdFromColumnIndex(blockMetadata.firstTriple_, evaluationColumn) + : getIdFromColumnIndex(blockMetadata.lastTriple_, evaluationColumn); + return compare(blockId, referenceId); + }; + return std::lower_bound(input.begin(), input.end(), referenceId_, + makeCompare); +}; + +//______________________________________________________________________________ +template +auto RelationalExpressions::upperIndex( + std::vector& input, size_t evaluationColumn) + requires(Comparison == CompOp::EQ || Comparison == CompOp::NE) { + // Custom compare function for binary search (upper bound). + auto makeCompare = [this, evaluationColumn](const ValueId& referenceId, + BlockMetadata& blockMetadata) { + return compare( + referenceId, + Comparison == CompOp::EQ + ? getIdFromColumnIndex(blockMetadata.firstTriple_, evaluationColumn) + : getIdFromColumnIndex(blockMetadata.lastTriple_, + evaluationColumn)); + }; + // Use the same comparison operations as for the method lowerIndex + // (std::lower_bound). However, given that std::upper_bound should return an + // index to the first block that is larger than the provided reference value, + // we have to swap the input values when calling compare() in makeCompare. + // compare(blockId, referenceId) -> compare(referenceId, blockId) + return std::upper_bound(input.begin(), input.end(), referenceId_, + makeCompare); +}; + +//______________________________________________________________________________ +template +std::vector RelationalExpressions::evaluate( + std::vector& input, size_t evaluationColumn) { + if constexpr (Comparison == CompOp::LT || Comparison == CompOp::LE) { + return std::vector(input.begin(), + lowerIndex(input, evaluationColumn)); + } else if constexpr (Comparison == CompOp::GE || Comparison == CompOp::GT) { + // Complementary block selection to LT and LE + return std::vector(lowerIndex(input, evaluationColumn), + input.end()); + } else if constexpr (Comparison == CompOp::EQ) { + return std::vector(lowerIndex(input, evaluationColumn), + upperIndex(input, evaluationColumn)); + } else { + // CompOP::NE + // Get the indices that define the boundaries w.r.t. blocks potentially + // containing non-equal values. + auto firstBound = lowerIndex(input, evaluationColumn); + auto secondBound = upperIndex(input, evaluationColumn); + if (firstBound > secondBound) { + return input; // all blocks are relevant + } + // Extract the values from the respective ranges defined by the bounding + // indices from vector input. + // [input.begin(), firstBound) and [secondBound, input.end()] + std::vector vecNe; + vecNe.reserve(std::distance(input.begin(), firstBound) + + std::distance(secondBound, input.end())); + vecNe.insert(vecNe.end(), input.begin(), firstBound); + vecNe.insert(vecNe.end(), secondBound, input.end()); + return vecNe; + } +}; + +//______________________________________________________________________________ +// Necessary instantiation of template specializations +template class RelationalExpressions; +template class RelationalExpressions; +template class RelationalExpressions; +template class RelationalExpressions; +template class RelationalExpressions; +template class RelationalExpressions; + +// SECTION LOGICAL OPERATIONS +//______________________________________________________________________________ +template +std::vector LogicalExpressions::evaluateOr( + std::vector& input, size_t evaluationColumn) + requires(Operation == LogicalOperators::OR) { + auto relevantBlocksChild1 = child1_->evaluate(input, evaluationColumn); + auto relevantBlocksChild2 = child2_->evaluate(input, evaluationColumn); + // The result vector, reserve space for the logical upper bound (num blocks) + // |relevantBlocksChild1| + |relevantBlocksChild2| regarding the OR + // operation (union of blocks) + std::vector unionedRelevantBlocks; + unionedRelevantBlocks.reserve(relevantBlocksChild1.size() + + relevantBlocksChild2.size()); + // lambda function that implements the less-than comparison required for + // std::set_union + auto lessThan = [evaluationColumn](const BlockMetadata& block1, + const BlockMetadata& block2) { + return getIdFromColumnIndex(block1.lastTriple_, evaluationColumn) < + getIdFromColumnIndex(block2.lastTriple_, evaluationColumn); + }; + // Given that we have vectors with sorted (BlockMedata) values, we can + // use std::set_union, thus the complexity is O(n + m). + std::set_union(relevantBlocksChild1.begin(), relevantBlocksChild1.end(), + relevantBlocksChild2.begin(), relevantBlocksChild2.end(), + std::back_inserter(unionedRelevantBlocks), lessThan); + return unionedRelevantBlocks; +}; + +//______________________________________________________________________________ +template +std::vector LogicalExpressions::evaluate( + std::vector& input, size_t evaluationColumn) { + if constexpr (Operation == LogicalOperators::AND) { + auto resultChild1 = child1_->evaluate(input, evaluationColumn); + return child2_->evaluate(resultChild1, evaluationColumn); + } else { + return evaluateOr(input, evaluationColumn); + } +}; + +//______________________________________________________________________________ +// Necessary instantiation of template specializations +template class LogicalExpressions; +template class LogicalExpressions; diff --git a/src/index/CompressedBlockPrefiltering.h b/src/index/CompressedBlockPrefiltering.h new file mode 100644 index 0000000000..a35b36e71d --- /dev/null +++ b/src/index/CompressedBlockPrefiltering.h @@ -0,0 +1,129 @@ +// Copyright 2024, University of Freiburg, +// Chair of Algorithms and Data Structures +// Author: Hannes Baumann + +#pragma once + +#include +#include + +#include "global/Id.h" +#include "global/ValueIdComparators.h" +#include "index/CompressedRelation.h" + +// The compressed block metadata (see `CompressedRelation.h`) that we use to +// filter out the non-relevant blocks by checking their content of +// `firstTriple_` and `lastTriple_` (`PermutedTriple`) +using BlockMetadata = CompressedBlockMetadata; + +//______________________________________________________________________________ +/* +`PrefilterExpression` represents a base class for the following sub-classes that +implement the block-filtering procedure for the specific relational + logical +operations/expressions. + +Remark: We do not actually evaluate the respective SPARQL Expression. We only +pre-filter w.r.t. blocks that contain relevant data for the actual evaluation of +those expressions to make the evaluation procedure more efficient. + +The block-filtering is applied with the following operations: +Relational Expressions - `<=`, `>=`, `<`, `>`, `==` and `!=` +Logical Operations - `and` and `or` +*/ + +class PrefilterExpression { + public: + // The respective metadata to the blocks is expected to be provided in + // a sorted order (w.r.t. the relevant column). + virtual std::vector evaluate(std::vector& input, + size_t evaluationColumn) = 0; + virtual ~PrefilterExpression() = default; +}; + +//______________________________________________________________________________ +// For the actual comparison of the relevant ValueIds from the metadata triples, +// we use the implementations from ValueIdComparators. +// +// Supported comparisons are: +// - LessThan, LessEqual, Equal, NotEqual, GreaterEqual, GreaterThan +using CompOp = valueIdComparators::Comparison; + +//______________________________________________________________________________ +// Given a PermutedTriple, retrieve the suitable Id w.r.t. a column (index). +constexpr auto getIdFromColumnIndex = + [](const BlockMetadata::PermutedTriple& permutedTriple, + const size_t columnIndex) { + switch (columnIndex) { + case 0: + return permutedTriple.col0Id_; + case 1: + return permutedTriple.col1Id_; + case 2: + return permutedTriple.col2Id_; + default: + // Triple! + AD_FAIL(); + } + }; + +//______________________________________________________________________________ +template +class RelationalExpressions : public PrefilterExpression { + public: + explicit RelationalExpressions(ValueId referenceId) + : referenceId_(referenceId) {} + + std::vector evaluate(std::vector& input, + size_t evaluationColumn) override; + + private: + // The ValueId the comparison refers to. + ValueId referenceId_; + // Helper function that implements the relational comparison on the block + // values. + bool compare(const ValueId& tripleId, const ValueId& otherId); + // Helper for providing the lower indices during the evaluation procedure. + auto lowerIndex(std::vector& input, size_t evaluationColumn); + // Helper to get the upper indices, necessary for EQ and NE. + auto upperIndex(std::vector& input, size_t evaluationColumn) + requires(Comparison == CompOp::EQ || Comparison == CompOp::NE); +}; + +//______________________________________________________________________________ +// Helper struct for a compact class implementation regarding the logical +// operations `AND`, `OR` and `NOT`. +enum struct LogicalOperators { AND, OR, NOT }; + +//______________________________________________________________________________ +template +class LogicalExpressions : public PrefilterExpression { + public: + explicit LogicalExpressions(std::unique_ptr child1, + std::unique_ptr child2) + : child1_(std::move(child1)), child2_(std::move(child2)) {} + + std::vector evaluate(std::vector& input, + size_t evaluationColumn) override; + + private: + std::unique_ptr child1_; + std::unique_ptr child2_; + // Introduce a helper method for the specification LogicalOperators::OR + std::vector evaluateOr(std::vector& input, + size_t evaluationColumn) + requires(Operation == LogicalOperators::OR); +}; + +//______________________________________________________________________________ +// Definition of the RelationalExpressions for LT, LE, EQ, NE, GE and GT. +using LessThanExpression = RelationalExpressions; +using LessEqualExpression = RelationalExpressions; +using EqualExpression = RelationalExpressions; +using NotEqualExpression = RelationalExpressions; +using GreaterEqualExpression = RelationalExpressions; +using GreaterThanExpression = RelationalExpressions; + +//______________________________________________________________________________ +// Defintion of the LogicalExpressions for AND, OR and NOT. +using AndExpression = LogicalExpressions; +using OrExpression = LogicalExpressions; diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index d9a5d2a0fe..d7e2a6d855 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -287,6 +287,8 @@ addLinkAndDiscoverTest(AlgorithmTest) addLinkAndDiscoverTestSerial(CompressedRelationsTest index) +addLinkAndDiscoverTestSerial(CompressedBlockPrefilteringTest index) + addLinkAndDiscoverTest(ExceptionTest) addLinkAndDiscoverTestSerial(RandomExpressionTest index) diff --git a/test/CompressedBlockPrefilteringTest.cpp b/test/CompressedBlockPrefilteringTest.cpp new file mode 100644 index 0000000000..3f99cc3448 --- /dev/null +++ b/test/CompressedBlockPrefilteringTest.cpp @@ -0,0 +1,482 @@ +// Copyright 2024, University of Freiburg, +// Chair of Algorithms and Data Structures +// Author: Hannes Baumann + +#include + +#include + +#include "./SparqlExpressionTestHelpers.h" +#include "index/CompressedBlockPrefiltering.h" +#include "util/IdTestHelpers.h" + +using ad_utility::testing::DoubleId; +using ad_utility::testing::IntId; +using ad_utility::testing::UndefId; +using sparqlExpression::TestContext; + +//______________________________________________________________________________ +struct MetadataBlocks { + // Define five blocks, for convenience all of their columns are sorted on + // their data when appended in the given order to a vector. + // Columns indexed: COLUMN 0 | COLUMN 1 | COLUMN 2 + BlockMetadata nb1{{}, + 0, + {IntId(16), IntId(0), DoubleId(0.0)}, // firstTriple + {IntId(38), IntId(0), DoubleId(12.5)}}; // lastTriple + BlockMetadata nb2{{}, + 0, + {IntId(42), IntId(0), DoubleId(12.5)}, + {IntId(42), IntId(2), DoubleId(14.575)}}; + BlockMetadata nb3{{}, + 0, + {IntId(42), IntId(2), DoubleId(16.33)}, + {IntId(45), IntId(2), DoubleId(18.32)}}; + BlockMetadata nb4{{}, + 0, + {IntId(46), IntId(2), DoubleId(22.29)}, + {IntId(47), IntId(6), DoubleId(111.223)}}; + BlockMetadata nb5{{}, + 0, + {IntId(48), IntId(6), DoubleId(111.333)}, + {IntId(51), IntId(6), DoubleId(112.00)}}; + std::vector numericBlocks = {nb1, nb2, nb3, nb4, nb5}; + + //____________________________________________________________________________ + // Define Blocks containing VocabIds + // Use TestContext from SparqlExpressionTestHelpers.h + sparqlExpression::TestContext tc{}; + Id Undef = UndefId(); + // Columns indexed: COLUMN 0 | COLUMN 1 | COLUMN 2 + // undef. | LocalVocab | undef. + BlockMetadata vb1{{}, 0, {Undef, tc.bonn, Undef}, {Undef, tc.cologne, Undef}}; + BlockMetadata vb2{ + {}, 0, {Undef, tc.dortmund, Undef}, {Undef, tc.essen, Undef}}; + BlockMetadata vb3{ + {}, 0, {Undef, tc.frankfurt, Undef}, {Undef, tc.frankfurt, Undef}}; + BlockMetadata vb4{ + {}, 0, {Undef, tc.hamburg, Undef}, {Undef, tc.karlsruhe, Undef}}; + BlockMetadata vb5{ + {}, 0, {Undef, tc.karlsruhe, Undef}, {Undef, tc.karlsruhe, Undef}}; + std::vector vocabBlocks = {vb1, vb2, vb3, vb4, vb5}; +}; + +// Static tests, they focus on corner case values for the given block triples. +//______________________________________________________________________________ +//______________________________________________________________________________ +// Test Relational Expressions + +// Test LessThanExpression +// Remark: We compare w.r.t. the first triple! +TEST(RelationalExpressions, testLessThanExpressions) { + MetadataBlocks blocks{}; + std::vector expectedResult = {}; + // NUMERIC + EXPECT_EQ(expectedResult, + LessThanExpression{IntId(10)}.evaluate(blocks.numericBlocks, 0)); + EXPECT_EQ(expectedResult, + LessThanExpression{IntId(16)}.evaluate(blocks.numericBlocks, 0)); + expectedResult = {blocks.nb1}; + EXPECT_EQ(expectedResult, + LessThanExpression{IntId(40)}.evaluate(blocks.numericBlocks, 0)); + EXPECT_EQ(expectedResult, + LessThanExpression{IntId(42)}.evaluate(blocks.numericBlocks, 0)); + expectedResult = {blocks.nb1, blocks.nb2, blocks.nb3}; + EXPECT_EQ(expectedResult, + LessThanExpression{IntId(46)}.evaluate(blocks.numericBlocks, 0)); + EXPECT_EQ(blocks.numericBlocks, + LessThanExpression{IntId(100)}.evaluate(blocks.numericBlocks, 0)); + // VOCAB + TestContext tc{}; + expectedResult = {}; + // tc.alpha is an Id of type Vocab, w.r.t. a lexicographical order, all the + // citiy IDs (LocalVocab) should be greater than the ID (Vocab) of "alpha". + // Thus we expect that none of the blocks are relevant. + EXPECT_EQ(expectedResult, + LessThanExpression{tc.alpha}.evaluate(blocks.vocabBlocks, 1)); + EXPECT_EQ(expectedResult, + LessThanExpression{tc.berlin}.evaluate(blocks.vocabBlocks, 1)); + // All cities used within vocabBlocks should be smaller than city "munich" + // (given their respective LocalVocab IDs). + EXPECT_EQ(blocks.vocabBlocks, + LessThanExpression{tc.munich}.evaluate(blocks.vocabBlocks, 1)); + // All blocks from vocabBlocks should contain values less-than the (Vocab) + // ID for "zz". + EXPECT_EQ(blocks.vocabBlocks, + LessThanExpression{tc.zz}.evaluate(blocks.vocabBlocks, 1)); + expectedResult = {blocks.vb1, blocks.vb2}; + EXPECT_EQ(expectedResult, + LessThanExpression{tc.düsseldorf}.evaluate(blocks.vocabBlocks, 1)); + expectedResult = {blocks.vb1, blocks.vb2, blocks.vb3}; + EXPECT_EQ(expectedResult, LessThanExpression{tc.frankfurt_oder}.evaluate( + blocks.vocabBlocks, 1)); + expectedResult = {blocks.vb1, blocks.vb2, blocks.vb3, blocks.vb4}; + EXPECT_EQ(expectedResult, + LessThanExpression{tc.ingolstadt}.evaluate(blocks.vocabBlocks, 1)); +} + +//______________________________________________________________________________ +// Test LessEqualExpression +// Remark: We compare w.r.t. the first triple +TEST(RelationalExpressions, testLessEqualExpressions) { + MetadataBlocks blocks{}; + std::vector expectedResult = {}; + // NUMERIC + EXPECT_EQ(expectedResult, + LessEqualExpression{IntId(10)}.evaluate(blocks.numericBlocks, 0)); + expectedResult = {blocks.nb1}; + EXPECT_EQ(expectedResult, + LessEqualExpression{IntId(16)}.evaluate(blocks.numericBlocks, 0)); + EXPECT_EQ(expectedResult, + LessEqualExpression{IntId(40)}.evaluate(blocks.numericBlocks, 0)); + expectedResult = {blocks.nb1, blocks.nb2, blocks.nb3}; + EXPECT_EQ(expectedResult, + LessEqualExpression{IntId(42)}.evaluate(blocks.numericBlocks, 0)); + expectedResult = {blocks.nb1, blocks.nb2, blocks.nb3, blocks.nb4}; + EXPECT_EQ(expectedResult, + LessEqualExpression{IntId(46)}.evaluate(blocks.numericBlocks, 0)); + EXPECT_EQ(blocks.numericBlocks, + LessEqualExpression{IntId(100)}.evaluate(blocks.numericBlocks, 0)); + // VOCAB + TestContext tc{}; + expectedResult = {}; + EXPECT_EQ(expectedResult, + LessEqualExpression{tc.alpha}.evaluate(blocks.vocabBlocks, 1)); + EXPECT_EQ(expectedResult, + LessEqualExpression{tc.berlin}.evaluate(blocks.vocabBlocks, 1)); + expectedResult = {blocks.vb1, blocks.vb2}; + EXPECT_EQ(expectedResult, + LessEqualExpression{tc.dortmund}.evaluate(blocks.vocabBlocks, 1)); + expectedResult = {blocks.vb1, blocks.vb2, blocks.vb3, blocks.vb4}; + EXPECT_EQ(expectedResult, + LessEqualExpression{tc.hannover}.evaluate(blocks.vocabBlocks, 1)); + EXPECT_EQ(blocks.vocabBlocks, + LessEqualExpression{tc.karlsruhe}.evaluate(blocks.vocabBlocks, 1)); +} + +//______________________________________________________________________________ +// Test GreaterThanExpression +// Remark: We compare w.r.t. the last triple +TEST(RelationalExpressions, testGreaterThanExpression) { + MetadataBlocks blocks{}; + EXPECT_EQ(blocks.numericBlocks, + GreaterThanExpression{IntId(10)}.evaluate(blocks.numericBlocks, 0)); + EXPECT_EQ(blocks.numericBlocks, + GreaterThanExpression{IntId(16)}.evaluate(blocks.numericBlocks, 0)); + std::vector expectedResult = {blocks.nb2, blocks.nb3, + blocks.nb4, blocks.nb5}; + EXPECT_EQ(expectedResult, + GreaterThanExpression{IntId(38)}.evaluate(blocks.numericBlocks, 0)); + expectedResult = {blocks.nb3, blocks.nb4, blocks.nb5}; + EXPECT_EQ(expectedResult, + GreaterThanExpression{IntId(42)}.evaluate(blocks.numericBlocks, 0)); + expectedResult = {blocks.nb4, blocks.nb5}; + EXPECT_EQ(expectedResult, + GreaterThanExpression{IntId(46)}.evaluate(blocks.numericBlocks, 0)); + expectedResult = {}; + EXPECT_EQ(expectedResult, + GreaterThanExpression{IntId(52)}.evaluate(blocks.numericBlocks, 0)); + // VOCAB + TestContext tc{}; + expectedResult = {}; + EXPECT_EQ(expectedResult, + GreaterThanExpression{tc.munich}.evaluate(blocks.vocabBlocks, 1)); + EXPECT_EQ(expectedResult, GreaterThanExpression{tc.karlsruhe}.evaluate( + blocks.vocabBlocks, 1)); + EXPECT_EQ(blocks.vocabBlocks, + GreaterThanExpression{tc.alpha}.evaluate(blocks.vocabBlocks, 1)); + expectedResult = {blocks.vb4, blocks.vb5}; + EXPECT_EQ(expectedResult, + GreaterThanExpression{tc.hamburg}.evaluate(blocks.vocabBlocks, 1)); + expectedResult = {blocks.vb4, blocks.vb5}; + EXPECT_EQ(expectedResult, + GreaterThanExpression{tc.hannover}.evaluate(blocks.vocabBlocks, 1)); + expectedResult = {blocks.vb2, blocks.vb3, blocks.vb4, blocks.vb5}; + EXPECT_EQ(expectedResult, GreaterThanExpression{tc.düsseldorf}.evaluate( + blocks.vocabBlocks, 1)); +} + +//______________________________________________________________________________ +// Test GreaterEqualExpression +// Remark: We compare w.r.t. the last triple +TEST(RelationalExpressions, testGreaterEqualExpression) { + MetadataBlocks blocks{}; + EXPECT_EQ(blocks.numericBlocks, GreaterEqualExpression{IntId(10)}.evaluate( + blocks.numericBlocks, 0)); + EXPECT_EQ(blocks.numericBlocks, GreaterEqualExpression{IntId(38)}.evaluate( + blocks.numericBlocks, 0)); + std::vector expectedResult = {blocks.nb2, blocks.nb3, + blocks.nb4, blocks.nb5}; + EXPECT_EQ(expectedResult, GreaterEqualExpression{IntId(40)}.evaluate( + blocks.numericBlocks, 0)); + EXPECT_EQ(expectedResult, GreaterEqualExpression{IntId(42)}.evaluate( + blocks.numericBlocks, 0)); + expectedResult = {blocks.nb3, blocks.nb4, blocks.nb5}; + EXPECT_EQ(expectedResult, GreaterEqualExpression{IntId(45)}.evaluate( + blocks.numericBlocks, 0)); + expectedResult = {blocks.nb4, blocks.nb5}; + EXPECT_EQ(expectedResult, GreaterEqualExpression{IntId(47)}.evaluate( + blocks.numericBlocks, 0)); + expectedResult = {}; + EXPECT_EQ(expectedResult, GreaterEqualExpression{IntId(100)}.evaluate( + blocks.numericBlocks, 0)); + // VOCAB + TestContext tc{}; + EXPECT_EQ(blocks.vocabBlocks, + GreaterEqualExpression{tc.alpha}.evaluate(blocks.vocabBlocks, 1)); + EXPECT_EQ(blocks.vocabBlocks, + GreaterEqualExpression{tc.bonn}.evaluate(blocks.vocabBlocks, 1)); + EXPECT_EQ(blocks.vocabBlocks, + GreaterEqualExpression{tc.cologne}.evaluate(blocks.vocabBlocks, 1)); + expectedResult = {blocks.vb2, blocks.vb3, blocks.vb4, blocks.vb5}; + EXPECT_EQ(expectedResult, GreaterEqualExpression{tc.düsseldorf}.evaluate( + blocks.vocabBlocks, 1)); + expectedResult = {blocks.vb4, blocks.vb5}; + EXPECT_EQ(expectedResult, GreaterEqualExpression{tc.frankfurt_oder}.evaluate( + blocks.vocabBlocks, 1)); + EXPECT_EQ(expectedResult, GreaterEqualExpression{tc.karlsruhe}.evaluate( + blocks.vocabBlocks, 1)); + expectedResult = {}; + EXPECT_EQ(expectedResult, + GreaterEqualExpression{tc.munich}.evaluate(blocks.vocabBlocks, 1)); + EXPECT_EQ(expectedResult, + GreaterEqualExpression{tc.zz}.evaluate(blocks.vocabBlocks, 1)); +} + +//______________________________________________________________________________ +// Test EqualExpression +TEST(RelationalExpressions, testEqualExpression) { + MetadataBlocks blocks{}; + std::vector expectedResult = {}; + EXPECT_EQ(expectedResult, + EqualExpression{IntId(10)}.evaluate(blocks.numericBlocks, 1)); + EXPECT_EQ(expectedResult, + EqualExpression{IntId(10)}.evaluate(blocks.numericBlocks, 0)); + expectedResult = {blocks.nb4}; + EXPECT_EQ(expectedResult, + EqualExpression{IntId(5)}.evaluate(blocks.numericBlocks, 1)); + expectedResult = {blocks.nb2, blocks.nb3, blocks.nb4}; + EXPECT_EQ(expectedResult, + EqualExpression{IntId(2)}.evaluate(blocks.numericBlocks, 1)); + expectedResult = {blocks.nb4, blocks.nb5}; + EXPECT_EQ(expectedResult, + EqualExpression{IntId(6)}.evaluate(blocks.numericBlocks, 1)); + expectedResult = {blocks.nb2, blocks.nb3}; + EXPECT_EQ(expectedResult, + EqualExpression{IntId(42)}.evaluate(blocks.numericBlocks, 0)); + expectedResult = {blocks.nb5}; + EXPECT_EQ(expectedResult, + EqualExpression{IntId(112)}.evaluate(blocks.numericBlocks, 2)); + // VOCAB + TestContext tc{}; + expectedResult = {}; + EXPECT_EQ(expectedResult, + EqualExpression{tc.zz}.evaluate(blocks.vocabBlocks, 1)); + EXPECT_EQ(expectedResult, + EqualExpression{tc.alpha}.evaluate(blocks.vocabBlocks, 1)); + EXPECT_EQ(expectedResult, + EqualExpression{tc.munich}.evaluate(blocks.vocabBlocks, 1)); + EXPECT_EQ(expectedResult, + EqualExpression{tc.frankfurt_oder}.evaluate(blocks.vocabBlocks, 1)); + expectedResult = {blocks.vb4, blocks.vb5}; + EXPECT_EQ(expectedResult, + EqualExpression{tc.karlsruhe}.evaluate(blocks.vocabBlocks, 1)); + expectedResult = {blocks.vb3}; + EXPECT_EQ(expectedResult, + EqualExpression{tc.frankfurt}.evaluate(blocks.vocabBlocks, 1)); + expectedResult = {blocks.vb2}; + EXPECT_EQ(expectedResult, + EqualExpression{tc.düsseldorf}.evaluate(blocks.vocabBlocks, 1)); + expectedResult = {blocks.vb1}; + EXPECT_EQ(expectedResult, + EqualExpression{tc.bonn}.evaluate(blocks.vocabBlocks, 1)); + EXPECT_EQ(expectedResult, + EqualExpression{tc.cologne}.evaluate(blocks.vocabBlocks, 1)); +} + +//______________________________________________________________________________ +// Test NotEqualExpression +TEST(RelationalExpressions, testNotEqualExpression) { + MetadataBlocks blocks{}; + std::vector expectedResult{}; + EXPECT_EQ(blocks.numericBlocks, + NotEqualExpression{IntId(8)}.evaluate(blocks.numericBlocks, 0)); + EXPECT_EQ(blocks.numericBlocks, + NotEqualExpression{IntId(16)}.evaluate(blocks.numericBlocks, 0)); + EXPECT_EQ(blocks.numericBlocks, + NotEqualExpression{IntId(45)}.evaluate(blocks.numericBlocks, 0)); + EXPECT_EQ(blocks.numericBlocks, + NotEqualExpression{IntId(51)}.evaluate(blocks.numericBlocks, 0)); + EXPECT_EQ(blocks.numericBlocks, + NotEqualExpression{IntId(48)}.evaluate(blocks.numericBlocks, 0)); + EXPECT_EQ(blocks.numericBlocks, NotEqualExpression{DoubleId(18.32)}.evaluate( + blocks.numericBlocks, 2)); + EXPECT_EQ(blocks.numericBlocks, NotEqualExpression{DoubleId(22.33)}.evaluate( + blocks.numericBlocks, 2)); + EXPECT_EQ(blocks.numericBlocks, + NotEqualExpression{IntId(17)}.evaluate(blocks.numericBlocks, 0)); + expectedResult = {blocks.nb1, blocks.nb3, blocks.nb4, blocks.nb5}; + EXPECT_EQ(expectedResult, + NotEqualExpression{IntId(42)}.evaluate(blocks.numericBlocks, 0)); + expectedResult = {blocks.nb1, blocks.nb2, blocks.nb4, blocks.nb5}; + EXPECT_EQ(expectedResult, + NotEqualExpression{IntId(2)}.evaluate(blocks.numericBlocks, 1)); + expectedResult = {blocks.nb1, blocks.nb2, blocks.nb3, blocks.nb4}; + EXPECT_EQ(expectedResult, NotEqualExpression{DoubleId(6.00)}.evaluate( + blocks.numericBlocks, 1)); + expectedResult = {blocks.nb2, blocks.nb3, blocks.nb4, blocks.nb5}; + EXPECT_EQ(expectedResult, + NotEqualExpression{IntId(0)}.evaluate(blocks.numericBlocks, 1)); + // VOCAB + TestContext tc{}; + expectedResult = {}; + EXPECT_EQ(blocks.vocabBlocks, + NotEqualExpression{tc.zz}.evaluate(blocks.vocabBlocks, 1)); + EXPECT_EQ(blocks.vocabBlocks, + NotEqualExpression{tc.alpha}.evaluate(blocks.vocabBlocks, 1)); + EXPECT_EQ(blocks.vocabBlocks, NotEqualExpression{tc.frankfurt_oder}.evaluate( + blocks.vocabBlocks, 1)); + EXPECT_EQ(blocks.vocabBlocks, + NotEqualExpression{tc.munich}.evaluate(blocks.vocabBlocks, 1)); + EXPECT_EQ(blocks.vocabBlocks, + NotEqualExpression{tc.bonn}.evaluate(blocks.vocabBlocks, 1)); + EXPECT_EQ(blocks.vocabBlocks, + NotEqualExpression{tc.cologne}.evaluate(blocks.vocabBlocks, 1)); + EXPECT_EQ(blocks.vocabBlocks, + NotEqualExpression{tc.düsseldorf}.evaluate(blocks.vocabBlocks, 1)); + expectedResult = {blocks.vb1, blocks.vb2, blocks.vb4, blocks.vb5}; + EXPECT_EQ(expectedResult, + NotEqualExpression{tc.frankfurt}.evaluate(blocks.vocabBlocks, 1)); + expectedResult = {blocks.vb1, blocks.vb2, blocks.vb3, blocks.vb4}; + EXPECT_EQ(expectedResult, + NotEqualExpression{tc.karlsruhe}.evaluate(blocks.vocabBlocks, 1)); +} + +//______________________________________________________________________________ +//______________________________________________________________________________ +// Test Logical Expressions + +// Test AndExpression +TEST(LogicalExpressions, testAndExpression) { + MetadataBlocks blocks{}; + std::vector expectedResult{}; + EXPECT_EQ(expectedResult, + AndExpression(std::make_unique(IntId(42)), + std::make_unique(IntId(45))) + .evaluate(blocks.numericBlocks, 0)); + EXPECT_EQ( + expectedResult, + AndExpression(std::make_unique(IntId(42)), + std::make_unique(DoubleId(52.33))) + .evaluate(blocks.numericBlocks, 0)); + EXPECT_EQ( + expectedResult, + AndExpression(std::make_unique( + std::make_unique(IntId(42)), + std::make_unique(IntId(45))), + std::make_unique(IntId(49))) + .evaluate(blocks.numericBlocks, 0)); + expectedResult = {blocks.nb1, blocks.nb2}; + EXPECT_EQ(expectedResult, + AndExpression(std::make_unique( + std::make_unique(IntId(0)), + std::make_unique(IntId(0))), + std::make_unique(IntId(6))) + .evaluate(blocks.numericBlocks, 1)); + // !!! Reason that we don't expect an empty result here: + // Block blocks.nb3 contains value within the range [42, 45] on column 0. + // Thus, this block is a valid candidate for values unequal to 42 and values + // that are equal to 45. + expectedResult = {blocks.nb3}; + EXPECT_EQ(expectedResult, + AndExpression(std::make_unique(IntId(42)), + std::make_unique(DoubleId(42.00))) + .evaluate(blocks.numericBlocks, 0)); + expectedResult = {blocks.nb1, blocks.nb2, blocks.nb3, blocks.nb4}; + EXPECT_EQ(expectedResult, + AndExpression(std::make_unique(DoubleId(6.00)), + std::make_unique(IntId(2.00))) + .evaluate(blocks.numericBlocks, 1)); + expectedResult = {blocks.nb4, blocks.nb5}; + EXPECT_EQ( + expectedResult, + AndExpression(std::make_unique( + std::make_unique(IntId(7)), + std::make_unique(IntId(5))), + std::make_unique(IntId(0))) + .evaluate(blocks.numericBlocks, 1)); + expectedResult = {blocks.nb1, blocks.nb2}; + EXPECT_EQ( + expectedResult, + AndExpression(std::make_unique(DoubleId(14.575)), + std::make_unique(IntId(12))) + .evaluate(blocks.numericBlocks, 2)); + expectedResult = {blocks.nb2, blocks.nb3, blocks.nb4}; + EXPECT_EQ(expectedResult, + AndExpression(std::make_unique(DoubleId(0.00)), + std::make_unique(DoubleId(6.00))) + .evaluate(blocks.numericBlocks, 1)); + expectedResult = {blocks.nb1, blocks.nb2}; + EXPECT_EQ(expectedResult, + AndExpression(std::make_unique(DoubleId(1.99)), + std::make_unique(DoubleId(1.5))) + .evaluate(blocks.numericBlocks, 1)); +} + +//______________________________________________________________________________ +// Test OrExpression +TEST(LogicalExpressions, testOrExpression) { + MetadataBlocks blocks{}; + std::vector expectedResult = {blocks.nb1, blocks.nb4, + blocks.nb5}; + EXPECT_EQ(expectedResult, + OrExpression(std::make_unique(IntId(42)), + std::make_unique(IntId(45))) + .evaluate(blocks.numericBlocks, 0)); + expectedResult = {}; + EXPECT_EQ(expectedResult, + OrExpression(std::make_unique(DoubleId(-14.23)), + std::make_unique(IntId(51))) + .evaluate(blocks.numericBlocks, 0)); + expectedResult = {blocks.nb2, blocks.nb3, blocks.nb4, blocks.nb5}; + EXPECT_EQ( + expectedResult, + OrExpression(std::make_unique(IntId(0)), + std::make_unique( + std::make_unique(IntId(5)), + std::make_unique(DoubleId(1.00)))) + .evaluate(blocks.numericBlocks, 1)); + expectedResult = {blocks.nb1, blocks.nb2, blocks.nb4, blocks.nb5}; + EXPECT_EQ(expectedResult, + OrExpression(std::make_unique(IntId(0)), + std::make_unique( + std::make_unique(IntId(3)), + std::make_unique(DoubleId(6)))) + .evaluate(blocks.numericBlocks, 1)); + expectedResult = {blocks.nb4, blocks.nb5}; + EXPECT_EQ( + expectedResult, + OrExpression(std::make_unique(IntId(20)), + std::make_unique(DoubleId(113.3))) + .evaluate(blocks.numericBlocks, 2)); + expectedResult = {blocks.nb1, blocks.nb5}; + EXPECT_EQ( + expectedResult, + OrExpression(std::make_unique(IntId(42)), + std::make_unique( + std::make_unique(IntId(49)), + std::make_unique(DoubleId(2.00)))) + .evaluate(blocks.numericBlocks, 0)); + expectedResult = {blocks.nb1, blocks.nb2, blocks.nb3, blocks.nb4}; + EXPECT_EQ(expectedResult, + OrExpression(std::make_unique( + std::make_unique(IntId(0)), + std::make_unique(DoubleId(2.00))), + std::make_unique(IntId(6))) + .evaluate(blocks.numericBlocks, 1)); + expectedResult = {blocks.nb1, blocks.nb2, blocks.nb3}; + EXPECT_EQ(expectedResult, + OrExpression(std::make_unique(DoubleId(17.00)), + std::make_unique(IntId(16.35))) + .evaluate(blocks.numericBlocks, 2)); +} diff --git a/test/SparqlExpressionTestHelpers.h b/test/SparqlExpressionTestHelpers.h index 6ded863c41..cdbb4f90f4 100644 --- a/test/SparqlExpressionTestHelpers.h +++ b/test/SparqlExpressionTestHelpers.h @@ -61,6 +61,10 @@ struct TestContext { // vocab. Id notInVocabA, notInVocabB, notInVocabC, notInVocabD, notInVocabAelpha, notInVocabIri, notInVocabIriLit; + // LocalVocab IDs used for testing the block pre-filtering procedure in + // CompressedBlockPrefilteringTest + Id berlin, bonn, cologne, dortmund, düsseldorf, essen, frankfurt, + frankfurt_oder, hamburg, hannover, ingolstadt, karlsruhe, munich; TestContext() { // First get some IDs for strings from the vocabulary to later reuse them. // Note the `u_` inserted for the blank node (see 'BlankNode.cpp'). @@ -90,12 +94,40 @@ struct TestContext { localVocab.getIndexAndAddIfNotContained(iri(""))); notInVocabAelpha = Id::makeFromLocalVocabIndex( localVocab.getIndexAndAddIfNotContained(lit("notInVocabÄlpha"))); + notInVocabAelpha = Id::makeFromLocalVocabIndex( + localVocab.getIndexAndAddIfNotContained(lit("notInVocabÄlpha"))); notInVocabIri = Id::makeFromLocalVocabIndex(localVocab.getIndexAndAddIfNotContained( iri(""))); notInVocabIriLit = Id::makeFromLocalVocabIndex(localVocab.getIndexAndAddIfNotContained( lit("http://www.w3.org/1999/02/22-rdf-syntax-ns#langString"))); + berlin = Id::makeFromLocalVocabIndex( + localVocab.getIndexAndAddIfNotContained(lit("berlin"))); + bonn = Id::makeFromLocalVocabIndex( + localVocab.getIndexAndAddIfNotContained(lit("bonn"))); + cologne = Id::makeFromLocalVocabIndex( + localVocab.getIndexAndAddIfNotContained(lit("cologne"))); + düsseldorf = Id::makeFromLocalVocabIndex( + localVocab.getIndexAndAddIfNotContained(lit("düsseldorf"))); + dortmund = Id::makeFromLocalVocabIndex( + localVocab.getIndexAndAddIfNotContained(lit("dortmund"))); + essen = Id::makeFromLocalVocabIndex( + localVocab.getIndexAndAddIfNotContained(lit("essen"))); + frankfurt = Id::makeFromLocalVocabIndex( + localVocab.getIndexAndAddIfNotContained(lit("frankfurt"))); + frankfurt_oder = Id::makeFromLocalVocabIndex( + localVocab.getIndexAndAddIfNotContained(lit("frankfurt (oder)"))); + hamburg = Id::makeFromLocalVocabIndex( + localVocab.getIndexAndAddIfNotContained(lit("hamburg"))); + hannover = Id::makeFromLocalVocabIndex( + localVocab.getIndexAndAddIfNotContained(lit("hannover"))); + ingolstadt = Id::makeFromLocalVocabIndex( + localVocab.getIndexAndAddIfNotContained(lit("ingolstadt"))); + karlsruhe = Id::makeFromLocalVocabIndex( + localVocab.getIndexAndAddIfNotContained(lit("karlsruhe"))); + munich = Id::makeFromLocalVocabIndex( + localVocab.getIndexAndAddIfNotContained(lit("munich"))); // Set up the `table` that represents the previous partial query results. It // has five columns/variables: ?ints (only integers), ?doubles (only From 46be51acd98747911ade4b01f19f37d181bf7187 Mon Sep 17 00:00:00 2001 From: realHannes Date: Wed, 18 Sep 2024 09:53:24 +0200 Subject: [PATCH 19/50] fix spelling error --- src/index/CompressedBlockPrefiltering.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/index/CompressedBlockPrefiltering.h b/src/index/CompressedBlockPrefiltering.h index a35b36e71d..fbb5236345 100644 --- a/src/index/CompressedBlockPrefiltering.h +++ b/src/index/CompressedBlockPrefiltering.h @@ -124,6 +124,6 @@ using GreaterEqualExpression = RelationalExpressions; using GreaterThanExpression = RelationalExpressions; //______________________________________________________________________________ -// Defintion of the LogicalExpressions for AND, OR and NOT. +// Definition of the LogicalExpressions for AND, OR and NOT. using AndExpression = LogicalExpressions; using OrExpression = LogicalExpressions; From 0385b561e40ba5103d176fadb9a0f002d540b4e6 Mon Sep 17 00:00:00 2001 From: realHannes Date: Wed, 18 Sep 2024 11:57:29 +0200 Subject: [PATCH 20/50] add namespace for CompressedBlockPrefiltering.h --- src/index/CompressedBlockPrefiltering.cpp | 5 ++++ src/index/CompressedBlockPrefiltering.h | 29 ++++++++++++++++------- test/SparqlExpressionTestHelpers.h | 2 +- 3 files changed, 26 insertions(+), 10 deletions(-) diff --git a/src/index/CompressedBlockPrefiltering.cpp b/src/index/CompressedBlockPrefiltering.cpp index 564d768127..7e9712fa12 100644 --- a/src/index/CompressedBlockPrefiltering.cpp +++ b/src/index/CompressedBlockPrefiltering.cpp @@ -4,6 +4,11 @@ #include "index/CompressedBlockPrefiltering.h" +using prefilterExpressions::CompOp; +using prefilterExpressions::LogicalExpressions; +using prefilterExpressions::LogicalOperators; +using prefilterExpressions::RelationalExpressions; + // SECTION RELATIONAL OPERATIONS //______________________________________________________________________________ template diff --git a/src/index/CompressedBlockPrefiltering.h b/src/index/CompressedBlockPrefiltering.h index fbb5236345..28a78b5046 100644 --- a/src/index/CompressedBlockPrefiltering.h +++ b/src/index/CompressedBlockPrefiltering.h @@ -16,6 +16,7 @@ // `firstTriple_` and `lastTriple_` (`PermutedTriple`) using BlockMetadata = CompressedBlockMetadata; +namespace prefilterExpressions { //______________________________________________________________________________ /* `PrefilterExpression` represents a base class for the following sub-classes that @@ -114,16 +115,26 @@ class LogicalExpressions : public PrefilterExpression { requires(Operation == LogicalOperators::OR); }; +} // namespace prefilterExpressions + //______________________________________________________________________________ // Definition of the RelationalExpressions for LT, LE, EQ, NE, GE and GT. -using LessThanExpression = RelationalExpressions; -using LessEqualExpression = RelationalExpressions; -using EqualExpression = RelationalExpressions; -using NotEqualExpression = RelationalExpressions; -using GreaterEqualExpression = RelationalExpressions; -using GreaterThanExpression = RelationalExpressions; +using LessThanExpression = prefilterExpressions::RelationalExpressions< + prefilterExpressions::CompOp::LT>; +using LessEqualExpression = prefilterExpressions::RelationalExpressions< + prefilterExpressions::CompOp::LE>; +using EqualExpression = prefilterExpressions::RelationalExpressions< + prefilterExpressions::CompOp::EQ>; +using NotEqualExpression = prefilterExpressions::RelationalExpressions< + prefilterExpressions::CompOp::NE>; +using GreaterEqualExpression = prefilterExpressions::RelationalExpressions< + prefilterExpressions::CompOp::GE>; +using GreaterThanExpression = prefilterExpressions::RelationalExpressions< + prefilterExpressions::CompOp::GT>; //______________________________________________________________________________ -// Definition of the LogicalExpressions for AND, OR and NOT. -using AndExpression = LogicalExpressions; -using OrExpression = LogicalExpressions; +// Definition of the LogicalExpressions for AND and OR. +using AndExpression = prefilterExpressions::LogicalExpressions< + prefilterExpressions::LogicalOperators::AND>; +using OrExpression = prefilterExpressions::LogicalExpressions< + prefilterExpressions::LogicalOperators::OR>; diff --git a/test/SparqlExpressionTestHelpers.h b/test/SparqlExpressionTestHelpers.h index cdbb4f90f4..dc4d627ab2 100644 --- a/test/SparqlExpressionTestHelpers.h +++ b/test/SparqlExpressionTestHelpers.h @@ -117,7 +117,7 @@ struct TestContext { frankfurt = Id::makeFromLocalVocabIndex( localVocab.getIndexAndAddIfNotContained(lit("frankfurt"))); frankfurt_oder = Id::makeFromLocalVocabIndex( - localVocab.getIndexAndAddIfNotContained(lit("frankfurt (oder)"))); + localVocab.getIndexAndAddIfNotContained(lit("frankfurt (FFO)"))); hamburg = Id::makeFromLocalVocabIndex( localVocab.getIndexAndAddIfNotContained(lit("hamburg"))); hannover = Id::makeFromLocalVocabIndex( From 570733845248299a0b1785ce438cc026d3bc5b74 Mon Sep 17 00:00:00 2001 From: realHannes Date: Wed, 18 Sep 2024 13:44:25 +0200 Subject: [PATCH 21/50] use std::ranges::set_union, std::ranges::upper_bound/lower_bound --- src/index/CompressedBlockPrefiltering.cpp | 91 ++++++++++++----------- src/index/CompressedBlockPrefiltering.h | 2 +- 2 files changed, 50 insertions(+), 43 deletions(-) diff --git a/src/index/CompressedBlockPrefiltering.cpp b/src/index/CompressedBlockPrefiltering.cpp index 7e9712fa12..1849d9d062 100644 --- a/src/index/CompressedBlockPrefiltering.cpp +++ b/src/index/CompressedBlockPrefiltering.cpp @@ -12,13 +12,14 @@ using prefilterExpressions::RelationalExpressions; // SECTION RELATIONAL OPERATIONS //______________________________________________________________________________ template -bool RelationalExpressions::compare(const ValueId& firstId, - const ValueId& secondId) { +bool RelationalExpressions::compareImpl( + const ValueId& firstId, const ValueId& secondId) const { + using enum CompOp; // For compactness we combine LE and GT here. In evaluate (see below), for // LE we select all blocks at a smaller (lower_bound) index position, and // for GT all the respective blocks at larger or equal indices. - if constexpr (Comparison == CompOp::LE || Comparison == CompOp::GT) { - return compareIds(firstId, secondId, CompOp::LE) == + if constexpr (Comparison == LE || Comparison == GT) { + return compareIds(firstId, secondId, LE) == valueIdComparators::ComparisonResult::True; } else { // Follows the same logic for evaluation as explained above. @@ -27,7 +28,7 @@ bool RelationalExpressions::compare(const ValueId& firstId, // evaluate we calculate a lower_bound and upper_bound over this LT // operation, and those bounds act as the plausible range for equality // (evaluation of CompOp::NE follows a similar approach). - return compareIds(firstId, secondId, CompOp::LT) == + return compareIds(firstId, secondId, LT) == valueIdComparators::ComparisonResult::True; } } @@ -36,19 +37,22 @@ bool RelationalExpressions::compare(const ValueId& firstId, template auto RelationalExpressions::lowerIndex( std::vector& input, size_t evaluationColumn) { - // Custom compare function for binary search (lower bound). - auto makeCompare = [this, evaluationColumn]( - const BlockMetadata& blockMetadata, - const ValueId& referenceId) -> bool { - ValueId blockId = - Comparison == CompOp::LT || Comparison == CompOp::LE || - Comparison == CompOp::NE - ? getIdFromColumnIndex(blockMetadata.firstTriple_, evaluationColumn) - : getIdFromColumnIndex(blockMetadata.lastTriple_, evaluationColumn); - return compare(blockId, referenceId); - }; - return std::lower_bound(input.begin(), input.end(), referenceId_, - makeCompare); + using enum CompOp; + // Extract evaluationColumn specific ValueId from BlockMetadata + auto getRelevantIdFromBlock = + [this, evaluationColumn](const BlockMetadata& blockMetadata) { + return Comparison == LT || Comparison == LE || Comparison == NE + ? getIdFromColumnIndex(blockMetadata.firstTriple_, + evaluationColumn) + : getIdFromColumnIndex(blockMetadata.lastTriple_, + evaluationColumn); + }; + return std::ranges::lower_bound( + input, referenceId_, + [this](const ValueId& blockId, const ValueId& referenceId) { + return compareImpl(blockId, referenceId); + }, + getRelevantIdFromBlock); }; //______________________________________________________________________________ @@ -56,37 +60,41 @@ template auto RelationalExpressions::upperIndex( std::vector& input, size_t evaluationColumn) requires(Comparison == CompOp::EQ || Comparison == CompOp::NE) { - // Custom compare function for binary search (upper bound). - auto makeCompare = [this, evaluationColumn](const ValueId& referenceId, - BlockMetadata& blockMetadata) { - return compare( - referenceId, - Comparison == CompOp::EQ - ? getIdFromColumnIndex(blockMetadata.firstTriple_, evaluationColumn) - : getIdFromColumnIndex(blockMetadata.lastTriple_, - evaluationColumn)); - }; - // Use the same comparison operations as for the method lowerIndex - // (std::lower_bound). However, given that std::upper_bound should return an - // index to the first block that is larger than the provided reference value, - // we have to swap the input values when calling compare() in makeCompare. - // compare(blockId, referenceId) -> compare(referenceId, blockId) - return std::upper_bound(input.begin(), input.end(), referenceId_, - makeCompare); + // Extract evaluationColumn specific ValueId from BlockMetadata + auto getRelevantIdFromBlock = + [this, evaluationColumn](const BlockMetadata& blockMetadata) { + return Comparison == CompOp::EQ + ? getIdFromColumnIndex(blockMetadata.firstTriple_, + evaluationColumn) + : getIdFromColumnIndex(blockMetadata.lastTriple_, + evaluationColumn); + }; + // Use the same comparison operations compareImpl as for the method lowerIndex + // (with std::ranges::lower_bound). + // However, the difference here is the helper lambda getRelevantIdFromBlock(), + // for CompOp::EQ we have to consider the first triple of the respective + // BlockMetadata value. + return std::ranges::upper_bound( + input, referenceId_, + [this](const ValueId& blockId, const ValueId& referenceId) { + return compareImpl(blockId, referenceId); + }, + getRelevantIdFromBlock); }; //______________________________________________________________________________ template std::vector RelationalExpressions::evaluate( std::vector& input, size_t evaluationColumn) { - if constexpr (Comparison == CompOp::LT || Comparison == CompOp::LE) { + using enum CompOp; + if constexpr (Comparison == LT || Comparison == LE) { return std::vector(input.begin(), lowerIndex(input, evaluationColumn)); - } else if constexpr (Comparison == CompOp::GE || Comparison == CompOp::GT) { + } else if constexpr (Comparison == GE || Comparison == GT) { // Complementary block selection to LT and LE return std::vector(lowerIndex(input, evaluationColumn), input.end()); - } else if constexpr (Comparison == CompOp::EQ) { + } else if constexpr (Comparison == EQ) { return std::vector(lowerIndex(input, evaluationColumn), upperIndex(input, evaluationColumn)); } else { @@ -141,10 +149,9 @@ std::vector LogicalExpressions::evaluateOr( getIdFromColumnIndex(block2.lastTriple_, evaluationColumn); }; // Given that we have vectors with sorted (BlockMedata) values, we can - // use std::set_union, thus the complexity is O(n + m). - std::set_union(relevantBlocksChild1.begin(), relevantBlocksChild1.end(), - relevantBlocksChild2.begin(), relevantBlocksChild2.end(), - std::back_inserter(unionedRelevantBlocks), lessThan); + // use std::ranges::set_union, thus the complexity is O(n + m). + std::ranges::set_union(relevantBlocksChild1, relevantBlocksChild2, + std::back_inserter(unionedRelevantBlocks), lessThan); return unionedRelevantBlocks; }; diff --git a/src/index/CompressedBlockPrefiltering.h b/src/index/CompressedBlockPrefiltering.h index 28a78b5046..67c3da012c 100644 --- a/src/index/CompressedBlockPrefiltering.h +++ b/src/index/CompressedBlockPrefiltering.h @@ -82,7 +82,7 @@ class RelationalExpressions : public PrefilterExpression { ValueId referenceId_; // Helper function that implements the relational comparison on the block // values. - bool compare(const ValueId& tripleId, const ValueId& otherId); + bool compareImpl(const ValueId& tripleId, const ValueId& otherId) const; // Helper for providing the lower indices during the evaluation procedure. auto lowerIndex(std::vector& input, size_t evaluationColumn); // Helper to get the upper indices, necessary for EQ and NE. From c9687eca7e6094188faaf182badecf731ce6b524 Mon Sep 17 00:00:00 2001 From: realHannes Date: Wed, 18 Sep 2024 15:18:54 +0200 Subject: [PATCH 22/50] move template specializations into namespace prefilterExpressions --- src/index/CompressedBlockPrefiltering.cpp | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/src/index/CompressedBlockPrefiltering.cpp b/src/index/CompressedBlockPrefiltering.cpp index 1849d9d062..a5e18ceb62 100644 --- a/src/index/CompressedBlockPrefiltering.cpp +++ b/src/index/CompressedBlockPrefiltering.cpp @@ -118,15 +118,6 @@ std::vector RelationalExpressions::evaluate( } }; -//______________________________________________________________________________ -// Necessary instantiation of template specializations -template class RelationalExpressions; -template class RelationalExpressions; -template class RelationalExpressions; -template class RelationalExpressions; -template class RelationalExpressions; -template class RelationalExpressions; - // SECTION LOGICAL OPERATIONS //______________________________________________________________________________ template @@ -167,7 +158,17 @@ std::vector LogicalExpressions::evaluate( } }; +namespace prefilterExpressions { //______________________________________________________________________________ // Necessary instantiation of template specializations +template class RelationalExpressions; +template class RelationalExpressions; +template class RelationalExpressions; +template class RelationalExpressions; +template class RelationalExpressions; +template class RelationalExpressions; + template class LogicalExpressions; template class LogicalExpressions; + +} // namespace prefilterExpressions From d8246f709ab3001b59c1abf1fddded43bb3b3d91 Mon Sep 17 00:00:00 2001 From: realHannes Date: Wed, 18 Sep 2024 15:44:30 +0200 Subject: [PATCH 23/50] fix --- src/index/CompressedBlockPrefiltering.cpp | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/src/index/CompressedBlockPrefiltering.cpp b/src/index/CompressedBlockPrefiltering.cpp index a5e18ceb62..48a87ddb98 100644 --- a/src/index/CompressedBlockPrefiltering.cpp +++ b/src/index/CompressedBlockPrefiltering.cpp @@ -4,10 +4,7 @@ #include "index/CompressedBlockPrefiltering.h" -using prefilterExpressions::CompOp; -using prefilterExpressions::LogicalExpressions; -using prefilterExpressions::LogicalOperators; -using prefilterExpressions::RelationalExpressions; +using namespace prefilterExpressions; // SECTION RELATIONAL OPERATIONS //______________________________________________________________________________ @@ -40,7 +37,7 @@ auto RelationalExpressions::lowerIndex( using enum CompOp; // Extract evaluationColumn specific ValueId from BlockMetadata auto getRelevantIdFromBlock = - [this, evaluationColumn](const BlockMetadata& blockMetadata) { + [evaluationColumn](const BlockMetadata& blockMetadata) { return Comparison == LT || Comparison == LE || Comparison == NE ? getIdFromColumnIndex(blockMetadata.firstTriple_, evaluationColumn) @@ -62,7 +59,7 @@ auto RelationalExpressions::upperIndex( requires(Comparison == CompOp::EQ || Comparison == CompOp::NE) { // Extract evaluationColumn specific ValueId from BlockMetadata auto getRelevantIdFromBlock = - [this, evaluationColumn](const BlockMetadata& blockMetadata) { + [evaluationColumn](const BlockMetadata& blockMetadata) { return Comparison == CompOp::EQ ? getIdFromColumnIndex(blockMetadata.firstTriple_, evaluationColumn) @@ -158,7 +155,6 @@ std::vector LogicalExpressions::evaluate( } }; -namespace prefilterExpressions { //______________________________________________________________________________ // Necessary instantiation of template specializations template class RelationalExpressions; @@ -170,5 +166,3 @@ template class RelationalExpressions; template class LogicalExpressions; template class LogicalExpressions; - -} // namespace prefilterExpressions From 66b4872939231d2f785b2fcdb2050eb77447dd50 Mon Sep 17 00:00:00 2001 From: realHannes Date: Wed, 18 Sep 2024 16:08:24 +0200 Subject: [PATCH 24/50] hopefully fix namespace issue --- src/index/CompressedBlockPrefiltering.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/index/CompressedBlockPrefiltering.cpp b/src/index/CompressedBlockPrefiltering.cpp index 48a87ddb98..65cb605f6b 100644 --- a/src/index/CompressedBlockPrefiltering.cpp +++ b/src/index/CompressedBlockPrefiltering.cpp @@ -4,7 +4,7 @@ #include "index/CompressedBlockPrefiltering.h" -using namespace prefilterExpressions; +namespace prefilterExpressions { // SECTION RELATIONAL OPERATIONS //______________________________________________________________________________ @@ -166,3 +166,5 @@ template class RelationalExpressions; template class LogicalExpressions; template class LogicalExpressions; + +} // namespace prefilterExpressions From b5d7dd7adc2f69d218287ff3a6ae7e50986b4cf2 Mon Sep 17 00:00:00 2001 From: realHannes Date: Fri, 20 Sep 2024 12:07:37 +0200 Subject: [PATCH 25/50] first adaptations regarding the comments --- src/index/CMakeLists.txt | 5 +- src/index/CompressedBlockPrefiltering.cpp | 56 +++++++++----- src/index/CompressedBlockPrefiltering.h | 90 ++++++++++------------- test/CompressedBlockPrefilteringTest.cpp | 17 +++-- 4 files changed, 89 insertions(+), 79 deletions(-) diff --git a/src/index/CMakeLists.txt b/src/index/CMakeLists.txt index 1a56e78a76..81459f7a26 100644 --- a/src/index/CMakeLists.txt +++ b/src/index/CMakeLists.txt @@ -5,5 +5,6 @@ add_library(index LocatedTriples.cpp Permutation.cpp TextMetaData.cpp DocsDB.cpp FTSAlgorithms.cpp PrefixHeuristic.cpp CompressedRelation.cpp - PatternCreator.cpp ScanSpecification.cpp CompressedBlockPrefiltering.cpp) -qlever_target_link_libraries(index util parser vocabulary ${STXXL_LIBRARIES}) \ No newline at end of file + PatternCreator.cpp ScanSpecification.cpp + CompressedBlockPrefiltering.cpp) +qlever_target_link_libraries(index util parser vocabulary ${STXXL_LIBRARIES}) diff --git a/src/index/CompressedBlockPrefiltering.cpp b/src/index/CompressedBlockPrefiltering.cpp index 65cb605f6b..4456b01e8c 100644 --- a/src/index/CompressedBlockPrefiltering.cpp +++ b/src/index/CompressedBlockPrefiltering.cpp @@ -6,10 +6,28 @@ namespace prefilterExpressions { +// HELPER FUNCTION +//______________________________________________________________________________ +// Given a PermutedTriple retrieve the suitable Id w.r.t. a column (index). +static constexpr auto getIdFromColumnIndex( + const BlockMetadata::PermutedTriple& permutedTriple, size_t columnIndex) { + switch (columnIndex) { + case 0: + return permutedTriple.col0Id_; + case 1: + return permutedTriple.col1Id_; + case 2: + return permutedTriple.col2Id_; + default: + // Triple! + AD_FAIL(); + } +}; + // SECTION RELATIONAL OPERATIONS //______________________________________________________________________________ template -bool RelationalExpressions::compareImpl( +bool RelationalExpression::compareImpl( const ValueId& firstId, const ValueId& secondId) const { using enum CompOp; // For compactness we combine LE and GT here. In evaluate (see below), for @@ -32,8 +50,8 @@ bool RelationalExpressions::compareImpl( //______________________________________________________________________________ template -auto RelationalExpressions::lowerIndex( - std::vector& input, size_t evaluationColumn) { +auto RelationalExpression::lowerIndex( + const std::vector& input, size_t evaluationColumn) const { using enum CompOp; // Extract evaluationColumn specific ValueId from BlockMetadata auto getRelevantIdFromBlock = @@ -54,8 +72,8 @@ auto RelationalExpressions::lowerIndex( //______________________________________________________________________________ template -auto RelationalExpressions::upperIndex( - std::vector& input, size_t evaluationColumn) +auto RelationalExpression::upperIndex( + const std::vector& input, size_t evaluationColumn) const requires(Comparison == CompOp::EQ || Comparison == CompOp::NE) { // Extract evaluationColumn specific ValueId from BlockMetadata auto getRelevantIdFromBlock = @@ -81,8 +99,8 @@ auto RelationalExpressions::upperIndex( //______________________________________________________________________________ template -std::vector RelationalExpressions::evaluate( - std::vector& input, size_t evaluationColumn) { +std::vector RelationalExpression::evaluate( + const std::vector& input, size_t evaluationColumn) const { using enum CompOp; if constexpr (Comparison == LT || Comparison == LE) { return std::vector(input.begin(), @@ -118,8 +136,8 @@ std::vector RelationalExpressions::evaluate( // SECTION LOGICAL OPERATIONS //______________________________________________________________________________ template -std::vector LogicalExpressions::evaluateOr( - std::vector& input, size_t evaluationColumn) +std::vector LogicalExpression::evaluateOr( + const std::vector& input, size_t evaluationColumn) const requires(Operation == LogicalOperators::OR) { auto relevantBlocksChild1 = child1_->evaluate(input, evaluationColumn); auto relevantBlocksChild2 = child2_->evaluate(input, evaluationColumn); @@ -145,8 +163,8 @@ std::vector LogicalExpressions::evaluateOr( //______________________________________________________________________________ template -std::vector LogicalExpressions::evaluate( - std::vector& input, size_t evaluationColumn) { +std::vector LogicalExpression::evaluate( + const std::vector& input, size_t evaluationColumn) const { if constexpr (Operation == LogicalOperators::AND) { auto resultChild1 = child1_->evaluate(input, evaluationColumn); return child2_->evaluate(resultChild1, evaluationColumn); @@ -157,14 +175,14 @@ std::vector LogicalExpressions::evaluate( //______________________________________________________________________________ // Necessary instantiation of template specializations -template class RelationalExpressions; -template class RelationalExpressions; -template class RelationalExpressions; -template class RelationalExpressions; -template class RelationalExpressions; -template class RelationalExpressions; +template class RelationalExpression; +template class RelationalExpression; +template class RelationalExpression; +template class RelationalExpression; +template class RelationalExpression; +template class RelationalExpression; -template class LogicalExpressions; -template class LogicalExpressions; +template class LogicalExpression; +template class LogicalExpression; } // namespace prefilterExpressions diff --git a/src/index/CompressedBlockPrefiltering.h b/src/index/CompressedBlockPrefiltering.h index 67c3da012c..af37d1ee27 100644 --- a/src/index/CompressedBlockPrefiltering.h +++ b/src/index/CompressedBlockPrefiltering.h @@ -11,12 +11,13 @@ #include "global/ValueIdComparators.h" #include "index/CompressedRelation.h" +namespace prefilterExpressions { + // The compressed block metadata (see `CompressedRelation.h`) that we use to // filter out the non-relevant blocks by checking their content of // `firstTriple_` and `lastTriple_` (`PermutedTriple`) using BlockMetadata = CompressedBlockMetadata; -namespace prefilterExpressions { //______________________________________________________________________________ /* `PrefilterExpression` represents a base class for the following sub-classes that @@ -36,8 +37,9 @@ class PrefilterExpression { public: // The respective metadata to the blocks is expected to be provided in // a sorted order (w.r.t. the relevant column). - virtual std::vector evaluate(std::vector& input, - size_t evaluationColumn) = 0; + virtual std::vector evaluate( + const std::vector& input, + size_t evaluationColumn) const = 0; virtual ~PrefilterExpression() = default; }; @@ -49,44 +51,30 @@ class PrefilterExpression { // - LessThan, LessEqual, Equal, NotEqual, GreaterEqual, GreaterThan using CompOp = valueIdComparators::Comparison; -//______________________________________________________________________________ -// Given a PermutedTriple, retrieve the suitable Id w.r.t. a column (index). -constexpr auto getIdFromColumnIndex = - [](const BlockMetadata::PermutedTriple& permutedTriple, - const size_t columnIndex) { - switch (columnIndex) { - case 0: - return permutedTriple.col0Id_; - case 1: - return permutedTriple.col1Id_; - case 2: - return permutedTriple.col2Id_; - default: - // Triple! - AD_FAIL(); - } - }; - //______________________________________________________________________________ template -class RelationalExpressions : public PrefilterExpression { +class RelationalExpression : public PrefilterExpression { + private: + // The ValueId on which we perform the relational comparison on. + ValueId referenceId_; + public: - explicit RelationalExpressions(ValueId referenceId) + explicit RelationalExpression(const ValueId referenceId) : referenceId_(referenceId) {} - std::vector evaluate(std::vector& input, - size_t evaluationColumn) override; + std::vector evaluate(const std::vector& input, + size_t evaluationColumn) const override; private: - // The ValueId the comparison refers to. - ValueId referenceId_; // Helper function that implements the relational comparison on the block // values. bool compareImpl(const ValueId& tripleId, const ValueId& otherId) const; // Helper for providing the lower indices during the evaluation procedure. - auto lowerIndex(std::vector& input, size_t evaluationColumn); + auto lowerIndex(const std::vector& input, + size_t evaluationColumn) const; // Helper to get the upper indices, necessary for EQ and NE. - auto upperIndex(std::vector& input, size_t evaluationColumn) + auto upperIndex(const std::vector& input, + size_t evaluationColumn) const requires(Comparison == CompOp::EQ || Comparison == CompOp::NE); }; @@ -97,44 +85,46 @@ enum struct LogicalOperators { AND, OR, NOT }; //______________________________________________________________________________ template -class LogicalExpressions : public PrefilterExpression { +class LogicalExpression : public PrefilterExpression { + private: + std::unique_ptr child1_; + std::unique_ptr child2_; + public: - explicit LogicalExpressions(std::unique_ptr child1, - std::unique_ptr child2) + explicit LogicalExpression(std::unique_ptr child1, + std::unique_ptr child2) : child1_(std::move(child1)), child2_(std::move(child2)) {} - std::vector evaluate(std::vector& input, - size_t evaluationColumn) override; + std::vector evaluate(const std::vector& input, + size_t evaluationColumn) const override; private: - std::unique_ptr child1_; - std::unique_ptr child2_; // Introduce a helper method for the specification LogicalOperators::OR - std::vector evaluateOr(std::vector& input, - size_t evaluationColumn) + std::vector evaluateOr(const std::vector& input, + size_t evaluationColumn) const requires(Operation == LogicalOperators::OR); }; -} // namespace prefilterExpressions - //______________________________________________________________________________ -// Definition of the RelationalExpressions for LT, LE, EQ, NE, GE and GT. -using LessThanExpression = prefilterExpressions::RelationalExpressions< +// Definition of the RelationalExpression for LT, LE, EQ, NE, GE and GT. +using LessThanExpression = prefilterExpressions::RelationalExpression< prefilterExpressions::CompOp::LT>; -using LessEqualExpression = prefilterExpressions::RelationalExpressions< +using LessEqualExpression = prefilterExpressions::RelationalExpression< prefilterExpressions::CompOp::LE>; -using EqualExpression = prefilterExpressions::RelationalExpressions< +using EqualExpression = prefilterExpressions::RelationalExpression< prefilterExpressions::CompOp::EQ>; -using NotEqualExpression = prefilterExpressions::RelationalExpressions< +using NotEqualExpression = prefilterExpressions::RelationalExpression< prefilterExpressions::CompOp::NE>; -using GreaterEqualExpression = prefilterExpressions::RelationalExpressions< +using GreaterEqualExpression = prefilterExpressions::RelationalExpression< prefilterExpressions::CompOp::GE>; -using GreaterThanExpression = prefilterExpressions::RelationalExpressions< +using GreaterThanExpression = prefilterExpressions::RelationalExpression< prefilterExpressions::CompOp::GT>; //______________________________________________________________________________ -// Definition of the LogicalExpressions for AND and OR. -using AndExpression = prefilterExpressions::LogicalExpressions< +// Definition of the LogicalExpression for AND and OR. +using AndExpression = prefilterExpressions::LogicalExpression< prefilterExpressions::LogicalOperators::AND>; -using OrExpression = prefilterExpressions::LogicalExpressions< +using OrExpression = prefilterExpressions::LogicalExpression< prefilterExpressions::LogicalOperators::OR>; + +} // namespace prefilterExpressions diff --git a/test/CompressedBlockPrefilteringTest.cpp b/test/CompressedBlockPrefilteringTest.cpp index 3f99cc3448..a66cee6e9d 100644 --- a/test/CompressedBlockPrefilteringTest.cpp +++ b/test/CompressedBlockPrefilteringTest.cpp @@ -14,6 +14,7 @@ using ad_utility::testing::DoubleId; using ad_utility::testing::IntId; using ad_utility::testing::UndefId; using sparqlExpression::TestContext; +using namespace prefilterExpressions; //______________________________________________________________________________ struct MetadataBlocks { @@ -68,7 +69,7 @@ struct MetadataBlocks { // Test LessThanExpression // Remark: We compare w.r.t. the first triple! -TEST(RelationalExpressions, testLessThanExpressions) { +TEST(RelationalExpression, testLessThanExpressions) { MetadataBlocks blocks{}; std::vector expectedResult = {}; // NUMERIC @@ -118,7 +119,7 @@ TEST(RelationalExpressions, testLessThanExpressions) { //______________________________________________________________________________ // Test LessEqualExpression // Remark: We compare w.r.t. the first triple -TEST(RelationalExpressions, testLessEqualExpressions) { +TEST(RelationalExpression, testLessEqualExpressions) { MetadataBlocks blocks{}; std::vector expectedResult = {}; // NUMERIC @@ -157,7 +158,7 @@ TEST(RelationalExpressions, testLessEqualExpressions) { //______________________________________________________________________________ // Test GreaterThanExpression // Remark: We compare w.r.t. the last triple -TEST(RelationalExpressions, testGreaterThanExpression) { +TEST(RelationalExpression, testGreaterThanExpression) { MetadataBlocks blocks{}; EXPECT_EQ(blocks.numericBlocks, GreaterThanExpression{IntId(10)}.evaluate(blocks.numericBlocks, 0)); @@ -199,7 +200,7 @@ TEST(RelationalExpressions, testGreaterThanExpression) { //______________________________________________________________________________ // Test GreaterEqualExpression // Remark: We compare w.r.t. the last triple -TEST(RelationalExpressions, testGreaterEqualExpression) { +TEST(RelationalExpression, testGreaterEqualExpression) { MetadataBlocks blocks{}; EXPECT_EQ(blocks.numericBlocks, GreaterEqualExpression{IntId(10)}.evaluate( blocks.numericBlocks, 0)); @@ -245,7 +246,7 @@ TEST(RelationalExpressions, testGreaterEqualExpression) { //______________________________________________________________________________ // Test EqualExpression -TEST(RelationalExpressions, testEqualExpression) { +TEST(RelationalExpression, testEqualExpression) { MetadataBlocks blocks{}; std::vector expectedResult = {}; EXPECT_EQ(expectedResult, @@ -296,7 +297,7 @@ TEST(RelationalExpressions, testEqualExpression) { //______________________________________________________________________________ // Test NotEqualExpression -TEST(RelationalExpressions, testNotEqualExpression) { +TEST(RelationalExpression, testNotEqualExpression) { MetadataBlocks blocks{}; std::vector expectedResult{}; EXPECT_EQ(blocks.numericBlocks, @@ -357,7 +358,7 @@ TEST(RelationalExpressions, testNotEqualExpression) { // Test Logical Expressions // Test AndExpression -TEST(LogicalExpressions, testAndExpression) { +TEST(LogicalExpression, testAndExpression) { MetadataBlocks blocks{}; std::vector expectedResult{}; EXPECT_EQ(expectedResult, @@ -425,7 +426,7 @@ TEST(LogicalExpressions, testAndExpression) { //______________________________________________________________________________ // Test OrExpression -TEST(LogicalExpressions, testOrExpression) { +TEST(LogicalExpression, testOrExpression) { MetadataBlocks blocks{}; std::vector expectedResult = {blocks.nb1, blocks.nb4, blocks.nb5}; From 662b1d4cc153d7d9451c832f3e3a1fc6341a25f2 Mon Sep 17 00:00:00 2001 From: realHannes Date: Tue, 24 Sep 2024 18:18:23 +0200 Subject: [PATCH 26/50] applied suggestions from review --- src/global/ValueIdComparators.h | 30 ++- src/index/CompressedBlockPrefiltering.cpp | 220 +++++++++++----------- src/index/CompressedBlockPrefiltering.h | 29 +-- test/CompressedBlockPrefilteringTest.cpp | 102 +++++++++- test/SparqlExpressionTestHelpers.h | 80 ++++---- 5 files changed, 281 insertions(+), 180 deletions(-) diff --git a/src/global/ValueIdComparators.h b/src/global/ValueIdComparators.h index 9f46aab872..4acc0ef875 100644 --- a/src/global/ValueIdComparators.h +++ b/src/global/ValueIdComparators.h @@ -349,9 +349,12 @@ inline std::vector> getRangesForIndexTypes( // Helper function: Sort the non-overlapping ranges in `input` by the first // element, remove the empty ranges, and merge directly adjacent ranges inline auto simplifyRanges = - [](std::vector> input) { - // Eliminate empty ranges - std::erase_if(input, [](const auto& p) { return p.first == p.second; }); + [](std::vector> input, + bool removeEmptyRanges = true) { + if (removeEmptyRanges) { + // Eliminate empty ranges + std::erase_if(input, [](const auto& p) { return p.first == p.second; }); + } std::sort(input.begin(), input.end()); if (input.empty()) { return input; @@ -378,9 +381,13 @@ inline auto simplifyRanges = // 2. The condition x `comparison` value is fulfilled, where value is the value // of `valueId`. // 3. The datatype of x and `valueId` are compatible. +// +// When setting the flag argument `removeEmptyRanges` to false, empty ranges +// [`begin`, `end`] where `begin` is equal to `end` will not be discarded. template inline std::vector> getRangesForId( - RandomIt begin, RandomIt end, ValueId valueId, Comparison comparison) { + RandomIt begin, RandomIt end, ValueId valueId, Comparison comparison, + bool removeEmptyRanges = true) { // For the evaluation of FILTERs, comparisons that involve undefined values // are always false. if (valueId.getDatatype() == Datatype::Undefined) { @@ -389,11 +396,15 @@ inline std::vector> getRangesForId( // This lambda enforces the invariants `non-empty` and `sorted`. switch (valueId.getDatatype()) { case Datatype::Double: - return detail::simplifyRanges(detail::getRangesForIntsAndDoubles( - begin, end, valueId.getDouble(), comparison)); + return detail::simplifyRanges( + detail::getRangesForIntsAndDoubles(begin, end, valueId.getDouble(), + comparison), + removeEmptyRanges); case Datatype::Int: - return detail::simplifyRanges(detail::getRangesForIntsAndDoubles( - begin, end, valueId.getInt(), comparison)); + return detail::simplifyRanges( + detail::getRangesForIntsAndDoubles(begin, end, valueId.getInt(), + comparison), + removeEmptyRanges); case Datatype::Undefined: case Datatype::VocabIndex: case Datatype::LocalVocabIndex: @@ -404,7 +415,8 @@ inline std::vector> getRangesForId( case Datatype::BlankNodeIndex: // For `Date` the trivial comparison via bits is also correct. return detail::simplifyRanges( - detail::getRangesForIndexTypes(begin, end, valueId, comparison)); + detail::getRangesForIndexTypes(begin, end, valueId, comparison), + removeEmptyRanges); } AD_FAIL(); } diff --git a/src/index/CompressedBlockPrefiltering.cpp b/src/index/CompressedBlockPrefiltering.cpp index 4456b01e8c..85e8286a92 100644 --- a/src/index/CompressedBlockPrefiltering.cpp +++ b/src/index/CompressedBlockPrefiltering.cpp @@ -4,6 +4,8 @@ #include "index/CompressedBlockPrefiltering.h" +#include "global/ValueIdComparators.h" + namespace prefilterExpressions { // HELPER FUNCTION @@ -27,137 +29,136 @@ static constexpr auto getIdFromColumnIndex( // SECTION RELATIONAL OPERATIONS //______________________________________________________________________________ template -bool RelationalExpression::compareImpl( - const ValueId& firstId, const ValueId& secondId) const { +std::unique_ptr +RelationalExpression::logicalComplement() const { using enum CompOp; - // For compactness we combine LE and GT here. In evaluate (see below), for - // LE we select all blocks at a smaller (lower_bound) index position, and - // for GT all the respective blocks at larger or equal indices. - if constexpr (Comparison == LE || Comparison == GT) { - return compareIds(firstId, secondId, LE) == - valueIdComparators::ComparisonResult::True; - } else { - // Follows the same logic for evaluation as explained above. - // Used for CompOp::LT, CompOp::GE, CompOp::EQ and CompOp::NE. - // For CompOp::EQ we also use the LT operation: This is possible because in - // evaluate we calculate a lower_bound and upper_bound over this LT - // operation, and those bounds act as the plausible range for equality - // (evaluation of CompOp::NE follows a similar approach). - return compareIds(firstId, secondId, LT) == - valueIdComparators::ComparisonResult::True; + switch (Comparison) { + case LT: + // Complement X < Y: X >= Y + return std::make_unique(referenceId_); + case LE: + // Complement X <= Y: X > Y + return std::make_unique(referenceId_); + case EQ: + // Complement X == Y: X != Y + return std::make_unique(referenceId_); + case NE: + // Complement X != Y: X == Y + return std::make_unique(referenceId_); + case GE: + // Complement X >= Y: X < Y + return std::make_unique(referenceId_); + case GT: + // Complement X > Y: X <= Y + return std::make_unique(referenceId_); + default: + AD_FAIL(); } -} +}; //______________________________________________________________________________ template -auto RelationalExpression::lowerIndex( +std::vector RelationalExpression::evaluate( const std::vector& input, size_t evaluationColumn) const { - using enum CompOp; - // Extract evaluationColumn specific ValueId from BlockMetadata - auto getRelevantIdFromBlock = - [evaluationColumn](const BlockMetadata& blockMetadata) { - return Comparison == LT || Comparison == LE || Comparison == NE - ? getIdFromColumnIndex(blockMetadata.firstTriple_, - evaluationColumn) - : getIdFromColumnIndex(blockMetadata.lastTriple_, - evaluationColumn); - }; - return std::ranges::lower_bound( - input, referenceId_, - [this](const ValueId& blockId, const ValueId& referenceId) { - return compareImpl(blockId, referenceId); - }, - getRelevantIdFromBlock); -}; + using namespace valueIdComparators; + std::vector valueIdsInput; + // For each BlockMetadata value in vector input, we have a respective Id for + // firstTriple and lastTriple + valueIdsInput.reserve(2 * input.size()); -//______________________________________________________________________________ -template -auto RelationalExpression::upperIndex( - const std::vector& input, size_t evaluationColumn) const - requires(Comparison == CompOp::EQ || Comparison == CompOp::NE) { - // Extract evaluationColumn specific ValueId from BlockMetadata - auto getRelevantIdFromBlock = - [evaluationColumn](const BlockMetadata& blockMetadata) { - return Comparison == CompOp::EQ - ? getIdFromColumnIndex(blockMetadata.firstTriple_, - evaluationColumn) - : getIdFromColumnIndex(blockMetadata.lastTriple_, - evaluationColumn); - }; - // Use the same comparison operations compareImpl as for the method lowerIndex - // (with std::ranges::lower_bound). - // However, the difference here is the helper lambda getRelevantIdFromBlock(), - // for CompOp::EQ we have to consider the first triple of the respective - // BlockMetadata value. - return std::ranges::upper_bound( - input, referenceId_, - [this](const ValueId& blockId, const ValueId& referenceId) { - return compareImpl(blockId, referenceId); - }, - getRelevantIdFromBlock); + for (const auto& block : input) { + valueIdsInput.push_back( + getIdFromColumnIndex(block.firstTriple_, evaluationColumn)); + valueIdsInput.push_back( + getIdFromColumnIndex(block.lastTriple_, evaluationColumn)); + } + // Use getRangesForId (from valueIdComparators) to extract the ranges + // containing the relevant ValueIds. + // For pre-filtering with CompOp::EQ, we have to consider empty ranges. + // Reason: The referenceId_ could be contained within the bounds formed by + // the IDs of firstTriple_ and lastTriple_ (set false flag to keep + // empty ranges). + auto relevantIdRanges = + Comparison != CompOp::EQ + ? getRangesForId(valueIdsInput.begin(), valueIdsInput.end(), + referenceId_, Comparison) + : getRangesForId(valueIdsInput.begin(), valueIdsInput.end(), + referenceId_, Comparison, false); + // The vector for relevant BlockMetadata values that contain ValueIds defined + // as relevant relevantIdRanges. + std::vector relevantBlocks; + // Reserve memory, input.size() is upper bound. + relevantBlocks.reserve(input.size()); + // Given the relevant Id ranges, retrieve the corresponding relevant + // BlockMetadata values from vector input and add them to the relevantBlocks + // vector. + for (const auto& range : relevantIdRanges) { + relevantBlocks.insert( + relevantBlocks.end(), + input.begin() + std::distance(valueIdsInput.begin(), range.first) / 2, + // Round up, for Ids contained within the bounding Ids of firstTriple + // and lastTriple we have to include the respective metadata block + // (that block is partially relevant). + input.begin() + + std::distance(valueIdsInput.begin(), range.second + 1) / 2); + } + relevantBlocks.shrink_to_fit(); + return relevantBlocks; }; +// SECTION LOGICAL OPERATIONS //______________________________________________________________________________ -template -std::vector RelationalExpression::evaluate( - const std::vector& input, size_t evaluationColumn) const { - using enum CompOp; - if constexpr (Comparison == LT || Comparison == LE) { - return std::vector(input.begin(), - lowerIndex(input, evaluationColumn)); - } else if constexpr (Comparison == GE || Comparison == GT) { - // Complementary block selection to LT and LE - return std::vector(lowerIndex(input, evaluationColumn), - input.end()); - } else if constexpr (Comparison == EQ) { - return std::vector(lowerIndex(input, evaluationColumn), - upperIndex(input, evaluationColumn)); - } else { - // CompOP::NE - // Get the indices that define the boundaries w.r.t. blocks potentially - // containing non-equal values. - auto firstBound = lowerIndex(input, evaluationColumn); - auto secondBound = upperIndex(input, evaluationColumn); - if (firstBound > secondBound) { - return input; // all blocks are relevant - } - // Extract the values from the respective ranges defined by the bounding - // indices from vector input. - // [input.begin(), firstBound) and [secondBound, input.end()] - std::vector vecNe; - vecNe.reserve(std::distance(input.begin(), firstBound) + - std::distance(secondBound, input.end())); - vecNe.insert(vecNe.end(), input.begin(), firstBound); - vecNe.insert(vecNe.end(), secondBound, input.end()); - return vecNe; +template +std::unique_ptr +LogicalExpression::logicalComplement() const { + using enum LogicalOperators; + switch (Operation) { + case NOT: + // Logically we complement (negate) a NOT here => NOT cancels out. + // Therefore, we can simply return the child of the respective NOT + // expression after undoing its previous complementation. + return std::move(child1_->logicalComplement()); + // Source De-Morgan's laws: De Morgan's laws, Wikipedia. + // Reference: https://en.wikipedia.org/wiki/De_Morgan%27s_laws + case OR: + AD_CONTRACT_CHECK(child2_.has_value()); + // De Morgan's law: not (A or B) = (not A) and (not B) + return std::make_unique( + child1_->logicalComplement(), child2_.value()->logicalComplement()); + case AND: + AD_CONTRACT_CHECK(child2_.has_value()); + // De Morgan's law: not (A and B) = (not A) or (not B) + return std::make_unique( + child1_->logicalComplement(), child2_.value()->logicalComplement()); + default: + AD_FAIL(); } }; -// SECTION LOGICAL OPERATIONS //______________________________________________________________________________ template std::vector LogicalExpression::evaluateOr( const std::vector& input, size_t evaluationColumn) const requires(Operation == LogicalOperators::OR) { auto relevantBlocksChild1 = child1_->evaluate(input, evaluationColumn); - auto relevantBlocksChild2 = child2_->evaluate(input, evaluationColumn); + auto relevantBlocksChild2 = + child2_.value()->evaluate(input, evaluationColumn); // The result vector, reserve space for the logical upper bound (num blocks) // |relevantBlocksChild1| + |relevantBlocksChild2| regarding the OR // operation (union of blocks) std::vector unionedRelevantBlocks; unionedRelevantBlocks.reserve(relevantBlocksChild1.size() + relevantBlocksChild2.size()); - // lambda function that implements the less-than comparison required for - // std::set_union - auto lessThan = [evaluationColumn](const BlockMetadata& block1, - const BlockMetadata& block2) { - return getIdFromColumnIndex(block1.lastTriple_, evaluationColumn) < - getIdFromColumnIndex(block2.lastTriple_, evaluationColumn); + // Lambda function that implements the projection to retrieve ValueIds from + // BlockMetadata values. + auto retrieveValueId = [evaluationColumn](const BlockMetadata& block) { + return getIdFromColumnIndex(block.lastTriple_, evaluationColumn); }; // Given that we have vectors with sorted (BlockMedata) values, we can // use std::ranges::set_union, thus the complexity is O(n + m). std::ranges::set_union(relevantBlocksChild1, relevantBlocksChild2, - std::back_inserter(unionedRelevantBlocks), lessThan); + std::back_inserter(unionedRelevantBlocks), + std::ranges::less{}, retrieveValueId, retrieveValueId); return unionedRelevantBlocks; }; @@ -165,11 +166,19 @@ std::vector LogicalExpression::evaluateOr( template std::vector LogicalExpression::evaluate( const std::vector& input, size_t evaluationColumn) const { - if constexpr (Operation == LogicalOperators::AND) { + using enum LogicalOperators; + if constexpr (Operation == AND) { auto resultChild1 = child1_->evaluate(input, evaluationColumn); - return child2_->evaluate(resultChild1, evaluationColumn); - } else { + AD_CONTRACT_CHECK(child2_.has_value()); + return child2_.value()->evaluate(resultChild1, evaluationColumn); + } else if constexpr (Operation == OR) { + AD_CONTRACT_CHECK(child2_.has_value()); return evaluateOr(input, evaluationColumn); + } else { + static_assert(Operation == NOT); + // Given that the child expression has already been negated during the (NOT) + // expression construction, we just have to evaluate it. + return child1_->evaluate(input, evaluationColumn); } }; @@ -184,5 +193,6 @@ template class RelationalExpression; template class LogicalExpression; template class LogicalExpression; +template class LogicalExpression; } // namespace prefilterExpressions diff --git a/src/index/CompressedBlockPrefiltering.h b/src/index/CompressedBlockPrefiltering.h index af37d1ee27..7887733607 100644 --- a/src/index/CompressedBlockPrefiltering.h +++ b/src/index/CompressedBlockPrefiltering.h @@ -35,12 +35,15 @@ Logical Operations - `and` and `or` class PrefilterExpression { public: + virtual ~PrefilterExpression() = default; + + virtual std::unique_ptr logicalComplement() const = 0; + // The respective metadata to the blocks is expected to be provided in // a sorted order (w.r.t. the relevant column). virtual std::vector evaluate( const std::vector& input, size_t evaluationColumn) const = 0; - virtual ~PrefilterExpression() = default; }; //______________________________________________________________________________ @@ -62,20 +65,10 @@ class RelationalExpression : public PrefilterExpression { explicit RelationalExpression(const ValueId referenceId) : referenceId_(referenceId) {} + std::unique_ptr logicalComplement() const override; + std::vector evaluate(const std::vector& input, size_t evaluationColumn) const override; - - private: - // Helper function that implements the relational comparison on the block - // values. - bool compareImpl(const ValueId& tripleId, const ValueId& otherId) const; - // Helper for providing the lower indices during the evaluation procedure. - auto lowerIndex(const std::vector& input, - size_t evaluationColumn) const; - // Helper to get the upper indices, necessary for EQ and NE. - auto upperIndex(const std::vector& input, - size_t evaluationColumn) const - requires(Comparison == CompOp::EQ || Comparison == CompOp::NE); }; //______________________________________________________________________________ @@ -88,12 +81,18 @@ template class LogicalExpression : public PrefilterExpression { private: std::unique_ptr child1_; - std::unique_ptr child2_; + std::optional> child2_; public: + // AND and OR explicit LogicalExpression(std::unique_ptr child1, std::unique_ptr child2) : child1_(std::move(child1)), child2_(std::move(child2)) {} + // NOT + explicit LogicalExpression(std::unique_ptr child1) + : child1_(std::move(child1->logicalComplement())), child2_(nullptr) {} + + std::unique_ptr logicalComplement() const override; std::vector evaluate(const std::vector& input, size_t evaluationColumn) const override; @@ -126,5 +125,7 @@ using AndExpression = prefilterExpressions::LogicalExpression< prefilterExpressions::LogicalOperators::AND>; using OrExpression = prefilterExpressions::LogicalExpression< prefilterExpressions::LogicalOperators::OR>; +using NotExpression = prefilterExpressions::LogicalExpression< + prefilterExpressions::LogicalOperators::NOT>; } // namespace prefilterExpressions diff --git a/test/CompressedBlockPrefilteringTest.cpp b/test/CompressedBlockPrefilteringTest.cpp index a66cee6e9d..78f660d5c8 100644 --- a/test/CompressedBlockPrefilteringTest.cpp +++ b/test/CompressedBlockPrefilteringTest.cpp @@ -68,7 +68,6 @@ struct MetadataBlocks { // Test Relational Expressions // Test LessThanExpression -// Remark: We compare w.r.t. the first triple! TEST(RelationalExpression, testLessThanExpressions) { MetadataBlocks blocks{}; std::vector expectedResult = {}; @@ -118,7 +117,6 @@ TEST(RelationalExpression, testLessThanExpressions) { //______________________________________________________________________________ // Test LessEqualExpression -// Remark: We compare w.r.t. the first triple TEST(RelationalExpression, testLessEqualExpressions) { MetadataBlocks blocks{}; std::vector expectedResult = {}; @@ -157,7 +155,6 @@ TEST(RelationalExpression, testLessEqualExpressions) { //______________________________________________________________________________ // Test GreaterThanExpression -// Remark: We compare w.r.t. the last triple TEST(RelationalExpression, testGreaterThanExpression) { MetadataBlocks blocks{}; EXPECT_EQ(blocks.numericBlocks, @@ -199,7 +196,6 @@ TEST(RelationalExpression, testGreaterThanExpression) { //______________________________________________________________________________ // Test GreaterEqualExpression -// Remark: We compare w.r.t. the last triple TEST(RelationalExpression, testGreaterEqualExpression) { MetadataBlocks blocks{}; EXPECT_EQ(blocks.numericBlocks, GreaterEqualExpression{IntId(10)}.evaluate( @@ -481,3 +477,101 @@ TEST(LogicalExpression, testOrExpression) { std::make_unique(IntId(16.35))) .evaluate(blocks.numericBlocks, 2)); } + +//______________________________________________________________________________ +// Test NotExpression +TEST(LogicalExpression, testNotExpression) { + MetadataBlocks blocks{}; + TestContext tc{}; + std::vector expectedResult = {}; + EXPECT_EQ(expectedResult, + NotExpression(std::make_unique(IntId(16))) + .evaluate(blocks.numericBlocks, 0)); + EXPECT_EQ( + expectedResult, + NotExpression(std::make_unique( + std::make_unique(tc.munich))) + .evaluate(blocks.vocabBlocks, 1)); + EXPECT_EQ(expectedResult, + NotExpression(std::make_unique( + std::make_unique(tc.zz))) + .evaluate(blocks.vocabBlocks, 1)); + expectedResult = {blocks.vb4, blocks.vb5}; + EXPECT_EQ( + expectedResult, + NotExpression(std::make_unique( + std::make_unique(tc.karlsruhe))) + .evaluate(blocks.vocabBlocks, 1)); + EXPECT_EQ( + blocks.numericBlocks, + NotExpression(std::make_unique( + std::make_unique(IntId(16)))) + .evaluate(blocks.numericBlocks, 0)); + expectedResult = {blocks.vb1, blocks.vb2, blocks.vb3}; + EXPECT_EQ(expectedResult, + NotExpression(std::make_unique(tc.frankfurt)) + .evaluate(blocks.vocabBlocks, 1)); + EXPECT_EQ(blocks.vocabBlocks, + NotExpression(std::make_unique(tc.berlin)) + .evaluate(blocks.vocabBlocks, 1)); + expectedResult = {blocks.vb1, blocks.vb2}; + EXPECT_EQ( + expectedResult, + NotExpression(std::make_unique(tc.düsseldorf)) + .evaluate(blocks.vocabBlocks, 1)); + EXPECT_EQ(blocks.numericBlocks, + NotExpression( + std::make_unique( + std::make_unique(IntId(13)), + std::make_unique(DoubleId(111.01)))) + .evaluate(blocks.numericBlocks, 2)); + expectedResult = {}; + EXPECT_EQ( + expectedResult, + NotExpression( + std::make_unique(std::make_unique( + std::make_unique(IntId(13)), + std::make_unique(DoubleId(111.01))))) + .evaluate(blocks.numericBlocks, 2)); + expectedResult = {blocks.vb4, blocks.vb5}; + EXPECT_EQ( + expectedResult, + NotExpression(std::make_unique( + std::make_unique(tc.munich), + std::make_unique(tc.ingolstadt))) + .evaluate(blocks.vocabBlocks, 1)); + expectedResult = {blocks.vb2, blocks.vb3, blocks.vb4}; + EXPECT_EQ( + expectedResult, + NotExpression(std::make_unique( + std::make_unique(tc.hamburg), + std::make_unique(tc.düsseldorf))) + .evaluate(blocks.vocabBlocks, 1)); + expectedResult = {blocks.nb2, blocks.nb3, blocks.nb4}; + EXPECT_EQ(expectedResult, + NotExpression(std::make_unique( + std::make_unique(DoubleId(0.0)), + std::make_unique(IntId(6)))) + .evaluate(blocks.numericBlocks, 1)); + expectedResult = {blocks.nb1, blocks.nb2, blocks.nb4, blocks.nb5}; + EXPECT_EQ( + expectedResult, + NotExpression(std::make_unique( + std::make_unique(DoubleId(0.0)), + std::make_unique(IntId(6)))) + .evaluate(blocks.numericBlocks, 1)); + expectedResult = {blocks.nb3, blocks.nb4}; + EXPECT_EQ( + expectedResult, + NotExpression(std::make_unique( + std::make_unique(DoubleId(42.0)), + std::make_unique(IntId(48)))) + .evaluate(blocks.numericBlocks, 0)); + expectedResult = {blocks.nb1, blocks.nb2, blocks.nb3, blocks.nb5}; + EXPECT_EQ(expectedResult, + NotExpression( + std::make_unique(std::make_unique( + std::make_unique(DoubleId(42.25)), + std::make_unique(IntId(51))))) + .evaluate(blocks.numericBlocks, 0)); +} diff --git a/test/SparqlExpressionTestHelpers.h b/test/SparqlExpressionTestHelpers.h index dc4d627ab2..80cdae8a12 100644 --- a/test/SparqlExpressionTestHelpers.h +++ b/test/SparqlExpressionTestHelpers.h @@ -76,58 +76,42 @@ struct TestContext { zz = getId("\"zz\"@en"); blank = Id::makeFromBlankNodeIndex(BlankNodeIndex::make(0)); - constexpr auto lit = [](std::string_view s) { - return ad_utility::triple_component::LiteralOrIri::literalWithoutQuotes( - s); + auto addLocalLiteral = [this](std::string_view s) { + return Id::makeFromLocalVocabIndex( + this->localVocab.getIndexAndAddIfNotContained( + ad_utility::triple_component::LiteralOrIri::literalWithoutQuotes( + s))); }; - constexpr auto iri = [](const std::string& s) { - return ad_utility::triple_component::LiteralOrIri::iriref(s); + + auto addLocalIri = [this](const std::string& s) { + return Id::makeFromLocalVocabIndex( + this->localVocab.getIndexAndAddIfNotContained( + ad_utility::triple_component::LiteralOrIri::iriref(s))); }; - notInVocabA = Id::makeFromLocalVocabIndex( - localVocab.getIndexAndAddIfNotContained(lit("notInVocabA"))); - notInVocabB = Id::makeFromLocalVocabIndex( - localVocab.getIndexAndAddIfNotContained(lit("notInVocabB"))); - notInVocabC = Id::makeFromLocalVocabIndex( - localVocab.getIndexAndAddIfNotContained(iri(""))); - notInVocabD = Id::makeFromLocalVocabIndex( - localVocab.getIndexAndAddIfNotContained(iri(""))); - notInVocabAelpha = Id::makeFromLocalVocabIndex( - localVocab.getIndexAndAddIfNotContained(lit("notInVocabÄlpha"))); - notInVocabAelpha = Id::makeFromLocalVocabIndex( - localVocab.getIndexAndAddIfNotContained(lit("notInVocabÄlpha"))); + notInVocabA = addLocalLiteral("notInVocabA"); + notInVocabB = addLocalLiteral("notInVocabB"); + notInVocabC = addLocalIri(""); + notInVocabD = addLocalIri(""); + notInVocabAelpha = addLocalLiteral("notInVocabÄlpha"); + notInVocabAelpha = addLocalLiteral("notInVocabÄlpha"); notInVocabIri = - Id::makeFromLocalVocabIndex(localVocab.getIndexAndAddIfNotContained( - iri(""))); - notInVocabIriLit = - Id::makeFromLocalVocabIndex(localVocab.getIndexAndAddIfNotContained( - lit("http://www.w3.org/1999/02/22-rdf-syntax-ns#langString"))); - berlin = Id::makeFromLocalVocabIndex( - localVocab.getIndexAndAddIfNotContained(lit("berlin"))); - bonn = Id::makeFromLocalVocabIndex( - localVocab.getIndexAndAddIfNotContained(lit("bonn"))); - cologne = Id::makeFromLocalVocabIndex( - localVocab.getIndexAndAddIfNotContained(lit("cologne"))); - düsseldorf = Id::makeFromLocalVocabIndex( - localVocab.getIndexAndAddIfNotContained(lit("düsseldorf"))); - dortmund = Id::makeFromLocalVocabIndex( - localVocab.getIndexAndAddIfNotContained(lit("dortmund"))); - essen = Id::makeFromLocalVocabIndex( - localVocab.getIndexAndAddIfNotContained(lit("essen"))); - frankfurt = Id::makeFromLocalVocabIndex( - localVocab.getIndexAndAddIfNotContained(lit("frankfurt"))); - frankfurt_oder = Id::makeFromLocalVocabIndex( - localVocab.getIndexAndAddIfNotContained(lit("frankfurt (FFO)"))); - hamburg = Id::makeFromLocalVocabIndex( - localVocab.getIndexAndAddIfNotContained(lit("hamburg"))); - hannover = Id::makeFromLocalVocabIndex( - localVocab.getIndexAndAddIfNotContained(lit("hannover"))); - ingolstadt = Id::makeFromLocalVocabIndex( - localVocab.getIndexAndAddIfNotContained(lit("ingolstadt"))); - karlsruhe = Id::makeFromLocalVocabIndex( - localVocab.getIndexAndAddIfNotContained(lit("karlsruhe"))); - munich = Id::makeFromLocalVocabIndex( - localVocab.getIndexAndAddIfNotContained(lit("munich"))); + addLocalIri(""); + notInVocabIriLit = addLocalLiteral( + "http://www.w3.org/1999/02/22-rdf-syntax-ns#langString"); + berlin = addLocalLiteral("berlin"); + bonn = addLocalLiteral("bonn"); + cologne = addLocalLiteral("cologne"); + düsseldorf = addLocalLiteral("düsseldorf"); + dortmund = addLocalLiteral("dortmund"); + essen = addLocalLiteral("essen"); + frankfurt = addLocalLiteral("frankfurt"); + frankfurt_oder = addLocalLiteral("frankfurt (FFO)"); + hamburg = addLocalLiteral("hamburg"); + hannover = addLocalLiteral("hannover"); + ingolstadt = addLocalLiteral("ingolstadt"); + karlsruhe = addLocalLiteral("karlsruhe"); + munich = addLocalLiteral("munich"); // Set up the `table` that represents the previous partial query results. It // has five columns/variables: ?ints (only integers), ?doubles (only From 510aa9e773a0c5c58ace19b3b5a03cc2751f00a8 Mon Sep 17 00:00:00 2001 From: realHannes Date: Tue, 24 Sep 2024 21:06:48 +0200 Subject: [PATCH 27/50] fix std::move --- src/index/CompressedBlockPrefiltering.cpp | 11 ++++------- src/index/CompressedBlockPrefiltering.h | 2 +- 2 files changed, 5 insertions(+), 8 deletions(-) diff --git a/src/index/CompressedBlockPrefiltering.cpp b/src/index/CompressedBlockPrefiltering.cpp index 85e8286a92..91c673b9a0 100644 --- a/src/index/CompressedBlockPrefiltering.cpp +++ b/src/index/CompressedBlockPrefiltering.cpp @@ -92,15 +92,14 @@ std::vector RelationalExpression::evaluate( // Given the relevant Id ranges, retrieve the corresponding relevant // BlockMetadata values from vector input and add them to the relevantBlocks // vector. - for (const auto& range : relevantIdRanges) { + for (const auto& [firstId, secondId] : relevantIdRanges) { relevantBlocks.insert( relevantBlocks.end(), - input.begin() + std::distance(valueIdsInput.begin(), range.first) / 2, + input.begin() + std::distance(valueIdsInput.begin(), firstId) / 2, // Round up, for Ids contained within the bounding Ids of firstTriple // and lastTriple we have to include the respective metadata block // (that block is partially relevant). - input.begin() + - std::distance(valueIdsInput.begin(), range.second + 1) / 2); + input.begin() + std::distance(valueIdsInput.begin(), secondId + 1) / 2); } relevantBlocks.shrink_to_fit(); return relevantBlocks; @@ -117,7 +116,7 @@ LogicalExpression::logicalComplement() const { // Logically we complement (negate) a NOT here => NOT cancels out. // Therefore, we can simply return the child of the respective NOT // expression after undoing its previous complementation. - return std::move(child1_->logicalComplement()); + return child1_->logicalComplement(); // Source De-Morgan's laws: De Morgan's laws, Wikipedia. // Reference: https://en.wikipedia.org/wiki/De_Morgan%27s_laws case OR: @@ -176,8 +175,6 @@ std::vector LogicalExpression::evaluate( return evaluateOr(input, evaluationColumn); } else { static_assert(Operation == NOT); - // Given that the child expression has already been negated during the (NOT) - // expression construction, we just have to evaluate it. return child1_->evaluate(input, evaluationColumn); } }; diff --git a/src/index/CompressedBlockPrefiltering.h b/src/index/CompressedBlockPrefiltering.h index 7887733607..8cfe559091 100644 --- a/src/index/CompressedBlockPrefiltering.h +++ b/src/index/CompressedBlockPrefiltering.h @@ -90,7 +90,7 @@ class LogicalExpression : public PrefilterExpression { : child1_(std::move(child1)), child2_(std::move(child2)) {} // NOT explicit LogicalExpression(std::unique_ptr child1) - : child1_(std::move(child1->logicalComplement())), child2_(nullptr) {} + : child1_(child1->logicalComplement()), child2_(nullptr) {} std::unique_ptr logicalComplement() const override; From c11920021335684af13bec16095e3fbdfc003cd0 Mon Sep 17 00:00:00 2001 From: realHannes Date: Thu, 26 Sep 2024 16:18:25 +0200 Subject: [PATCH 28/50] adjusted CompressedBlockPrefiltering --- src/index/CompressedBlockPrefiltering.cpp | 163 ++-- src/index/CompressedBlockPrefiltering.h | 56 +- test/CompressedBlockPrefilteringTest.cpp | 962 +++++++++++----------- 3 files changed, 635 insertions(+), 546 deletions(-) diff --git a/src/index/CompressedBlockPrefiltering.cpp b/src/index/CompressedBlockPrefiltering.cpp index 91c673b9a0..73c341c06b 100644 --- a/src/index/CompressedBlockPrefiltering.cpp +++ b/src/index/CompressedBlockPrefiltering.cpp @@ -11,21 +11,83 @@ namespace prefilterExpressions { // HELPER FUNCTION //______________________________________________________________________________ // Given a PermutedTriple retrieve the suitable Id w.r.t. a column (index). -static constexpr auto getIdFromColumnIndex( - const BlockMetadata::PermutedTriple& permutedTriple, size_t columnIndex) { +static auto getIdFromColumnIndex(const BlockMetadata::PermutedTriple& triple, + size_t columnIndex) { switch (columnIndex) { case 0: - return permutedTriple.col0Id_; + return triple.col0Id_; case 1: - return permutedTriple.col1Id_; + return triple.col1Id_; case 2: - return permutedTriple.col2Id_; + return triple.col2Id_; default: - // Triple! + // columnIndex out of bounds AD_FAIL(); } }; +//______________________________________________________________________________ +// Extract the Ids from the given `PermutedTriple` in a tuple up to the +// position (column index) defined by `ignoreIndex`. The ignored positions are +// filled with Ids `Id::makeUndefined()`. `Id::makeUndefined()` is guaranteed +// to be smaller than Ids of all other types. +static auto getTiedMaskedPermutedTriple( + const BlockMetadata::PermutedTriple& triple, size_t ignoreIndex = 3) { + const auto undefined = Id::makeUndefined(); + switch (ignoreIndex) { + case 3: + return std::tie(triple.col0Id_, triple.col1Id_, triple.col2Id_); + case 2: + return std::tie(triple.col0Id_, triple.col1Id_, undefined); + case 1: + return std::tie(triple.col0Id_, undefined, undefined); + case 0: + return std::tie(undefined, undefined, undefined); + default: + // ignoreIndex out of bounds + AD_FAIL(); + } +}; + +//______________________________________________________________________________ +// Check required conditions. +static auto checkEvalRequirements(const std::vector& input, + size_t evaluationColumn) { + auto throwRuntimeError = [](const std::string& errorMessage) { + throw std::runtime_error(errorMessage); + }; + + for (size_t i = 1; i < input.size(); i++) { + const auto& block1 = input[i - 1]; + const auto& block2 = input[i]; + if (block1 == block2) { + throwRuntimeError("The provided data blocks must be unique."); + } + const auto& triple1 = block1.lastTriple_; + const auto& triple2 = block2.firstTriple_; + if (getIdFromColumnIndex(triple1, evaluationColumn) > + getIdFromColumnIndex(triple2, evaluationColumn)) { + throwRuntimeError("The data blocks must be provided in sorted order."); + } + if (getTiedMaskedPermutedTriple(triple1, evaluationColumn) != + getTiedMaskedPermutedTriple(triple2, evaluationColumn)) { + throwRuntimeError( + "The columns up to the evaluation column must contain the same " + "values."); + } + } +}; + +// SECTION PREFILTER EXPRESSION (BASE CLASS) +//______________________________________________________________________________ +std::vector PrefilterExpression::evaluate( + const std::vector& input, size_t evaluationColumn) const { + checkEvalRequirements(input, evaluationColumn); + const auto& relevantBlocks = evaluateImpl(input, evaluationColumn); + checkEvalRequirements(relevantBlocks, evaluationColumn); + return relevantBlocks; +}; + // SECTION RELATIONAL OPERATIONS //______________________________________________________________________________ template @@ -58,7 +120,7 @@ RelationalExpression::logicalComplement() const { //______________________________________________________________________________ template -std::vector RelationalExpression::evaluate( +std::vector RelationalExpression::evaluateImpl( const std::vector& input, size_t evaluationColumn) const { using namespace valueIdComparators; std::vector valueIdsInput; @@ -85,21 +147,26 @@ std::vector RelationalExpression::evaluate( : getRangesForId(valueIdsInput.begin(), valueIdsInput.end(), referenceId_, Comparison, false); // The vector for relevant BlockMetadata values that contain ValueIds defined - // as relevant relevantIdRanges. + // as relevant by relevantIdRanges. std::vector relevantBlocks; // Reserve memory, input.size() is upper bound. relevantBlocks.reserve(input.size()); // Given the relevant Id ranges, retrieve the corresponding relevant // BlockMetadata values from vector input and add them to the relevantBlocks // vector. + auto endValueIdsInput = valueIdsInput.end(); for (const auto& [firstId, secondId] : relevantIdRanges) { + // Ensures that index is within bounds of index vector. + auto secondIdAdjusted = + secondId < endValueIdsInput ? secondId + 1 : secondId; relevantBlocks.insert( relevantBlocks.end(), input.begin() + std::distance(valueIdsInput.begin(), firstId) / 2, // Round up, for Ids contained within the bounding Ids of firstTriple // and lastTriple we have to include the respective metadata block // (that block is partially relevant). - input.begin() + std::distance(valueIdsInput.begin(), secondId + 1) / 2); + input.begin() + + std::distance(valueIdsInput.begin(), secondIdAdjusted) / 2); } relevantBlocks.shrink_to_fit(); return relevantBlocks; @@ -111,74 +178,73 @@ template std::unique_ptr LogicalExpression::logicalComplement() const { using enum LogicalOperators; - switch (Operation) { - case NOT: - // Logically we complement (negate) a NOT here => NOT cancels out. - // Therefore, we can simply return the child of the respective NOT - // expression after undoing its previous complementation. - return child1_->logicalComplement(); - // Source De-Morgan's laws: De Morgan's laws, Wikipedia. - // Reference: https://en.wikipedia.org/wiki/De_Morgan%27s_laws - case OR: - AD_CONTRACT_CHECK(child2_.has_value()); - // De Morgan's law: not (A or B) = (not A) and (not B) - return std::make_unique( - child1_->logicalComplement(), child2_.value()->logicalComplement()); - case AND: - AD_CONTRACT_CHECK(child2_.has_value()); - // De Morgan's law: not (A and B) = (not A) or (not B) - return std::make_unique( - child1_->logicalComplement(), child2_.value()->logicalComplement()); - default: - AD_FAIL(); + // Source De-Morgan's laws: De Morgan's laws, Wikipedia. + // Reference: https://en.wikipedia.org/wiki/De_Morgan%27s_laws + if constexpr (Operation == OR) { + // De Morgan's law: not (A or B) = (not A) and (not B) + return std::make_unique(child1_->logicalComplement(), + child2_->logicalComplement()); + } else { + static_assert(Operation == AND); + // De Morgan's law: not (A and B) = (not A) or (not B) + return std::make_unique(child1_->logicalComplement(), + child2_->logicalComplement()); } }; +//______________________________________________________________________________ +std::unique_ptr NotExpression::logicalComplement() const { + // Logically we complement (negate) a NOT here => NOT cancels out. + // Therefore, we can simply return the child of the respective NOT + // expression after undoing its previous complementation. + return child_->logicalComplement(); +}; + //______________________________________________________________________________ template -std::vector LogicalExpression::evaluateOr( +std::vector LogicalExpression::evaluateOrImpl( const std::vector& input, size_t evaluationColumn) const requires(Operation == LogicalOperators::OR) { auto relevantBlocksChild1 = child1_->evaluate(input, evaluationColumn); - auto relevantBlocksChild2 = - child2_.value()->evaluate(input, evaluationColumn); - // The result vector, reserve space for the logical upper bound (num blocks) - // |relevantBlocksChild1| + |relevantBlocksChild2| regarding the OR - // operation (union of blocks) + auto relevantBlocksChild2 = child2_->evaluate(input, evaluationColumn); + std::vector unionedRelevantBlocks; unionedRelevantBlocks.reserve(relevantBlocksChild1.size() + relevantBlocksChild2.size()); - // Lambda function that implements the projection to retrieve ValueIds from - // BlockMetadata values. - auto retrieveValueId = [evaluationColumn](const BlockMetadata& block) { - return getIdFromColumnIndex(block.lastTriple_, evaluationColumn); + + auto blockLessThanBlock = [](const BlockMetadata& block1, + const BlockMetadata& block2) { + return getTiedMaskedPermutedTriple(block1.lastTriple_) < + getTiedMaskedPermutedTriple(block2.firstTriple_); }; // Given that we have vectors with sorted (BlockMedata) values, we can // use std::ranges::set_union, thus the complexity is O(n + m). std::ranges::set_union(relevantBlocksChild1, relevantBlocksChild2, std::back_inserter(unionedRelevantBlocks), - std::ranges::less{}, retrieveValueId, retrieveValueId); + blockLessThanBlock); return unionedRelevantBlocks; }; //______________________________________________________________________________ template -std::vector LogicalExpression::evaluate( +std::vector LogicalExpression::evaluateImpl( const std::vector& input, size_t evaluationColumn) const { using enum LogicalOperators; if constexpr (Operation == AND) { auto resultChild1 = child1_->evaluate(input, evaluationColumn); - AD_CONTRACT_CHECK(child2_.has_value()); - return child2_.value()->evaluate(resultChild1, evaluationColumn); - } else if constexpr (Operation == OR) { - AD_CONTRACT_CHECK(child2_.has_value()); - return evaluateOr(input, evaluationColumn); + return child2_->evaluate(resultChild1, evaluationColumn); } else { - static_assert(Operation == NOT); - return child1_->evaluate(input, evaluationColumn); + static_assert(Operation == OR); + return evaluateOrImpl(input, evaluationColumn); } }; +//______________________________________________________________________________ +std::vector NotExpression::evaluateImpl( + const std::vector& input, size_t evaluationColumn) const { + return child_->evaluate(input, evaluationColumn); +}; + //______________________________________________________________________________ // Necessary instantiation of template specializations template class RelationalExpression; @@ -190,6 +256,5 @@ template class RelationalExpression; template class LogicalExpression; template class LogicalExpression; -template class LogicalExpression; } // namespace prefilterExpressions diff --git a/src/index/CompressedBlockPrefiltering.h b/src/index/CompressedBlockPrefiltering.h index 8cfe559091..613fd5c9c2 100644 --- a/src/index/CompressedBlockPrefiltering.h +++ b/src/index/CompressedBlockPrefiltering.h @@ -29,8 +29,8 @@ pre-filter w.r.t. blocks that contain relevant data for the actual evaluation of those expressions to make the evaluation procedure more efficient. The block-filtering is applied with the following operations: -Relational Expressions - `<=`, `>=`, `<`, `>`, `==` and `!=` -Logical Operations - `and` and `or` +Relational Expressions - `<=`, `>=`, `<`, `>`, `==` and `!=`. +Logical Operations - `and`, `or` and `not`. */ class PrefilterExpression { @@ -41,7 +41,11 @@ class PrefilterExpression { // The respective metadata to the blocks is expected to be provided in // a sorted order (w.r.t. the relevant column). - virtual std::vector evaluate( + std::vector evaluate(const std::vector& input, + size_t evaluationColumn) const; + + private: + virtual std::vector evaluateImpl( const std::vector& input, size_t evaluationColumn) const = 0; }; @@ -67,41 +71,59 @@ class RelationalExpression : public PrefilterExpression { std::unique_ptr logicalComplement() const override; - std::vector evaluate(const std::vector& input, - size_t evaluationColumn) const override; + private: + std::vector evaluateImpl( + const std::vector& input, + size_t evaluationColumn) const override; }; //______________________________________________________________________________ // Helper struct for a compact class implementation regarding the logical -// operations `AND`, `OR` and `NOT`. -enum struct LogicalOperators { AND, OR, NOT }; +// operations `AND` and `OR`. `NOT` is implemented separately given that the +// expression is unary (single child expression). +enum struct LogicalOperators { AND, OR }; //______________________________________________________________________________ template class LogicalExpression : public PrefilterExpression { private: std::unique_ptr child1_; - std::optional> child2_; + std::unique_ptr child2_; public: // AND and OR explicit LogicalExpression(std::unique_ptr child1, std::unique_ptr child2) : child1_(std::move(child1)), child2_(std::move(child2)) {} - // NOT - explicit LogicalExpression(std::unique_ptr child1) - : child1_(child1->logicalComplement()), child2_(nullptr) {} std::unique_ptr logicalComplement() const override; - std::vector evaluate(const std::vector& input, - size_t evaluationColumn) const override; - private: // Introduce a helper method for the specification LogicalOperators::OR - std::vector evaluateOr(const std::vector& input, - size_t evaluationColumn) const + std::vector evaluateOrImpl( + const std::vector& input, size_t evaluationColumn) const requires(Operation == LogicalOperators::OR); + + std::vector evaluateImpl( + const std::vector& input, + size_t evaluationColumn) const override; +}; + +//______________________________________________________________________________ +class NotExpression : public PrefilterExpression { + private: + std::unique_ptr child_; + + public: + explicit NotExpression(std::unique_ptr child) + : child_(child->logicalComplement()) {} + + std::unique_ptr logicalComplement() const; + + private: + std::vector evaluateImpl( + const std::vector& input, + size_t evaluationColumn) const override; }; //______________________________________________________________________________ @@ -125,7 +147,5 @@ using AndExpression = prefilterExpressions::LogicalExpression< prefilterExpressions::LogicalOperators::AND>; using OrExpression = prefilterExpressions::LogicalExpression< prefilterExpressions::LogicalOperators::OR>; -using NotExpression = prefilterExpressions::LogicalExpression< - prefilterExpressions::LogicalOperators::NOT>; } // namespace prefilterExpressions diff --git a/test/CompressedBlockPrefilteringTest.cpp b/test/CompressedBlockPrefilteringTest.cpp index 78f660d5c8..69eea2c2c6 100644 --- a/test/CompressedBlockPrefilteringTest.cpp +++ b/test/CompressedBlockPrefilteringTest.cpp @@ -42,24 +42,26 @@ struct MetadataBlocks { {IntId(48), IntId(6), DoubleId(111.333)}, {IntId(51), IntId(6), DoubleId(112.00)}}; std::vector numericBlocks = {nb1, nb2, nb3, nb4, nb5}; +}; +/* - //____________________________________________________________________________ - // Define Blocks containing VocabIds - // Use TestContext from SparqlExpressionTestHelpers.h - sparqlExpression::TestContext tc{}; - Id Undef = UndefId(); - // Columns indexed: COLUMN 0 | COLUMN 1 | COLUMN 2 - // undef. | LocalVocab | undef. - BlockMetadata vb1{{}, 0, {Undef, tc.bonn, Undef}, {Undef, tc.cologne, Undef}}; - BlockMetadata vb2{ - {}, 0, {Undef, tc.dortmund, Undef}, {Undef, tc.essen, Undef}}; - BlockMetadata vb3{ - {}, 0, {Undef, tc.frankfurt, Undef}, {Undef, tc.frankfurt, Undef}}; - BlockMetadata vb4{ - {}, 0, {Undef, tc.hamburg, Undef}, {Undef, tc.karlsruhe, Undef}}; - BlockMetadata vb5{ - {}, 0, {Undef, tc.karlsruhe, Undef}, {Undef, tc.karlsruhe, Undef}}; - std::vector vocabBlocks = {vb1, vb2, vb3, vb4, vb5}; +//____________________________________________________________________________ +// Define Blocks containing VocabIds +// Use TestContext from SparqlExpressionTestHelpers.h +sparqlExpression::TestContext tc{}; +Id Undef = UndefId(); +// Columns indexed: COLUMN 0 | COLUMN 1 | COLUMN 2 +// undef. | LocalVocab | undef. +BlockMetadata vb1{{}, 0, {Undef, tc.bonn, Undef}, {Undef, tc.cologne, Undef}}; +BlockMetadata vb2{ + {}, 0, {Undef, tc.dortmund, Undef}, {Undef, tc.essen, Undef}}; +BlockMetadata vb3{ + {}, 0, {Undef, tc.frankfurt, Undef}, {Undef, tc.frankfurt, Undef}}; +BlockMetadata vb4{ + {}, 0, {Undef, tc.hamburg, Undef}, {Undef, tc.karlsruhe, Undef}}; +BlockMetadata vb5{ + {}, 0, {Undef, tc.karlsruhe, Undef}, {Undef, tc.karlsruhe, Undef}}; +std::vector vocabBlocks = {vb1, vb2, vb3, vb4, vb5}; }; // Static tests, they focus on corner case values for the given block triples. @@ -69,284 +71,284 @@ struct MetadataBlocks { // Test LessThanExpression TEST(RelationalExpression, testLessThanExpressions) { - MetadataBlocks blocks{}; - std::vector expectedResult = {}; - // NUMERIC - EXPECT_EQ(expectedResult, - LessThanExpression{IntId(10)}.evaluate(blocks.numericBlocks, 0)); - EXPECT_EQ(expectedResult, - LessThanExpression{IntId(16)}.evaluate(blocks.numericBlocks, 0)); - expectedResult = {blocks.nb1}; - EXPECT_EQ(expectedResult, - LessThanExpression{IntId(40)}.evaluate(blocks.numericBlocks, 0)); - EXPECT_EQ(expectedResult, - LessThanExpression{IntId(42)}.evaluate(blocks.numericBlocks, 0)); - expectedResult = {blocks.nb1, blocks.nb2, blocks.nb3}; - EXPECT_EQ(expectedResult, - LessThanExpression{IntId(46)}.evaluate(blocks.numericBlocks, 0)); - EXPECT_EQ(blocks.numericBlocks, - LessThanExpression{IntId(100)}.evaluate(blocks.numericBlocks, 0)); - // VOCAB - TestContext tc{}; - expectedResult = {}; - // tc.alpha is an Id of type Vocab, w.r.t. a lexicographical order, all the - // citiy IDs (LocalVocab) should be greater than the ID (Vocab) of "alpha". - // Thus we expect that none of the blocks are relevant. - EXPECT_EQ(expectedResult, - LessThanExpression{tc.alpha}.evaluate(blocks.vocabBlocks, 1)); - EXPECT_EQ(expectedResult, - LessThanExpression{tc.berlin}.evaluate(blocks.vocabBlocks, 1)); - // All cities used within vocabBlocks should be smaller than city "munich" - // (given their respective LocalVocab IDs). - EXPECT_EQ(blocks.vocabBlocks, - LessThanExpression{tc.munich}.evaluate(blocks.vocabBlocks, 1)); - // All blocks from vocabBlocks should contain values less-than the (Vocab) - // ID for "zz". - EXPECT_EQ(blocks.vocabBlocks, - LessThanExpression{tc.zz}.evaluate(blocks.vocabBlocks, 1)); - expectedResult = {blocks.vb1, blocks.vb2}; - EXPECT_EQ(expectedResult, - LessThanExpression{tc.düsseldorf}.evaluate(blocks.vocabBlocks, 1)); - expectedResult = {blocks.vb1, blocks.vb2, blocks.vb3}; - EXPECT_EQ(expectedResult, LessThanExpression{tc.frankfurt_oder}.evaluate( - blocks.vocabBlocks, 1)); - expectedResult = {blocks.vb1, blocks.vb2, blocks.vb3, blocks.vb4}; - EXPECT_EQ(expectedResult, - LessThanExpression{tc.ingolstadt}.evaluate(blocks.vocabBlocks, 1)); +MetadataBlocks blocks{}; +std::vector expectedResult = {}; +// NUMERIC +EXPECT_EQ(expectedResult, + LessThanExpression{IntId(10)}.evaluate(blocks.numericBlocks, 0)); +EXPECT_EQ(expectedResult, + LessThanExpression{IntId(16)}.evaluate(blocks.numericBlocks, 0)); +expectedResult = {blocks.nb1}; +EXPECT_EQ(expectedResult, + LessThanExpression{IntId(40)}.evaluate(blocks.numericBlocks, 0)); +EXPECT_EQ(expectedResult, + LessThanExpression{IntId(42)}.evaluate(blocks.numericBlocks, 0)); +expectedResult = {blocks.nb1, blocks.nb2, blocks.nb3}; +EXPECT_EQ(expectedResult, + LessThanExpression{IntId(46)}.evaluate(blocks.numericBlocks, 0)); +EXPECT_EQ(blocks.numericBlocks, + LessThanExpression{IntId(100)}.evaluate(blocks.numericBlocks, 0)); +// VOCAB +TestContext tc{}; +expectedResult = {}; +// tc.alpha is an Id of type Vocab, w.r.t. a lexicographical order, all the +// citiy IDs (LocalVocab) should be greater than the ID (Vocab) of "alpha". +// Thus we expect that none of the blocks are relevant. +EXPECT_EQ(expectedResult, + LessThanExpression{tc.alpha}.evaluate(blocks.vocabBlocks, 1)); +EXPECT_EQ(expectedResult, + LessThanExpression{tc.berlin}.evaluate(blocks.vocabBlocks, 1)); +// All cities used within vocabBlocks should be smaller than city "munich" +// (given their respective LocalVocab IDs). +EXPECT_EQ(blocks.vocabBlocks, + LessThanExpression{tc.munich}.evaluate(blocks.vocabBlocks, 1)); +// All blocks from vocabBlocks should contain values less-than the (Vocab) +// ID for "zz". +EXPECT_EQ(blocks.vocabBlocks, + LessThanExpression{tc.zz}.evaluate(blocks.vocabBlocks, 1)); +expectedResult = {blocks.vb1, blocks.vb2}; +EXPECT_EQ(expectedResult, + LessThanExpression{tc.düsseldorf}.evaluate(blocks.vocabBlocks, 1)); +expectedResult = {blocks.vb1, blocks.vb2, blocks.vb3}; +EXPECT_EQ(expectedResult, LessThanExpression{tc.frankfurt_oder}.evaluate( + blocks.vocabBlocks, 1)); +expectedResult = {blocks.vb1, blocks.vb2, blocks.vb3, blocks.vb4}; +EXPECT_EQ(expectedResult, + LessThanExpression{tc.ingolstadt}.evaluate(blocks.vocabBlocks, 1)); } //______________________________________________________________________________ // Test LessEqualExpression TEST(RelationalExpression, testLessEqualExpressions) { - MetadataBlocks blocks{}; - std::vector expectedResult = {}; - // NUMERIC - EXPECT_EQ(expectedResult, - LessEqualExpression{IntId(10)}.evaluate(blocks.numericBlocks, 0)); - expectedResult = {blocks.nb1}; - EXPECT_EQ(expectedResult, - LessEqualExpression{IntId(16)}.evaluate(blocks.numericBlocks, 0)); - EXPECT_EQ(expectedResult, - LessEqualExpression{IntId(40)}.evaluate(blocks.numericBlocks, 0)); - expectedResult = {blocks.nb1, blocks.nb2, blocks.nb3}; - EXPECT_EQ(expectedResult, - LessEqualExpression{IntId(42)}.evaluate(blocks.numericBlocks, 0)); - expectedResult = {blocks.nb1, blocks.nb2, blocks.nb3, blocks.nb4}; - EXPECT_EQ(expectedResult, - LessEqualExpression{IntId(46)}.evaluate(blocks.numericBlocks, 0)); - EXPECT_EQ(blocks.numericBlocks, - LessEqualExpression{IntId(100)}.evaluate(blocks.numericBlocks, 0)); - // VOCAB - TestContext tc{}; - expectedResult = {}; - EXPECT_EQ(expectedResult, - LessEqualExpression{tc.alpha}.evaluate(blocks.vocabBlocks, 1)); - EXPECT_EQ(expectedResult, - LessEqualExpression{tc.berlin}.evaluate(blocks.vocabBlocks, 1)); - expectedResult = {blocks.vb1, blocks.vb2}; - EXPECT_EQ(expectedResult, - LessEqualExpression{tc.dortmund}.evaluate(blocks.vocabBlocks, 1)); - expectedResult = {blocks.vb1, blocks.vb2, blocks.vb3, blocks.vb4}; - EXPECT_EQ(expectedResult, - LessEqualExpression{tc.hannover}.evaluate(blocks.vocabBlocks, 1)); - EXPECT_EQ(blocks.vocabBlocks, - LessEqualExpression{tc.karlsruhe}.evaluate(blocks.vocabBlocks, 1)); +MetadataBlocks blocks{}; +std::vector expectedResult = {}; +// NUMERIC +EXPECT_EQ(expectedResult, + LessEqualExpression{IntId(10)}.evaluate(blocks.numericBlocks, 0)); +expectedResult = {blocks.nb1}; +EXPECT_EQ(expectedResult, + LessEqualExpression{IntId(16)}.evaluate(blocks.numericBlocks, 0)); +EXPECT_EQ(expectedResult, + LessEqualExpression{IntId(40)}.evaluate(blocks.numericBlocks, 0)); +expectedResult = {blocks.nb1, blocks.nb2, blocks.nb3}; +EXPECT_EQ(expectedResult, + LessEqualExpression{IntId(42)}.evaluate(blocks.numericBlocks, 0)); +expectedResult = {blocks.nb1, blocks.nb2, blocks.nb3, blocks.nb4}; +EXPECT_EQ(expectedResult, + LessEqualExpression{IntId(46)}.evaluate(blocks.numericBlocks, 0)); +EXPECT_EQ(blocks.numericBlocks, + LessEqualExpression{IntId(100)}.evaluate(blocks.numericBlocks, 0)); +// VOCAB +TestContext tc{}; +expectedResult = {}; +EXPECT_EQ(expectedResult, + LessEqualExpression{tc.alpha}.evaluate(blocks.vocabBlocks, 1)); +EXPECT_EQ(expectedResult, + LessEqualExpression{tc.berlin}.evaluate(blocks.vocabBlocks, 1)); +expectedResult = {blocks.vb1, blocks.vb2}; +EXPECT_EQ(expectedResult, + LessEqualExpression{tc.dortmund}.evaluate(blocks.vocabBlocks, 1)); +expectedResult = {blocks.vb1, blocks.vb2, blocks.vb3, blocks.vb4}; +EXPECT_EQ(expectedResult, + LessEqualExpression{tc.hannover}.evaluate(blocks.vocabBlocks, 1)); +EXPECT_EQ(blocks.vocabBlocks, + LessEqualExpression{tc.karlsruhe}.evaluate(blocks.vocabBlocks, 1)); } //______________________________________________________________________________ // Test GreaterThanExpression TEST(RelationalExpression, testGreaterThanExpression) { - MetadataBlocks blocks{}; - EXPECT_EQ(blocks.numericBlocks, - GreaterThanExpression{IntId(10)}.evaluate(blocks.numericBlocks, 0)); - EXPECT_EQ(blocks.numericBlocks, - GreaterThanExpression{IntId(16)}.evaluate(blocks.numericBlocks, 0)); - std::vector expectedResult = {blocks.nb2, blocks.nb3, - blocks.nb4, blocks.nb5}; - EXPECT_EQ(expectedResult, - GreaterThanExpression{IntId(38)}.evaluate(blocks.numericBlocks, 0)); - expectedResult = {blocks.nb3, blocks.nb4, blocks.nb5}; - EXPECT_EQ(expectedResult, - GreaterThanExpression{IntId(42)}.evaluate(blocks.numericBlocks, 0)); - expectedResult = {blocks.nb4, blocks.nb5}; - EXPECT_EQ(expectedResult, - GreaterThanExpression{IntId(46)}.evaluate(blocks.numericBlocks, 0)); - expectedResult = {}; - EXPECT_EQ(expectedResult, - GreaterThanExpression{IntId(52)}.evaluate(blocks.numericBlocks, 0)); - // VOCAB - TestContext tc{}; - expectedResult = {}; - EXPECT_EQ(expectedResult, - GreaterThanExpression{tc.munich}.evaluate(blocks.vocabBlocks, 1)); - EXPECT_EQ(expectedResult, GreaterThanExpression{tc.karlsruhe}.evaluate( - blocks.vocabBlocks, 1)); - EXPECT_EQ(blocks.vocabBlocks, - GreaterThanExpression{tc.alpha}.evaluate(blocks.vocabBlocks, 1)); - expectedResult = {blocks.vb4, blocks.vb5}; - EXPECT_EQ(expectedResult, - GreaterThanExpression{tc.hamburg}.evaluate(blocks.vocabBlocks, 1)); - expectedResult = {blocks.vb4, blocks.vb5}; - EXPECT_EQ(expectedResult, - GreaterThanExpression{tc.hannover}.evaluate(blocks.vocabBlocks, 1)); - expectedResult = {blocks.vb2, blocks.vb3, blocks.vb4, blocks.vb5}; - EXPECT_EQ(expectedResult, GreaterThanExpression{tc.düsseldorf}.evaluate( - blocks.vocabBlocks, 1)); +MetadataBlocks blocks{}; +EXPECT_EQ(blocks.numericBlocks, + GreaterThanExpression{IntId(10)}.evaluate(blocks.numericBlocks, 0)); +EXPECT_EQ(blocks.numericBlocks, + GreaterThanExpression{IntId(16)}.evaluate(blocks.numericBlocks, 0)); +std::vector expectedResult = {blocks.nb2, blocks.nb3, + blocks.nb4, blocks.nb5}; +EXPECT_EQ(expectedResult, + GreaterThanExpression{IntId(38)}.evaluate(blocks.numericBlocks, 0)); +expectedResult = {blocks.nb3, blocks.nb4, blocks.nb5}; +EXPECT_EQ(expectedResult, + GreaterThanExpression{IntId(42)}.evaluate(blocks.numericBlocks, 0)); +expectedResult = {blocks.nb4, blocks.nb5}; +EXPECT_EQ(expectedResult, + GreaterThanExpression{IntId(46)}.evaluate(blocks.numericBlocks, 0)); +expectedResult = {}; +EXPECT_EQ(expectedResult, + GreaterThanExpression{IntId(52)}.evaluate(blocks.numericBlocks, 0)); +// VOCAB +TestContext tc{}; +expectedResult = {}; +EXPECT_EQ(expectedResult, + GreaterThanExpression{tc.munich}.evaluate(blocks.vocabBlocks, 1)); +EXPECT_EQ(expectedResult, GreaterThanExpression{tc.karlsruhe}.evaluate( + blocks.vocabBlocks, 1)); +EXPECT_EQ(blocks.vocabBlocks, + GreaterThanExpression{tc.alpha}.evaluate(blocks.vocabBlocks, 1)); +expectedResult = {blocks.vb4, blocks.vb5}; +EXPECT_EQ(expectedResult, + GreaterThanExpression{tc.hamburg}.evaluate(blocks.vocabBlocks, 1)); +expectedResult = {blocks.vb4, blocks.vb5}; +EXPECT_EQ(expectedResult, + GreaterThanExpression{tc.hannover}.evaluate(blocks.vocabBlocks, 1)); +expectedResult = {blocks.vb2, blocks.vb3, blocks.vb4, blocks.vb5}; +EXPECT_EQ(expectedResult, GreaterThanExpression{tc.düsseldorf}.evaluate( + blocks.vocabBlocks, 1)); } //______________________________________________________________________________ // Test GreaterEqualExpression TEST(RelationalExpression, testGreaterEqualExpression) { - MetadataBlocks blocks{}; - EXPECT_EQ(blocks.numericBlocks, GreaterEqualExpression{IntId(10)}.evaluate( - blocks.numericBlocks, 0)); - EXPECT_EQ(blocks.numericBlocks, GreaterEqualExpression{IntId(38)}.evaluate( - blocks.numericBlocks, 0)); - std::vector expectedResult = {blocks.nb2, blocks.nb3, - blocks.nb4, blocks.nb5}; - EXPECT_EQ(expectedResult, GreaterEqualExpression{IntId(40)}.evaluate( - blocks.numericBlocks, 0)); - EXPECT_EQ(expectedResult, GreaterEqualExpression{IntId(42)}.evaluate( - blocks.numericBlocks, 0)); - expectedResult = {blocks.nb3, blocks.nb4, blocks.nb5}; - EXPECT_EQ(expectedResult, GreaterEqualExpression{IntId(45)}.evaluate( - blocks.numericBlocks, 0)); - expectedResult = {blocks.nb4, blocks.nb5}; - EXPECT_EQ(expectedResult, GreaterEqualExpression{IntId(47)}.evaluate( - blocks.numericBlocks, 0)); - expectedResult = {}; - EXPECT_EQ(expectedResult, GreaterEqualExpression{IntId(100)}.evaluate( - blocks.numericBlocks, 0)); - // VOCAB - TestContext tc{}; - EXPECT_EQ(blocks.vocabBlocks, - GreaterEqualExpression{tc.alpha}.evaluate(blocks.vocabBlocks, 1)); - EXPECT_EQ(blocks.vocabBlocks, - GreaterEqualExpression{tc.bonn}.evaluate(blocks.vocabBlocks, 1)); - EXPECT_EQ(blocks.vocabBlocks, - GreaterEqualExpression{tc.cologne}.evaluate(blocks.vocabBlocks, 1)); - expectedResult = {blocks.vb2, blocks.vb3, blocks.vb4, blocks.vb5}; - EXPECT_EQ(expectedResult, GreaterEqualExpression{tc.düsseldorf}.evaluate( - blocks.vocabBlocks, 1)); - expectedResult = {blocks.vb4, blocks.vb5}; - EXPECT_EQ(expectedResult, GreaterEqualExpression{tc.frankfurt_oder}.evaluate( - blocks.vocabBlocks, 1)); - EXPECT_EQ(expectedResult, GreaterEqualExpression{tc.karlsruhe}.evaluate( - blocks.vocabBlocks, 1)); - expectedResult = {}; - EXPECT_EQ(expectedResult, - GreaterEqualExpression{tc.munich}.evaluate(blocks.vocabBlocks, 1)); - EXPECT_EQ(expectedResult, - GreaterEqualExpression{tc.zz}.evaluate(blocks.vocabBlocks, 1)); +MetadataBlocks blocks{}; +EXPECT_EQ(blocks.numericBlocks, GreaterEqualExpression{IntId(10)}.evaluate( + blocks.numericBlocks, 0)); +EXPECT_EQ(blocks.numericBlocks, GreaterEqualExpression{IntId(38)}.evaluate( + blocks.numericBlocks, 0)); +std::vector expectedResult = {blocks.nb2, blocks.nb3, + blocks.nb4, blocks.nb5}; +EXPECT_EQ(expectedResult, GreaterEqualExpression{IntId(40)}.evaluate( + blocks.numericBlocks, 0)); +EXPECT_EQ(expectedResult, GreaterEqualExpression{IntId(42)}.evaluate( + blocks.numericBlocks, 0)); +expectedResult = {blocks.nb3, blocks.nb4, blocks.nb5}; +EXPECT_EQ(expectedResult, GreaterEqualExpression{IntId(45)}.evaluate( + blocks.numericBlocks, 0)); +expectedResult = {blocks.nb4, blocks.nb5}; +EXPECT_EQ(expectedResult, GreaterEqualExpression{IntId(47)}.evaluate( + blocks.numericBlocks, 0)); +expectedResult = {}; +EXPECT_EQ(expectedResult, GreaterEqualExpression{IntId(100)}.evaluate( + blocks.numericBlocks, 0)); +// VOCAB +TestContext tc{}; +EXPECT_EQ(blocks.vocabBlocks, + GreaterEqualExpression{tc.alpha}.evaluate(blocks.vocabBlocks, 1)); +EXPECT_EQ(blocks.vocabBlocks, + GreaterEqualExpression{tc.bonn}.evaluate(blocks.vocabBlocks, 1)); +EXPECT_EQ(blocks.vocabBlocks, + GreaterEqualExpression{tc.cologne}.evaluate(blocks.vocabBlocks, 1)); +expectedResult = {blocks.vb2, blocks.vb3, blocks.vb4, blocks.vb5}; +EXPECT_EQ(expectedResult, GreaterEqualExpression{tc.düsseldorf}.evaluate( + blocks.vocabBlocks, 1)); +expectedResult = {blocks.vb4, blocks.vb5}; +EXPECT_EQ(expectedResult, GreaterEqualExpression{tc.frankfurt_oder}.evaluate( + blocks.vocabBlocks, 1)); +EXPECT_EQ(expectedResult, GreaterEqualExpression{tc.karlsruhe}.evaluate( + blocks.vocabBlocks, 1)); +expectedResult = {}; +EXPECT_EQ(expectedResult, + GreaterEqualExpression{tc.munich}.evaluate(blocks.vocabBlocks, 1)); +EXPECT_EQ(expectedResult, + GreaterEqualExpression{tc.zz}.evaluate(blocks.vocabBlocks, 1)); } //______________________________________________________________________________ // Test EqualExpression TEST(RelationalExpression, testEqualExpression) { - MetadataBlocks blocks{}; - std::vector expectedResult = {}; - EXPECT_EQ(expectedResult, - EqualExpression{IntId(10)}.evaluate(blocks.numericBlocks, 1)); - EXPECT_EQ(expectedResult, - EqualExpression{IntId(10)}.evaluate(blocks.numericBlocks, 0)); - expectedResult = {blocks.nb4}; - EXPECT_EQ(expectedResult, - EqualExpression{IntId(5)}.evaluate(blocks.numericBlocks, 1)); - expectedResult = {blocks.nb2, blocks.nb3, blocks.nb4}; - EXPECT_EQ(expectedResult, - EqualExpression{IntId(2)}.evaluate(blocks.numericBlocks, 1)); - expectedResult = {blocks.nb4, blocks.nb5}; - EXPECT_EQ(expectedResult, - EqualExpression{IntId(6)}.evaluate(blocks.numericBlocks, 1)); - expectedResult = {blocks.nb2, blocks.nb3}; - EXPECT_EQ(expectedResult, - EqualExpression{IntId(42)}.evaluate(blocks.numericBlocks, 0)); - expectedResult = {blocks.nb5}; - EXPECT_EQ(expectedResult, - EqualExpression{IntId(112)}.evaluate(blocks.numericBlocks, 2)); - // VOCAB - TestContext tc{}; - expectedResult = {}; - EXPECT_EQ(expectedResult, - EqualExpression{tc.zz}.evaluate(blocks.vocabBlocks, 1)); - EXPECT_EQ(expectedResult, - EqualExpression{tc.alpha}.evaluate(blocks.vocabBlocks, 1)); - EXPECT_EQ(expectedResult, - EqualExpression{tc.munich}.evaluate(blocks.vocabBlocks, 1)); - EXPECT_EQ(expectedResult, - EqualExpression{tc.frankfurt_oder}.evaluate(blocks.vocabBlocks, 1)); - expectedResult = {blocks.vb4, blocks.vb5}; - EXPECT_EQ(expectedResult, - EqualExpression{tc.karlsruhe}.evaluate(blocks.vocabBlocks, 1)); - expectedResult = {blocks.vb3}; - EXPECT_EQ(expectedResult, - EqualExpression{tc.frankfurt}.evaluate(blocks.vocabBlocks, 1)); - expectedResult = {blocks.vb2}; - EXPECT_EQ(expectedResult, - EqualExpression{tc.düsseldorf}.evaluate(blocks.vocabBlocks, 1)); - expectedResult = {blocks.vb1}; - EXPECT_EQ(expectedResult, - EqualExpression{tc.bonn}.evaluate(blocks.vocabBlocks, 1)); - EXPECT_EQ(expectedResult, - EqualExpression{tc.cologne}.evaluate(blocks.vocabBlocks, 1)); +MetadataBlocks blocks{}; +std::vector expectedResult = {}; +EXPECT_EQ(expectedResult, + EqualExpression{IntId(10)}.evaluate(blocks.numericBlocks, 1)); +EXPECT_EQ(expectedResult, + EqualExpression{IntId(10)}.evaluate(blocks.numericBlocks, 0)); +expectedResult = {blocks.nb4}; +EXPECT_EQ(expectedResult, + EqualExpression{IntId(5)}.evaluate(blocks.numericBlocks, 1)); +expectedResult = {blocks.nb2, blocks.nb3, blocks.nb4}; +EXPECT_EQ(expectedResult, + EqualExpression{IntId(2)}.evaluate(blocks.numericBlocks, 1)); +expectedResult = {blocks.nb4, blocks.nb5}; +EXPECT_EQ(expectedResult, + EqualExpression{IntId(6)}.evaluate(blocks.numericBlocks, 1)); +expectedResult = {blocks.nb2, blocks.nb3}; +EXPECT_EQ(expectedResult, + EqualExpression{IntId(42)}.evaluate(blocks.numericBlocks, 0)); +expectedResult = {blocks.nb5}; +EXPECT_EQ(expectedResult, + EqualExpression{IntId(112)}.evaluate(blocks.numericBlocks, 2)); +// VOCAB +TestContext tc{}; +expectedResult = {}; +EXPECT_EQ(expectedResult, + EqualExpression{tc.zz}.evaluate(blocks.vocabBlocks, 1)); +EXPECT_EQ(expectedResult, + EqualExpression{tc.alpha}.evaluate(blocks.vocabBlocks, 1)); +EXPECT_EQ(expectedResult, + EqualExpression{tc.munich}.evaluate(blocks.vocabBlocks, 1)); +EXPECT_EQ(expectedResult, + EqualExpression{tc.frankfurt_oder}.evaluate(blocks.vocabBlocks, 1)); +expectedResult = {blocks.vb4, blocks.vb5}; +EXPECT_EQ(expectedResult, + EqualExpression{tc.karlsruhe}.evaluate(blocks.vocabBlocks, 1)); +expectedResult = {blocks.vb3}; +EXPECT_EQ(expectedResult, + EqualExpression{tc.frankfurt}.evaluate(blocks.vocabBlocks, 1)); +expectedResult = {blocks.vb2}; +EXPECT_EQ(expectedResult, + EqualExpression{tc.düsseldorf}.evaluate(blocks.vocabBlocks, 1)); +expectedResult = {blocks.vb1}; +EXPECT_EQ(expectedResult, + EqualExpression{tc.bonn}.evaluate(blocks.vocabBlocks, 1)); +EXPECT_EQ(expectedResult, + EqualExpression{tc.cologne}.evaluate(blocks.vocabBlocks, 1)); } //______________________________________________________________________________ // Test NotEqualExpression TEST(RelationalExpression, testNotEqualExpression) { - MetadataBlocks blocks{}; - std::vector expectedResult{}; - EXPECT_EQ(blocks.numericBlocks, - NotEqualExpression{IntId(8)}.evaluate(blocks.numericBlocks, 0)); - EXPECT_EQ(blocks.numericBlocks, - NotEqualExpression{IntId(16)}.evaluate(blocks.numericBlocks, 0)); - EXPECT_EQ(blocks.numericBlocks, - NotEqualExpression{IntId(45)}.evaluate(blocks.numericBlocks, 0)); - EXPECT_EQ(blocks.numericBlocks, - NotEqualExpression{IntId(51)}.evaluate(blocks.numericBlocks, 0)); - EXPECT_EQ(blocks.numericBlocks, - NotEqualExpression{IntId(48)}.evaluate(blocks.numericBlocks, 0)); - EXPECT_EQ(blocks.numericBlocks, NotEqualExpression{DoubleId(18.32)}.evaluate( - blocks.numericBlocks, 2)); - EXPECT_EQ(blocks.numericBlocks, NotEqualExpression{DoubleId(22.33)}.evaluate( - blocks.numericBlocks, 2)); - EXPECT_EQ(blocks.numericBlocks, - NotEqualExpression{IntId(17)}.evaluate(blocks.numericBlocks, 0)); - expectedResult = {blocks.nb1, blocks.nb3, blocks.nb4, blocks.nb5}; - EXPECT_EQ(expectedResult, - NotEqualExpression{IntId(42)}.evaluate(blocks.numericBlocks, 0)); - expectedResult = {blocks.nb1, blocks.nb2, blocks.nb4, blocks.nb5}; - EXPECT_EQ(expectedResult, - NotEqualExpression{IntId(2)}.evaluate(blocks.numericBlocks, 1)); - expectedResult = {blocks.nb1, blocks.nb2, blocks.nb3, blocks.nb4}; - EXPECT_EQ(expectedResult, NotEqualExpression{DoubleId(6.00)}.evaluate( - blocks.numericBlocks, 1)); - expectedResult = {blocks.nb2, blocks.nb3, blocks.nb4, blocks.nb5}; - EXPECT_EQ(expectedResult, - NotEqualExpression{IntId(0)}.evaluate(blocks.numericBlocks, 1)); - // VOCAB - TestContext tc{}; - expectedResult = {}; - EXPECT_EQ(blocks.vocabBlocks, - NotEqualExpression{tc.zz}.evaluate(blocks.vocabBlocks, 1)); - EXPECT_EQ(blocks.vocabBlocks, - NotEqualExpression{tc.alpha}.evaluate(blocks.vocabBlocks, 1)); - EXPECT_EQ(blocks.vocabBlocks, NotEqualExpression{tc.frankfurt_oder}.evaluate( - blocks.vocabBlocks, 1)); - EXPECT_EQ(blocks.vocabBlocks, - NotEqualExpression{tc.munich}.evaluate(blocks.vocabBlocks, 1)); - EXPECT_EQ(blocks.vocabBlocks, - NotEqualExpression{tc.bonn}.evaluate(blocks.vocabBlocks, 1)); - EXPECT_EQ(blocks.vocabBlocks, - NotEqualExpression{tc.cologne}.evaluate(blocks.vocabBlocks, 1)); - EXPECT_EQ(blocks.vocabBlocks, - NotEqualExpression{tc.düsseldorf}.evaluate(blocks.vocabBlocks, 1)); - expectedResult = {blocks.vb1, blocks.vb2, blocks.vb4, blocks.vb5}; - EXPECT_EQ(expectedResult, - NotEqualExpression{tc.frankfurt}.evaluate(blocks.vocabBlocks, 1)); - expectedResult = {blocks.vb1, blocks.vb2, blocks.vb3, blocks.vb4}; - EXPECT_EQ(expectedResult, - NotEqualExpression{tc.karlsruhe}.evaluate(blocks.vocabBlocks, 1)); +MetadataBlocks blocks{}; +std::vector expectedResult{}; +EXPECT_EQ(blocks.numericBlocks, + NotEqualExpression{IntId(8)}.evaluate(blocks.numericBlocks, 0)); +EXPECT_EQ(blocks.numericBlocks, + NotEqualExpression{IntId(16)}.evaluate(blocks.numericBlocks, 0)); +EXPECT_EQ(blocks.numericBlocks, + NotEqualExpression{IntId(45)}.evaluate(blocks.numericBlocks, 0)); +EXPECT_EQ(blocks.numericBlocks, + NotEqualExpression{IntId(51)}.evaluate(blocks.numericBlocks, 0)); +EXPECT_EQ(blocks.numericBlocks, + NotEqualExpression{IntId(48)}.evaluate(blocks.numericBlocks, 0)); +EXPECT_EQ(blocks.numericBlocks, NotEqualExpression{DoubleId(18.32)}.evaluate( + blocks.numericBlocks, 2)); +EXPECT_EQ(blocks.numericBlocks, NotEqualExpression{DoubleId(22.33)}.evaluate( + blocks.numericBlocks, 2)); +EXPECT_EQ(blocks.numericBlocks, + NotEqualExpression{IntId(17)}.evaluate(blocks.numericBlocks, 0)); +expectedResult = {blocks.nb1, blocks.nb3, blocks.nb4, blocks.nb5}; +EXPECT_EQ(expectedResult, + NotEqualExpression{IntId(42)}.evaluate(blocks.numericBlocks, 0)); +expectedResult = {blocks.nb1, blocks.nb2, blocks.nb4, blocks.nb5}; +EXPECT_EQ(expectedResult, + NotEqualExpression{IntId(2)}.evaluate(blocks.numericBlocks, 1)); +expectedResult = {blocks.nb1, blocks.nb2, blocks.nb3, blocks.nb4}; +EXPECT_EQ(expectedResult, NotEqualExpression{DoubleId(6.00)}.evaluate( + blocks.numericBlocks, 1)); +expectedResult = {blocks.nb2, blocks.nb3, blocks.nb4, blocks.nb5}; +EXPECT_EQ(expectedResult, + NotEqualExpression{IntId(0)}.evaluate(blocks.numericBlocks, 1)); +// VOCAB +TestContext tc{}; +expectedResult = {}; +EXPECT_EQ(blocks.vocabBlocks, + NotEqualExpression{tc.zz}.evaluate(blocks.vocabBlocks, 1)); +EXPECT_EQ(blocks.vocabBlocks, + NotEqualExpression{tc.alpha}.evaluate(blocks.vocabBlocks, 1)); +EXPECT_EQ(blocks.vocabBlocks, NotEqualExpression{tc.frankfurt_oder}.evaluate( + blocks.vocabBlocks, 1)); +EXPECT_EQ(blocks.vocabBlocks, + NotEqualExpression{tc.munich}.evaluate(blocks.vocabBlocks, 1)); +EXPECT_EQ(blocks.vocabBlocks, + NotEqualExpression{tc.bonn}.evaluate(blocks.vocabBlocks, 1)); +EXPECT_EQ(blocks.vocabBlocks, + NotEqualExpression{tc.cologne}.evaluate(blocks.vocabBlocks, 1)); +EXPECT_EQ(blocks.vocabBlocks, + NotEqualExpression{tc.düsseldorf}.evaluate(blocks.vocabBlocks, 1)); +expectedResult = {blocks.vb1, blocks.vb2, blocks.vb4, blocks.vb5}; +EXPECT_EQ(expectedResult, + NotEqualExpression{tc.frankfurt}.evaluate(blocks.vocabBlocks, 1)); +expectedResult = {blocks.vb1, blocks.vb2, blocks.vb3, blocks.vb4}; +EXPECT_EQ(expectedResult, + NotEqualExpression{tc.karlsruhe}.evaluate(blocks.vocabBlocks, 1)); } //______________________________________________________________________________ @@ -355,223 +357,225 @@ TEST(RelationalExpression, testNotEqualExpression) { // Test AndExpression TEST(LogicalExpression, testAndExpression) { - MetadataBlocks blocks{}; - std::vector expectedResult{}; - EXPECT_EQ(expectedResult, - AndExpression(std::make_unique(IntId(42)), - std::make_unique(IntId(45))) - .evaluate(blocks.numericBlocks, 0)); - EXPECT_EQ( - expectedResult, - AndExpression(std::make_unique(IntId(42)), - std::make_unique(DoubleId(52.33))) - .evaluate(blocks.numericBlocks, 0)); - EXPECT_EQ( - expectedResult, - AndExpression(std::make_unique( - std::make_unique(IntId(42)), - std::make_unique(IntId(45))), - std::make_unique(IntId(49))) - .evaluate(blocks.numericBlocks, 0)); - expectedResult = {blocks.nb1, blocks.nb2}; - EXPECT_EQ(expectedResult, - AndExpression(std::make_unique( - std::make_unique(IntId(0)), - std::make_unique(IntId(0))), - std::make_unique(IntId(6))) - .evaluate(blocks.numericBlocks, 1)); - // !!! Reason that we don't expect an empty result here: - // Block blocks.nb3 contains value within the range [42, 45] on column 0. - // Thus, this block is a valid candidate for values unequal to 42 and values - // that are equal to 45. - expectedResult = {blocks.nb3}; - EXPECT_EQ(expectedResult, - AndExpression(std::make_unique(IntId(42)), - std::make_unique(DoubleId(42.00))) - .evaluate(blocks.numericBlocks, 0)); - expectedResult = {blocks.nb1, blocks.nb2, blocks.nb3, blocks.nb4}; - EXPECT_EQ(expectedResult, - AndExpression(std::make_unique(DoubleId(6.00)), - std::make_unique(IntId(2.00))) - .evaluate(blocks.numericBlocks, 1)); - expectedResult = {blocks.nb4, blocks.nb5}; - EXPECT_EQ( - expectedResult, - AndExpression(std::make_unique( - std::make_unique(IntId(7)), - std::make_unique(IntId(5))), - std::make_unique(IntId(0))) - .evaluate(blocks.numericBlocks, 1)); - expectedResult = {blocks.nb1, blocks.nb2}; - EXPECT_EQ( - expectedResult, - AndExpression(std::make_unique(DoubleId(14.575)), - std::make_unique(IntId(12))) - .evaluate(blocks.numericBlocks, 2)); - expectedResult = {blocks.nb2, blocks.nb3, blocks.nb4}; - EXPECT_EQ(expectedResult, - AndExpression(std::make_unique(DoubleId(0.00)), - std::make_unique(DoubleId(6.00))) - .evaluate(blocks.numericBlocks, 1)); - expectedResult = {blocks.nb1, blocks.nb2}; - EXPECT_EQ(expectedResult, - AndExpression(std::make_unique(DoubleId(1.99)), - std::make_unique(DoubleId(1.5))) - .evaluate(blocks.numericBlocks, 1)); +MetadataBlocks blocks{}; +std::vector expectedResult{}; +EXPECT_EQ(expectedResult, + AndExpression(std::make_unique(IntId(42)), + std::make_unique(IntId(45))) + .evaluate(blocks.numericBlocks, 0)); +EXPECT_EQ( + expectedResult, + AndExpression(std::make_unique(IntId(42)), + std::make_unique(DoubleId(52.33))) + .evaluate(blocks.numericBlocks, 0)); +EXPECT_EQ( + expectedResult, + AndExpression(std::make_unique( + std::make_unique(IntId(42)), + std::make_unique(IntId(45))), + std::make_unique(IntId(49))) + .evaluate(blocks.numericBlocks, 0)); +expectedResult = {blocks.nb1, blocks.nb2}; +EXPECT_EQ(expectedResult, + AndExpression(std::make_unique( + std::make_unique(IntId(0)), + std::make_unique(IntId(0))), + std::make_unique(IntId(6))) + .evaluate(blocks.numericBlocks, 1)); +// !!! Reason that we don't expect an empty result here: +// Block blocks.nb3 contains value within the range [42, 45] on column 0. +// Thus, this block is a valid candidate for values unequal to 42 and values +// that are equal to 45. +expectedResult = {blocks.nb3}; +EXPECT_EQ(expectedResult, + AndExpression(std::make_unique(IntId(42)), + std::make_unique(DoubleId(42.00))) + .evaluate(blocks.numericBlocks, 0)); +expectedResult = {blocks.nb1, blocks.nb2, blocks.nb3, blocks.nb4}; +EXPECT_EQ(expectedResult, + AndExpression(std::make_unique(DoubleId(6.00)), + std::make_unique(IntId(2.00))) + .evaluate(blocks.numericBlocks, 1)); +expectedResult = {blocks.nb4, blocks.nb5}; +EXPECT_EQ( + expectedResult, + AndExpression(std::make_unique( + std::make_unique(IntId(7)), + std::make_unique(IntId(5))), + std::make_unique(IntId(0))) + .evaluate(blocks.numericBlocks, 1)); +expectedResult = {blocks.nb1, blocks.nb2}; +EXPECT_EQ( + expectedResult, + AndExpression(std::make_unique(DoubleId(14.575)), + std::make_unique(IntId(12))) + .evaluate(blocks.numericBlocks, 2)); +expectedResult = {blocks.nb2, blocks.nb3, blocks.nb4}; +EXPECT_EQ(expectedResult, + AndExpression(std::make_unique(DoubleId(0.00)), + std::make_unique(DoubleId(6.00))) + .evaluate(blocks.numericBlocks, 1)); +expectedResult = {blocks.nb1, blocks.nb2}; +EXPECT_EQ(expectedResult, + AndExpression(std::make_unique(DoubleId(1.99)), + std::make_unique(DoubleId(1.5))) + .evaluate(blocks.numericBlocks, 1)); } //______________________________________________________________________________ // Test OrExpression TEST(LogicalExpression, testOrExpression) { - MetadataBlocks blocks{}; - std::vector expectedResult = {blocks.nb1, blocks.nb4, - blocks.nb5}; - EXPECT_EQ(expectedResult, - OrExpression(std::make_unique(IntId(42)), - std::make_unique(IntId(45))) - .evaluate(blocks.numericBlocks, 0)); - expectedResult = {}; - EXPECT_EQ(expectedResult, - OrExpression(std::make_unique(DoubleId(-14.23)), - std::make_unique(IntId(51))) - .evaluate(blocks.numericBlocks, 0)); - expectedResult = {blocks.nb2, blocks.nb3, blocks.nb4, blocks.nb5}; - EXPECT_EQ( - expectedResult, - OrExpression(std::make_unique(IntId(0)), - std::make_unique( - std::make_unique(IntId(5)), - std::make_unique(DoubleId(1.00)))) - .evaluate(blocks.numericBlocks, 1)); - expectedResult = {blocks.nb1, blocks.nb2, blocks.nb4, blocks.nb5}; - EXPECT_EQ(expectedResult, - OrExpression(std::make_unique(IntId(0)), - std::make_unique( - std::make_unique(IntId(3)), - std::make_unique(DoubleId(6)))) - .evaluate(blocks.numericBlocks, 1)); - expectedResult = {blocks.nb4, blocks.nb5}; - EXPECT_EQ( - expectedResult, - OrExpression(std::make_unique(IntId(20)), - std::make_unique(DoubleId(113.3))) - .evaluate(blocks.numericBlocks, 2)); - expectedResult = {blocks.nb1, blocks.nb5}; - EXPECT_EQ( - expectedResult, - OrExpression(std::make_unique(IntId(42)), - std::make_unique( - std::make_unique(IntId(49)), - std::make_unique(DoubleId(2.00)))) - .evaluate(blocks.numericBlocks, 0)); - expectedResult = {blocks.nb1, blocks.nb2, blocks.nb3, blocks.nb4}; - EXPECT_EQ(expectedResult, - OrExpression(std::make_unique( - std::make_unique(IntId(0)), - std::make_unique(DoubleId(2.00))), - std::make_unique(IntId(6))) - .evaluate(blocks.numericBlocks, 1)); - expectedResult = {blocks.nb1, blocks.nb2, blocks.nb3}; - EXPECT_EQ(expectedResult, - OrExpression(std::make_unique(DoubleId(17.00)), - std::make_unique(IntId(16.35))) - .evaluate(blocks.numericBlocks, 2)); +MetadataBlocks blocks{}; +std::vector expectedResult = {blocks.nb1, blocks.nb4, + blocks.nb5}; +EXPECT_EQ(expectedResult, + OrExpression(std::make_unique(IntId(42)), + std::make_unique(IntId(45))) + .evaluate(blocks.numericBlocks, 0)); +expectedResult = {}; +EXPECT_EQ(expectedResult, + OrExpression(std::make_unique(DoubleId(-14.23)), + std::make_unique(IntId(51))) + .evaluate(blocks.numericBlocks, 0)); +expectedResult = {blocks.nb2, blocks.nb3, blocks.nb4, blocks.nb5}; +EXPECT_EQ( + expectedResult, + OrExpression(std::make_unique(IntId(0)), + std::make_unique( + std::make_unique(IntId(5)), + std::make_unique(DoubleId(1.00)))) + .evaluate(blocks.numericBlocks, 1)); +expectedResult = {blocks.nb1, blocks.nb2, blocks.nb4, blocks.nb5}; +EXPECT_EQ(expectedResult, + OrExpression(std::make_unique(IntId(0)), + std::make_unique( + std::make_unique(IntId(3)), + std::make_unique(DoubleId(6)))) + .evaluate(blocks.numericBlocks, 1)); +expectedResult = {blocks.nb4, blocks.nb5}; +EXPECT_EQ( + expectedResult, + OrExpression(std::make_unique(IntId(20)), + std::make_unique(DoubleId(113.3))) + .evaluate(blocks.numericBlocks, 2)); +expectedResult = {blocks.nb1, blocks.nb5}; +EXPECT_EQ( + expectedResult, + OrExpression(std::make_unique(IntId(42)), + std::make_unique( + std::make_unique(IntId(49)), + std::make_unique(DoubleId(2.00)))) + .evaluate(blocks.numericBlocks, 0)); +expectedResult = {blocks.nb1, blocks.nb2, blocks.nb3, blocks.nb4}; +EXPECT_EQ(expectedResult, + OrExpression(std::make_unique( + std::make_unique(IntId(0)), + std::make_unique(DoubleId(2.00))), + std::make_unique(IntId(6))) + .evaluate(blocks.numericBlocks, 1)); +expectedResult = {blocks.nb1, blocks.nb2, blocks.nb3}; +EXPECT_EQ(expectedResult, + OrExpression(std::make_unique(DoubleId(17.00)), + std::make_unique(IntId(16.35))) + .evaluate(blocks.numericBlocks, 2)); } //______________________________________________________________________________ // Test NotExpression TEST(LogicalExpression, testNotExpression) { - MetadataBlocks blocks{}; - TestContext tc{}; - std::vector expectedResult = {}; - EXPECT_EQ(expectedResult, - NotExpression(std::make_unique(IntId(16))) - .evaluate(blocks.numericBlocks, 0)); - EXPECT_EQ( - expectedResult, - NotExpression(std::make_unique( - std::make_unique(tc.munich))) - .evaluate(blocks.vocabBlocks, 1)); - EXPECT_EQ(expectedResult, - NotExpression(std::make_unique( - std::make_unique(tc.zz))) - .evaluate(blocks.vocabBlocks, 1)); - expectedResult = {blocks.vb4, blocks.vb5}; - EXPECT_EQ( - expectedResult, - NotExpression(std::make_unique( - std::make_unique(tc.karlsruhe))) - .evaluate(blocks.vocabBlocks, 1)); - EXPECT_EQ( - blocks.numericBlocks, - NotExpression(std::make_unique( - std::make_unique(IntId(16)))) - .evaluate(blocks.numericBlocks, 0)); - expectedResult = {blocks.vb1, blocks.vb2, blocks.vb3}; - EXPECT_EQ(expectedResult, - NotExpression(std::make_unique(tc.frankfurt)) - .evaluate(blocks.vocabBlocks, 1)); - EXPECT_EQ(blocks.vocabBlocks, - NotExpression(std::make_unique(tc.berlin)) - .evaluate(blocks.vocabBlocks, 1)); - expectedResult = {blocks.vb1, blocks.vb2}; - EXPECT_EQ( - expectedResult, - NotExpression(std::make_unique(tc.düsseldorf)) - .evaluate(blocks.vocabBlocks, 1)); - EXPECT_EQ(blocks.numericBlocks, - NotExpression( - std::make_unique( - std::make_unique(IntId(13)), - std::make_unique(DoubleId(111.01)))) - .evaluate(blocks.numericBlocks, 2)); - expectedResult = {}; - EXPECT_EQ( - expectedResult, - NotExpression( - std::make_unique(std::make_unique( - std::make_unique(IntId(13)), - std::make_unique(DoubleId(111.01))))) - .evaluate(blocks.numericBlocks, 2)); - expectedResult = {blocks.vb4, blocks.vb5}; - EXPECT_EQ( - expectedResult, - NotExpression(std::make_unique( - std::make_unique(tc.munich), - std::make_unique(tc.ingolstadt))) - .evaluate(blocks.vocabBlocks, 1)); - expectedResult = {blocks.vb2, blocks.vb3, blocks.vb4}; - EXPECT_EQ( - expectedResult, - NotExpression(std::make_unique( - std::make_unique(tc.hamburg), - std::make_unique(tc.düsseldorf))) - .evaluate(blocks.vocabBlocks, 1)); - expectedResult = {blocks.nb2, blocks.nb3, blocks.nb4}; - EXPECT_EQ(expectedResult, - NotExpression(std::make_unique( - std::make_unique(DoubleId(0.0)), - std::make_unique(IntId(6)))) - .evaluate(blocks.numericBlocks, 1)); - expectedResult = {blocks.nb1, blocks.nb2, blocks.nb4, blocks.nb5}; - EXPECT_EQ( - expectedResult, - NotExpression(std::make_unique( - std::make_unique(DoubleId(0.0)), - std::make_unique(IntId(6)))) - .evaluate(blocks.numericBlocks, 1)); - expectedResult = {blocks.nb3, blocks.nb4}; - EXPECT_EQ( - expectedResult, - NotExpression(std::make_unique( - std::make_unique(DoubleId(42.0)), - std::make_unique(IntId(48)))) - .evaluate(blocks.numericBlocks, 0)); - expectedResult = {blocks.nb1, blocks.nb2, blocks.nb3, blocks.nb5}; - EXPECT_EQ(expectedResult, - NotExpression( - std::make_unique(std::make_unique( - std::make_unique(DoubleId(42.25)), - std::make_unique(IntId(51))))) - .evaluate(blocks.numericBlocks, 0)); +MetadataBlocks blocks{}; +TestContext tc{}; +std::vector expectedResult = {}; +EXPECT_EQ(expectedResult, + NotExpression(std::make_unique(IntId(16))) + .evaluate(blocks.numericBlocks, 0)); +EXPECT_EQ( + expectedResult, + NotExpression(std::make_unique( + std::make_unique(tc.munich))) + .evaluate(blocks.vocabBlocks, 1)); +EXPECT_EQ(expectedResult, + NotExpression(std::make_unique( + std::make_unique(tc.zz))) + .evaluate(blocks.vocabBlocks, 1)); +expectedResult = {blocks.vb4, blocks.vb5}; +EXPECT_EQ( + expectedResult, + NotExpression(std::make_unique( + std::make_unique(tc.karlsruhe))) + .evaluate(blocks.vocabBlocks, 1)); +EXPECT_EQ( + blocks.numericBlocks, + NotExpression(std::make_unique( + std::make_unique(IntId(16)))) + .evaluate(blocks.numericBlocks, 0)); +expectedResult = {blocks.vb1, blocks.vb2, blocks.vb3}; +EXPECT_EQ(expectedResult, + NotExpression(std::make_unique(tc.frankfurt)) + .evaluate(blocks.vocabBlocks, 1)); +EXPECT_EQ(blocks.vocabBlocks, + NotExpression(std::make_unique(tc.berlin)) + .evaluate(blocks.vocabBlocks, 1)); +expectedResult = {blocks.vb1, blocks.vb2}; +EXPECT_EQ( + expectedResult, + NotExpression(std::make_unique(tc.düsseldorf)) + .evaluate(blocks.vocabBlocks, 1)); +EXPECT_EQ(blocks.numericBlocks, + NotExpression( + std::make_unique( + std::make_unique(IntId(13)), + std::make_unique(DoubleId(111.01)))) + .evaluate(blocks.numericBlocks, 2)); +expectedResult = {}; +EXPECT_EQ( + expectedResult, + NotExpression( + std::make_unique(std::make_unique( + std::make_unique(IntId(13)), + std::make_unique(DoubleId(111.01))))) + .evaluate(blocks.numericBlocks, 2)); +expectedResult = {blocks.vb4, blocks.vb5}; +EXPECT_EQ( + expectedResult, + NotExpression(std::make_unique( + std::make_unique(tc.munich), + std::make_unique(tc.ingolstadt))) + .evaluate(blocks.vocabBlocks, 1)); +expectedResult = {blocks.vb2, blocks.vb3, blocks.vb4}; +EXPECT_EQ( + expectedResult, + NotExpression(std::make_unique( + std::make_unique(tc.hamburg), + std::make_unique(tc.düsseldorf))) + .evaluate(blocks.vocabBlocks, 1)); +expectedResult = {blocks.nb2, blocks.nb3, blocks.nb4}; +EXPECT_EQ(expectedResult, + NotExpression(std::make_unique( + std::make_unique(DoubleId(0.0)), + std::make_unique(IntId(6)))) + .evaluate(blocks.numericBlocks, 1)); +expectedResult = {blocks.nb1, blocks.nb2, blocks.nb4, blocks.nb5}; +EXPECT_EQ( + expectedResult, + NotExpression(std::make_unique( + std::make_unique(DoubleId(0.0)), + std::make_unique(IntId(6)))) + .evaluate(blocks.numericBlocks, 1)); +expectedResult = {blocks.nb3, blocks.nb4}; +EXPECT_EQ( + expectedResult, + NotExpression(std::make_unique( + std::make_unique(DoubleId(42.0)), + std::make_unique(IntId(48)))) + .evaluate(blocks.numericBlocks, 0)); +expectedResult = {blocks.nb1, blocks.nb2, blocks.nb3, blocks.nb5}; +EXPECT_EQ(expectedResult, + NotExpression( + std::make_unique(std::make_unique( + std::make_unique(DoubleId(42.25)), + std::make_unique(IntId(51))))) + .evaluate(blocks.numericBlocks, 0)); } + +*/ From af171811deea7320e829926348ec0a00c46f2056 Mon Sep 17 00:00:00 2001 From: realHannes Date: Thu, 26 Sep 2024 17:16:06 +0200 Subject: [PATCH 29/50] mark method with override --- src/index/CompressedBlockPrefiltering.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/index/CompressedBlockPrefiltering.h b/src/index/CompressedBlockPrefiltering.h index 613fd5c9c2..60eed61d8d 100644 --- a/src/index/CompressedBlockPrefiltering.h +++ b/src/index/CompressedBlockPrefiltering.h @@ -118,7 +118,7 @@ class NotExpression : public PrefilterExpression { explicit NotExpression(std::unique_ptr child) : child_(child->logicalComplement()) {} - std::unique_ptr logicalComplement() const; + std::unique_ptr logicalComplement() const override; private: std::vector evaluateImpl( From 5af6138e8955896106c548fda5b10a819d503125 Mon Sep 17 00:00:00 2001 From: realHannes Date: Tue, 8 Oct 2024 10:59:39 +0200 Subject: [PATCH 30/50] make further changes to prefiltering procedure and its tests --- src/global/ValueId.h | 2 +- src/index/CompressedBlockPrefiltering.cpp | 140 ++- src/index/CompressedBlockPrefiltering.h | 5 - src/index/CompressedRelation.h | 9 + test/CompressedBlockPrefilteringTest.cpp | 1083 +++++++++++---------- 5 files changed, 681 insertions(+), 558 deletions(-) diff --git a/src/global/ValueId.h b/src/global/ValueId.h index fe429b98fa..e344fe4f52 100644 --- a/src/global/ValueId.h +++ b/src/global/ValueId.h @@ -29,9 +29,9 @@ enum struct Datatype { VocabIndex, LocalVocabIndex, TextRecordIndex, + WordVocabIndex, Date, GeoPoint, - WordVocabIndex, BlankNodeIndex, MaxValue = BlankNodeIndex // Note: Unfortunately we cannot easily get the size of an enum. diff --git a/src/index/CompressedBlockPrefiltering.cpp b/src/index/CompressedBlockPrefiltering.cpp index 73c341c06b..801d713e05 100644 --- a/src/index/CompressedBlockPrefiltering.cpp +++ b/src/index/CompressedBlockPrefiltering.cpp @@ -31,9 +31,9 @@ static auto getIdFromColumnIndex(const BlockMetadata::PermutedTriple& triple, // position (column index) defined by `ignoreIndex`. The ignored positions are // filled with Ids `Id::makeUndefined()`. `Id::makeUndefined()` is guaranteed // to be smaller than Ids of all other types. -static auto getTiedMaskedPermutedTriple( - const BlockMetadata::PermutedTriple& triple, size_t ignoreIndex = 3) { - const auto undefined = Id::makeUndefined(); +static auto getTiedMaskedTriple(const BlockMetadata::PermutedTriple& triple, + size_t ignoreIndex = 3) { + static const auto undefined = Id::makeUndefined(); switch (ignoreIndex) { case 3: return std::tie(triple.col0Id_, triple.col1Id_, triple.col2Id_); @@ -63,21 +63,74 @@ static auto checkEvalRequirements(const std::vector& input, if (block1 == block2) { throwRuntimeError("The provided data blocks must be unique."); } - const auto& triple1 = block1.lastTriple_; - const auto& triple2 = block2.firstTriple_; - if (getIdFromColumnIndex(triple1, evaluationColumn) > - getIdFromColumnIndex(triple2, evaluationColumn)) { - throwRuntimeError("The data blocks must be provided in sorted order."); - } - if (getTiedMaskedPermutedTriple(triple1, evaluationColumn) != - getTiedMaskedPermutedTriple(triple2, evaluationColumn)) { + const auto& triple1First = block1.firstTriple_; + const auto& triple1Last = block1.lastTriple_; + const auto& triple2First = block2.firstTriple_; + const auto& triple2Last = block2.lastTriple_; + + const auto checkInvalid = [triple1First, triple1Last, triple2First, + triple2Last](auto comp, size_t evalCol) { + const auto& maskedT1Last = getTiedMaskedTriple(triple1Last, evalCol); + const auto& maskedT2First = getTiedMaskedTriple(triple2First, evalCol); + return comp(getTiedMaskedTriple(triple1First, evalCol), maskedT1Last) || + comp(maskedT1Last, maskedT2First) || + comp(maskedT2First, getTiedMaskedTriple(triple2Last, evalCol)); + }; + // Check for equality of IDs up to the evaluation column. + if (checkInvalid(std::not_equal_to<>{}, evaluationColumn)) { throwRuntimeError( "The columns up to the evaluation column must contain the same " "values."); } + // Check for order of IDs on evaluation Column. + if (checkInvalid(std::greater<>{}, evaluationColumn + 1)) { + throwRuntimeError( + "The data blocks must be provided in sorted order regarding the " + "evaluation column."); + } } }; +//______________________________________________________________________________ +// Given two sorted `vector`s containing `BlockMetadata`, this function +// returns their merged `BlockMetadata` content in a `vector` which is free of +// duplicates and ordered. +static auto getSetUnion(const std::vector& blocks1, + const std::vector& blocks2, + size_t evalCol) { + evalCol += 1; + std::vector mergedVectors; + mergedVectors.reserve(blocks1.size() + blocks2.size()); + auto blockLessThanBlock = [evalCol](const BlockMetadata& block1, + const BlockMetadata& block2) { + const auto& lastTripleBlock1 = + getTiedMaskedTriple(block1.lastTriple_, evalCol); + const auto& firstTripleBlock2 = + getTiedMaskedTriple(block2.firstTriple_, evalCol); + // Given two blocks with the respective ranges [-4, -4] (block1) + // and [-4, 0.0] (block2) on the evaluation column, we see that [-4, -4] < + // [-4, 0.0] should hold true. To assess if block1 is less than block2, it + // is not enough to just compare -4 (ID from last triple of block1) to -4 + // (ID from first triple of block2). With this simple comparison [-4, -4] < + // [-4, 0.0] wouldn't hold, because the comparison -4 < -4 will yield false. + // Thus for proper implementation of the < operator, we have to check here + // on the IDs of the last triple. -4 < 0.0 will yield true => [-4, -4] < + // [-4, 0.0] holds true. + if (lastTripleBlock1 == firstTripleBlock2) { + return lastTripleBlock1 < + getTiedMaskedTriple(block2.lastTriple_, evalCol); + } else { + return lastTripleBlock1 < firstTripleBlock2; + } + }; + // Given that we have vectors with sorted (BlockMedata) values, we can + // use std::ranges::set_union. Thus the complexity is O(n + m). + std::ranges::set_union(blocks1, blocks2, std::back_inserter(mergedVectors), + blockLessThanBlock); + mergedVectors.shrink_to_fit(); + return mergedVectors; +} + // SECTION PREFILTER EXPRESSION (BASE CLASS) //______________________________________________________________________________ std::vector PrefilterExpression::evaluate( @@ -127,13 +180,31 @@ std::vector RelationalExpression::evaluateImpl( // For each BlockMetadata value in vector input, we have a respective Id for // firstTriple and lastTriple valueIdsInput.reserve(2 * input.size()); + std::vector mixedDatatypeBlocks; for (const auto& block : input) { - valueIdsInput.push_back( - getIdFromColumnIndex(block.firstTriple_, evaluationColumn)); - valueIdsInput.push_back( - getIdFromColumnIndex(block.lastTriple_, evaluationColumn)); + const auto firstId = + getIdFromColumnIndex(block.firstTriple_, evaluationColumn); + const auto secondId = + getIdFromColumnIndex(block.lastTriple_, evaluationColumn); + const auto firstIdDatatype = firstId.getDatatype(); + const auto secondIdDatatype = secondId.getDatatype(); + const auto referenceIdDatatype = referenceId_.getDatatype(); + valueIdsInput.push_back(firstId); + valueIdsInput.push_back(secondId); + + if ((firstIdDatatype != secondIdDatatype) && + // The block is only potentially relevant if at least one of the + // respective bounding Ids is of similar (comparable) type to + // refernceId_. + (valueIdComparators::detail::areTypesCompatible(referenceIdDatatype, + firstIdDatatype) || + valueIdComparators::detail::areTypesCompatible(referenceIdDatatype, + secondIdDatatype))) { + mixedDatatypeBlocks.push_back(block); + } } + // Use getRangesForId (from valueIdComparators) to extract the ranges // containing the relevant ValueIds. // For pre-filtering with CompOp::EQ, we have to consider empty ranges. @@ -146,11 +217,13 @@ std::vector RelationalExpression::evaluateImpl( referenceId_, Comparison) : getRangesForId(valueIdsInput.begin(), valueIdsInput.end(), referenceId_, Comparison, false); - // The vector for relevant BlockMetadata values that contain ValueIds defined - // as relevant by relevantIdRanges. + + // The vector for relevant BlockMetadata values which contain ValueIds + // defined as relevant by relevantIdRanges. std::vector relevantBlocks; // Reserve memory, input.size() is upper bound. relevantBlocks.reserve(input.size()); + // Given the relevant Id ranges, retrieve the corresponding relevant // BlockMetadata values from vector input and add them to the relevantBlocks // vector. @@ -169,7 +242,9 @@ std::vector RelationalExpression::evaluateImpl( std::distance(valueIdsInput.begin(), secondIdAdjusted) / 2); } relevantBlocks.shrink_to_fit(); - return relevantBlocks; + // Merge mixedDatatypeBlocks into relevantBlocks while maintaing order and + // avoiding dublicates. + return getSetUnion(relevantBlocks, mixedDatatypeBlocks, evaluationColumn); }; // SECTION LOGICAL OPERATIONS @@ -200,31 +275,6 @@ std::unique_ptr NotExpression::logicalComplement() const { return child_->logicalComplement(); }; -//______________________________________________________________________________ -template -std::vector LogicalExpression::evaluateOrImpl( - const std::vector& input, size_t evaluationColumn) const - requires(Operation == LogicalOperators::OR) { - auto relevantBlocksChild1 = child1_->evaluate(input, evaluationColumn); - auto relevantBlocksChild2 = child2_->evaluate(input, evaluationColumn); - - std::vector unionedRelevantBlocks; - unionedRelevantBlocks.reserve(relevantBlocksChild1.size() + - relevantBlocksChild2.size()); - - auto blockLessThanBlock = [](const BlockMetadata& block1, - const BlockMetadata& block2) { - return getTiedMaskedPermutedTriple(block1.lastTriple_) < - getTiedMaskedPermutedTriple(block2.firstTriple_); - }; - // Given that we have vectors with sorted (BlockMedata) values, we can - // use std::ranges::set_union, thus the complexity is O(n + m). - std::ranges::set_union(relevantBlocksChild1, relevantBlocksChild2, - std::back_inserter(unionedRelevantBlocks), - blockLessThanBlock); - return unionedRelevantBlocks; -}; - //______________________________________________________________________________ template std::vector LogicalExpression::evaluateImpl( @@ -235,7 +285,9 @@ std::vector LogicalExpression::evaluateImpl( return child2_->evaluate(resultChild1, evaluationColumn); } else { static_assert(Operation == OR); - return evaluateOrImpl(input, evaluationColumn); + return getSetUnion(child1_->evaluate(input, evaluationColumn), + child2_->evaluate(input, evaluationColumn), + evaluationColumn); } }; diff --git a/src/index/CompressedBlockPrefiltering.h b/src/index/CompressedBlockPrefiltering.h index 60eed61d8d..75960c05ec 100644 --- a/src/index/CompressedBlockPrefiltering.h +++ b/src/index/CompressedBlockPrefiltering.h @@ -99,11 +99,6 @@ class LogicalExpression : public PrefilterExpression { std::unique_ptr logicalComplement() const override; private: - // Introduce a helper method for the specification LogicalOperators::OR - std::vector evaluateOrImpl( - const std::vector& input, size_t evaluationColumn) const - requires(Operation == LogicalOperators::OR); - std::vector evaluateImpl( const std::vector& input, size_t evaluationColumn) const override; diff --git a/src/index/CompressedRelation.h b/src/index/CompressedRelation.h index 231301e0ae..9038646de6 100644 --- a/src/index/CompressedRelation.h +++ b/src/index/CompressedRelation.h @@ -105,6 +105,15 @@ struct CompressedBlockMetadata { // Two of these are equal if all members are equal. bool operator==(const CompressedBlockMetadata&) const = default; + + // Format BlockMetadata contents for debugging. + friend std::ostream& operator<<( + std::ostream& str, const CompressedBlockMetadata& blockMetadata) { + str << "#BlockMetadata\n(first) " << blockMetadata.firstTriple_ << "(last) " + << blockMetadata.lastTriple_ << "num. rows: " << blockMetadata.numRows_ + << "." << std::endl; + return str; + } }; // Serialization of the `OffsetAndcompressedSize` subclass. diff --git a/test/CompressedBlockPrefilteringTest.cpp b/test/CompressedBlockPrefilteringTest.cpp index 69eea2c2c6..56edbe0684 100644 --- a/test/CompressedBlockPrefilteringTest.cpp +++ b/test/CompressedBlockPrefilteringTest.cpp @@ -12,343 +12,425 @@ using ad_utility::testing::DoubleId; using ad_utility::testing::IntId; +using ad_utility::testing::LocalVocabId; using ad_utility::testing::UndefId; +using ad_utility::testing::VocabId; using sparqlExpression::TestContext; using namespace prefilterExpressions; //______________________________________________________________________________ struct MetadataBlocks { - // Define five blocks, for convenience all of their columns are sorted on - // their data when appended in the given order to a vector. - // Columns indexed: COLUMN 0 | COLUMN 1 | COLUMN 2 - BlockMetadata nb1{{}, - 0, - {IntId(16), IntId(0), DoubleId(0.0)}, // firstTriple - {IntId(38), IntId(0), DoubleId(12.5)}}; // lastTriple - BlockMetadata nb2{{}, - 0, - {IntId(42), IntId(0), DoubleId(12.5)}, - {IntId(42), IntId(2), DoubleId(14.575)}}; - BlockMetadata nb3{{}, - 0, - {IntId(42), IntId(2), DoubleId(16.33)}, - {IntId(45), IntId(2), DoubleId(18.32)}}; - BlockMetadata nb4{{}, - 0, - {IntId(46), IntId(2), DoubleId(22.29)}, - {IntId(47), IntId(6), DoubleId(111.223)}}; - BlockMetadata nb5{{}, - 0, - {IntId(48), IntId(6), DoubleId(111.333)}, - {IntId(51), IntId(6), DoubleId(112.00)}}; - std::vector numericBlocks = {nb1, nb2, nb3, nb4, nb5}; -}; -/* - -//____________________________________________________________________________ -// Define Blocks containing VocabIds -// Use TestContext from SparqlExpressionTestHelpers.h -sparqlExpression::TestContext tc{}; -Id Undef = UndefId(); -// Columns indexed: COLUMN 0 | COLUMN 1 | COLUMN 2 -// undef. | LocalVocab | undef. -BlockMetadata vb1{{}, 0, {Undef, tc.bonn, Undef}, {Undef, tc.cologne, Undef}}; -BlockMetadata vb2{ - {}, 0, {Undef, tc.dortmund, Undef}, {Undef, tc.essen, Undef}}; -BlockMetadata vb3{ - {}, 0, {Undef, tc.frankfurt, Undef}, {Undef, tc.frankfurt, Undef}}; -BlockMetadata vb4{ - {}, 0, {Undef, tc.hamburg, Undef}, {Undef, tc.karlsruhe, Undef}}; -BlockMetadata vb5{ - {}, 0, {Undef, tc.karlsruhe, Undef}, {Undef, tc.karlsruhe, Undef}}; -std::vector vocabBlocks = {vb1, vb2, vb3, vb4, vb5}; + /* + Our pre-filtering procedure expects blocks that are in correct (ascending) + order w.r.t. their contained ValueIds given the first and last triple. + + The correct order of the ValueIds is dependent on their type and underlying + representation. + + Short overview on the ascending order logic for the underlying values: + Order ValueIds for (signed) integer values - [0... max, -max... -1] + Order ValueIds for (signed) doubles values - [0.0... max, -0.0... -max] + Order ValueIds for Vocab and LocalVocab values given the vocabulary with + indices (up to N) - [VocabId(0), .... VocabId(N)] + + COLUMN 0 and COLUMN 1 contain fixed values, this is a necessary condition + that is also checked during the pre-filtering procedure. The actual evaluation + column (we filter w.r.t. values of COLUMN 2) contains mixed types. + */ + + // Values for fixed columns + const Id VocabId10 = VocabId(10); + const Id DoubleId33 = DoubleId(33); + + auto makeBlock(const ValueId& firstId, const ValueId& lastId) + -> BlockMetadata { + return {{}, + 0, + // COLUMN 0 | COLUMN 1 | COLUMN 2 + {VocabId10, DoubleId33, firstId}, // firstTriple + {VocabId10, DoubleId33, lastId}, // lastTriple + {}, + false}; + }; + + BlockMetadata b1 = makeBlock(IntId(0), IntId(0)); + BlockMetadata b2 = makeBlock(IntId(0), IntId(5)); + BlockMetadata b3 = makeBlock(IntId(5), IntId(6)); + BlockMetadata b4 = makeBlock(IntId(8), IntId(9)); + BlockMetadata b5 = makeBlock(IntId(-10), IntId(-8)); + BlockMetadata b6 = makeBlock(IntId(-4), IntId(-4)); + // b7 contains mixed datatypes in COLUMN 2 + BlockMetadata b7 = makeBlock(IntId(-4), DoubleId(2)); + BlockMetadata b8 = makeBlock(DoubleId(2), DoubleId(2)); + BlockMetadata b9 = makeBlock(DoubleId(4), DoubleId(4)); + BlockMetadata b10 = makeBlock(DoubleId(4), DoubleId(10)); + BlockMetadata b11 = makeBlock(DoubleId(-1.23), DoubleId(-6.25)); + BlockMetadata b12 = makeBlock(DoubleId(-6.25), DoubleId(-6.25)); + BlockMetadata b13 = makeBlock(DoubleId(-10.42), DoubleId(-12.00)); + // b14 contains mixed datatypes + BlockMetadata b14 = makeBlock(DoubleId(-14.01), VocabId(0)); + BlockMetadata b15 = makeBlock(VocabId(10), VocabId(14)); + BlockMetadata b16 = makeBlock(VocabId(14), VocabId(14)); + BlockMetadata b17 = makeBlock(VocabId(14), VocabId(17)); + std::vector blocks = {b1, b2, b3, b4, b5, b6, + b7, b8, b9, b10, b11, b12, + b13, b14, b15, b16, b17}; + + // The following blocks will be swapped with their respective correct block, + // to test if the method evaluate checks the pre-conditions properly. + BlockMetadata b1_1{{}, + 0, + {VocabId10, DoubleId33, IntId(0)}, + {VocabId10, DoubleId(22), IntId(0)}, + {}, + false}; + std::vector blocksInvalidCol1 = {b1_1, b2, b3, b4, b5, b6, + b7, b8, b9, b10, b11, b12, + b13, b14, b15, b16, b17}; + BlockMetadata b5_1{{}, + 0, + {VocabId(11), DoubleId33, IntId(-10)}, + {VocabId10, DoubleId33, IntId(-8)}, + {}, + false}; + std::vector blocksInvalidCol02 = { + b1, b2, b3, b4, b5_1, b6, b7, b8, b9, + b10, b11, b12, b13, b14, b15, b16, b17}; + std::vector blocksInvalidOrder1 = { + b2, b1, b3, b4, b5, b6, b7, b8, b9, + b10, b11, b12, b13, b14, b15, b16, b17}; + std::vector blocksInvalidOrder2 = { + b1, b2, b3, b4, b5, b6, b7, b8, b9, + b10, b11, b12, b14, b13, b15, b16, b17}; + std::vector blocksWithDuplicate1 = { + b1, b1, b2, b3, b4, b5, b6, b7, b8, + b9, b10, b11, b12, b13, b14, b15, b16, b17}; + std::vector blocksWithDuplicate2 = { + b1, b2, b3, b4, b5, b6, b7, b8, b9, + b10, b11, b12, b13, b14, b15, b16, b17, b17}; }; // Static tests, they focus on corner case values for the given block triples. //______________________________________________________________________________ //______________________________________________________________________________ -// Test Relational Expressions +// Test PrefilterExpressions + +template +struct check_is : std::false_type {}; + +template +struct check_is> : std::true_type {}; +template +struct check_is> : std::true_type {}; + +template +constexpr bool check_is_logical_v = check_is::value; +template +constexpr bool check_is_relational_v = check_is::value; + +//______________________________________________________________________________ +static const auto testThrowError = + [](std::unique_ptr expression, size_t evaluationColumn, + const std::vector& input, + const std::string& expectedErrorMessage) { + try { + expression->evaluate(input, evaluationColumn); + FAIL() << "Expected thrown error message."; + } catch (const std::runtime_error& error) { + EXPECT_EQ(std::string(error.what()), expectedErrorMessage); + } catch (...) { + FAIL() << "Expected an error of type std::runtime_error, but a " + "different error was thrown."; + } + }; + +//______________________________________________________________________________ +template +struct TestRelationalExpression { + void operator()(size_t evaluationColumn, ValueId referenceId, + const std::vector& input, const ResT& expected) + requires check_is_relational_v { + auto expression = std::make_unique(referenceId); + if constexpr (std::is_same_v) { + testThrowError(std::move(expression), evaluationColumn, input, expected); + } else { + static_assert(std::is_same_v>); + EXPECT_EQ(expression->evaluate(input, evaluationColumn), expected); + } + } +}; + +//______________________________________________________________________________ +template +struct TestLogicalExpression { + template + void test(size_t evaluationColumn, ValueId referenceIdChild1, + ValueId referenceIdChild2, const std::vector& input, + const ResT& expected) + requires(check_is_logical_v && check_is_relational_v && + check_is_relational_v) { + auto expression = std::make_unique( + std::make_unique(referenceIdChild1), + std::make_unique(referenceIdChild2)); + if constexpr (std::is_same_v) { + testThrowError(std::move(expression), evaluationColumn, input, expected); + } else { + static_assert(std::is_same_v>); + EXPECT_EQ(expression->evaluate(input, evaluationColumn), expected); + } + } +}; + +//______________________________________________________________________________ +template +struct TestNotExpression { + template + void test(size_t evaluationColumn, ValueId referenceIdChild, + const std::vector& input, const ResT& expected) + requires check_is_relational_v { + auto expression = std::make_unique( + std::make_unique(referenceIdChild)); + if constexpr (std::is_same_v) { + testThrowError(std::move(expression), evaluationColumn, input, expected); + } else { + static_assert(std::is_same_v>); + EXPECT_EQ(expression->evaluate(input, evaluationColumn), expected); + } + } +}; +//______________________________________________________________________________ // Test LessThanExpression TEST(RelationalExpression, testLessThanExpressions) { -MetadataBlocks blocks{}; -std::vector expectedResult = {}; -// NUMERIC -EXPECT_EQ(expectedResult, - LessThanExpression{IntId(10)}.evaluate(blocks.numericBlocks, 0)); -EXPECT_EQ(expectedResult, - LessThanExpression{IntId(16)}.evaluate(blocks.numericBlocks, 0)); -expectedResult = {blocks.nb1}; -EXPECT_EQ(expectedResult, - LessThanExpression{IntId(40)}.evaluate(blocks.numericBlocks, 0)); -EXPECT_EQ(expectedResult, - LessThanExpression{IntId(42)}.evaluate(blocks.numericBlocks, 0)); -expectedResult = {blocks.nb1, blocks.nb2, blocks.nb3}; -EXPECT_EQ(expectedResult, - LessThanExpression{IntId(46)}.evaluate(blocks.numericBlocks, 0)); -EXPECT_EQ(blocks.numericBlocks, - LessThanExpression{IntId(100)}.evaluate(blocks.numericBlocks, 0)); -// VOCAB -TestContext tc{}; -expectedResult = {}; -// tc.alpha is an Id of type Vocab, w.r.t. a lexicographical order, all the -// citiy IDs (LocalVocab) should be greater than the ID (Vocab) of "alpha". -// Thus we expect that none of the blocks are relevant. -EXPECT_EQ(expectedResult, - LessThanExpression{tc.alpha}.evaluate(blocks.vocabBlocks, 1)); -EXPECT_EQ(expectedResult, - LessThanExpression{tc.berlin}.evaluate(blocks.vocabBlocks, 1)); -// All cities used within vocabBlocks should be smaller than city "munich" -// (given their respective LocalVocab IDs). -EXPECT_EQ(blocks.vocabBlocks, - LessThanExpression{tc.munich}.evaluate(blocks.vocabBlocks, 1)); -// All blocks from vocabBlocks should contain values less-than the (Vocab) -// ID for "zz". -EXPECT_EQ(blocks.vocabBlocks, - LessThanExpression{tc.zz}.evaluate(blocks.vocabBlocks, 1)); -expectedResult = {blocks.vb1, blocks.vb2}; -EXPECT_EQ(expectedResult, - LessThanExpression{tc.düsseldorf}.evaluate(blocks.vocabBlocks, 1)); -expectedResult = {blocks.vb1, blocks.vb2, blocks.vb3}; -EXPECT_EQ(expectedResult, LessThanExpression{tc.frankfurt_oder}.evaluate( - blocks.vocabBlocks, 1)); -expectedResult = {blocks.vb1, blocks.vb2, blocks.vb3, blocks.vb4}; -EXPECT_EQ(expectedResult, - LessThanExpression{tc.ingolstadt}.evaluate(blocks.vocabBlocks, 1)); + MetadataBlocks blocks{}; + TestRelationalExpression> + testExpression; + testExpression( + 2, IntId(5), blocks.blocks, + {blocks.b1, blocks.b2, blocks.b5, blocks.b6, blocks.b7, blocks.b8, + blocks.b9, blocks.b10, blocks.b11, blocks.b12, blocks.b13, blocks.b14}); + + testExpression(2, IntId(-12), blocks.blocks, {blocks.b7, blocks.b14}); + testExpression(2, IntId(0), blocks.blocks, + {blocks.b5, blocks.b6, blocks.b7, blocks.b11, blocks.b12, + blocks.b13, blocks.b14}); + testExpression(2, IntId(100), blocks.blocks, + {blocks.b1, blocks.b2, blocks.b3, blocks.b4, blocks.b5, + blocks.b6, blocks.b7, blocks.b8, blocks.b9, blocks.b10, + blocks.b11, blocks.b12, blocks.b13, blocks.b14}); + testExpression(2, DoubleId(-3), blocks.blocks, + {blocks.b5, blocks.b6, blocks.b7, blocks.b11, blocks.b12, + blocks.b13, blocks.b14}); + testExpression(2, DoubleId(-14.01), blocks.blocks, {blocks.b7, blocks.b14}); + testExpression(2, DoubleId(-11.22), blocks.blocks, + {blocks.b7, blocks.b13, blocks.b14}); + testExpression( + 2, DoubleId(-4.121), blocks.blocks, + {blocks.b5, blocks.b7, blocks.b11, blocks.b12, blocks.b13, blocks.b14}); + testExpression(2, VocabId(0), blocks.blocks, {blocks.b14}); + testExpression(2, VocabId(12), blocks.blocks, {blocks.b14, blocks.b15}); + testExpression(2, VocabId(14), blocks.blocks, {blocks.b14, blocks.b15}); + testExpression(2, VocabId(16), blocks.blocks, + {blocks.b14, blocks.b15, blocks.b16, blocks.b17}); } //______________________________________________________________________________ // Test LessEqualExpression TEST(RelationalExpression, testLessEqualExpressions) { -MetadataBlocks blocks{}; -std::vector expectedResult = {}; -// NUMERIC -EXPECT_EQ(expectedResult, - LessEqualExpression{IntId(10)}.evaluate(blocks.numericBlocks, 0)); -expectedResult = {blocks.nb1}; -EXPECT_EQ(expectedResult, - LessEqualExpression{IntId(16)}.evaluate(blocks.numericBlocks, 0)); -EXPECT_EQ(expectedResult, - LessEqualExpression{IntId(40)}.evaluate(blocks.numericBlocks, 0)); -expectedResult = {blocks.nb1, blocks.nb2, blocks.nb3}; -EXPECT_EQ(expectedResult, - LessEqualExpression{IntId(42)}.evaluate(blocks.numericBlocks, 0)); -expectedResult = {blocks.nb1, blocks.nb2, blocks.nb3, blocks.nb4}; -EXPECT_EQ(expectedResult, - LessEqualExpression{IntId(46)}.evaluate(blocks.numericBlocks, 0)); -EXPECT_EQ(blocks.numericBlocks, - LessEqualExpression{IntId(100)}.evaluate(blocks.numericBlocks, 0)); -// VOCAB -TestContext tc{}; -expectedResult = {}; -EXPECT_EQ(expectedResult, - LessEqualExpression{tc.alpha}.evaluate(blocks.vocabBlocks, 1)); -EXPECT_EQ(expectedResult, - LessEqualExpression{tc.berlin}.evaluate(blocks.vocabBlocks, 1)); -expectedResult = {blocks.vb1, blocks.vb2}; -EXPECT_EQ(expectedResult, - LessEqualExpression{tc.dortmund}.evaluate(blocks.vocabBlocks, 1)); -expectedResult = {blocks.vb1, blocks.vb2, blocks.vb3, blocks.vb4}; -EXPECT_EQ(expectedResult, - LessEqualExpression{tc.hannover}.evaluate(blocks.vocabBlocks, 1)); -EXPECT_EQ(blocks.vocabBlocks, - LessEqualExpression{tc.karlsruhe}.evaluate(blocks.vocabBlocks, 1)); + MetadataBlocks blocks{}; + TestRelationalExpression> + testExpression; + testExpression(2, IntId(0), blocks.blocks, + {blocks.b1, blocks.b2, blocks.b5, blocks.b6, blocks.b7, + blocks.b11, blocks.b12, blocks.b13, blocks.b14}); + testExpression( + 2, IntId(-6), blocks.blocks, + {blocks.b5, blocks.b7, blocks.b11, blocks.b12, blocks.b13, blocks.b14}); + testExpression(2, IntId(7), blocks.blocks, + {blocks.b1, blocks.b2, blocks.b3, blocks.b5, blocks.b6, + blocks.b7, blocks.b8, blocks.b9, blocks.b10, blocks.b11, + blocks.b12, blocks.b13, blocks.b14}); + testExpression(2, IntId(-9), blocks.blocks, + {blocks.b5, blocks.b7, blocks.b13, blocks.b14}); + testExpression(2, DoubleId(-9.131), blocks.blocks, + {blocks.b5, blocks.b7, blocks.b13, blocks.b14}); + testExpression(2, DoubleId(1.1415), blocks.blocks, + {blocks.b1, blocks.b2, blocks.b5, blocks.b6, blocks.b7, + blocks.b11, blocks.b12, blocks.b13, blocks.b14}); + testExpression(2, DoubleId(3.1415), blocks.blocks, + {blocks.b1, blocks.b2, blocks.b5, blocks.b6, blocks.b7, + blocks.b8, blocks.b11, blocks.b12, blocks.b13, blocks.b14}); + testExpression(2, DoubleId(-11.99999999999999), blocks.blocks, + {blocks.b7, blocks.b13, blocks.b14}); + testExpression(2, DoubleId(-14.03), blocks.blocks, {blocks.b7, blocks.b14}); + testExpression(2, VocabId(0), blocks.blocks, {blocks.b14}); + testExpression(2, VocabId(11), blocks.blocks, {blocks.b14, blocks.b15}); + testExpression(2, VocabId(14), blocks.blocks, + {blocks.b14, blocks.b15, blocks.b16, blocks.b17}); } //______________________________________________________________________________ // Test GreaterThanExpression TEST(RelationalExpression, testGreaterThanExpression) { -MetadataBlocks blocks{}; -EXPECT_EQ(blocks.numericBlocks, - GreaterThanExpression{IntId(10)}.evaluate(blocks.numericBlocks, 0)); -EXPECT_EQ(blocks.numericBlocks, - GreaterThanExpression{IntId(16)}.evaluate(blocks.numericBlocks, 0)); -std::vector expectedResult = {blocks.nb2, blocks.nb3, - blocks.nb4, blocks.nb5}; -EXPECT_EQ(expectedResult, - GreaterThanExpression{IntId(38)}.evaluate(blocks.numericBlocks, 0)); -expectedResult = {blocks.nb3, blocks.nb4, blocks.nb5}; -EXPECT_EQ(expectedResult, - GreaterThanExpression{IntId(42)}.evaluate(blocks.numericBlocks, 0)); -expectedResult = {blocks.nb4, blocks.nb5}; -EXPECT_EQ(expectedResult, - GreaterThanExpression{IntId(46)}.evaluate(blocks.numericBlocks, 0)); -expectedResult = {}; -EXPECT_EQ(expectedResult, - GreaterThanExpression{IntId(52)}.evaluate(blocks.numericBlocks, 0)); -// VOCAB -TestContext tc{}; -expectedResult = {}; -EXPECT_EQ(expectedResult, - GreaterThanExpression{tc.munich}.evaluate(blocks.vocabBlocks, 1)); -EXPECT_EQ(expectedResult, GreaterThanExpression{tc.karlsruhe}.evaluate( - blocks.vocabBlocks, 1)); -EXPECT_EQ(blocks.vocabBlocks, - GreaterThanExpression{tc.alpha}.evaluate(blocks.vocabBlocks, 1)); -expectedResult = {blocks.vb4, blocks.vb5}; -EXPECT_EQ(expectedResult, - GreaterThanExpression{tc.hamburg}.evaluate(blocks.vocabBlocks, 1)); -expectedResult = {blocks.vb4, blocks.vb5}; -EXPECT_EQ(expectedResult, - GreaterThanExpression{tc.hannover}.evaluate(blocks.vocabBlocks, 1)); -expectedResult = {blocks.vb2, blocks.vb3, blocks.vb4, blocks.vb5}; -EXPECT_EQ(expectedResult, GreaterThanExpression{tc.düsseldorf}.evaluate( - blocks.vocabBlocks, 1)); + MetadataBlocks blocks{}; + TestRelationalExpression> + testExpression; + testExpression(2, DoubleId(5.5375), blocks.blocks, + {blocks.b3, blocks.b4, blocks.b7, blocks.b10, blocks.b14}); + testExpression(2, DoubleId(9.9994), blocks.blocks, + {blocks.b7, blocks.b10, blocks.b14}); + testExpression( + 2, IntId(-5), blocks.blocks, + {blocks.b1, blocks.b2, blocks.b3, blocks.b4, blocks.b6, blocks.b7, + blocks.b8, blocks.b9, blocks.b10, blocks.b11, blocks.b14}); + testExpression( + 2, DoubleId(-5.5375), blocks.blocks, + {blocks.b1, blocks.b2, blocks.b3, blocks.b4, blocks.b6, blocks.b7, + blocks.b8, blocks.b9, blocks.b10, blocks.b11, blocks.b14}); + testExpression( + 2, DoubleId(-6.2499999), blocks.blocks, + {blocks.b1, blocks.b2, blocks.b3, blocks.b4, blocks.b6, blocks.b7, + blocks.b8, blocks.b9, blocks.b10, blocks.b11, blocks.b14}); + testExpression(2, IntId(1), blocks.blocks, + {blocks.b2, blocks.b3, blocks.b4, blocks.b7, blocks.b8, + blocks.b9, blocks.b10, blocks.b14}); + testExpression(2, IntId(3), blocks.blocks, + {blocks.b2, blocks.b3, blocks.b4, blocks.b7, blocks.b9, + blocks.b10, blocks.b14}); + testExpression( + 2, IntId(4), blocks.blocks, + {blocks.b2, blocks.b3, blocks.b4, blocks.b7, blocks.b10, blocks.b14}); + testExpression(2, IntId(-4), blocks.blocks, + {blocks.b1, blocks.b2, blocks.b3, blocks.b4, blocks.b7, + blocks.b8, blocks.b9, blocks.b10, blocks.b11, blocks.b14}); + testExpression(2, IntId(33), blocks.blocks, {blocks.b7, blocks.b14}); + testExpression(2, VocabId(22), blocks.blocks, {blocks.b14}); + testExpression(2, VocabId(14), blocks.blocks, {blocks.b14, blocks.b17}); + testExpression(2, VocabId(12), blocks.blocks, + {blocks.b14, blocks.b15, blocks.b16, blocks.b17}); } //______________________________________________________________________________ // Test GreaterEqualExpression TEST(RelationalExpression, testGreaterEqualExpression) { -MetadataBlocks blocks{}; -EXPECT_EQ(blocks.numericBlocks, GreaterEqualExpression{IntId(10)}.evaluate( - blocks.numericBlocks, 0)); -EXPECT_EQ(blocks.numericBlocks, GreaterEqualExpression{IntId(38)}.evaluate( - blocks.numericBlocks, 0)); -std::vector expectedResult = {blocks.nb2, blocks.nb3, - blocks.nb4, blocks.nb5}; -EXPECT_EQ(expectedResult, GreaterEqualExpression{IntId(40)}.evaluate( - blocks.numericBlocks, 0)); -EXPECT_EQ(expectedResult, GreaterEqualExpression{IntId(42)}.evaluate( - blocks.numericBlocks, 0)); -expectedResult = {blocks.nb3, blocks.nb4, blocks.nb5}; -EXPECT_EQ(expectedResult, GreaterEqualExpression{IntId(45)}.evaluate( - blocks.numericBlocks, 0)); -expectedResult = {blocks.nb4, blocks.nb5}; -EXPECT_EQ(expectedResult, GreaterEqualExpression{IntId(47)}.evaluate( - blocks.numericBlocks, 0)); -expectedResult = {}; -EXPECT_EQ(expectedResult, GreaterEqualExpression{IntId(100)}.evaluate( - blocks.numericBlocks, 0)); -// VOCAB -TestContext tc{}; -EXPECT_EQ(blocks.vocabBlocks, - GreaterEqualExpression{tc.alpha}.evaluate(blocks.vocabBlocks, 1)); -EXPECT_EQ(blocks.vocabBlocks, - GreaterEqualExpression{tc.bonn}.evaluate(blocks.vocabBlocks, 1)); -EXPECT_EQ(blocks.vocabBlocks, - GreaterEqualExpression{tc.cologne}.evaluate(blocks.vocabBlocks, 1)); -expectedResult = {blocks.vb2, blocks.vb3, blocks.vb4, blocks.vb5}; -EXPECT_EQ(expectedResult, GreaterEqualExpression{tc.düsseldorf}.evaluate( - blocks.vocabBlocks, 1)); -expectedResult = {blocks.vb4, blocks.vb5}; -EXPECT_EQ(expectedResult, GreaterEqualExpression{tc.frankfurt_oder}.evaluate( - blocks.vocabBlocks, 1)); -EXPECT_EQ(expectedResult, GreaterEqualExpression{tc.karlsruhe}.evaluate( - blocks.vocabBlocks, 1)); -expectedResult = {}; -EXPECT_EQ(expectedResult, - GreaterEqualExpression{tc.munich}.evaluate(blocks.vocabBlocks, 1)); -EXPECT_EQ(expectedResult, - GreaterEqualExpression{tc.zz}.evaluate(blocks.vocabBlocks, 1)); + MetadataBlocks blocks{}; + TestRelationalExpression> + testExpression; + testExpression(2, IntId(0), blocks.blocks, + {blocks.b1, blocks.b2, blocks.b3, blocks.b4, blocks.b7, + blocks.b8, blocks.b9, blocks.b10, blocks.b14}); + testExpression(2, IntId(8), blocks.blocks, + {blocks.b4, blocks.b7, blocks.b10, blocks.b14}); + testExpression(2, DoubleId(9.98), blocks.blocks, + {blocks.b7, blocks.b10, blocks.b14}); + testExpression(2, IntId(-3), blocks.blocks, + {blocks.b1, blocks.b2, blocks.b3, blocks.b4, blocks.b7, + blocks.b8, blocks.b9, blocks.b10, blocks.b11, blocks.b14}); + testExpression(2, IntId(-10), blocks.blocks, + {blocks.b1, blocks.b2, blocks.b3, blocks.b4, blocks.b5, + blocks.b6, blocks.b7, blocks.b8, blocks.b9, blocks.b10, + blocks.b11, blocks.b12, blocks.b14}); + testExpression(2, DoubleId(-3.1415), blocks.blocks, + {blocks.b1, blocks.b2, blocks.b3, blocks.b4, blocks.b7, + blocks.b8, blocks.b9, blocks.b10, blocks.b11, blocks.b14}); + testExpression( + 2, DoubleId(-4.000001), blocks.blocks, + {blocks.b1, blocks.b2, blocks.b3, blocks.b4, blocks.b6, blocks.b7, + blocks.b8, blocks.b9, blocks.b10, blocks.b11, blocks.b14}); + testExpression(2, DoubleId(10.000), blocks.blocks, + {blocks.b7, blocks.b10, blocks.b14}); + testExpression(2, DoubleId(-15.22), blocks.blocks, + {blocks.b1, blocks.b2, blocks.b3, blocks.b4, blocks.b5, + blocks.b6, blocks.b7, blocks.b8, blocks.b9, blocks.b10, + blocks.b11, blocks.b12, blocks.b13, blocks.b14}); + testExpression(2, DoubleId(7.999999), blocks.blocks, + {blocks.b4, blocks.b7, blocks.b10, blocks.b14}); + testExpression(2, DoubleId(10.0001), blocks.blocks, {blocks.b7, blocks.b14}); + testExpression(2, VocabId(14), blocks.blocks, + {blocks.b14, blocks.b15, blocks.b16, blocks.b17}); + testExpression(2, VocabId(10), blocks.blocks, + {blocks.b14, blocks.b15, blocks.b16, blocks.b17}); + testExpression(2, VocabId(17), blocks.blocks, {blocks.b14, blocks.b17}); } //______________________________________________________________________________ // Test EqualExpression TEST(RelationalExpression, testEqualExpression) { -MetadataBlocks blocks{}; -std::vector expectedResult = {}; -EXPECT_EQ(expectedResult, - EqualExpression{IntId(10)}.evaluate(blocks.numericBlocks, 1)); -EXPECT_EQ(expectedResult, - EqualExpression{IntId(10)}.evaluate(blocks.numericBlocks, 0)); -expectedResult = {blocks.nb4}; -EXPECT_EQ(expectedResult, - EqualExpression{IntId(5)}.evaluate(blocks.numericBlocks, 1)); -expectedResult = {blocks.nb2, blocks.nb3, blocks.nb4}; -EXPECT_EQ(expectedResult, - EqualExpression{IntId(2)}.evaluate(blocks.numericBlocks, 1)); -expectedResult = {blocks.nb4, blocks.nb5}; -EXPECT_EQ(expectedResult, - EqualExpression{IntId(6)}.evaluate(blocks.numericBlocks, 1)); -expectedResult = {blocks.nb2, blocks.nb3}; -EXPECT_EQ(expectedResult, - EqualExpression{IntId(42)}.evaluate(blocks.numericBlocks, 0)); -expectedResult = {blocks.nb5}; -EXPECT_EQ(expectedResult, - EqualExpression{IntId(112)}.evaluate(blocks.numericBlocks, 2)); -// VOCAB -TestContext tc{}; -expectedResult = {}; -EXPECT_EQ(expectedResult, - EqualExpression{tc.zz}.evaluate(blocks.vocabBlocks, 1)); -EXPECT_EQ(expectedResult, - EqualExpression{tc.alpha}.evaluate(blocks.vocabBlocks, 1)); -EXPECT_EQ(expectedResult, - EqualExpression{tc.munich}.evaluate(blocks.vocabBlocks, 1)); -EXPECT_EQ(expectedResult, - EqualExpression{tc.frankfurt_oder}.evaluate(blocks.vocabBlocks, 1)); -expectedResult = {blocks.vb4, blocks.vb5}; -EXPECT_EQ(expectedResult, - EqualExpression{tc.karlsruhe}.evaluate(blocks.vocabBlocks, 1)); -expectedResult = {blocks.vb3}; -EXPECT_EQ(expectedResult, - EqualExpression{tc.frankfurt}.evaluate(blocks.vocabBlocks, 1)); -expectedResult = {blocks.vb2}; -EXPECT_EQ(expectedResult, - EqualExpression{tc.düsseldorf}.evaluate(blocks.vocabBlocks, 1)); -expectedResult = {blocks.vb1}; -EXPECT_EQ(expectedResult, - EqualExpression{tc.bonn}.evaluate(blocks.vocabBlocks, 1)); -EXPECT_EQ(expectedResult, - EqualExpression{tc.cologne}.evaluate(blocks.vocabBlocks, 1)); + MetadataBlocks blocks{}; + TestRelationalExpression> + testExpression; + testExpression(2, IntId(0), blocks.blocks, + {blocks.b1, blocks.b2, blocks.b7, blocks.b14}); + testExpression(2, IntId(5), blocks.blocks, + {blocks.b2, blocks.b3, blocks.b7, blocks.b10, blocks.b14}); + testExpression(2, IntId(22), blocks.blocks, {blocks.b7, blocks.b14}); + testExpression(2, IntId(-10), blocks.blocks, + {blocks.b5, blocks.b7, blocks.b14}); + testExpression(2, DoubleId(-6.25), blocks.blocks, + {blocks.b7, blocks.b11, blocks.b12, blocks.b14}); + testExpression(2, IntId(-11), blocks.blocks, + {blocks.b7, blocks.b13, blocks.b14}); + testExpression(2, DoubleId(-14.02), blocks.blocks, {blocks.b7, blocks.b14}); + testExpression(2, DoubleId(-0.001), blocks.blocks, {blocks.b7, blocks.b14}); + testExpression(2, DoubleId(0), blocks.blocks, + {blocks.b1, blocks.b2, blocks.b7, blocks.b14}); + testExpression(2, IntId(2), blocks.blocks, + {blocks.b2, blocks.b7, blocks.b8, blocks.b14}); + testExpression(2, DoubleId(5.5), blocks.blocks, + {blocks.b3, blocks.b7, blocks.b10, blocks.b14}); + testExpression(2, DoubleId(1.5), blocks.blocks, + {blocks.b2, blocks.b7, blocks.b14}); + testExpression(2, VocabId(1), blocks.blocks, {blocks.b14}); + testExpression(2, VocabId(14), blocks.blocks, + {blocks.b14, blocks.b15, blocks.b16, blocks.b17}); + testExpression(2, VocabId(11), blocks.blocks, {blocks.b14, blocks.b15}); + testExpression(2, VocabId(17), blocks.blocks, {blocks.b14, blocks.b17}); + testExpression(2, IntId(-4), blocks.blocks, + {blocks.b6, blocks.b7, blocks.b11, blocks.b14}); } //______________________________________________________________________________ // Test NotEqualExpression TEST(RelationalExpression, testNotEqualExpression) { -MetadataBlocks blocks{}; -std::vector expectedResult{}; -EXPECT_EQ(blocks.numericBlocks, - NotEqualExpression{IntId(8)}.evaluate(blocks.numericBlocks, 0)); -EXPECT_EQ(blocks.numericBlocks, - NotEqualExpression{IntId(16)}.evaluate(blocks.numericBlocks, 0)); -EXPECT_EQ(blocks.numericBlocks, - NotEqualExpression{IntId(45)}.evaluate(blocks.numericBlocks, 0)); -EXPECT_EQ(blocks.numericBlocks, - NotEqualExpression{IntId(51)}.evaluate(blocks.numericBlocks, 0)); -EXPECT_EQ(blocks.numericBlocks, - NotEqualExpression{IntId(48)}.evaluate(blocks.numericBlocks, 0)); -EXPECT_EQ(blocks.numericBlocks, NotEqualExpression{DoubleId(18.32)}.evaluate( - blocks.numericBlocks, 2)); -EXPECT_EQ(blocks.numericBlocks, NotEqualExpression{DoubleId(22.33)}.evaluate( - blocks.numericBlocks, 2)); -EXPECT_EQ(blocks.numericBlocks, - NotEqualExpression{IntId(17)}.evaluate(blocks.numericBlocks, 0)); -expectedResult = {blocks.nb1, blocks.nb3, blocks.nb4, blocks.nb5}; -EXPECT_EQ(expectedResult, - NotEqualExpression{IntId(42)}.evaluate(blocks.numericBlocks, 0)); -expectedResult = {blocks.nb1, blocks.nb2, blocks.nb4, blocks.nb5}; -EXPECT_EQ(expectedResult, - NotEqualExpression{IntId(2)}.evaluate(blocks.numericBlocks, 1)); -expectedResult = {blocks.nb1, blocks.nb2, blocks.nb3, blocks.nb4}; -EXPECT_EQ(expectedResult, NotEqualExpression{DoubleId(6.00)}.evaluate( - blocks.numericBlocks, 1)); -expectedResult = {blocks.nb2, blocks.nb3, blocks.nb4, blocks.nb5}; -EXPECT_EQ(expectedResult, - NotEqualExpression{IntId(0)}.evaluate(blocks.numericBlocks, 1)); -// VOCAB -TestContext tc{}; -expectedResult = {}; -EXPECT_EQ(blocks.vocabBlocks, - NotEqualExpression{tc.zz}.evaluate(blocks.vocabBlocks, 1)); -EXPECT_EQ(blocks.vocabBlocks, - NotEqualExpression{tc.alpha}.evaluate(blocks.vocabBlocks, 1)); -EXPECT_EQ(blocks.vocabBlocks, NotEqualExpression{tc.frankfurt_oder}.evaluate( - blocks.vocabBlocks, 1)); -EXPECT_EQ(blocks.vocabBlocks, - NotEqualExpression{tc.munich}.evaluate(blocks.vocabBlocks, 1)); -EXPECT_EQ(blocks.vocabBlocks, - NotEqualExpression{tc.bonn}.evaluate(blocks.vocabBlocks, 1)); -EXPECT_EQ(blocks.vocabBlocks, - NotEqualExpression{tc.cologne}.evaluate(blocks.vocabBlocks, 1)); -EXPECT_EQ(blocks.vocabBlocks, - NotEqualExpression{tc.düsseldorf}.evaluate(blocks.vocabBlocks, 1)); -expectedResult = {blocks.vb1, blocks.vb2, blocks.vb4, blocks.vb5}; -EXPECT_EQ(expectedResult, - NotEqualExpression{tc.frankfurt}.evaluate(blocks.vocabBlocks, 1)); -expectedResult = {blocks.vb1, blocks.vb2, blocks.vb3, blocks.vb4}; -EXPECT_EQ(expectedResult, - NotEqualExpression{tc.karlsruhe}.evaluate(blocks.vocabBlocks, 1)); + MetadataBlocks blocks{}; + TestRelationalExpression> + testExpression; + testExpression(2, DoubleId(0.00), blocks.blocks, + {blocks.b2, blocks.b3, blocks.b4, blocks.b5, blocks.b6, + blocks.b7, blocks.b8, blocks.b9, blocks.b10, blocks.b11, + blocks.b12, blocks.b13, blocks.b14}); + testExpression(2, IntId(-4), blocks.blocks, + {blocks.b1, blocks.b2, blocks.b3, blocks.b4, blocks.b5, + blocks.b7, blocks.b8, blocks.b9, blocks.b10, blocks.b11, + blocks.b12, blocks.b13, blocks.b14}); + testExpression(2, DoubleId(0.001), blocks.blocks, + {blocks.b1, blocks.b2, blocks.b3, blocks.b4, blocks.b5, + blocks.b6, blocks.b7, blocks.b8, blocks.b9, blocks.b10, + blocks.b11, blocks.b12, blocks.b13, blocks.b14}); + testExpression(2, IntId(2), blocks.blocks, + {blocks.b1, blocks.b2, blocks.b3, blocks.b4, blocks.b5, + blocks.b6, blocks.b7, blocks.b9, blocks.b10, blocks.b11, + blocks.b12, blocks.b13, blocks.b14}); + testExpression(2, DoubleId(-6.2500), blocks.blocks, + {blocks.b1, blocks.b2, blocks.b3, blocks.b4, blocks.b5, + blocks.b6, blocks.b7, blocks.b8, blocks.b9, blocks.b10, + blocks.b11, blocks.b13, blocks.b14}); + testExpression(2, IntId(5), blocks.blocks, + {blocks.b1, blocks.b2, blocks.b3, blocks.b4, blocks.b5, + blocks.b6, blocks.b7, blocks.b8, blocks.b9, blocks.b10, + blocks.b11, blocks.b12, blocks.b13, blocks.b14}); + testExpression(2, DoubleId(-101.23), blocks.blocks, + {blocks.b1, blocks.b2, blocks.b3, blocks.b4, blocks.b5, + blocks.b6, blocks.b7, blocks.b8, blocks.b9, blocks.b10, + blocks.b11, blocks.b12, blocks.b13, blocks.b14}); + testExpression(2, VocabId(0), blocks.blocks, + {blocks.b14, blocks.b15, blocks.b16, blocks.b17}); + testExpression(2, VocabId(7), blocks.blocks, + {blocks.b14, blocks.b15, blocks.b16, blocks.b17}); + testExpression(2, VocabId(14), blocks.blocks, + {blocks.b14, blocks.b15, blocks.b17}); + testExpression(2, VocabId(17), blocks.blocks, + {blocks.b14, blocks.b15, blocks.b16, blocks.b17}); } //______________________________________________________________________________ @@ -357,225 +439,210 @@ EXPECT_EQ(expectedResult, // Test AndExpression TEST(LogicalExpression, testAndExpression) { -MetadataBlocks blocks{}; -std::vector expectedResult{}; -EXPECT_EQ(expectedResult, - AndExpression(std::make_unique(IntId(42)), - std::make_unique(IntId(45))) - .evaluate(blocks.numericBlocks, 0)); -EXPECT_EQ( - expectedResult, - AndExpression(std::make_unique(IntId(42)), - std::make_unique(DoubleId(52.33))) - .evaluate(blocks.numericBlocks, 0)); -EXPECT_EQ( - expectedResult, - AndExpression(std::make_unique( - std::make_unique(IntId(42)), - std::make_unique(IntId(45))), - std::make_unique(IntId(49))) - .evaluate(blocks.numericBlocks, 0)); -expectedResult = {blocks.nb1, blocks.nb2}; -EXPECT_EQ(expectedResult, - AndExpression(std::make_unique( - std::make_unique(IntId(0)), - std::make_unique(IntId(0))), - std::make_unique(IntId(6))) - .evaluate(blocks.numericBlocks, 1)); -// !!! Reason that we don't expect an empty result here: -// Block blocks.nb3 contains value within the range [42, 45] on column 0. -// Thus, this block is a valid candidate for values unequal to 42 and values -// that are equal to 45. -expectedResult = {blocks.nb3}; -EXPECT_EQ(expectedResult, - AndExpression(std::make_unique(IntId(42)), - std::make_unique(DoubleId(42.00))) - .evaluate(blocks.numericBlocks, 0)); -expectedResult = {blocks.nb1, blocks.nb2, blocks.nb3, blocks.nb4}; -EXPECT_EQ(expectedResult, - AndExpression(std::make_unique(DoubleId(6.00)), - std::make_unique(IntId(2.00))) - .evaluate(blocks.numericBlocks, 1)); -expectedResult = {blocks.nb4, blocks.nb5}; -EXPECT_EQ( - expectedResult, - AndExpression(std::make_unique( - std::make_unique(IntId(7)), - std::make_unique(IntId(5))), - std::make_unique(IntId(0))) - .evaluate(blocks.numericBlocks, 1)); -expectedResult = {blocks.nb1, blocks.nb2}; -EXPECT_EQ( - expectedResult, - AndExpression(std::make_unique(DoubleId(14.575)), - std::make_unique(IntId(12))) - .evaluate(blocks.numericBlocks, 2)); -expectedResult = {blocks.nb2, blocks.nb3, blocks.nb4}; -EXPECT_EQ(expectedResult, - AndExpression(std::make_unique(DoubleId(0.00)), - std::make_unique(DoubleId(6.00))) - .evaluate(blocks.numericBlocks, 1)); -expectedResult = {blocks.nb1, blocks.nb2}; -EXPECT_EQ(expectedResult, - AndExpression(std::make_unique(DoubleId(1.99)), - std::make_unique(DoubleId(1.5))) - .evaluate(blocks.numericBlocks, 1)); + MetadataBlocks blocks{}; + TestLogicalExpression> + testExpression; + testExpression.test( + 2, VocabId(10), VocabId(10), blocks.blocks, + {blocks.b14, blocks.b15, blocks.b16, blocks.b17}); + testExpression.test( + 2, VocabId(0), VocabId(17), blocks.blocks, {blocks.b14, blocks.b17}); + testExpression.test( + 2, VocabId(12), VocabId(17), blocks.blocks, {blocks.b14}); + testExpression.test( + 2, VocabId(10), VocabId(14), blocks.blocks, {blocks.b14, blocks.b15}); + testExpression.test( + 2, VocabId(0), VocabId(10), blocks.blocks, {blocks.b14}); + testExpression.test( + 2, VocabId(17), VocabId(17), blocks.blocks, + {blocks.b14, blocks.b15, blocks.b16, blocks.b17}); + testExpression.test( + 2, DoubleId(-6.25), IntId(-7), blocks.blocks, {blocks.b7, blocks.b14}); + testExpression.test( + 2, DoubleId(-6.25), DoubleId(-6.25), blocks.blocks, + {blocks.b7, blocks.b14}); + // Corner case: Logically it is impossible to satisfy (x > 0) and (x < 0) at + // the same time. But given that we evaluate on block boundaries and their + // possible values (Ids) in between, block b7 satisfies both conditions over + // its range [IntId(-4)... DoubleId(2)] for column 2. + testExpression.test( + 2, IntId(0), IntId(0), blocks.blocks, {blocks.b7, blocks.b14}); + testExpression.test( + 2, IntId(-10), DoubleId(0.00), blocks.blocks, + {blocks.b5, blocks.b6, blocks.b7, blocks.b11, blocks.b12, blocks.b14}); + // Also a corner case. + testExpression.test( + 2, IntId(0), DoubleId(0), blocks.blocks, + {blocks.b2, blocks.b7, blocks.b14}); + testExpression.test( + 2, IntId(0), IntId(0), blocks.blocks, + {blocks.b1, blocks.b2, blocks.b7, blocks.b14}); + testExpression.test( + 2, DoubleId(-34.23), DoubleId(15.1), blocks.blocks, + {blocks.b7, blocks.b14}); + testExpression.test( + 2, IntId(0), DoubleId(-4), blocks.blocks, + {blocks.b5, blocks.b6, blocks.b7, blocks.b11, blocks.b12, blocks.b13, + blocks.b14}); + testExpression.test( + 2, IntId(0), IntId(-4), blocks.blocks, + {blocks.b2, blocks.b3, blocks.b4, blocks.b5, blocks.b7, blocks.b8, + blocks.b9, blocks.b10, blocks.b11, blocks.b12, blocks.b13, blocks.b14}); + testExpression.test( + 2, DoubleId(-3.1415), DoubleId(4.5), blocks.blocks, + {blocks.b2, blocks.b7, blocks.b10, blocks.b14}); + testExpression.test( + 2, DoubleId(-6.25), IntId(0), blocks.blocks, + {blocks.b5, blocks.b6, blocks.b7, blocks.b11, blocks.b13, blocks.b14}); + testExpression.test( + 2, DoubleId(-4), DoubleId(1), blocks.blocks, {blocks.b7, blocks.b14}); + testExpression.test( + 2, DoubleId(-2), IntId(-3), blocks.blocks, + {blocks.b7, blocks.b11, blocks.b14}); } //______________________________________________________________________________ // Test OrExpression TEST(LogicalExpression, testOrExpression) { -MetadataBlocks blocks{}; -std::vector expectedResult = {blocks.nb1, blocks.nb4, - blocks.nb5}; -EXPECT_EQ(expectedResult, - OrExpression(std::make_unique(IntId(42)), - std::make_unique(IntId(45))) - .evaluate(blocks.numericBlocks, 0)); -expectedResult = {}; -EXPECT_EQ(expectedResult, - OrExpression(std::make_unique(DoubleId(-14.23)), - std::make_unique(IntId(51))) - .evaluate(blocks.numericBlocks, 0)); -expectedResult = {blocks.nb2, blocks.nb3, blocks.nb4, blocks.nb5}; -EXPECT_EQ( - expectedResult, - OrExpression(std::make_unique(IntId(0)), - std::make_unique( - std::make_unique(IntId(5)), - std::make_unique(DoubleId(1.00)))) - .evaluate(blocks.numericBlocks, 1)); -expectedResult = {blocks.nb1, blocks.nb2, blocks.nb4, blocks.nb5}; -EXPECT_EQ(expectedResult, - OrExpression(std::make_unique(IntId(0)), - std::make_unique( - std::make_unique(IntId(3)), - std::make_unique(DoubleId(6)))) - .evaluate(blocks.numericBlocks, 1)); -expectedResult = {blocks.nb4, blocks.nb5}; -EXPECT_EQ( - expectedResult, - OrExpression(std::make_unique(IntId(20)), - std::make_unique(DoubleId(113.3))) - .evaluate(blocks.numericBlocks, 2)); -expectedResult = {blocks.nb1, blocks.nb5}; -EXPECT_EQ( - expectedResult, - OrExpression(std::make_unique(IntId(42)), - std::make_unique( - std::make_unique(IntId(49)), - std::make_unique(DoubleId(2.00)))) - .evaluate(blocks.numericBlocks, 0)); -expectedResult = {blocks.nb1, blocks.nb2, blocks.nb3, blocks.nb4}; -EXPECT_EQ(expectedResult, - OrExpression(std::make_unique( - std::make_unique(IntId(0)), - std::make_unique(DoubleId(2.00))), - std::make_unique(IntId(6))) - .evaluate(blocks.numericBlocks, 1)); -expectedResult = {blocks.nb1, blocks.nb2, blocks.nb3}; -EXPECT_EQ(expectedResult, - OrExpression(std::make_unique(DoubleId(17.00)), - std::make_unique(IntId(16.35))) - .evaluate(blocks.numericBlocks, 2)); + MetadataBlocks blocks{}; + TestLogicalExpression> + testExpression; + testExpression.test( + 2, VocabId(22), VocabId(0), blocks.blocks, + {blocks.b14, blocks.b15, blocks.b16, blocks.b17}); + testExpression.test( + 2, VocabId(0), VocabId(16), blocks.blocks, {blocks.b14, blocks.b17}); + testExpression.test( + 2, VocabId(17), VocabId(242), blocks.blocks, {blocks.b14}); + testExpression.test( + 2, DoubleId(-5.95), VocabId(14), blocks.blocks, + {blocks.b5, blocks.b7, blocks.b11, blocks.b12, blocks.b13, blocks.b14, + blocks.b15, blocks.b16, blocks.b17}); + testExpression.test( + 2, DoubleId(0), VocabId(14), blocks.blocks, + {blocks.b1, blocks.b2, blocks.b7, blocks.b14, blocks.b15, blocks.b17}); + testExpression.test( + 2, DoubleId(0.0), DoubleId(-6.25), blocks.blocks, + {blocks.b1, blocks.b2, blocks.b7, blocks.b11, blocks.b12, blocks.b14}); + testExpression.test( + 2, DoubleId(-11.99), DoubleId(-15.22), blocks.blocks, + {blocks.b7, blocks.b13, blocks.b14}); + testExpression.test( + 2, DoubleId(7.99), DoubleId(-7.99), blocks.blocks, + {blocks.b4, blocks.b5, blocks.b7, blocks.b10, blocks.b13, blocks.b14}); + testExpression.test( + 2, IntId(-15), IntId(2), blocks.blocks, + {blocks.b1, blocks.b2, blocks.b3, blocks.b4, blocks.b5, blocks.b6, + blocks.b7, blocks.b8, blocks.b9, blocks.b10, blocks.b11, blocks.b12, + blocks.b13, blocks.b14}); + testExpression.test( + 2, IntId(0), IntId(-4), blocks.blocks, + {blocks.b1, blocks.b2, blocks.b6, blocks.b7, blocks.b11, blocks.b14}); + testExpression.test( + 2, VocabId(14), IntId(2), blocks.blocks, + {blocks.b2, blocks.b7, blocks.b8, blocks.b14, blocks.b15, blocks.b17}); + testExpression.test( + 2, DoubleId(-1), IntId(1), blocks.blocks, + {blocks.b2, blocks.b3, blocks.b4, blocks.b5, blocks.b6, blocks.b7, + blocks.b8, blocks.b9, blocks.b10, blocks.b11, blocks.b12, blocks.b13, + blocks.b14}); + testExpression.test( + 2, DoubleId(-4), IntId(-4), blocks.blocks, + {blocks.b5, blocks.b6, blocks.b7, blocks.b11, blocks.b12, blocks.b13, + blocks.b14}); } //______________________________________________________________________________ // Test NotExpression TEST(LogicalExpression, testNotExpression) { -MetadataBlocks blocks{}; -TestContext tc{}; -std::vector expectedResult = {}; -EXPECT_EQ(expectedResult, - NotExpression(std::make_unique(IntId(16))) - .evaluate(blocks.numericBlocks, 0)); -EXPECT_EQ( - expectedResult, - NotExpression(std::make_unique( - std::make_unique(tc.munich))) - .evaluate(blocks.vocabBlocks, 1)); -EXPECT_EQ(expectedResult, - NotExpression(std::make_unique( - std::make_unique(tc.zz))) - .evaluate(blocks.vocabBlocks, 1)); -expectedResult = {blocks.vb4, blocks.vb5}; -EXPECT_EQ( - expectedResult, - NotExpression(std::make_unique( - std::make_unique(tc.karlsruhe))) - .evaluate(blocks.vocabBlocks, 1)); -EXPECT_EQ( - blocks.numericBlocks, - NotExpression(std::make_unique( - std::make_unique(IntId(16)))) - .evaluate(blocks.numericBlocks, 0)); -expectedResult = {blocks.vb1, blocks.vb2, blocks.vb3}; -EXPECT_EQ(expectedResult, - NotExpression(std::make_unique(tc.frankfurt)) - .evaluate(blocks.vocabBlocks, 1)); -EXPECT_EQ(blocks.vocabBlocks, - NotExpression(std::make_unique(tc.berlin)) - .evaluate(blocks.vocabBlocks, 1)); -expectedResult = {blocks.vb1, blocks.vb2}; -EXPECT_EQ( - expectedResult, - NotExpression(std::make_unique(tc.düsseldorf)) - .evaluate(blocks.vocabBlocks, 1)); -EXPECT_EQ(blocks.numericBlocks, - NotExpression( - std::make_unique( - std::make_unique(IntId(13)), - std::make_unique(DoubleId(111.01)))) - .evaluate(blocks.numericBlocks, 2)); -expectedResult = {}; -EXPECT_EQ( - expectedResult, - NotExpression( - std::make_unique(std::make_unique( - std::make_unique(IntId(13)), - std::make_unique(DoubleId(111.01))))) - .evaluate(blocks.numericBlocks, 2)); -expectedResult = {blocks.vb4, blocks.vb5}; -EXPECT_EQ( - expectedResult, - NotExpression(std::make_unique( - std::make_unique(tc.munich), - std::make_unique(tc.ingolstadt))) - .evaluate(blocks.vocabBlocks, 1)); -expectedResult = {blocks.vb2, blocks.vb3, blocks.vb4}; -EXPECT_EQ( - expectedResult, - NotExpression(std::make_unique( - std::make_unique(tc.hamburg), - std::make_unique(tc.düsseldorf))) - .evaluate(blocks.vocabBlocks, 1)); -expectedResult = {blocks.nb2, blocks.nb3, blocks.nb4}; -EXPECT_EQ(expectedResult, - NotExpression(std::make_unique( - std::make_unique(DoubleId(0.0)), - std::make_unique(IntId(6)))) - .evaluate(blocks.numericBlocks, 1)); -expectedResult = {blocks.nb1, blocks.nb2, blocks.nb4, blocks.nb5}; -EXPECT_EQ( - expectedResult, - NotExpression(std::make_unique( - std::make_unique(DoubleId(0.0)), - std::make_unique(IntId(6)))) - .evaluate(blocks.numericBlocks, 1)); -expectedResult = {blocks.nb3, blocks.nb4}; -EXPECT_EQ( - expectedResult, - NotExpression(std::make_unique( - std::make_unique(DoubleId(42.0)), - std::make_unique(IntId(48)))) - .evaluate(blocks.numericBlocks, 0)); -expectedResult = {blocks.nb1, blocks.nb2, blocks.nb3, blocks.nb5}; -EXPECT_EQ(expectedResult, - NotExpression( - std::make_unique(std::make_unique( - std::make_unique(DoubleId(42.25)), - std::make_unique(IntId(51))))) - .evaluate(blocks.numericBlocks, 0)); + MetadataBlocks blocks{}; + TestNotExpression> testExpression{}; + testExpression.test( + 2, VocabId(2), blocks.blocks, + {blocks.b14, blocks.b15, blocks.b16, blocks.b17}); + testExpression.test(2, VocabId(14), blocks.blocks, + {blocks.b14, blocks.b15, blocks.b17}); + testExpression.test( + 2, VocabId(14), blocks.blocks, + {blocks.b14, blocks.b15, blocks.b16, blocks.b17}); + testExpression.test( + 2, VocabId(0), blocks.blocks, + {blocks.b14, blocks.b15, blocks.b16, blocks.b17}); + testExpression.test( + 2, DoubleId(-14.01), blocks.blocks, + {blocks.b1, blocks.b2, blocks.b3, blocks.b4, blocks.b5, blocks.b6, + blocks.b7, blocks.b8, blocks.b9, blocks.b10, blocks.b11, blocks.b12, + blocks.b13, blocks.b14}); + testExpression.test( + 2, DoubleId(-14.01), blocks.blocks, {blocks.b7, blocks.b14}); + testExpression.test( + 2, DoubleId(-4.00), blocks.blocks, + {blocks.b5, blocks.b6, blocks.b7, blocks.b11, blocks.b12, blocks.b13, + blocks.b14}); + testExpression.test(2, DoubleId(-24.4), blocks.blocks, + {blocks.b7, blocks.b14}); + testExpression.test( + 2, IntId(0), blocks.blocks, + {blocks.b2, blocks.b3, blocks.b4, blocks.b7, blocks.b8, blocks.b9, + blocks.b10, blocks.b14}); + testExpression.test( + 2, DoubleId(-6.25), blocks.blocks, + {blocks.b1, blocks.b2, blocks.b3, blocks.b4, blocks.b5, blocks.b6, + blocks.b7, blocks.b8, blocks.b9, blocks.b10, blocks.b11, blocks.b13, + blocks.b14}); + testExpression.test( + 2, DoubleId(4), blocks.blocks, + {blocks.b2, blocks.b7, blocks.b9, blocks.b10, blocks.b14}); + testExpression.test( + 2, DoubleId(0), blocks.blocks, + {blocks.b1, blocks.b2, blocks.b5, blocks.b6, blocks.b7, blocks.b11, + blocks.b12, blocks.b13, blocks.b14}); } -*/ +//______________________________________________________________________________ +// Test that correct errors are thrown for invalid input (condition checking). +TEST(PrefilterExpression, testInputConditionCheck) { + MetadataBlocks blocks{}; + TestRelationalExpression + testRelationalExpression{}; + testRelationalExpression( + 2, DoubleId(10), blocks.blocksInvalidCol1, + "The columns up to the evaluation column must contain the same values."); + testRelationalExpression( + 1, DoubleId(10), blocks.blocksInvalidCol1, + "The data blocks must be provided in sorted order regarding the " + "evaluation column."); + testRelationalExpression(2, DoubleId(10), blocks.blocksWithDuplicate2, + "The provided data blocks must be unique."); + + TestNotExpression testNotExpression{}; + testNotExpression.test( + 2, VocabId(2), blocks.blocksWithDuplicate1, + "The provided data blocks must be unique."); + testNotExpression.test( + 2, DoubleId(-14.1), blocks.blocksInvalidOrder1, + "The data blocks must be provided in sorted order regarding the " + "evaluation column."); + testNotExpression.test( + 0, IntId(0), blocks.blocksInvalidCol02, + "The data blocks must be provided in sorted order regarding the " + "evaluation column."); + testNotExpression.test( + 1, IntId(0), blocks.blocksInvalidCol02, + "The columns up to the evaluation column must contain the same values."); + testNotExpression.test( + 2, IntId(0), blocks.blocksInvalidCol02, + "The columns up to the evaluation column must contain the same values."); + + TestLogicalExpression testAndExpression{}; + testAndExpression.test( + 2, DoubleId(-4.24), IntId(5), blocks.blocksWithDuplicate2, + "The provided data blocks must be unique."); + testAndExpression.test( + 2, DoubleId(-4.24), IntId(5), blocks.blocksInvalidOrder1, + "The data blocks must be provided in sorted order regarding the " + "evaluation column."); + testAndExpression.test( + 2, DoubleId(-4.24), IntId(5), blocks.blocksInvalidCol02, + "The columns up to the evaluation column must contain the same values."); +} From b02dc57ffb3d3fd8f6438d6b4e72f2262f1de2f3 Mon Sep 17 00:00:00 2001 From: realHannes Date: Tue, 8 Oct 2024 17:36:09 +0200 Subject: [PATCH 31/50] add additional testing on IDs from other datatypes --- src/index/CompressedBlockPrefiltering.cpp | 4 +- test/CompressedBlockPrefilteringTest.cpp | 177 +++++++++++++++++----- test/util/IdTestHelpers.h | 4 + 3 files changed, 146 insertions(+), 39 deletions(-) diff --git a/src/index/CompressedBlockPrefiltering.cpp b/src/index/CompressedBlockPrefiltering.cpp index 801d713e05..69e3696807 100644 --- a/src/index/CompressedBlockPrefiltering.cpp +++ b/src/index/CompressedBlockPrefiltering.cpp @@ -242,8 +242,8 @@ std::vector RelationalExpression::evaluateImpl( std::distance(valueIdsInput.begin(), secondIdAdjusted) / 2); } relevantBlocks.shrink_to_fit(); - // Merge mixedDatatypeBlocks into relevantBlocks while maintaing order and - // avoiding dublicates. + // Merge mixedDatatypeBlocks into relevantBlocks while maintaining order and + // avoiding duplicates. return getSetUnion(relevantBlocks, mixedDatatypeBlocks, evaluationColumn); }; diff --git a/test/CompressedBlockPrefilteringTest.cpp b/test/CompressedBlockPrefilteringTest.cpp index 56edbe0684..13e0c7ce9b 100644 --- a/test/CompressedBlockPrefilteringTest.cpp +++ b/test/CompressedBlockPrefilteringTest.cpp @@ -8,11 +8,14 @@ #include "./SparqlExpressionTestHelpers.h" #include "index/CompressedBlockPrefiltering.h" +#include "util/DateYearDuration.h" #include "util/IdTestHelpers.h" +using ad_utility::testing::BlankNodeId; +using ad_utility::testing::BoolId; +using ad_utility::testing::DateId; using ad_utility::testing::DoubleId; using ad_utility::testing::IntId; -using ad_utility::testing::LocalVocabId; using ad_utility::testing::UndefId; using ad_utility::testing::VocabId; using sparqlExpression::TestContext; @@ -53,49 +56,49 @@ struct MetadataBlocks { false}; }; - BlockMetadata b1 = makeBlock(IntId(0), IntId(0)); - BlockMetadata b2 = makeBlock(IntId(0), IntId(5)); - BlockMetadata b3 = makeBlock(IntId(5), IntId(6)); - BlockMetadata b4 = makeBlock(IntId(8), IntId(9)); - BlockMetadata b5 = makeBlock(IntId(-10), IntId(-8)); - BlockMetadata b6 = makeBlock(IntId(-4), IntId(-4)); + const BlockMetadata b1 = makeBlock(IntId(0), IntId(0)); + const BlockMetadata b2 = makeBlock(IntId(0), IntId(5)); + const BlockMetadata b3 = makeBlock(IntId(5), IntId(6)); + const BlockMetadata b4 = makeBlock(IntId(8), IntId(9)); + const BlockMetadata b5 = makeBlock(IntId(-10), IntId(-8)); + const BlockMetadata b6 = makeBlock(IntId(-4), IntId(-4)); // b7 contains mixed datatypes in COLUMN 2 - BlockMetadata b7 = makeBlock(IntId(-4), DoubleId(2)); - BlockMetadata b8 = makeBlock(DoubleId(2), DoubleId(2)); - BlockMetadata b9 = makeBlock(DoubleId(4), DoubleId(4)); - BlockMetadata b10 = makeBlock(DoubleId(4), DoubleId(10)); - BlockMetadata b11 = makeBlock(DoubleId(-1.23), DoubleId(-6.25)); - BlockMetadata b12 = makeBlock(DoubleId(-6.25), DoubleId(-6.25)); - BlockMetadata b13 = makeBlock(DoubleId(-10.42), DoubleId(-12.00)); + const BlockMetadata b7 = makeBlock(IntId(-4), DoubleId(2)); + const BlockMetadata b8 = makeBlock(DoubleId(2), DoubleId(2)); + const BlockMetadata b9 = makeBlock(DoubleId(4), DoubleId(4)); + const BlockMetadata b10 = makeBlock(DoubleId(4), DoubleId(10)); + const BlockMetadata b11 = makeBlock(DoubleId(-1.23), DoubleId(-6.25)); + const BlockMetadata b12 = makeBlock(DoubleId(-6.25), DoubleId(-6.25)); + const BlockMetadata b13 = makeBlock(DoubleId(-10.42), DoubleId(-12.00)); // b14 contains mixed datatypes - BlockMetadata b14 = makeBlock(DoubleId(-14.01), VocabId(0)); - BlockMetadata b15 = makeBlock(VocabId(10), VocabId(14)); - BlockMetadata b16 = makeBlock(VocabId(14), VocabId(14)); - BlockMetadata b17 = makeBlock(VocabId(14), VocabId(17)); + const BlockMetadata b14 = makeBlock(DoubleId(-14.01), VocabId(0)); + const BlockMetadata b15 = makeBlock(VocabId(10), VocabId(14)); + const BlockMetadata b16 = makeBlock(VocabId(14), VocabId(14)); + const BlockMetadata b17 = makeBlock(VocabId(14), VocabId(17)); std::vector blocks = {b1, b2, b3, b4, b5, b6, b7, b8, b9, b10, b11, b12, b13, b14, b15, b16, b17}; // The following blocks will be swapped with their respective correct block, // to test if the method evaluate checks the pre-conditions properly. - BlockMetadata b1_1{{}, - 0, - {VocabId10, DoubleId33, IntId(0)}, - {VocabId10, DoubleId(22), IntId(0)}, - {}, - false}; + const BlockMetadata b1_1{{}, + 0, + {VocabId10, DoubleId33, IntId(0)}, + {VocabId10, DoubleId(22), IntId(0)}, + {}, + false}; std::vector blocksInvalidCol1 = {b1_1, b2, b3, b4, b5, b6, b7, b8, b9, b10, b11, b12, b13, b14, b15, b16, b17}; - BlockMetadata b5_1{{}, - 0, - {VocabId(11), DoubleId33, IntId(-10)}, - {VocabId10, DoubleId33, IntId(-8)}, - {}, - false}; - std::vector blocksInvalidCol02 = { - b1, b2, b3, b4, b5_1, b6, b7, b8, b9, - b10, b11, b12, b13, b14, b15, b16, b17}; + const BlockMetadata b5_1{{}, + 0, + {VocabId(11), DoubleId33, IntId(-10)}, + {VocabId10, DoubleId33, IntId(-8)}, + {}, + false}; + std::vector blocksInvalidCol2 = {b1, b2, b3, b4, b5_1, b6, + b7, b8, b9, b10, b11, b12, + b13, b14, b15, b16, b17}; std::vector blocksInvalidOrder1 = { b2, b1, b3, b4, b5, b6, b7, b8, b9, b10, b11, b12, b13, b14, b15, b16, b17}; @@ -108,6 +111,30 @@ struct MetadataBlocks { std::vector blocksWithDuplicate2 = { b1, b2, b3, b4, b5, b6, b7, b8, b9, b10, b11, b12, b13, b14, b15, b16, b17, b17}; + + //____________________________________________________________________________ + // Additional blocks for different datatype mix. + const Id undef = Id::makeUndefined(); + const Id falseId = BoolId(false); + const Id trueId = BoolId(true); + const Id referenceDate1 = + DateId(DateYearOrDuration::parseXsdDate, "1999-11-11"); + const Id referenceDate2 = + DateId(DateYearOrDuration::parseXsdDate, "2005-02-27"); + const Id referenceDateEqual = + DateId(DateYearOrDuration::parseXsdDate, "2000-01-01"); + const BlockMetadata bd1 = makeBlock(undef, undef); + const BlockMetadata bd2 = makeBlock(undef, falseId); + const BlockMetadata bd3 = makeBlock(falseId, falseId); + const BlockMetadata bd4 = makeBlock(trueId, trueId); + const BlockMetadata bd5 = + makeBlock(trueId, DateId(DateYearOrDuration::parseXsdDate, "1999-12-12")); + const BlockMetadata bd6 = + makeBlock(DateId(DateYearOrDuration::parseXsdDate, "2000-01-01"), + DateId(DateYearOrDuration::parseXsdDate, "2000-01-01")); + const BlockMetadata bd7 = makeBlock( + DateId(DateYearOrDuration::parseXsdDate, "2024-10-08"), BlankNodeId(10)); + std::vector otherBlocks = {bd1, bd2, bd3, bd4, bd5, bd6, bd7}; }; // Static tests, they focus on corner case values for the given block triples. @@ -199,6 +226,23 @@ struct TestNotExpression { } }; +//______________________________________________________________________________ +TEST(BlockMetadata, testBlockFormatForDebugging) { + MetadataBlocks blocks{}; + EXPECT_EQ( + "#BlockMetadata\n(first) Triple: V:10 D:33.000000 I:0\n(last) Triple: " + "V:10 D:33.000000 I:0\nnum. rows: 0.\n", + (std::stringstream() << blocks.b1).str()); + EXPECT_EQ( + "#BlockMetadata\n(first) Triple: V:10 D:33.000000 I:-4\n(last) Triple: " + "V:10 D:33.000000 D:2.000000\nnum. rows: 0.\n", + (std::stringstream() << blocks.b7).str()); + EXPECT_EQ( + "#BlockMetadata\n(first) Triple: V:10 D:33.000000 V:14\n(last) Triple: " + "V:10 D:33.000000 V:17\nnum. rows: 0.\n", + (std::stringstream() << blocks.b17).str()); +} + //______________________________________________________________________________ // Test LessThanExpression TEST(RelationalExpression, testLessThanExpressions) { @@ -232,6 +276,18 @@ TEST(RelationalExpression, testLessThanExpressions) { testExpression(2, VocabId(14), blocks.blocks, {blocks.b14, blocks.b15}); testExpression(2, VocabId(16), blocks.blocks, {blocks.b14, blocks.b15, blocks.b16, blocks.b17}); + testExpression(2, blocks.undef, blocks.otherBlocks, {}); + testExpression(2, blocks.falseId, blocks.otherBlocks, + {blocks.bd2, blocks.bd5}); + testExpression(2, blocks.trueId, blocks.otherBlocks, + {blocks.bd2, blocks.bd3, blocks.bd5}); + testExpression(2, blocks.referenceDate1, blocks.otherBlocks, + {blocks.bd5, blocks.bd7}); + testExpression(2, blocks.referenceDateEqual, blocks.otherBlocks, + {blocks.bd5, blocks.bd7}); + testExpression(2, blocks.referenceDate2, blocks.otherBlocks, + {blocks.bd5, blocks.bd6, blocks.bd7}); + testExpression(2, BlankNodeId(11), blocks.otherBlocks, {blocks.bd7}); } //______________________________________________________________________________ @@ -267,6 +323,14 @@ TEST(RelationalExpression, testLessEqualExpressions) { testExpression(2, VocabId(11), blocks.blocks, {blocks.b14, blocks.b15}); testExpression(2, VocabId(14), blocks.blocks, {blocks.b14, blocks.b15, blocks.b16, blocks.b17}); + testExpression(2, blocks.undef, blocks.otherBlocks, {}); + testExpression(2, blocks.falseId, blocks.otherBlocks, + {blocks.bd2, blocks.bd3, blocks.bd5}); + testExpression(2, blocks.trueId, blocks.otherBlocks, + {blocks.bd2, blocks.bd3, blocks.bd4, blocks.bd5}); + testExpression(2, blocks.referenceDateEqual, blocks.otherBlocks, + {blocks.bd5, blocks.bd6, blocks.bd7}); + testExpression(2, BlankNodeId(11), blocks.otherBlocks, {blocks.bd7}); } //______________________________________________________________________________ @@ -308,6 +372,16 @@ TEST(RelationalExpression, testGreaterThanExpression) { testExpression(2, VocabId(14), blocks.blocks, {blocks.b14, blocks.b17}); testExpression(2, VocabId(12), blocks.blocks, {blocks.b14, blocks.b15, blocks.b16, blocks.b17}); + testExpression(2, blocks.undef, blocks.otherBlocks, {}); + testExpression(2, blocks.falseId, blocks.otherBlocks, + {blocks.bd2, blocks.bd4, blocks.bd5}); + testExpression(2, blocks.trueId, blocks.otherBlocks, + {blocks.bd2, blocks.bd5}); + testExpression(2, blocks.referenceDateEqual, blocks.otherBlocks, + {blocks.bd5, blocks.bd7}); + testExpression(2, blocks.referenceDate1, blocks.otherBlocks, + {blocks.bd5, blocks.bd6, blocks.bd7}); + testExpression(2, IntId(5), blocks.otherBlocks, {}); } //______________________________________________________________________________ @@ -351,6 +425,14 @@ TEST(RelationalExpression, testGreaterEqualExpression) { testExpression(2, VocabId(10), blocks.blocks, {blocks.b14, blocks.b15, blocks.b16, blocks.b17}); testExpression(2, VocabId(17), blocks.blocks, {blocks.b14, blocks.b17}); + testExpression(2, blocks.undef, blocks.otherBlocks, {}); + testExpression(2, blocks.falseId, blocks.otherBlocks, + {blocks.bd2, blocks.bd3, blocks.bd4, blocks.bd5}); + testExpression(2, blocks.trueId, blocks.otherBlocks, + {blocks.bd2, blocks.bd4, blocks.bd5}); + testExpression(2, blocks.referenceDateEqual, blocks.otherBlocks, + {blocks.bd5, blocks.bd6, blocks.bd7}); + testExpression(2, VocabId(0), blocks.otherBlocks, {}); } //______________________________________________________________________________ @@ -387,6 +469,14 @@ TEST(RelationalExpression, testEqualExpression) { testExpression(2, VocabId(17), blocks.blocks, {blocks.b14, blocks.b17}); testExpression(2, IntId(-4), blocks.blocks, {blocks.b6, blocks.b7, blocks.b11, blocks.b14}); + testExpression(2, blocks.trueId, blocks.otherBlocks, + {blocks.bd2, blocks.bd4, blocks.bd5}); + testExpression(2, blocks.referenceDate1, blocks.otherBlocks, + {blocks.bd5, blocks.bd7}); + testExpression(2, blocks.referenceDateEqual, blocks.otherBlocks, + {blocks.bd5, blocks.bd6, blocks.bd7}); + testExpression(2, blocks.referenceDate2, blocks.otherBlocks, + {blocks.bd5, blocks.bd7}); } //______________________________________________________________________________ @@ -431,6 +521,13 @@ TEST(RelationalExpression, testNotEqualExpression) { {blocks.b14, blocks.b15, blocks.b17}); testExpression(2, VocabId(17), blocks.blocks, {blocks.b14, blocks.b15, blocks.b16, blocks.b17}); + testExpression(2, blocks.undef, blocks.otherBlocks, {}); + testExpression(2, blocks.falseId, blocks.otherBlocks, + {blocks.bd2, blocks.bd4, blocks.bd5}); + testExpression(2, blocks.referenceDateEqual, blocks.otherBlocks, + {blocks.bd5, blocks.bd7}); + testExpression(2, blocks.referenceDate1, blocks.otherBlocks, + {blocks.bd5, blocks.bd6, blocks.bd7}); } //______________________________________________________________________________ @@ -597,6 +694,12 @@ TEST(LogicalExpression, testNotExpression) { 2, DoubleId(0), blocks.blocks, {blocks.b1, blocks.b2, blocks.b5, blocks.b6, blocks.b7, blocks.b11, blocks.b12, blocks.b13, blocks.b14}); + testExpression.test(0, blocks.VocabId10, blocks.blocks, {}); + testExpression.test(1, blocks.DoubleId33, blocks.blocks, {}); + testExpression.test(0, blocks.VocabId10, blocks.blocks, + blocks.blocks); + testExpression.test(1, blocks.DoubleId33, + blocks.blocks, {}); } //______________________________________________________________________________ @@ -624,14 +727,14 @@ TEST(PrefilterExpression, testInputConditionCheck) { "The data blocks must be provided in sorted order regarding the " "evaluation column."); testNotExpression.test( - 0, IntId(0), blocks.blocksInvalidCol02, + 0, IntId(0), blocks.blocksInvalidCol2, "The data blocks must be provided in sorted order regarding the " "evaluation column."); testNotExpression.test( - 1, IntId(0), blocks.blocksInvalidCol02, + 1, IntId(0), blocks.blocksInvalidCol2, "The columns up to the evaluation column must contain the same values."); testNotExpression.test( - 2, IntId(0), blocks.blocksInvalidCol02, + 2, IntId(0), blocks.blocksInvalidCol2, "The columns up to the evaluation column must contain the same values."); TestLogicalExpression testAndExpression{}; @@ -643,6 +746,6 @@ TEST(PrefilterExpression, testInputConditionCheck) { "The data blocks must be provided in sorted order regarding the " "evaluation column."); testAndExpression.test( - 2, DoubleId(-4.24), IntId(5), blocks.blocksInvalidCol02, + 2, DoubleId(-4.24), IntId(5), blocks.blocksInvalidCol2, "The columns up to the evaluation column must contain the same values."); } diff --git a/test/util/IdTestHelpers.h b/test/util/IdTestHelpers.h index a8818ba296..dd5dfbd715 100644 --- a/test/util/IdTestHelpers.h +++ b/test/util/IdTestHelpers.h @@ -25,6 +25,10 @@ inline auto VocabId = [](const auto& v) { return Id::makeFromVocabIndex(VocabIndex::make(v)); }; +inline auto BlankNodeId = [](const auto& v) { + return Id::makeFromBlankNodeIndex(BlankNodeIndex::make(v)); +}; + inline auto LocalVocabId = [](std::integral auto v) { static ad_utility::Synchronized localVocab; using namespace ad_utility::triple_component; From eb86f1b8c1b057680572495990bfe8e75996c56c Mon Sep 17 00:00:00 2001 From: realHannes Date: Tue, 8 Oct 2024 19:53:06 +0200 Subject: [PATCH 32/50] addition to test Not Expression with And and Or --- test/CompressedBlockPrefilteringTest.cpp | 144 ++++++++++++++--------- 1 file changed, 91 insertions(+), 53 deletions(-) diff --git a/test/CompressedBlockPrefilteringTest.cpp b/test/CompressedBlockPrefilteringTest.cpp index 13e0c7ce9b..ade741f92b 100644 --- a/test/CompressedBlockPrefilteringTest.cpp +++ b/test/CompressedBlockPrefilteringTest.cpp @@ -172,12 +172,40 @@ static const auto testThrowError = }; //______________________________________________________________________________ -template +template +constexpr std::unique_ptr makeRelExpr( + const ValueId& referenceId) { + return std::make_unique(referenceId); +} + +//______________________________________________________________________________ +template +constexpr std::unique_ptr makeLogExpr( + const ValueId& referenceId1, const ValueId& referenceId2) { + return std::make_unique(makeRelExpr(referenceId1), + makeRelExpr(referenceId2)); +} + +//______________________________________________________________________________ +template +constexpr std::unique_ptr makeNotExpression( + const ValueId& referenceId1, std::optional optId2) { + if constexpr (check_is_relational_v && + check_is_logical_v && optId2.has_value()) { + return std::make_unique( + makeLogExpr(referenceId1, optId2.value())); + } else { + return std::make_unique(makeRelExpr(referenceId1)); + } +} + +//______________________________________________________________________________ +template struct TestRelationalExpression { - void operator()(size_t evaluationColumn, ValueId referenceId, + void operator()(size_t evaluationColumn, const ValueId& referenceId, const std::vector& input, const ResT& expected) - requires check_is_relational_v { - auto expression = std::make_unique(referenceId); + requires check_is_relational_v { + auto expression = makeRelExpr(referenceId); if constexpr (std::is_same_v) { testThrowError(std::move(expression), evaluationColumn, input, expected); } else { @@ -191,14 +219,13 @@ struct TestRelationalExpression { template struct TestLogicalExpression { template - void test(size_t evaluationColumn, ValueId referenceIdChild1, + void test(size_t evaluationColumn, const ValueId& referenceIdChild1, ValueId referenceIdChild2, const std::vector& input, const ResT& expected) requires(check_is_logical_v && check_is_relational_v && check_is_relational_v) { - auto expression = std::make_unique( - std::make_unique(referenceIdChild1), - std::make_unique(referenceIdChild2)); + auto expression = makeLogExpr( + referenceIdChild1, referenceIdChild2); if constexpr (std::is_same_v) { testThrowError(std::move(expression), evaluationColumn, input, expected); } else { @@ -211,12 +238,14 @@ struct TestLogicalExpression { //______________________________________________________________________________ template struct TestNotExpression { - template - void test(size_t evaluationColumn, ValueId referenceIdChild, - const std::vector& input, const ResT& expected) - requires check_is_relational_v { - auto expression = std::make_unique( - std::make_unique(referenceIdChild)); + template + void test(size_t evaluationColumn, const std::vector& input, + const ResT& expected, const ValueId& referenceId1, + std::optional optId2 = std::nullopt) + requires check_is_relational_v { + auto expression = + makeNotExpression(referenceId1, optId2); if constexpr (std::is_same_v) { testThrowError(std::move(expression), evaluationColumn, input, expected); } else { @@ -655,51 +684,56 @@ TEST(LogicalExpression, testNotExpression) { MetadataBlocks blocks{}; TestNotExpression> testExpression{}; testExpression.test( - 2, VocabId(2), blocks.blocks, - {blocks.b14, blocks.b15, blocks.b16, blocks.b17}); - testExpression.test(2, VocabId(14), blocks.blocks, - {blocks.b14, blocks.b15, blocks.b17}); + 2, blocks.blocks, {blocks.b14, blocks.b15, blocks.b16, blocks.b17}, + VocabId(2)); + testExpression.test( + 2, blocks.blocks, {blocks.b14, blocks.b15, blocks.b17}, VocabId(14)); testExpression.test( - 2, VocabId(14), blocks.blocks, - {blocks.b14, blocks.b15, blocks.b16, blocks.b17}); + 2, blocks.blocks, {blocks.b14, blocks.b15, blocks.b16, blocks.b17}, + VocabId(14)); testExpression.test( - 2, VocabId(0), blocks.blocks, - {blocks.b14, blocks.b15, blocks.b16, blocks.b17}); + 2, blocks.blocks, {blocks.b14, blocks.b15, blocks.b16, blocks.b17}, + VocabId(0)); testExpression.test( - 2, DoubleId(-14.01), blocks.blocks, + 2, blocks.blocks, {blocks.b1, blocks.b2, blocks.b3, blocks.b4, blocks.b5, blocks.b6, blocks.b7, blocks.b8, blocks.b9, blocks.b10, blocks.b11, blocks.b12, - blocks.b13, blocks.b14}); + blocks.b13, blocks.b14}, + DoubleId(-14.01)); testExpression.test( - 2, DoubleId(-14.01), blocks.blocks, {blocks.b7, blocks.b14}); + 2, blocks.blocks, {blocks.b7, blocks.b14}, DoubleId(-14.01)); testExpression.test( - 2, DoubleId(-4.00), blocks.blocks, + 2, blocks.blocks, {blocks.b5, blocks.b6, blocks.b7, blocks.b11, blocks.b12, blocks.b13, - blocks.b14}); - testExpression.test(2, DoubleId(-24.4), blocks.blocks, - {blocks.b7, blocks.b14}); + blocks.b14}, + DoubleId(-4.00)); + testExpression.test( + 2, blocks.blocks, {blocks.b7, blocks.b14}, DoubleId(-24.4)); testExpression.test( - 2, IntId(0), blocks.blocks, + 2, blocks.blocks, {blocks.b2, blocks.b3, blocks.b4, blocks.b7, blocks.b8, blocks.b9, - blocks.b10, blocks.b14}); + blocks.b10, blocks.b14}, + IntId(0)); testExpression.test( - 2, DoubleId(-6.25), blocks.blocks, + 2, blocks.blocks, {blocks.b1, blocks.b2, blocks.b3, blocks.b4, blocks.b5, blocks.b6, blocks.b7, blocks.b8, blocks.b9, blocks.b10, blocks.b11, blocks.b13, - blocks.b14}); + blocks.b14}, + DoubleId(-6.25)); testExpression.test( - 2, DoubleId(4), blocks.blocks, - {blocks.b2, blocks.b7, blocks.b9, blocks.b10, blocks.b14}); + 2, blocks.blocks, + {blocks.b2, blocks.b7, blocks.b9, blocks.b10, blocks.b14}, DoubleId(4)); testExpression.test( - 2, DoubleId(0), blocks.blocks, + 2, blocks.blocks, {blocks.b1, blocks.b2, blocks.b5, blocks.b6, blocks.b7, blocks.b11, - blocks.b12, blocks.b13, blocks.b14}); - testExpression.test(0, blocks.VocabId10, blocks.blocks, {}); - testExpression.test(1, blocks.DoubleId33, blocks.blocks, {}); - testExpression.test(0, blocks.VocabId10, blocks.blocks, - blocks.blocks); - testExpression.test(1, blocks.DoubleId33, - blocks.blocks, {}); + blocks.b12, blocks.b13, blocks.b14}, + DoubleId(0)); + testExpression.test(0, blocks.blocks, {}, blocks.VocabId10); + testExpression.test(1, blocks.blocks, {}, blocks.DoubleId33); + testExpression.test(0, blocks.blocks, blocks.blocks, + blocks.VocabId10); + testExpression.test(1, blocks.blocks, {}, + blocks.DoubleId33); } //______________________________________________________________________________ @@ -720,22 +754,26 @@ TEST(PrefilterExpression, testInputConditionCheck) { TestNotExpression testNotExpression{}; testNotExpression.test( - 2, VocabId(2), blocks.blocksWithDuplicate1, - "The provided data blocks must be unique."); + 2, blocks.blocksWithDuplicate1, + "The provided data blocks must be unique.", VocabId(2)); testNotExpression.test( - 2, DoubleId(-14.1), blocks.blocksInvalidOrder1, + 2, blocks.blocksInvalidOrder1, "The data blocks must be provided in sorted order regarding the " - "evaluation column."); + "evaluation column.", + DoubleId(-14.1)); testNotExpression.test( - 0, IntId(0), blocks.blocksInvalidCol2, + 0, blocks.blocksInvalidCol2, "The data blocks must be provided in sorted order regarding the " - "evaluation column."); + "evaluation column.", + IntId(0)); testNotExpression.test( - 1, IntId(0), blocks.blocksInvalidCol2, - "The columns up to the evaluation column must contain the same values."); + 1, blocks.blocksInvalidCol2, + "The columns up to the evaluation column must contain the same values.", + IntId(0)); testNotExpression.test( - 2, IntId(0), blocks.blocksInvalidCol2, - "The columns up to the evaluation column must contain the same values."); + 2, blocks.blocksInvalidCol2, + "The columns up to the evaluation column must contain the same values.", + IntId(0)); TestLogicalExpression testAndExpression{}; testAndExpression.test( From 69ed0195a83b380d7e868203b9db0f29e8b8f65d Mon Sep 17 00:00:00 2001 From: realHannes Date: Wed, 9 Oct 2024 11:06:04 +0200 Subject: [PATCH 33/50] changes to test and fix index check fail --- src/global/ValueId.h | 2 +- src/index/CompressedBlockPrefiltering.cpp | 6 +++-- test/CompressedBlockPrefilteringTest.cpp | 31 ++++++++++++++--------- 3 files changed, 24 insertions(+), 15 deletions(-) diff --git a/src/global/ValueId.h b/src/global/ValueId.h index e344fe4f52..fe429b98fa 100644 --- a/src/global/ValueId.h +++ b/src/global/ValueId.h @@ -29,9 +29,9 @@ enum struct Datatype { VocabIndex, LocalVocabIndex, TextRecordIndex, - WordVocabIndex, Date, GeoPoint, + WordVocabIndex, BlankNodeIndex, MaxValue = BlankNodeIndex // Note: Unfortunately we cannot easily get the size of an enum. diff --git a/src/index/CompressedBlockPrefiltering.cpp b/src/index/CompressedBlockPrefiltering.cpp index 69e3696807..2f0b3b8956 100644 --- a/src/index/CompressedBlockPrefiltering.cpp +++ b/src/index/CompressedBlockPrefiltering.cpp @@ -8,7 +8,7 @@ namespace prefilterExpressions { -// HELPER FUNCTION +// HELPER FUNCTIONS //______________________________________________________________________________ // Given a PermutedTriple retrieve the suitable Id w.r.t. a column (index). static auto getIdFromColumnIndex(const BlockMetadata::PermutedTriple& triple, @@ -196,7 +196,9 @@ std::vector RelationalExpression::evaluateImpl( if ((firstIdDatatype != secondIdDatatype) && // The block is only potentially relevant if at least one of the // respective bounding Ids is of similar (comparable) type to - // refernceId_. + // refernceId_. However, this requires that the comparable datatypes + // are grouped/stand next to each other given the ValueId ordering + // on datatype (most relevant) bits. (valueIdComparators::detail::areTypesCompatible(referenceIdDatatype, firstIdDatatype) || valueIdComparators::detail::areTypesCompatible(referenceIdDatatype, diff --git a/test/CompressedBlockPrefilteringTest.cpp b/test/CompressedBlockPrefilteringTest.cpp index ade741f92b..5c88477ccc 100644 --- a/test/CompressedBlockPrefilteringTest.cpp +++ b/test/CompressedBlockPrefilteringTest.cpp @@ -62,7 +62,7 @@ struct MetadataBlocks { const BlockMetadata b4 = makeBlock(IntId(8), IntId(9)); const BlockMetadata b5 = makeBlock(IntId(-10), IntId(-8)); const BlockMetadata b6 = makeBlock(IntId(-4), IntId(-4)); - // b7 contains mixed datatypes in COLUMN 2 + // b7 contains mixed datatypes (COLUMN 2) const BlockMetadata b7 = makeBlock(IntId(-4), DoubleId(2)); const BlockMetadata b8 = makeBlock(DoubleId(2), DoubleId(2)); const BlockMetadata b9 = makeBlock(DoubleId(4), DoubleId(4)); @@ -70,7 +70,7 @@ struct MetadataBlocks { const BlockMetadata b11 = makeBlock(DoubleId(-1.23), DoubleId(-6.25)); const BlockMetadata b12 = makeBlock(DoubleId(-6.25), DoubleId(-6.25)); const BlockMetadata b13 = makeBlock(DoubleId(-10.42), DoubleId(-12.00)); - // b14 contains mixed datatypes + // b14 contains mixed datatypes (COLUMN 2) const BlockMetadata b14 = makeBlock(DoubleId(-14.01), VocabId(0)); const BlockMetadata b15 = makeBlock(VocabId(10), VocabId(14)); const BlockMetadata b16 = makeBlock(VocabId(14), VocabId(14)); @@ -173,27 +173,28 @@ static const auto testThrowError = //______________________________________________________________________________ template -constexpr std::unique_ptr makeRelExpr( - const ValueId& referenceId) { +auto makeRelExpr(const ValueId& referenceId) { return std::make_unique(referenceId); } //______________________________________________________________________________ template -constexpr std::unique_ptr makeLogExpr( - const ValueId& referenceId1, const ValueId& referenceId2) { +auto makeLogExpr(const ValueId& referenceId1, const ValueId& referenceId2) { return std::make_unique(makeRelExpr(referenceId1), makeRelExpr(referenceId2)); } //______________________________________________________________________________ -template -constexpr std::unique_ptr makeNotExpression( - const ValueId& referenceId1, std::optional optId2) { +template +auto makeNotExpression(const ValueId& referenceId1, + std::optional optId2) { if constexpr (check_is_relational_v && check_is_logical_v && optId2.has_value()) { return std::make_unique( makeLogExpr(referenceId1, optId2.value())); + } else if constexpr (std::is_same_v) { + return std::make_unique( + makeNotExpression(referenceId1)); } else { return std::make_unique(makeRelExpr(referenceId1)); } @@ -238,14 +239,14 @@ struct TestLogicalExpression { //______________________________________________________________________________ template struct TestNotExpression { - template + template void test(size_t evaluationColumn, const std::vector& input, const ResT& expected, const ValueId& referenceId1, std::optional optId2 = std::nullopt) requires check_is_relational_v { auto expression = - makeNotExpression(referenceId1, optId2); + makeNotExpression(referenceId1, optId2); if constexpr (std::is_same_v) { testThrowError(std::move(expression), evaluationColumn, input, expected); } else { @@ -305,6 +306,7 @@ TEST(RelationalExpression, testLessThanExpressions) { testExpression(2, VocabId(14), blocks.blocks, {blocks.b14, blocks.b15}); testExpression(2, VocabId(16), blocks.blocks, {blocks.b14, blocks.b15, blocks.b16, blocks.b17}); + // test blocks.otherBlocks testExpression(2, blocks.undef, blocks.otherBlocks, {}); testExpression(2, blocks.falseId, blocks.otherBlocks, {blocks.bd2, blocks.bd5}); @@ -352,6 +354,7 @@ TEST(RelationalExpression, testLessEqualExpressions) { testExpression(2, VocabId(11), blocks.blocks, {blocks.b14, blocks.b15}); testExpression(2, VocabId(14), blocks.blocks, {blocks.b14, blocks.b15, blocks.b16, blocks.b17}); + // test block.otherBlocks testExpression(2, blocks.undef, blocks.otherBlocks, {}); testExpression(2, blocks.falseId, blocks.otherBlocks, {blocks.bd2, blocks.bd3, blocks.bd5}); @@ -401,6 +404,7 @@ TEST(RelationalExpression, testGreaterThanExpression) { testExpression(2, VocabId(14), blocks.blocks, {blocks.b14, blocks.b17}); testExpression(2, VocabId(12), blocks.blocks, {blocks.b14, blocks.b15, blocks.b16, blocks.b17}); + // test blocks.otherBlocks testExpression(2, blocks.undef, blocks.otherBlocks, {}); testExpression(2, blocks.falseId, blocks.otherBlocks, {blocks.bd2, blocks.bd4, blocks.bd5}); @@ -454,6 +458,7 @@ TEST(RelationalExpression, testGreaterEqualExpression) { testExpression(2, VocabId(10), blocks.blocks, {blocks.b14, blocks.b15, blocks.b16, blocks.b17}); testExpression(2, VocabId(17), blocks.blocks, {blocks.b14, blocks.b17}); + // test blocks.otherBlocks testExpression(2, blocks.undef, blocks.otherBlocks, {}); testExpression(2, blocks.falseId, blocks.otherBlocks, {blocks.bd2, blocks.bd3, blocks.bd4, blocks.bd5}); @@ -498,6 +503,7 @@ TEST(RelationalExpression, testEqualExpression) { testExpression(2, VocabId(17), blocks.blocks, {blocks.b14, blocks.b17}); testExpression(2, IntId(-4), blocks.blocks, {blocks.b6, blocks.b7, blocks.b11, blocks.b14}); + // test blocks.otherBlocks testExpression(2, blocks.trueId, blocks.otherBlocks, {blocks.bd2, blocks.bd4, blocks.bd5}); testExpression(2, blocks.referenceDate1, blocks.otherBlocks, @@ -550,6 +556,7 @@ TEST(RelationalExpression, testNotEqualExpression) { {blocks.b14, blocks.b15, blocks.b17}); testExpression(2, VocabId(17), blocks.blocks, {blocks.b14, blocks.b15, blocks.b16, blocks.b17}); + // test blocks.otherBlocks testExpression(2, blocks.undef, blocks.otherBlocks, {}); testExpression(2, blocks.falseId, blocks.otherBlocks, {blocks.bd2, blocks.bd4, blocks.bd5}); From 854322b91e444406e2afad588474358fdd128438 Mon Sep 17 00:00:00 2001 From: realHannes Date: Wed, 9 Oct 2024 13:48:01 +0200 Subject: [PATCH 34/50] increase test coverage for Not expression --- test/CompressedBlockPrefilteringTest.cpp | 87 ++++++++++++++++++++++-- 1 file changed, 80 insertions(+), 7 deletions(-) diff --git a/test/CompressedBlockPrefilteringTest.cpp b/test/CompressedBlockPrefilteringTest.cpp index 5c88477ccc..c9c7d5f9b8 100644 --- a/test/CompressedBlockPrefilteringTest.cpp +++ b/test/CompressedBlockPrefilteringTest.cpp @@ -173,28 +173,31 @@ static const auto testThrowError = //______________________________________________________________________________ template -auto makeRelExpr(const ValueId& referenceId) { +std::unique_ptr makeRelExpr(const ValueId& referenceId) { return std::make_unique(referenceId); } //______________________________________________________________________________ template -auto makeLogExpr(const ValueId& referenceId1, const ValueId& referenceId2) { +std::unique_ptr makeLogExpr(const ValueId& referenceId1, + const ValueId& referenceId2) { return std::make_unique(makeRelExpr(referenceId1), makeRelExpr(referenceId2)); } //______________________________________________________________________________ -template -auto makeNotExpression(const ValueId& referenceId1, - std::optional optId2) { +template +std::unique_ptr makeNotExpression( + const ValueId& referenceId1, const std::optional optId2) { if constexpr (check_is_relational_v && - check_is_logical_v && optId2.has_value()) { + check_is_logical_v) { + assert(optId2.has_value() && "Logical Expressions require two ValueIds"); return std::make_unique( makeLogExpr(referenceId1, optId2.value())); } else if constexpr (std::is_same_v) { return std::make_unique( - makeNotExpression(referenceId1)); + makeNotExpression(referenceId1, optId2)); } else { return std::make_unique(makeRelExpr(referenceId1)); } @@ -741,6 +744,76 @@ TEST(LogicalExpression, testNotExpression) { blocks.VocabId10); testExpression.test(1, blocks.blocks, {}, blocks.DoubleId33); + testExpression.test( + 2, blocks.blocks, {blocks.b1, blocks.b2, blocks.b7, blocks.b14}, + IntId(0)); + testExpression.test( + 2, blocks.blocks, + {blocks.b1, blocks.b2, blocks.b3, blocks.b4, blocks.b5, blocks.b6, + blocks.b7, blocks.b8, blocks.b9, blocks.b10, blocks.b11, blocks.b13, + blocks.b14}, + DoubleId(-6.25)); + testExpression.test( + 2, blocks.blocks, {blocks.b14}, VocabId(10)); + testExpression.test( + 2, blocks.blocks, + {blocks.b2, blocks.b3, blocks.b4, blocks.b7, blocks.b9, blocks.b10, + blocks.b14}, + DoubleId(3.99)); + testExpression + .test( + 2, blocks.blocks, + {blocks.b2, blocks.b3, blocks.b4, blocks.b5, blocks.b6, blocks.b7, + blocks.b8, blocks.b9, blocks.b10, blocks.b11, blocks.b12, blocks.b13, + blocks.b14}, + IntId(0), IntId(0)); + testExpression.test( + 2, blocks.blocks, {blocks.b5, blocks.b7, blocks.b14}, IntId(-10), + DoubleId(-14.02)); + testExpression + .test( + 2, blocks.blocks, + {blocks.b1, blocks.b2, blocks.b3, blocks.b4, blocks.b5, blocks.b6, + blocks.b7, blocks.b8, blocks.b9, blocks.b10, blocks.b11, blocks.b12, + blocks.b13, blocks.b14}, + IntId(10), DoubleId(-6.25)); + testExpression + .test( + 2, blocks.blocks, + {blocks.b5, blocks.b6, blocks.b7, blocks.b11, blocks.b12, blocks.b13, + blocks.b14}, + IntId(-4), DoubleId(-6.25)); + testExpression + .test( + 2, blocks.blocks, + {blocks.b1, blocks.b2, blocks.b3, blocks.b4, blocks.b5, blocks.b6, + blocks.b7, blocks.b8, blocks.b9, blocks.b10, blocks.b11, blocks.b12, + blocks.b13, blocks.b14}, + DoubleId(-7), IntId(6)); + testExpression + .test( + 2, blocks.blocks, + {blocks.b2, blocks.b3, blocks.b7, blocks.b8, blocks.b9, blocks.b10, + blocks.b14}, + IntId(0), DoubleId(6)); + testExpression + .test( + 2, blocks.blocks, {blocks.b5, blocks.b7, blocks.b13, blocks.b14}, + DoubleId(0), IntId(-10)); + testExpression.test( + 2, blocks.blocks, {blocks.b14, blocks.b15}, VocabId(10), VocabId(10)); + testExpression.test( + 2, blocks.blocks, {blocks.b6, blocks.b7, blocks.b11, blocks.b14}, + DoubleId(-4), IntId(-4)); + testExpression + .test( + 2, blocks.blocks, {blocks.b14}, IntId(-42), VocabId(0)); + testExpression + .test( + 2, blocks.blocks, {blocks.b14, blocks.b15}, VocabId(14), VocabId(15)); + testExpression.test( + 2, blocks.blocks, {blocks.b7, blocks.b11, blocks.b12, blocks.b14}, + DoubleId(-7.25), DoubleId(-6.25)); } //______________________________________________________________________________ From b2aa68930f3fdf365296a7335b74dbd59487e4e5 Mon Sep 17 00:00:00 2001 From: realHannes Date: Wed, 9 Oct 2024 16:24:25 +0200 Subject: [PATCH 35/50] just add all mixed blocks --- src/index/CompressedBlockPrefiltering.cpp | 14 +- test/CompressedBlockPrefilteringTest.cpp | 170 ++++++++++++---------- 2 files changed, 95 insertions(+), 89 deletions(-) diff --git a/src/index/CompressedBlockPrefiltering.cpp b/src/index/CompressedBlockPrefiltering.cpp index 2f0b3b8956..85a4cf13b0 100644 --- a/src/index/CompressedBlockPrefiltering.cpp +++ b/src/index/CompressedBlockPrefiltering.cpp @@ -187,22 +187,10 @@ std::vector RelationalExpression::evaluateImpl( getIdFromColumnIndex(block.firstTriple_, evaluationColumn); const auto secondId = getIdFromColumnIndex(block.lastTriple_, evaluationColumn); - const auto firstIdDatatype = firstId.getDatatype(); - const auto secondIdDatatype = secondId.getDatatype(); - const auto referenceIdDatatype = referenceId_.getDatatype(); valueIdsInput.push_back(firstId); valueIdsInput.push_back(secondId); - if ((firstIdDatatype != secondIdDatatype) && - // The block is only potentially relevant if at least one of the - // respective bounding Ids is of similar (comparable) type to - // refernceId_. However, this requires that the comparable datatypes - // are grouped/stand next to each other given the ValueId ordering - // on datatype (most relevant) bits. - (valueIdComparators::detail::areTypesCompatible(referenceIdDatatype, - firstIdDatatype) || - valueIdComparators::detail::areTypesCompatible(referenceIdDatatype, - secondIdDatatype))) { + if ((firstId.getDatatype() != secondId.getDatatype())) { mixedDatatypeBlocks.push_back(block); } } diff --git a/test/CompressedBlockPrefilteringTest.cpp b/test/CompressedBlockPrefilteringTest.cpp index c9c7d5f9b8..8caab1c0ff 100644 --- a/test/CompressedBlockPrefilteringTest.cpp +++ b/test/CompressedBlockPrefilteringTest.cpp @@ -304,24 +304,28 @@ TEST(RelationalExpression, testLessThanExpressions) { testExpression( 2, DoubleId(-4.121), blocks.blocks, {blocks.b5, blocks.b7, blocks.b11, blocks.b12, blocks.b13, blocks.b14}); - testExpression(2, VocabId(0), blocks.blocks, {blocks.b14}); - testExpression(2, VocabId(12), blocks.blocks, {blocks.b14, blocks.b15}); - testExpression(2, VocabId(14), blocks.blocks, {blocks.b14, blocks.b15}); + testExpression(2, VocabId(0), blocks.blocks, {blocks.b7, blocks.b14}); + testExpression(2, VocabId(12), blocks.blocks, + {blocks.b7, blocks.b14, blocks.b15}); + testExpression(2, VocabId(14), blocks.blocks, + {blocks.b7, blocks.b14, blocks.b15}); testExpression(2, VocabId(16), blocks.blocks, - {blocks.b14, blocks.b15, blocks.b16, blocks.b17}); + {blocks.b7, blocks.b14, blocks.b15, blocks.b16, blocks.b17}); // test blocks.otherBlocks - testExpression(2, blocks.undef, blocks.otherBlocks, {}); + testExpression(2, blocks.undef, blocks.otherBlocks, + {blocks.bd2, blocks.bd5, blocks.bd7}); testExpression(2, blocks.falseId, blocks.otherBlocks, - {blocks.bd2, blocks.bd5}); + {blocks.bd2, blocks.bd5, blocks.bd7}); testExpression(2, blocks.trueId, blocks.otherBlocks, - {blocks.bd2, blocks.bd3, blocks.bd5}); + {blocks.bd2, blocks.bd3, blocks.bd5, blocks.bd7}); testExpression(2, blocks.referenceDate1, blocks.otherBlocks, - {blocks.bd5, blocks.bd7}); + {blocks.bd2, blocks.bd5, blocks.bd7}); testExpression(2, blocks.referenceDateEqual, blocks.otherBlocks, - {blocks.bd5, blocks.bd7}); + {blocks.bd2, blocks.bd5, blocks.bd7}); testExpression(2, blocks.referenceDate2, blocks.otherBlocks, - {blocks.bd5, blocks.bd6, blocks.bd7}); - testExpression(2, BlankNodeId(11), blocks.otherBlocks, {blocks.bd7}); + {blocks.bd2, blocks.bd5, blocks.bd6, blocks.bd7}); + testExpression(2, BlankNodeId(11), blocks.otherBlocks, + {blocks.bd2, blocks.bd5, blocks.bd7}); } //______________________________________________________________________________ @@ -353,19 +357,22 @@ TEST(RelationalExpression, testLessEqualExpressions) { testExpression(2, DoubleId(-11.99999999999999), blocks.blocks, {blocks.b7, blocks.b13, blocks.b14}); testExpression(2, DoubleId(-14.03), blocks.blocks, {blocks.b7, blocks.b14}); - testExpression(2, VocabId(0), blocks.blocks, {blocks.b14}); - testExpression(2, VocabId(11), blocks.blocks, {blocks.b14, blocks.b15}); + testExpression(2, VocabId(0), blocks.blocks, {blocks.b7, blocks.b14}); + testExpression(2, VocabId(11), blocks.blocks, + {blocks.b7, blocks.b14, blocks.b15}); testExpression(2, VocabId(14), blocks.blocks, - {blocks.b14, blocks.b15, blocks.b16, blocks.b17}); + {blocks.b7, blocks.b14, blocks.b15, blocks.b16, blocks.b17}); // test block.otherBlocks - testExpression(2, blocks.undef, blocks.otherBlocks, {}); + testExpression(2, blocks.undef, blocks.otherBlocks, + {blocks.bd2, blocks.bd5, blocks.bd7}); testExpression(2, blocks.falseId, blocks.otherBlocks, - {blocks.bd2, blocks.bd3, blocks.bd5}); + {blocks.bd2, blocks.bd3, blocks.bd5, blocks.bd7}); testExpression(2, blocks.trueId, blocks.otherBlocks, - {blocks.bd2, blocks.bd3, blocks.bd4, blocks.bd5}); + {blocks.bd2, blocks.bd3, blocks.bd4, blocks.bd5, blocks.bd7}); testExpression(2, blocks.referenceDateEqual, blocks.otherBlocks, - {blocks.bd5, blocks.bd6, blocks.bd7}); - testExpression(2, BlankNodeId(11), blocks.otherBlocks, {blocks.bd7}); + {blocks.bd2, blocks.bd5, blocks.bd6, blocks.bd7}); + testExpression(2, BlankNodeId(11), blocks.otherBlocks, + {blocks.bd2, blocks.bd5, blocks.bd7}); } //______________________________________________________________________________ @@ -403,21 +410,24 @@ TEST(RelationalExpression, testGreaterThanExpression) { {blocks.b1, blocks.b2, blocks.b3, blocks.b4, blocks.b7, blocks.b8, blocks.b9, blocks.b10, blocks.b11, blocks.b14}); testExpression(2, IntId(33), blocks.blocks, {blocks.b7, blocks.b14}); - testExpression(2, VocabId(22), blocks.blocks, {blocks.b14}); - testExpression(2, VocabId(14), blocks.blocks, {blocks.b14, blocks.b17}); + testExpression(2, VocabId(22), blocks.blocks, {blocks.b7, blocks.b14}); + testExpression(2, VocabId(14), blocks.blocks, + {blocks.b7, blocks.b14, blocks.b17}); testExpression(2, VocabId(12), blocks.blocks, - {blocks.b14, blocks.b15, blocks.b16, blocks.b17}); + {blocks.b7, blocks.b14, blocks.b15, blocks.b16, blocks.b17}); // test blocks.otherBlocks - testExpression(2, blocks.undef, blocks.otherBlocks, {}); + testExpression(2, blocks.undef, blocks.otherBlocks, + {blocks.bd2, blocks.bd5, blocks.bd7}); testExpression(2, blocks.falseId, blocks.otherBlocks, - {blocks.bd2, blocks.bd4, blocks.bd5}); + {blocks.bd2, blocks.bd4, blocks.bd5, blocks.bd7}); testExpression(2, blocks.trueId, blocks.otherBlocks, - {blocks.bd2, blocks.bd5}); + {blocks.bd2, blocks.bd5, blocks.bd7}); testExpression(2, blocks.referenceDateEqual, blocks.otherBlocks, - {blocks.bd5, blocks.bd7}); + {blocks.bd2, blocks.bd5, blocks.bd7}); testExpression(2, blocks.referenceDate1, blocks.otherBlocks, - {blocks.bd5, blocks.bd6, blocks.bd7}); - testExpression(2, IntId(5), blocks.otherBlocks, {}); + {blocks.bd2, blocks.bd5, blocks.bd6, blocks.bd7}); + testExpression(2, IntId(5), blocks.otherBlocks, + {blocks.bd2, blocks.bd5, blocks.bd7}); } //______________________________________________________________________________ @@ -457,19 +467,22 @@ TEST(RelationalExpression, testGreaterEqualExpression) { {blocks.b4, blocks.b7, blocks.b10, blocks.b14}); testExpression(2, DoubleId(10.0001), blocks.blocks, {blocks.b7, blocks.b14}); testExpression(2, VocabId(14), blocks.blocks, - {blocks.b14, blocks.b15, blocks.b16, blocks.b17}); + {blocks.b7, blocks.b14, blocks.b15, blocks.b16, blocks.b17}); testExpression(2, VocabId(10), blocks.blocks, - {blocks.b14, blocks.b15, blocks.b16, blocks.b17}); - testExpression(2, VocabId(17), blocks.blocks, {blocks.b14, blocks.b17}); + {blocks.b7, blocks.b14, blocks.b15, blocks.b16, blocks.b17}); + testExpression(2, VocabId(17), blocks.blocks, + {blocks.b7, blocks.b14, blocks.b17}); // test blocks.otherBlocks - testExpression(2, blocks.undef, blocks.otherBlocks, {}); + testExpression(2, blocks.undef, blocks.otherBlocks, + {blocks.bd2, blocks.bd5, blocks.bd7}); testExpression(2, blocks.falseId, blocks.otherBlocks, - {blocks.bd2, blocks.bd3, blocks.bd4, blocks.bd5}); + {blocks.bd2, blocks.bd3, blocks.bd4, blocks.bd5, blocks.bd7}); testExpression(2, blocks.trueId, blocks.otherBlocks, - {blocks.bd2, blocks.bd4, blocks.bd5}); + {blocks.bd2, blocks.bd4, blocks.bd5, blocks.bd7}); testExpression(2, blocks.referenceDateEqual, blocks.otherBlocks, - {blocks.bd5, blocks.bd6, blocks.bd7}); - testExpression(2, VocabId(0), blocks.otherBlocks, {}); + {blocks.bd2, blocks.bd5, blocks.bd6, blocks.bd7}); + testExpression(2, VocabId(0), blocks.otherBlocks, + {blocks.bd2, blocks.bd5, blocks.bd7}); } //______________________________________________________________________________ @@ -499,22 +512,24 @@ TEST(RelationalExpression, testEqualExpression) { {blocks.b3, blocks.b7, blocks.b10, blocks.b14}); testExpression(2, DoubleId(1.5), blocks.blocks, {blocks.b2, blocks.b7, blocks.b14}); - testExpression(2, VocabId(1), blocks.blocks, {blocks.b14}); + testExpression(2, VocabId(1), blocks.blocks, {blocks.b7, blocks.b14}); testExpression(2, VocabId(14), blocks.blocks, - {blocks.b14, blocks.b15, blocks.b16, blocks.b17}); - testExpression(2, VocabId(11), blocks.blocks, {blocks.b14, blocks.b15}); - testExpression(2, VocabId(17), blocks.blocks, {blocks.b14, blocks.b17}); + {blocks.b7, blocks.b14, blocks.b15, blocks.b16, blocks.b17}); + testExpression(2, VocabId(11), blocks.blocks, + {blocks.b7, blocks.b14, blocks.b15}); + testExpression(2, VocabId(17), blocks.blocks, + {blocks.b7, blocks.b14, blocks.b17}); testExpression(2, IntId(-4), blocks.blocks, {blocks.b6, blocks.b7, blocks.b11, blocks.b14}); // test blocks.otherBlocks testExpression(2, blocks.trueId, blocks.otherBlocks, - {blocks.bd2, blocks.bd4, blocks.bd5}); + {blocks.bd2, blocks.bd4, blocks.bd5, blocks.bd7}); testExpression(2, blocks.referenceDate1, blocks.otherBlocks, - {blocks.bd5, blocks.bd7}); + {blocks.bd2, blocks.bd5, blocks.bd7}); testExpression(2, blocks.referenceDateEqual, blocks.otherBlocks, - {blocks.bd5, blocks.bd6, blocks.bd7}); + {blocks.bd2, blocks.bd5, blocks.bd6, blocks.bd7}); testExpression(2, blocks.referenceDate2, blocks.otherBlocks, - {blocks.bd5, blocks.bd7}); + {blocks.bd2, blocks.bd5, blocks.bd7}); } //______________________________________________________________________________ @@ -552,21 +567,22 @@ TEST(RelationalExpression, testNotEqualExpression) { blocks.b6, blocks.b7, blocks.b8, blocks.b9, blocks.b10, blocks.b11, blocks.b12, blocks.b13, blocks.b14}); testExpression(2, VocabId(0), blocks.blocks, - {blocks.b14, blocks.b15, blocks.b16, blocks.b17}); + {blocks.b7, blocks.b14, blocks.b15, blocks.b16, blocks.b17}); testExpression(2, VocabId(7), blocks.blocks, - {blocks.b14, blocks.b15, blocks.b16, blocks.b17}); + {blocks.b7, blocks.b14, blocks.b15, blocks.b16, blocks.b17}); testExpression(2, VocabId(14), blocks.blocks, - {blocks.b14, blocks.b15, blocks.b17}); + {blocks.b7, blocks.b14, blocks.b15, blocks.b17}); testExpression(2, VocabId(17), blocks.blocks, - {blocks.b14, blocks.b15, blocks.b16, blocks.b17}); + {blocks.b7, blocks.b14, blocks.b15, blocks.b16, blocks.b17}); // test blocks.otherBlocks - testExpression(2, blocks.undef, blocks.otherBlocks, {}); + testExpression(2, blocks.undef, blocks.otherBlocks, + {blocks.bd2, blocks.bd5, blocks.bd7}); testExpression(2, blocks.falseId, blocks.otherBlocks, - {blocks.bd2, blocks.bd4, blocks.bd5}); + {blocks.bd2, blocks.bd4, blocks.bd5, blocks.bd7}); testExpression(2, blocks.referenceDateEqual, blocks.otherBlocks, - {blocks.bd5, blocks.bd7}); + {blocks.bd2, blocks.bd5, blocks.bd7}); testExpression(2, blocks.referenceDate1, blocks.otherBlocks, - {blocks.bd5, blocks.bd6, blocks.bd7}); + {blocks.bd2, blocks.bd5, blocks.bd6, blocks.bd7}); } //______________________________________________________________________________ @@ -580,27 +596,25 @@ TEST(LogicalExpression, testAndExpression) { testExpression; testExpression.test( 2, VocabId(10), VocabId(10), blocks.blocks, - {blocks.b14, blocks.b15, blocks.b16, blocks.b17}); + {blocks.b7, blocks.b14, blocks.b15, blocks.b16, blocks.b17}); testExpression.test( - 2, VocabId(0), VocabId(17), blocks.blocks, {blocks.b14, blocks.b17}); + 2, VocabId(0), VocabId(17), blocks.blocks, + {blocks.b7, blocks.b14, blocks.b17}); testExpression.test( - 2, VocabId(12), VocabId(17), blocks.blocks, {blocks.b14}); + 2, VocabId(12), VocabId(17), blocks.blocks, {blocks.b7, blocks.b14}); testExpression.test( - 2, VocabId(10), VocabId(14), blocks.blocks, {blocks.b14, blocks.b15}); + 2, VocabId(10), VocabId(14), blocks.blocks, + {blocks.b7, blocks.b14, blocks.b15}); testExpression.test( - 2, VocabId(0), VocabId(10), blocks.blocks, {blocks.b14}); + 2, VocabId(0), VocabId(10), blocks.blocks, {blocks.b7, blocks.b14}); testExpression.test( 2, VocabId(17), VocabId(17), blocks.blocks, - {blocks.b14, blocks.b15, blocks.b16, blocks.b17}); + {blocks.b7, blocks.b14, blocks.b15, blocks.b16, blocks.b17}); testExpression.test( 2, DoubleId(-6.25), IntId(-7), blocks.blocks, {blocks.b7, blocks.b14}); testExpression.test( 2, DoubleId(-6.25), DoubleId(-6.25), blocks.blocks, {blocks.b7, blocks.b14}); - // Corner case: Logically it is impossible to satisfy (x > 0) and (x < 0) at - // the same time. But given that we evaluate on block boundaries and their - // possible values (Ids) in between, block b7 satisfies both conditions over - // its range [IntId(-4)... DoubleId(2)] for column 2. testExpression.test( 2, IntId(0), IntId(0), blocks.blocks, {blocks.b7, blocks.b14}); testExpression.test( @@ -645,11 +659,12 @@ TEST(LogicalExpression, testOrExpression) { testExpression; testExpression.test( 2, VocabId(22), VocabId(0), blocks.blocks, - {blocks.b14, blocks.b15, blocks.b16, blocks.b17}); + {blocks.b7, blocks.b14, blocks.b15, blocks.b16, blocks.b17}); testExpression.test( - 2, VocabId(0), VocabId(16), blocks.blocks, {blocks.b14, blocks.b17}); + 2, VocabId(0), VocabId(16), blocks.blocks, + {blocks.b7, blocks.b14, blocks.b17}); testExpression.test( - 2, VocabId(17), VocabId(242), blocks.blocks, {blocks.b14}); + 2, VocabId(17), VocabId(242), blocks.blocks, {blocks.b7, blocks.b14}); testExpression.test( 2, DoubleId(-5.95), VocabId(14), blocks.blocks, {blocks.b5, blocks.b7, blocks.b11, blocks.b12, blocks.b13, blocks.b14, @@ -694,16 +709,17 @@ TEST(LogicalExpression, testNotExpression) { MetadataBlocks blocks{}; TestNotExpression> testExpression{}; testExpression.test( - 2, blocks.blocks, {blocks.b14, blocks.b15, blocks.b16, blocks.b17}, - VocabId(2)); + 2, blocks.blocks, + {blocks.b7, blocks.b14, blocks.b15, blocks.b16, blocks.b17}, VocabId(2)); testExpression.test( - 2, blocks.blocks, {blocks.b14, blocks.b15, blocks.b17}, VocabId(14)); - testExpression.test( - 2, blocks.blocks, {blocks.b14, blocks.b15, blocks.b16, blocks.b17}, + 2, blocks.blocks, {blocks.b7, blocks.b14, blocks.b15, blocks.b17}, VocabId(14)); + testExpression.test( + 2, blocks.blocks, + {blocks.b7, blocks.b14, blocks.b15, blocks.b16, blocks.b17}, VocabId(14)); testExpression.test( - 2, blocks.blocks, {blocks.b14, blocks.b15, blocks.b16, blocks.b17}, - VocabId(0)); + 2, blocks.blocks, + {blocks.b7, blocks.b14, blocks.b15, blocks.b16, blocks.b17}, VocabId(0)); testExpression.test( 2, blocks.blocks, {blocks.b1, blocks.b2, blocks.b3, blocks.b4, blocks.b5, blocks.b6, @@ -754,7 +770,7 @@ TEST(LogicalExpression, testNotExpression) { blocks.b14}, DoubleId(-6.25)); testExpression.test( - 2, blocks.blocks, {blocks.b14}, VocabId(10)); + 2, blocks.blocks, {blocks.b7, blocks.b14}, VocabId(10)); testExpression.test( 2, blocks.blocks, {blocks.b2, blocks.b3, blocks.b4, blocks.b7, blocks.b9, blocks.b10, @@ -801,16 +817,18 @@ TEST(LogicalExpression, testNotExpression) { 2, blocks.blocks, {blocks.b5, blocks.b7, blocks.b13, blocks.b14}, DoubleId(0), IntId(-10)); testExpression.test( - 2, blocks.blocks, {blocks.b14, blocks.b15}, VocabId(10), VocabId(10)); + 2, blocks.blocks, {blocks.b7, blocks.b14, blocks.b15}, VocabId(10), + VocabId(10)); testExpression.test( 2, blocks.blocks, {blocks.b6, blocks.b7, blocks.b11, blocks.b14}, DoubleId(-4), IntId(-4)); testExpression .test( - 2, blocks.blocks, {blocks.b14}, IntId(-42), VocabId(0)); + 2, blocks.blocks, {blocks.b7, blocks.b14}, IntId(-42), VocabId(0)); testExpression .test( - 2, blocks.blocks, {blocks.b14, blocks.b15}, VocabId(14), VocabId(15)); + 2, blocks.blocks, {blocks.b7, blocks.b14, blocks.b15}, VocabId(14), + VocabId(15)); testExpression.test( 2, blocks.blocks, {blocks.b7, blocks.b11, blocks.b12, blocks.b14}, DoubleId(-7.25), DoubleId(-6.25)); From 7e61cb18baa272c077e70dc0ed5c3319557a97c9 Mon Sep 17 00:00:00 2001 From: realHannes Date: Wed, 9 Oct 2024 18:37:38 +0200 Subject: [PATCH 36/50] remove unnecessary values in test context --- test/SparqlExpressionTestHelpers.h | 17 ----------------- 1 file changed, 17 deletions(-) diff --git a/test/SparqlExpressionTestHelpers.h b/test/SparqlExpressionTestHelpers.h index 80cdae8a12..55a6734b51 100644 --- a/test/SparqlExpressionTestHelpers.h +++ b/test/SparqlExpressionTestHelpers.h @@ -61,10 +61,6 @@ struct TestContext { // vocab. Id notInVocabA, notInVocabB, notInVocabC, notInVocabD, notInVocabAelpha, notInVocabIri, notInVocabIriLit; - // LocalVocab IDs used for testing the block pre-filtering procedure in - // CompressedBlockPrefilteringTest - Id berlin, bonn, cologne, dortmund, düsseldorf, essen, frankfurt, - frankfurt_oder, hamburg, hannover, ingolstadt, karlsruhe, munich; TestContext() { // First get some IDs for strings from the vocabulary to later reuse them. // Note the `u_` inserted for the blank node (see 'BlankNode.cpp'). @@ -99,19 +95,6 @@ struct TestContext { addLocalIri(""); notInVocabIriLit = addLocalLiteral( "http://www.w3.org/1999/02/22-rdf-syntax-ns#langString"); - berlin = addLocalLiteral("berlin"); - bonn = addLocalLiteral("bonn"); - cologne = addLocalLiteral("cologne"); - düsseldorf = addLocalLiteral("düsseldorf"); - dortmund = addLocalLiteral("dortmund"); - essen = addLocalLiteral("essen"); - frankfurt = addLocalLiteral("frankfurt"); - frankfurt_oder = addLocalLiteral("frankfurt (FFO)"); - hamburg = addLocalLiteral("hamburg"); - hannover = addLocalLiteral("hannover"); - ingolstadt = addLocalLiteral("ingolstadt"); - karlsruhe = addLocalLiteral("karlsruhe"); - munich = addLocalLiteral("munich"); // Set up the `table` that represents the previous partial query results. It // has five columns/variables: ?ints (only integers), ?doubles (only From 009ebed90c0d98d8758ec65c210941641537f035 Mon Sep 17 00:00:00 2001 From: realHannes Date: Thu, 10 Oct 2024 07:44:08 +0200 Subject: [PATCH 37/50] for sonar check --- src/index/CompressedBlockPrefiltering.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/index/CompressedBlockPrefiltering.cpp b/src/index/CompressedBlockPrefiltering.cpp index 85a4cf13b0..47d503e514 100644 --- a/src/index/CompressedBlockPrefiltering.cpp +++ b/src/index/CompressedBlockPrefiltering.cpp @@ -190,7 +190,7 @@ std::vector RelationalExpression::evaluateImpl( valueIdsInput.push_back(firstId); valueIdsInput.push_back(secondId); - if ((firstId.getDatatype() != secondId.getDatatype())) { + if (firstId.getDatatype() != secondId.getDatatype()) { mixedDatatypeBlocks.push_back(block); } } From 5b42c1f91126c6780ed532b0e0dab6f30feeafcf Mon Sep 17 00:00:00 2001 From: realHannes Date: Sat, 12 Oct 2024 14:02:36 +0200 Subject: [PATCH 38/50] methods to get PrefilterExpresssions from RelationalExpressions (SparqlExpression) --- .../sparqlExpressions/LiteralExpression.h | 10 ++-- .../RelationalExpressions.cpp | 50 +++++++++++++++++++ .../sparqlExpressions/RelationalExpressions.h | 8 +++ .../sparqlExpressions/SparqlExpression.cpp | 7 +++ .../sparqlExpressions/SparqlExpression.h | 8 +++ .../SparqlExpressionPimpl.cpp | 7 +++ .../sparqlExpressions/SparqlExpressionPimpl.h | 5 ++ src/index/CompressedBlockPrefiltering.cpp | 12 ----- src/index/CompressedBlockPrefiltering.h | 12 +++++ 9 files changed, 102 insertions(+), 17 deletions(-) diff --git a/src/engine/sparqlExpressions/LiteralExpression.h b/src/engine/sparqlExpressions/LiteralExpression.h index 9bd27b3406..9e913a0b30 100644 --- a/src/engine/sparqlExpressions/LiteralExpression.h +++ b/src/engine/sparqlExpressions/LiteralExpression.h @@ -24,7 +24,7 @@ class LiteralExpression : public SparqlExpression { mutable std::atomic cachedResult_ = nullptr; public: - // _________________________________________________________________________ + // ___________________________________________________________________________ explicit LiteralExpression(T _value) : _value{std::move(_value)} {} ~LiteralExpression() override { delete cachedResult_.load(std::memory_order_relaxed); @@ -83,7 +83,7 @@ class LiteralExpression : public SparqlExpression { } } - // _________________________________________________________________________ + // ___________________________________________________________________________ vector getUnaggregatedVariables() const override { if constexpr (std::is_same_v) { return {_value}; @@ -92,7 +92,7 @@ class LiteralExpression : public SparqlExpression { } } - // ______________________________________________________________________ + // ___________________________________________________________________________ string getCacheKey(const VariableToColumnMap& varColMap) const override { if constexpr (std::is_same_v) { if (!varColMap.contains(_value)) { @@ -118,13 +118,13 @@ class LiteralExpression : public SparqlExpression { } } - // ______________________________________________________________________ + // ___________________________________________________________________________ bool isConstantExpression() const override { return !std::is_same_v; } protected: - // _________________________________________________________________________ + // ___________________________________________________________________________ std::optional<::Variable> getVariableOrNullopt() const override { if constexpr (std::is_same_v) { return _value; diff --git a/src/engine/sparqlExpressions/RelationalExpressions.cpp b/src/engine/sparqlExpressions/RelationalExpressions.cpp index 91effef243..c60d49909c 100644 --- a/src/engine/sparqlExpressions/RelationalExpressions.cpp +++ b/src/engine/sparqlExpressions/RelationalExpressions.cpp @@ -409,6 +409,56 @@ ExpressionResult InExpression::evaluate( return result; } +// _____________________________________________________________________________ +template +std::optional, Variable>> +RelationalExpression::getPrefilterExpressionForMetadata() const { + AD_CORRECTNESS_CHECK(children_.size() == 2); + const SparqlExpression::Ptr& child0 = children_.at(0); + const SparqlExpression::Ptr& child1 = children_.at(1); + + auto checkBinarySearchEvaluable = [](const SparqlExpression::Ptr& child0, + const SparqlExpression::Ptr& child1) + -> std::optional> { + if (const auto* literalValueId = + dynamic_cast(child0.get())) { + if (const auto* literalVariable = + dynamic_cast(child1.get())) { + return std::make_pair(literalValueId->value(), + literalVariable->value()); + } + } + return std::nullopt; + }; + + auto createPairFilterExpressionVariable = + [this](std::optional> optValuePair) + -> std::pair, + Variable> { + const auto& [valueId, variable] = optValuePair.value(); + return std::make_pair( + std::make_unique>( + valueId), + variable); + }; + + // Option 1: child0 is a (constant) reference ValueId, while child1 contains a + // Variable to the respective column on which we want to filter. + const auto& optionalPair1 = checkBinarySearchEvaluable(child0, child1); + if (optionalPair1.has_value()) { + return createPairFilterExpressionVariable(optionalPair1); + } + + // Option 2: child1 is a (constant) reference ValueId, while child0 contains a + // Variable to the respective column on which we want to filter. + const auto& optionalPair2 = checkBinarySearchEvaluable(child1, child0); + if (optionalPair2.has_value()) { + return createPairFilterExpressionVariable(optionalPair2); + } + return std::nullopt; +} + // _____________________________________________________________________________ std::span InExpression::childrenImpl() { return children_; diff --git a/src/engine/sparqlExpressions/RelationalExpressions.h b/src/engine/sparqlExpressions/RelationalExpressions.h index f8364f0137..4eae15531c 100644 --- a/src/engine/sparqlExpressions/RelationalExpressions.h +++ b/src/engine/sparqlExpressions/RelationalExpressions.h @@ -38,6 +38,14 @@ class RelationalExpression : public SparqlExpression { // the appropriate data. std::optional getLanguageFilterExpression() const override; + // If this `RelationalExpression` is binary evaluable, return the + // corresponding `PrefilterExpression` for the pre-filtering procedure on + // `CompressedBlockMetadata`. In addition we return the `Variable` that + // corresponds to the sorted column. + std::optional, Variable>> + getPrefilterExpressionForMetadata() const override; + // These expressions are typically used inside `FILTER` clauses, so we need // proper estimates. Estimates getEstimatesForFilterExpression( diff --git a/src/engine/sparqlExpressions/SparqlExpression.cpp b/src/engine/sparqlExpressions/SparqlExpression.cpp index 7d1ad5dcf6..c62092498f 100644 --- a/src/engine/sparqlExpressions/SparqlExpression.cpp +++ b/src/engine/sparqlExpressions/SparqlExpression.cpp @@ -108,6 +108,13 @@ Estimates SparqlExpression::getEstimatesForFilterExpression( return {inputSizeEstimate, inputSizeEstimate}; } +// _____________________________________________________________________________ +std::optional, Variable>> +SparqlExpression::getPrefilterExpressionForMetadata() const { + return std::nullopt; +}; + // _____________________________________________________________________________ bool SparqlExpression::isConstantExpression() const { return false; } diff --git a/src/engine/sparqlExpressions/SparqlExpression.h b/src/engine/sparqlExpressions/SparqlExpression.h index 6a64c4551e..e82e86275b 100644 --- a/src/engine/sparqlExpressions/SparqlExpression.h +++ b/src/engine/sparqlExpressions/SparqlExpression.h @@ -96,6 +96,14 @@ class SparqlExpression { [[maybe_unused]] const std::optional& primarySortKeyVariable) const; + // Returns a `PrefilterExpression` with the respective `Variable` for the + // necessarily sorted column, if there are relational expressions (e.g. + // `>=` or `!=`) that are binary evaluable. Those expressions can also be + // linked via logical expressions (`&&`, `||` and `!`). + virtual std::optional, Variable>> + getPrefilterExpressionForMetadata() const; + // Returns true iff this expression is a simple constant. Default // implementation returns `false`. virtual bool isConstantExpression() const; diff --git a/src/engine/sparqlExpressions/SparqlExpressionPimpl.cpp b/src/engine/sparqlExpressions/SparqlExpressionPimpl.cpp index ed1e004bd0..92fa18cbab 100644 --- a/src/engine/sparqlExpressions/SparqlExpressionPimpl.cpp +++ b/src/engine/sparqlExpressions/SparqlExpressionPimpl.cpp @@ -92,6 +92,13 @@ auto SparqlExpressionPimpl::getEstimatesForFilterExpression( primarySortKeyVariable); } +//_____________________________________________________________________________ +std::optional, Variable>> +SparqlExpressionPimpl::getPrefilterExpressionForMetadata() const { + return _pimpl->getPrefilterExpressionForMetadata(); +} + // _____________________________________________________________________________ bool SparqlExpressionPimpl::containsLangExpression() const { return _pimpl->containsLangExpression(); diff --git a/src/engine/sparqlExpressions/SparqlExpressionPimpl.h b/src/engine/sparqlExpressions/SparqlExpressionPimpl.h index c9b023c020..f32f23203d 100644 --- a/src/engine/sparqlExpressions/SparqlExpressionPimpl.h +++ b/src/engine/sparqlExpressions/SparqlExpressionPimpl.h @@ -9,6 +9,7 @@ #include #include "engine/VariableToColumnMap.h" +#include "index/CompressedBlockPrefiltering.h" #include "parser/data/Variable.h" #include "util/HashMap.h" #include "util/HashSet.h" @@ -111,6 +112,10 @@ class SparqlExpressionPimpl { uint64_t inputSizeEstimate, const std::optional& primarySortKeyVariable); + std::optional, Variable>> + getPrefilterExpressionForMetadata() const; + SparqlExpression* getPimpl() { return _pimpl.get(); } [[nodiscard]] const SparqlExpression* getPimpl() const { return _pimpl.get(); diff --git a/src/index/CompressedBlockPrefiltering.cpp b/src/index/CompressedBlockPrefiltering.cpp index 47d503e514..6b7607af09 100644 --- a/src/index/CompressedBlockPrefiltering.cpp +++ b/src/index/CompressedBlockPrefiltering.cpp @@ -287,16 +287,4 @@ std::vector NotExpression::evaluateImpl( return child_->evaluate(input, evaluationColumn); }; -//______________________________________________________________________________ -// Necessary instantiation of template specializations -template class RelationalExpression; -template class RelationalExpression; -template class RelationalExpression; -template class RelationalExpression; -template class RelationalExpression; -template class RelationalExpression; - -template class LogicalExpression; -template class LogicalExpression; - } // namespace prefilterExpressions diff --git a/src/index/CompressedBlockPrefiltering.h b/src/index/CompressedBlockPrefiltering.h index 75960c05ec..70ff7fdfd7 100644 --- a/src/index/CompressedBlockPrefiltering.h +++ b/src/index/CompressedBlockPrefiltering.h @@ -121,6 +121,18 @@ class NotExpression : public PrefilterExpression { size_t evaluationColumn) const override; }; +//______________________________________________________________________________ +// Instanitate templates with specializations (for linking accessibility) +template class RelationalExpression; +template class RelationalExpression; +template class RelationalExpression; +template class RelationalExpression; +template class RelationalExpression; +template class RelationalExpression; + +template class LogicalExpression; +template class LogicalExpression; + //______________________________________________________________________________ // Definition of the RelationalExpression for LT, LE, EQ, NE, GE and GT. using LessThanExpression = prefilterExpressions::RelationalExpression< From 6cf66095085d5bdc9e6291f068657b6e165fc8a7 Mon Sep 17 00:00:00 2001 From: realHannes Date: Tue, 22 Oct 2024 10:52:39 +0200 Subject: [PATCH 39/50] Completed the implementation for constructing PrefilterExpressions from a given SparqlExpression. --- .../NumericBinaryExpressions.cpp | 206 ++++++- .../NumericUnaryExpressions.cpp | 37 +- .../RelationalExpressions.cpp | 33 +- .../sparqlExpressions/RelationalExpressions.h | 6 +- .../sparqlExpressions/SparqlExpression.cpp | 6 +- .../sparqlExpressions/SparqlExpression.h | 15 +- .../SparqlExpressionPimpl.cpp | 7 +- .../sparqlExpressions/SparqlExpressionPimpl.h | 11 +- src/index/CompressedBlockPrefiltering.cpp | 87 ++- src/index/CompressedBlockPrefiltering.h | 39 ++ src/parser/data/Variable.h | 5 + test/CMakeLists.txt | 2 +- test/CompressedBlockPrefilteringTest.cpp | 565 +++++++++++++++++- 13 files changed, 947 insertions(+), 72 deletions(-) diff --git a/src/engine/sparqlExpressions/NumericBinaryExpressions.cpp b/src/engine/sparqlExpressions/NumericBinaryExpressions.cpp index fca4da662d..a201cd0d35 100644 --- a/src/engine/sparqlExpressions/NumericBinaryExpressions.cpp +++ b/src/engine/sparqlExpressions/NumericBinaryExpressions.cpp @@ -33,7 +33,8 @@ inline auto subtract = makeNumericExpression>(); NARY_EXPRESSION(SubtractExpression, 2, FV); -// Or +// OR and AND +// _____________________________________________________________________________ inline auto orLambda = [](TernaryBool a, TernaryBool b) { using enum TernaryBool; if (a == True || b == True) { @@ -45,11 +46,7 @@ inline auto orLambda = [](TernaryBool a, TernaryBool b) { return Id::makeUndefined(); }; -NARY_EXPRESSION(OrExpression, 2, - FV, - SET); - -// And +// _____________________________________________________________________________ inline auto andLambda = [](TernaryBool a, TernaryBool b) { using enum TernaryBool; if (a == True && b == True) { @@ -60,9 +57,198 @@ inline auto andLambda = [](TernaryBool a, TernaryBool b) { } return Id::makeUndefined(); }; -NARY_EXPRESSION(AndExpression, 2, - FV, - SET); + +namespace constructPrefilterExpr { + +// _____________________________________________________________________________ +template +std::optional> mergeChildrenAndLogicImpl( + std::optional>&& leftChildExprs, + std::optional>&& rightChildExprs) { + if (!leftChildExprs.has_value() || !rightChildExprs.has_value()) { + // If we have two children that yield no PrefilterExpressions, then there + // is also no PrefilterExpression over the AND (&&) conjunction available. + if (!leftChildExprs.has_value() && !rightChildExprs.has_value()) { + return std::nullopt; + } + // Given that only one of the children returned a PrefilterExpression + // vector and the pre-filter construction logic of AND (&&), we can just + // return the corresponding (non-empty) vector. + // + // EXAMPLE why this works: + // Assume that left is - {(>=5, ?x), (<=6, ?y)} + // and right is - std::nullopt ({undefinable prefilter}) + // Remark: left represents the expression ?x >= 5 && ?y <= 6 + // + // For this AND conjunction {(>=5, ?x), (<=6, ?y)} && {undef. + // prefilter}, we can at least (partly) pre-define the plausible true + // evaluation range with the left child. This is because the respective + // expressions contained in left child must always yield true + // for making this AND conjunction expression true. + return leftChildExprs.has_value() ? std::move(leftChildExprs.value()) + : std::move(rightChildExprs.value()); + } + // Merge the PrefilterExpression vectors from the left and right child. + // Remark: The vectors contain std::pairs, sorted by their respective + // variable to the PrefilterExpression. + auto& expressionsLeft = leftChildExprs.value(); + auto& expressionsRight = rightChildExprs.value(); + auto itLeft = expressionsLeft.begin(); + auto itRight = expressionsRight.begin(); + std::vector resPairs; + while (itLeft != expressionsLeft.end() && itRight != expressionsRight.end()) { + auto& [exprLeft, varLeft] = *itLeft; + auto& [exprRight, varRight] = *itRight; + // For our pre-filtering logic over index scans, we need exactly one + // PrefilterExpression for each of the variables. Thus if the left and right + // child contain a PrefilterExpression w.r.t. the same variable, combine + // them here over a (new) prefilter AndExpression. + if (varLeft == varRight) { + resPairs.emplace_back(std::make_unique( + std::move(exprLeft), std::move(exprRight)), + varLeft); + ++itLeft; + ++itRight; + } else if (varLeft < varRight) { + resPairs.emplace_back(std::move(*itLeft)); + ++itLeft; + } else { + resPairs.emplace_back(std::move(*itRight)); + ++itRight; + } + } + std::ranges::move(itLeft, expressionsLeft.end(), + std::back_inserter(resPairs)); + std::ranges::move(itRight, expressionsRight.end(), + std::back_inserter(resPairs)); + return resPairs; +} + +//______________________________________________________________________________ +template +std::optional> mergeChildrenOrLogicImpl( + std::optional>&& leftChildExprs, + std::optional>&& rightChildExprs) { + if (!leftChildExprs.has_value() || !rightChildExprs.has_value()) { + // If one of the children yields no PrefilterExpressions, we simply can't + // further define a PrefilterExpression with this OR (||) conjunction. + // + // EXAMPLE / REASON: + // Assume that left is - {(=10, ?x), (=10, ?y)} + // and right is - std::nullopt ({undefinable prefilter}) + // Remark: left represents the expression ?x = 10 && ?y = 10. + // We can't define a PrefilterExpression here because left must not always + // yield true when this OR expression evaluates to true (because right can + // plausibly yield true). + // And because we can't definitely say that one child (and which of the two + // children) must be true, pre-filtering w.r.t. underlying compressed blocks + // is not possible here. + // In short: OR has multiple congifurations that yield true w.r.t. two child + // expressions => we can't define a distinct PrefilterExpression. + return std::nullopt; + } + auto& expressionsLeft = leftChildExprs.value(); + auto& expressionsRight = rightChildExprs.value(); + auto itLeft = expressionsLeft.begin(); + auto itRight = expressionsRight.begin(); + std::vector resPairs; + while (itLeft != expressionsLeft.end() && itRight != expressionsRight.end()) { + auto& [exprLeft, varLeft] = *itLeft; + auto& [exprRight, varRight] = *itRight; + // The logic that is implemented with this loop. + // + // EXAMPLE 1 + // left child: {(>=5, ?y)} + // right child: {(<=3, ?x)} + // We want to pre-filter the blocks for the expression: ?y >= 5 || ?x <= 3 + // No PrefilterExpression will be returned. + // + // EXAMPLE 2 + // left child: {(>=5, ?x)} + // right child: {(=0, ?x)} + // We want to pre-filter the blocks to the expression: ?x >= 5 || ?x = 0 + // The resulting PrefilterExpression is {((>=5 OR =0), ?x)} + // + // EXAMPLE 3 + // left child: {(>=10, ?x), (!=0, ?y)} + // right child: {(<= 0, ?x)} + // We have to construct a PrefilterExpression for (?x >= 10 && ?y != 0) || + // ?x <= 0. If this OR expression yields true, at least ?x >= 10 || ?x <= 0 + // must be staisifed; for this objective we can construct a + // PrefiterExpression. We can't make a distinct predicition w.r.t. ?y != 0 + // => not relevant for the PrefilterExpression. Thus, we return the + // PrefilterExpresion {((>= 10 OR <= 0), ?x)}. + if (varLeft == varRight) { + resPairs.emplace_back(std::make_unique( + std::move(exprLeft), std::move(exprRight)), + varLeft); + ++itLeft; + ++itRight; + } else if (varLeft < varRight) { + ++itLeft; + } else { + ++itRight; + } + } + if (resPairs.empty()) { + return std::nullopt; + } + return resPairs; +} + +template +constexpr auto getCombineLogic(bool isNegated) { + if constexpr (std::is_same_v) { + // This follows the principles of De Morgan's law. + // If this is a child of a NotExpression, we have to swap the combination + // procedure (swap AND(&&) and OR(||) respectively). This equals a partial + // application of De Morgan's law. On the resulting PrefilterExpressions, + // NOT is applied in UnaryNegateExpression(Impl). For more details take a + // look at the implementation in NumericUnaryExpressions.cpp + return !isNegated ? mergeChildrenAndLogicImpl + : mergeChildrenOrLogicImpl; + } else { + static_assert(std::is_same_v); + return !isNegated ? mergeChildrenOrLogicImpl + : mergeChildrenAndLogicImpl; + } +} +} // namespace constructPrefilterExpr + +//______________________________________________________________________________ +template +requires(isOperation && + prefilterExpressions::check_is_logical_v) +class LogicalBinaryExpressionImpl : public NaryExpression { + public: + using NaryExpression::NaryExpression; + + std::optional> + getPrefilterExpressionForMetadata(bool isNegated) const override { + static_assert(this->N == 2); + auto leftChild = + this->getNthChild(0).value()->getPrefilterExpressionForMetadata( + isNegated); + auto rightChild = + this->getNthChild(1).value()->getPrefilterExpressionForMetadata( + isNegated); + return constructPrefilterExpr::getCombineLogic( + isNegated)(std::move(leftChild), std::move(rightChild)); + } +}; + +//______________________________________________________________________________ +using AndExpression = LogicalBinaryExpressionImpl< + prefilterExpressions::AndExpression, + Operation<2, FV, + SET>>; + +using OrExpression = LogicalBinaryExpressionImpl< + prefilterExpressions::OrExpression, + Operation<2, FV, + SET>>; } // namespace detail @@ -92,8 +278,10 @@ SparqlExpression::Ptr makeAndExpression(SparqlExpression::Ptr child1, SparqlExpression::Ptr child2) { return std::make_unique(std::move(child1), std::move(child2)); } + SparqlExpression::Ptr makeOrExpression(SparqlExpression::Ptr child1, SparqlExpression::Ptr child2) { return std::make_unique(std::move(child1), std::move(child2)); } + } // namespace sparqlExpression diff --git a/src/engine/sparqlExpressions/NumericUnaryExpressions.cpp b/src/engine/sparqlExpressions/NumericUnaryExpressions.cpp index fdc464b666..f612cfc954 100644 --- a/src/engine/sparqlExpressions/NumericUnaryExpressions.cpp +++ b/src/engine/sparqlExpressions/NumericUnaryExpressions.cpp @@ -2,11 +2,14 @@ // Chair of Algorithms and Data Structures. // Author: Johannes Kalmbach +#include + #include "engine/sparqlExpressions/NaryExpressionImpl.h" namespace sparqlExpression { namespace detail { +// _____________________________________________________________________________ // Unary negation. inline auto unaryNegate = [](TernaryBool a) { using enum TernaryBool; @@ -20,10 +23,38 @@ inline auto unaryNegate = [](TernaryBool a) { } AD_FAIL(); }; -NARY_EXPRESSION(UnaryNegateExpression, 1, - FV, - SET); +template +requires(isOperation) +class UnaryNegateExpressionImpl : public NaryExpression { + public: + using NaryExpression::NaryExpression; + + std::optional> + getPrefilterExpressionForMetadata( + [[maybe_unused]] bool isNegated) const override { + static_assert(this->N == 1); + auto optExprVarVec = + this->getNthChild(0).value()->getPrefilterExpressionForMetadata( + !isNegated); + if (!optExprVarVec.has_value()) { + return std::nullopt; + } + std::ranges::for_each( + optExprVarVec.value(), [](PrefilterExprVariablePair& exprVarPair) { + exprVarPair.first = + std::make_unique( + std::move(exprVarPair.first)); + }); + return optExprVarVec; + } +}; + +using UnaryNegateExpression = UnaryNegateExpressionImpl< + Operation<1, FV, + SET>>; + +// _____________________________________________________________________________ // Unary Minus. inline auto unaryMinus = makeNumericExpression>(); NARY_EXPRESSION(UnaryMinusExpression, 1, diff --git a/src/engine/sparqlExpressions/RelationalExpressions.cpp b/src/engine/sparqlExpressions/RelationalExpressions.cpp index c60d49909c..e1d056ba7e 100644 --- a/src/engine/sparqlExpressions/RelationalExpressions.cpp +++ b/src/engine/sparqlExpressions/RelationalExpressions.cpp @@ -411,15 +411,16 @@ ExpressionResult InExpression::evaluate( // _____________________________________________________________________________ template -std::optional, Variable>> -RelationalExpression::getPrefilterExpressionForMetadata() const { +std::optional> +RelationalExpression::getPrefilterExpressionForMetadata( + [[maybe_unused]] bool isNegated) const { AD_CORRECTNESS_CHECK(children_.size() == 2); const SparqlExpression::Ptr& child0 = children_.at(0); const SparqlExpression::Ptr& child1 = children_.at(1); - auto checkBinarySearchEvaluable = [](const SparqlExpression::Ptr& child0, - const SparqlExpression::Ptr& child1) + const auto checkBinarySearchEvaluable = + [](const SparqlExpression::Ptr& child0, + const SparqlExpression::Ptr& child1) -> std::optional> { if (const auto* literalValueId = dynamic_cast(child0.get())) { @@ -432,29 +433,27 @@ RelationalExpression::getPrefilterExpressionForMetadata() const { return std::nullopt; }; - auto createPairFilterExpressionVariable = - [this](std::optional> optValuePair) - -> std::pair, - Variable> { - const auto& [valueId, variable] = optValuePair.value(); - return std::make_pair( + const auto createPrefilterExprVariablePair = + [this](const std::pair& valuePair) + -> std::vector { + std::vector pairVec; + pairVec.emplace_back( std::make_unique>( - valueId), - variable); + valuePair.first), + valuePair.second); + return pairVec; }; - // Option 1: child0 is a (constant) reference ValueId, while child1 contains a // Variable to the respective column on which we want to filter. const auto& optionalPair1 = checkBinarySearchEvaluable(child0, child1); if (optionalPair1.has_value()) { - return createPairFilterExpressionVariable(optionalPair1); + return createPrefilterExprVariablePair(optionalPair1.value()); } - // Option 2: child1 is a (constant) reference ValueId, while child0 contains a // Variable to the respective column on which we want to filter. const auto& optionalPair2 = checkBinarySearchEvaluable(child1, child0); if (optionalPair2.has_value()) { - return createPairFilterExpressionVariable(optionalPair2); + return createPrefilterExprVariablePair(optionalPair2.value()); } return std::nullopt; } diff --git a/src/engine/sparqlExpressions/RelationalExpressions.h b/src/engine/sparqlExpressions/RelationalExpressions.h index 4eae15531c..6448435c0c 100644 --- a/src/engine/sparqlExpressions/RelationalExpressions.h +++ b/src/engine/sparqlExpressions/RelationalExpressions.h @@ -42,9 +42,9 @@ class RelationalExpression : public SparqlExpression { // corresponding `PrefilterExpression` for the pre-filtering procedure on // `CompressedBlockMetadata`. In addition we return the `Variable` that // corresponds to the sorted column. - std::optional, Variable>> - getPrefilterExpressionForMetadata() const override; + std::optional> + getPrefilterExpressionForMetadata( + [[maybe_unused]] bool isNegated) const override; // These expressions are typically used inside `FILTER` clauses, so we need // proper estimates. diff --git a/src/engine/sparqlExpressions/SparqlExpression.cpp b/src/engine/sparqlExpressions/SparqlExpression.cpp index c62092498f..4482bf7182 100644 --- a/src/engine/sparqlExpressions/SparqlExpression.cpp +++ b/src/engine/sparqlExpressions/SparqlExpression.cpp @@ -109,9 +109,9 @@ Estimates SparqlExpression::getEstimatesForFilterExpression( } // _____________________________________________________________________________ -std::optional, Variable>> -SparqlExpression::getPrefilterExpressionForMetadata() const { +std::optional> +SparqlExpression::getPrefilterExpressionForMetadata( + [[maybe_unused]] bool isNegated) const { return std::nullopt; }; diff --git a/src/engine/sparqlExpressions/SparqlExpression.h b/src/engine/sparqlExpressions/SparqlExpression.h index e82e86275b..a864a809b5 100644 --- a/src/engine/sparqlExpressions/SparqlExpression.h +++ b/src/engine/sparqlExpressions/SparqlExpression.h @@ -96,13 +96,14 @@ class SparqlExpression { [[maybe_unused]] const std::optional& primarySortKeyVariable) const; - // Returns a `PrefilterExpression` with the respective `Variable` for the - // necessarily sorted column, if there are relational expressions (e.g. - // `>=` or `!=`) that are binary evaluable. Those expressions can also be - // linked via logical expressions (`&&`, `||` and `!`). - virtual std::optional, Variable>> - getPrefilterExpressionForMetadata() const; + // Returns a vector with pairs, each containing a `PrefilterExpression` and + // the corresponding `Variable`. The `Variable` indicates which + // column is required to be sorted, and hence is as a consequence also binary + // evaluable regarding the relational (e.g. `>=`) / logical (`&&`, `||` and + // `!`) expressions. + virtual std::optional> + getPrefilterExpressionForMetadata( + [[maybe_unused]] bool isNegated = false) const; // Returns true iff this expression is a simple constant. Default // implementation returns `false`. diff --git a/src/engine/sparqlExpressions/SparqlExpressionPimpl.cpp b/src/engine/sparqlExpressions/SparqlExpressionPimpl.cpp index 92fa18cbab..8a05ec147d 100644 --- a/src/engine/sparqlExpressions/SparqlExpressionPimpl.cpp +++ b/src/engine/sparqlExpressions/SparqlExpressionPimpl.cpp @@ -93,10 +93,9 @@ auto SparqlExpressionPimpl::getEstimatesForFilterExpression( } //_____________________________________________________________________________ -std::optional, Variable>> -SparqlExpressionPimpl::getPrefilterExpressionForMetadata() const { - return _pimpl->getPrefilterExpressionForMetadata(); +std::optional> +SparqlExpressionPimpl::getPrefilterExpressionForMetadata(bool isNegated) const { + return _pimpl->getPrefilterExpressionForMetadata(isNegated); } // _____________________________________________________________________________ diff --git a/src/engine/sparqlExpressions/SparqlExpressionPimpl.h b/src/engine/sparqlExpressions/SparqlExpressionPimpl.h index f32f23203d..80a7766260 100644 --- a/src/engine/sparqlExpressions/SparqlExpressionPimpl.h +++ b/src/engine/sparqlExpressions/SparqlExpressionPimpl.h @@ -19,6 +19,12 @@ namespace sparqlExpression { class SparqlExpression; struct EvaluationContext; +// Improve return type readability. +// Pair containing `PrefilterExpression` pointer and a `Variable`. +using PrefilterExprVariablePair = + std::pair, + Variable>; + // Hide the `SparqlExpression` implementation in a Pimpl class, so that code // using this implementation only has to include the (small and therefore cheap // to include) `SparqlExpressionPimpl.h` @@ -112,9 +118,8 @@ class SparqlExpressionPimpl { uint64_t inputSizeEstimate, const std::optional& primarySortKeyVariable); - std::optional, Variable>> - getPrefilterExpressionForMetadata() const; + std::optional> + getPrefilterExpressionForMetadata(bool isNegated = false) const; SparqlExpression* getPimpl() { return _pimpl.get(); } [[nodiscard]] const SparqlExpression* getPimpl() const { diff --git a/src/index/CompressedBlockPrefiltering.cpp b/src/index/CompressedBlockPrefiltering.cpp index 6b7607af09..4517c379e0 100644 --- a/src/index/CompressedBlockPrefiltering.cpp +++ b/src/index/CompressedBlockPrefiltering.cpp @@ -237,6 +237,28 @@ std::vector RelationalExpression::evaluateImpl( return getSetUnion(relevantBlocks, mixedDatatypeBlocks, evaluationColumn); }; +//______________________________________________________________________________ +template +bool RelationalExpression::operator==( + const PrefilterExpression& other) const { + const RelationalExpression* otherRelational = + dynamic_cast*>(&other); + if (!otherRelational) { + return false; + } + return referenceId_ == otherRelational->referenceId_; +}; + +//______________________________________________________________________________ +template +std::string RelationalExpression::info( + [[maybe_unused]] size_t depth) const { + std::stringstream stream; + stream << "Prefilter RelationalExpression<" << static_cast(Comparison) + << ">\nValueId: " << referenceId_ << std::endl; + return stream.str(); +}; + // SECTION LOGICAL OPERATIONS //______________________________________________________________________________ template @@ -257,14 +279,6 @@ LogicalExpression::logicalComplement() const { } }; -//______________________________________________________________________________ -std::unique_ptr NotExpression::logicalComplement() const { - // Logically we complement (negate) a NOT here => NOT cancels out. - // Therefore, we can simply return the child of the respective NOT - // expression after undoing its previous complementation. - return child_->logicalComplement(); -}; - //______________________________________________________________________________ template std::vector LogicalExpression::evaluateImpl( @@ -281,10 +295,67 @@ std::vector LogicalExpression::evaluateImpl( } }; +//______________________________________________________________________________ +template +bool LogicalExpression::operator==( + const PrefilterExpression& other) const { + const LogicalExpression* otherlogical = + dynamic_cast*>(&other); + if (!otherlogical) { + return false; + } + return *child1_ == *otherlogical->child1_ && + *child2_ == *otherlogical->child2_; +}; + +//______________________________________________________________________________ +template +std::string LogicalExpression::info(size_t depth) const { + std::string child1Info = + depth < maxInfoRecursion ? child1_->info(depth + 1) : "MAX_DEPTH"; + std::string child2Info = + depth < maxInfoRecursion ? child2_->info(depth + 1) : "MAX_DEPTH"; + std::stringstream stream; + stream << "Prefilter LogicalExpression<" << static_cast(Operation) + << ">\n" + << "child1 {" << child1Info << "}" + << "child2 {" << child2Info << "}" << std::endl; + return stream.str(); +}; + +// SECTION NOT-EXPRESSION +//______________________________________________________________________________ +std::unique_ptr NotExpression::logicalComplement() const { + // Logically we complement (negate) a NOT here => NOT cancels out. + // Therefore, we can simply return the child of the respective NOT + // expression after undoing its previous complementation. + return child_->logicalComplement(); +}; + //______________________________________________________________________________ std::vector NotExpression::evaluateImpl( const std::vector& input, size_t evaluationColumn) const { return child_->evaluate(input, evaluationColumn); }; +//______________________________________________________________________________ +bool NotExpression::operator==(const PrefilterExpression& other) const { + const NotExpression* otherNotExpression = + dynamic_cast(&other); + if (!otherNotExpression) { + return false; + } + return *child_ == *otherNotExpression->child_; +} + +//______________________________________________________________________________ +std::string NotExpression::info(size_t depth) const { + std::string childInfo = + depth < maxInfoRecursion ? child_->info(depth + 1) : "MAX_DEPTH"; + std::stringstream stream; + stream << "Prefilter NotExpression:\n" + << "child {" << childInfo << "}" << std::endl; + return stream.str(); +} + } // namespace prefilterExpressions diff --git a/src/index/CompressedBlockPrefiltering.h b/src/index/CompressedBlockPrefiltering.h index 70ff7fdfd7..6b3b7f5dc9 100644 --- a/src/index/CompressedBlockPrefiltering.h +++ b/src/index/CompressedBlockPrefiltering.h @@ -13,6 +13,12 @@ namespace prefilterExpressions { +//______________________________________________________________________________ +// The maximum recursion depth for `info()` / `operator<<()`. A depth of `3` +// should be sufficient for most `PrefilterExpressions` with our use case. +constexpr size_t maxInfoRecursion = 3; + +//______________________________________________________________________________ // The compressed block metadata (see `CompressedRelation.h`) that we use to // filter out the non-relevant blocks by checking their content of // `firstTriple_` and `lastTriple_` (`PermutedTriple`) @@ -37,6 +43,11 @@ class PrefilterExpression { public: virtual ~PrefilterExpression() = default; + virtual bool operator==(const PrefilterExpression& other) const = 0; + + // Format content for debugging. + virtual std::string info(size_t depth) const = 0; + virtual std::unique_ptr logicalComplement() const = 0; // The respective metadata to the blocks is expected to be provided in @@ -44,6 +55,13 @@ class PrefilterExpression { std::vector evaluate(const std::vector& input, size_t evaluationColumn) const; + // Format for debugging + friend std::ostream& operator<<(std::ostream& str, + const PrefilterExpression& expression) { + str << expression.info(0) << "." << std::endl; + return str; + } + private: virtual std::vector evaluateImpl( const std::vector& input, @@ -70,6 +88,8 @@ class RelationalExpression : public PrefilterExpression { : referenceId_(referenceId) {} std::unique_ptr logicalComplement() const override; + bool operator==(const PrefilterExpression& other) const override; + std::string info([[maybe_unused]] size_t depth) const override; private: std::vector evaluateImpl( @@ -97,6 +117,8 @@ class LogicalExpression : public PrefilterExpression { : child1_(std::move(child1)), child2_(std::move(child2)) {} std::unique_ptr logicalComplement() const override; + bool operator==(const PrefilterExpression& other) const override; + std::string info(size_t depth) const override; private: std::vector evaluateImpl( @@ -114,6 +136,8 @@ class NotExpression : public PrefilterExpression { : child_(child->logicalComplement()) {} std::unique_ptr logicalComplement() const override; + bool operator==(const PrefilterExpression& other) const override; + std::string info(size_t depth) const override; private: std::vector evaluateImpl( @@ -155,4 +179,19 @@ using AndExpression = prefilterExpressions::LogicalExpression< using OrExpression = prefilterExpressions::LogicalExpression< prefilterExpressions::LogicalOperators::OR>; +//______________________________________________________________________________ +// Helpers to check for the respective type of `PrefilterExpression` +template +struct check_is : std::false_type {}; + +template +struct check_is> : std::true_type {}; +template +struct check_is> : std::true_type {}; + +template +constexpr bool check_is_logical_v = check_is::value; +template +constexpr bool check_is_relational_v = check_is::value; + } // namespace prefilterExpressions diff --git a/src/parser/data/Variable.h b/src/parser/data/Variable.h index 32a1ee99a9..ffa39b5e12 100644 --- a/src/parser/data/Variable.h +++ b/src/parser/data/Variable.h @@ -56,6 +56,11 @@ class Variable { bool operator==(const Variable&) const = default; + // The construction of PrefilterExpressions requires a defined < order. + bool operator<(const Variable& other) const { + return this->_name < other._name; + }; + // Make the type hashable for absl, see // https://abseil.io/docs/cpp/guides/hash. template diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 9c13fc826d..e4e3ed73d1 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -289,7 +289,7 @@ addLinkAndDiscoverTest(AlgorithmTest) addLinkAndDiscoverTestSerial(CompressedRelationsTest index) -addLinkAndDiscoverTestSerial(CompressedBlockPrefilteringTest index) +addLinkAndDiscoverTestSerial(CompressedBlockPrefilteringTest sparqlExpressions index engine) addLinkAndDiscoverTest(ExceptionTest) diff --git a/test/CompressedBlockPrefilteringTest.cpp b/test/CompressedBlockPrefilteringTest.cpp index 8caab1c0ff..023bf5e752 100644 --- a/test/CompressedBlockPrefilteringTest.cpp +++ b/test/CompressedBlockPrefilteringTest.cpp @@ -7,10 +7,14 @@ #include #include "./SparqlExpressionTestHelpers.h" +#include "./util/GTestHelpers.h" +#include "engine/sparqlExpressions/NaryExpression.h" +#include "engine/sparqlExpressions/RelationalExpressions.h" #include "index/CompressedBlockPrefiltering.h" #include "util/DateYearDuration.h" #include "util/IdTestHelpers.h" +using ad_utility::source_location; using ad_utility::testing::BlankNodeId; using ad_utility::testing::BoolId; using ad_utility::testing::DateId; @@ -18,9 +22,10 @@ using ad_utility::testing::DoubleId; using ad_utility::testing::IntId; using ad_utility::testing::UndefId; using ad_utility::testing::VocabId; -using sparqlExpression::TestContext; using namespace prefilterExpressions; +// TEST SECTION 1 (PrefilterExpression) +//______________________________________________________________________________ //______________________________________________________________________________ struct MetadataBlocks { /* @@ -142,19 +147,6 @@ struct MetadataBlocks { //______________________________________________________________________________ // Test PrefilterExpressions -template -struct check_is : std::false_type {}; - -template -struct check_is> : std::true_type {}; -template -struct check_is> : std::true_type {}; - -template -constexpr bool check_is_logical_v = check_is::value; -template -constexpr bool check_is_relational_v = check_is::value; - //______________________________________________________________________________ static const auto testThrowError = [](std::unique_ptr expression, size_t evaluationColumn, @@ -885,3 +877,548 @@ TEST(PrefilterExpression, testInputConditionCheck) { 2, DoubleId(-4.24), IntId(5), blocks.blocksInvalidCol2, "The columns up to the evaluation column must contain the same values."); } + +// TEST SECTION 2 (getPrefilterExpressionForMetadata) +//______________________________________________________________________________ +//______________________________________________________________________________ +using namespace sparqlExpression; +using optPrefilterVec = std::optional>; +using Literal = ad_utility::triple_component::Literal; +using Iri = ad_utility::triple_component::Iri; +using MakeBinarySprqlExprFunc = std::function; +using RelValues = std::variant; + +// TEST HELPER SECTION +//______________________________________________________________________________ +// make `Literal` +const auto L = [](const std::string& content) -> Literal { + return Literal::fromStringRepresentation(content); +}; + +//______________________________________________________________________________ +// make `Iri` +const auto I = [](const std::string& content) -> Iri { + return Iri::fromIriref(content); +}; + +//______________________________________________________________________________ +const auto makeLiteralSparqlExpr = + [](const auto& child) -> std::unique_ptr { + using T = std::decay_t; + if constexpr (std::is_same_v) { + return std::make_unique(child); + } else if constexpr (std::is_same_v) { + return std::make_unique(child); + } else if constexpr (std::is_same_v) { + return std::make_unique(child); + } else if constexpr (std::is_same_v) { + return std::make_unique(child); + } else { + FAIL() << "Unexpected Type."; + } +}; + +//______________________________________________________________________________ +template +std::unique_ptr makeRelationalSparqlExprImpl( + const RelValues& child0, const RelValues& child1) { + auto getExpr = + [](const auto& variantVal) -> std::unique_ptr { + return makeLiteralSparqlExpr(variantVal); + }; + return std::make_unique(std::array{ + std::visit(getExpr, child0), std::visit(getExpr, child1)}); +}; + +//______________________________________________________________________________ +template +std::unique_ptr makeLogicalBinaryPrefilterExprImpl( + std::unique_ptr child1, + std::unique_ptr child2) { + return std::make_unique(std::move(child1), std::move(child2)); +}; + +//______________________________________________________________________________ +std::unique_ptr makeNotPrefilterExprImpl( + std::unique_ptr child) { + return std::make_unique( + std::move(child)); +}; + +//______________________________________________________________________________ +constexpr auto lessThanSparqlExpr = + &makeRelationalSparqlExprImpl; +constexpr auto lessThanFilterExpr = + &makeRelExpr; +constexpr auto lessEqSparqlExpr = + &makeRelationalSparqlExprImpl; +constexpr auto lessEqFilterExpr = + &makeRelExpr; +constexpr auto eqSparqlExpr = + &makeRelationalSparqlExprImpl; +constexpr auto eqFilterExpr = + &makeRelExpr; +constexpr auto notEqSparqlExpr = + &makeRelationalSparqlExprImpl; +constexpr auto notEqFilterExpr = + &makeRelExpr; +constexpr auto greaterEqSparqlExpr = + &makeRelationalSparqlExprImpl; +constexpr auto greaterEqFilterExpr = + &makeRelExpr; +constexpr auto greaterThanSparqlExpr = + &makeRelationalSparqlExprImpl; +constexpr auto greaterThanFilterExpr = + &makeRelExpr; +constexpr auto andSparqlExpr = &makeAndExpression; +constexpr auto orSparqlExpr = &makeOrExpression; +constexpr auto notSparqlExpr = &makeUnaryNegateExpression; +constexpr auto andFilterExpr = + &makeLogicalBinaryPrefilterExprImpl; +constexpr auto orFilterExpr = + &makeLogicalBinaryPrefilterExprImpl; +constexpr auto notFilterExpr = &makeNotPrefilterExprImpl; + +//______________________________________________________________________________ +const auto checkVectorPrefilterExprVariablePair = + [](const std::vector& result, + const std::vector& expected) -> bool { + if (result.size() != expected.size()) { + ADD_FAILURE() << "Expected vectors (result vs. expected) of equal length"; + return false; + }; + const auto isEqualImpl = [](const PrefilterExprVariablePair& resPair, + const PrefilterExprVariablePair& expPair) { + if (*resPair.first != *expPair.first || resPair.second != expPair.second) { + std::stringstream stream; + stream << "The following value pairs don't match:" + << "\nRESULT: " << *resPair.first << "EXPECTED: " << *expPair.first + << "RESULT: VARIABLE" << resPair.second.name() + << "\nEXPECTED: VARIABLE" << expPair.second.name() << std::endl; + ADD_FAILURE() << stream.str(); + return false; + } + return true; + }; + return std::equal(result.begin(), result.end(), expected.begin(), + isEqualImpl); +}; + +//______________________________________________________________________________ +const auto checkEqualityPrefilterMethodT = + [](const optPrefilterVec& result, const optPrefilterVec& expected) { + const auto testEquality = + [](const optPrefilterVec& result, const optPrefilterVec& expected) { + if (!result.has_value() && !expected.has_value()) { + return true; + } else if (result.has_value() && expected.has_value()) { + return checkVectorPrefilterExprVariablePair(result.value(), + expected.value()); + } else { + ADD_FAILURE() + << "Expected both values to either contain a value or to be " + "std::optional."; + return false; + } + }; + ASSERT_TRUE(testEquality(result, expected)); + }; + +//______________________________________________________________________________ +const auto evalToEmptyCheck = [](std::unique_ptr sparqlExpr) { + std::vector prefilterVarPair; + checkEqualityPrefilterMethodT(sparqlExpr->getPrefilterExpressionForMetadata(), + std::nullopt); +}; + +//______________________________________________________________________________ +const auto evalAndEqualityCheck = + [](std::unique_ptr sparqlExpr, + std::convertible_to auto&&... prefilterArgs) { + std::vector prefilterVarPair; + (prefilterVarPair.emplace_back( + std::forward(prefilterArgs)), + ...); + checkEqualityPrefilterMethodT( + sparqlExpr->getPrefilterExpressionForMetadata(), + std::move(prefilterVarPair)); + }; + +//______________________________________________________________________________ +// Construct a pair with the given `PrefilterExpression` and `Variable` value. +auto pr = [](std::unique_ptr expr, + const Variable& var) -> PrefilterExprVariablePair { + return {std::move(expr), var}; +}; + +//______________________________________________________________________________ +// TEST SECTION +//______________________________________________________________________________ +// Test coverage for the default implementation of +// getPrefilterExpressionForMetadata. +TEST(SparqlExpression, testGetPrefilterExpressionDefault) { + evalToEmptyCheck(makeUnaryMinusExpression(makeLiteralSparqlExpr(IntId(0)))); + evalToEmptyCheck(makeMultiplyExpression(makeLiteralSparqlExpr(DoubleId(11)), + makeLiteralSparqlExpr(DoubleId(3)))); + evalToEmptyCheck( + makeStrEndsExpression(makeLiteralSparqlExpr(L("\"Freiburg\"")), + makeLiteralSparqlExpr(L("\"burg\"")))); + evalToEmptyCheck(makeIsIriExpression(makeLiteralSparqlExpr(I("")))); + evalToEmptyCheck(makeLogExpression(makeLiteralSparqlExpr(DoubleId(8)))); + evalToEmptyCheck( + makeStrIriDtExpression(makeLiteralSparqlExpr(L("\"test\"")), + makeLiteralSparqlExpr(I("")))); +} + +//______________________________________________________________________________ +// Check that the (Sparql) RelationalExpression returns the expected +// PrefilterExpression. +TEST(SparqlExpression, getPrefilterExpressionFromSparqlRelational) { + const Variable var = Variable{"?x"}; + MetadataBlocks blocks{}; + evalAndEqualityCheck(eqSparqlExpr(var, BoolId(true)), + pr(eqFilterExpr(BoolId(true)), var)); + evalAndEqualityCheck(greaterEqSparqlExpr(var, IntId(1)), + pr(greaterEqFilterExpr(IntId(1)), var)); + evalAndEqualityCheck(notEqSparqlExpr(VocabId(10), var), + pr(notEqFilterExpr(VocabId(10)), var)); + evalAndEqualityCheck(greaterEqSparqlExpr(BlankNodeId(1), var), + pr(greaterEqFilterExpr(BlankNodeId(1)), var)); + evalAndEqualityCheck(lessEqSparqlExpr(var, blocks.referenceDate1), + pr(lessEqFilterExpr(blocks.referenceDate1), var)); + evalAndEqualityCheck(lessThanSparqlExpr(DoubleId(10.2), var), + pr(lessThanFilterExpr(DoubleId(10.2)), var)); +} + +//______________________________________________________________________________ +// More complex relational SparqlExpressions for which +// getPrefilterExpressionForMetadata should yield a vector containing the actual +// corresponding PrefilterExpression values. +TEST(SparqlExpression, getPrefilterExpressionsToComplexSparqlExpressions) { + const Variable varX = Variable{"?x"}; + const Variable varY = Variable{"?y"}; + const Variable varZ = Variable{"?z"}; + // ?x >= 10 AND ?x != 20 + // expected prefilter pairs: + // {<((>= 10) AND (!= 20)), ?x>} + evalAndEqualityCheck(andSparqlExpr(greaterEqSparqlExpr(varX, IntId(10)), + notEqSparqlExpr(varX, IntId(20))), + pr(andFilterExpr(greaterEqFilterExpr(IntId(10)), + notEqFilterExpr(IntId(20))), + varX)); + // ?x == VocabId(10) AND ?y >= VocabId(10) + // expected prefilter pairs: + // {<(== VocabId(10)), ?x>, <(>= VocabId(10)), ?y>} + evalAndEqualityCheck(andSparqlExpr(eqSparqlExpr(varX, VocabId(10)), + greaterEqSparqlExpr(varY, VocabId(10))), + pr(eqFilterExpr(VocabId(10)), varX), + pr(greaterEqFilterExpr(VocabId(10)), varY)); + // !(?x >= 10 OR ?x <= 0) + // expected prefilter pairs: + // {= 10 OR ?x <= 0), ?x>} + evalAndEqualityCheck( + notSparqlExpr(orSparqlExpr(greaterEqSparqlExpr(varX, IntId(10)), + lessEqSparqlExpr(varX, IntId(0)))), + pr(notFilterExpr(orFilterExpr(greaterEqFilterExpr(IntId(10)), + lessEqFilterExpr(IntId(0)))), + varX)); + // !(?z == VocabId(10) AND ?z >= VocabId(20)) + // expected prefilter pairs: + // {= VocabId(20)) , ?z>} + evalAndEqualityCheck( + notSparqlExpr(andSparqlExpr(eqSparqlExpr(varZ, VocabId(10)), + greaterEqSparqlExpr(varZ, VocabId(20)))), + pr(notFilterExpr(andFilterExpr(eqFilterExpr(VocabId(10)), + greaterEqFilterExpr(VocabId(20)))), + varZ)); + // (?x == VocabId(10) AND ?z == VocabId(0)) AND ?y != DoubleId(22.1) + // expected prefilter pairs: + // {<(==VocabId(10)) , ?x>, <(!=DoubleId(22.1)), ?y>, <(==VocabId(0)), ?z>} + evalAndEqualityCheck( + andSparqlExpr(andSparqlExpr(eqSparqlExpr(VocabId(10), varX), + eqSparqlExpr(varZ, VocabId(0))), + notEqSparqlExpr(DoubleId(22.1), varY)), + pr(eqFilterExpr(VocabId(10)), varX), + pr(notEqFilterExpr(DoubleId(22.1)), varY), + pr(eqFilterExpr(VocabId(0)), varZ)); + // (?z >= 1000 AND ?x == VocabId(10)) OR ?z >= 10000 + // expected prefilter pairs: + // {<((>=1000) OR (>= 10000)), ?z>} + evalAndEqualityCheck( + orSparqlExpr(andSparqlExpr(greaterEqSparqlExpr(varZ, IntId(1000)), + eqSparqlExpr(varX, VocabId(10))), + greaterEqSparqlExpr(varZ, IntId(10000))), + pr(orFilterExpr(greaterEqFilterExpr(IntId(1000)), + greaterEqFilterExpr(IntId(10000))), + varZ)); + // !((?z <= VocabId(10) OR ?y <= VocabId(10)) OR ?x <= VocabId(10)) + // expected prefilter pairs: + // {, , } + evalAndEqualityCheck(notSparqlExpr(orSparqlExpr( + orSparqlExpr(lessEqSparqlExpr(varZ, VocabId(10)), + lessEqSparqlExpr(varY, VocabId(10))), + lessEqSparqlExpr(varX, VocabId(10)))), + pr(notFilterExpr(lessEqFilterExpr(VocabId(10))), varX), + pr(notFilterExpr(lessEqFilterExpr(VocabId(10))), varY), + pr(notFilterExpr(lessEqFilterExpr(VocabId(10))), varZ)); + // ?x >= 10 AND ?y >= 10 + // expected prefilter pairs: + // {<(>= 10), ?x>, <(>= 10), ?y>} + evalAndEqualityCheck(andSparqlExpr(greaterEqSparqlExpr(varX, IntId(10)), + greaterEqSparqlExpr(varY, IntId(10))), + pr(greaterEqFilterExpr(IntId(10)), varX), + pr(greaterEqFilterExpr(IntId(10)), varY)); + // ?x <= 0 AND ?y <= 0 + // expected prefilter pairs: + // {<(<= 0), ?x>, <(<= 0), ?y>} + evalAndEqualityCheck(andSparqlExpr(lessEqSparqlExpr(varX, IntId(0)), + lessEqSparqlExpr(varY, IntId(0))), + pr(lessEqFilterExpr(IntId(0)), varX), + pr(lessEqFilterExpr(IntId(0)), varY)); + // (?x >= 10 AND ?y >= 10) OR (?x <= 0 AND ?y <= 0) + // expected prefilter pairs: + // {<((>= 10) OR (<= 0)), ?x> <(>= 10) OR (<= 0)), ?y>} + evalAndEqualityCheck( + orSparqlExpr(andSparqlExpr(greaterEqSparqlExpr(varX, IntId(10)), + greaterEqSparqlExpr(varY, IntId(10))), + andSparqlExpr(lessEqSparqlExpr(varX, IntId(0)), + lessEqSparqlExpr(varY, IntId(0)))), + pr(orFilterExpr(greaterEqFilterExpr(IntId(10)), + lessEqFilterExpr(IntId(0))), + varX), + pr(orFilterExpr(greaterEqFilterExpr(IntId(10)), + lessEqFilterExpr(IntId(0))), + varY)); + // !(?x >= 10 OR ?y >= 10) OR !(?x <= 0 OR ?y <= 0) + // expected prefilter pairs: + // {<((!(>= 10) OR !(<= 0))), ?x> <(!(>= 10) OR !(<= 0))), ?y>} + evalAndEqualityCheck( + orSparqlExpr( + notSparqlExpr(orSparqlExpr(greaterEqSparqlExpr(varX, IntId(10)), + greaterEqSparqlExpr(varY, IntId(10)))), + notSparqlExpr(orSparqlExpr(lessEqSparqlExpr(varX, IntId(0)), + lessEqSparqlExpr(varY, IntId(0))))), + pr(orFilterExpr(notFilterExpr(greaterEqFilterExpr(IntId(10))), + notFilterExpr(lessEqFilterExpr(IntId(0)))), + varX), + pr(orFilterExpr(notFilterExpr(greaterEqFilterExpr(IntId(10))), + notFilterExpr(lessEqFilterExpr(IntId(0)))), + varY)); + // !(?x == VocabId(10) OR ?x == VocabId(20)) AND !(?z >= 10.00 OR ?y == false) + // expected prefilter pairs: + // {, , + // = 10), ?z>} + evalAndEqualityCheck( + andSparqlExpr( + notSparqlExpr(orSparqlExpr(eqSparqlExpr(varX, VocabId(10)), + eqSparqlExpr(varX, VocabId(20)))), + notSparqlExpr(orSparqlExpr(greaterEqSparqlExpr(varZ, DoubleId(10)), + eqSparqlExpr(varY, BoolId(false))))), + pr(notFilterExpr(orFilterExpr(eqFilterExpr(VocabId(10)), + eqFilterExpr(VocabId(20)))), + varX), + pr(notFilterExpr(eqFilterExpr(BoolId(false))), varY), + pr(notFilterExpr(greaterEqFilterExpr(DoubleId(10))), varZ)); + // !(!(?x >= 10 AND ?y >= 10)) OR !(!(?x <= 0 AND ?y <= 0)) + // expected prefilter pairs: + // {<(!!(>= 10) OR !!(<= 0)), ?x>, <(!!(>= 10) OR !!(<= 0)) ,?y>} + evalAndEqualityCheck( + orSparqlExpr(notSparqlExpr(notSparqlExpr( + andSparqlExpr(greaterEqSparqlExpr(varX, IntId(10)), + greaterEqSparqlExpr(varY, IntId(10))))), + notSparqlExpr(notSparqlExpr( + andSparqlExpr(lessEqSparqlExpr(varX, IntId(0)), + lessEqSparqlExpr(varY, IntId(0)))))), + pr(orFilterExpr( + notFilterExpr(notFilterExpr(greaterEqFilterExpr(IntId(10)))), + notFilterExpr(notFilterExpr(lessEqFilterExpr(IntId(0))))), + varX), + pr(orFilterExpr( + notFilterExpr(notFilterExpr(greaterEqFilterExpr(IntId(10)))), + notFilterExpr(notFilterExpr(lessEqFilterExpr(IntId(0))))), + varY)); + // !((?x >= VocabId(0) AND ?x <= VocabId(10)) OR !(?x != VocabId(99))) + // expected prefilter pairs: + // {= VocabId(0)) AND (<= VocabId(10))) OR !(!= VocabId(99))) , ?x>} + evalAndEqualityCheck(notSparqlExpr(orSparqlExpr( + andSparqlExpr(greaterEqSparqlExpr(varX, VocabId(0)), + lessEqSparqlExpr(varX, VocabId(10))), + notSparqlExpr(notEqSparqlExpr(varX, VocabId(99))))), + pr(notFilterExpr(orFilterExpr( + andFilterExpr(greaterEqFilterExpr(VocabId(0)), + lessEqFilterExpr(VocabId(10))), + notFilterExpr(notEqFilterExpr(VocabId(99))))), + varX)); + // !((?y >= 10 AND ?y <= 100) OR !(?x >= VocabId(99))) + // expected prefilter pairs: + // {= VocabId(0)) AND (<= VocabId(10)), ?y>, = VocabId(99))), ?x>} + evalAndEqualityCheck( + notSparqlExpr( + orSparqlExpr(andSparqlExpr(greaterEqSparqlExpr(varY, VocabId(0)), + lessEqSparqlExpr(varY, VocabId(10))), + notSparqlExpr(greaterEqSparqlExpr(varX, VocabId(99))))), + pr(notFilterExpr(notFilterExpr(greaterEqFilterExpr(VocabId(99)))), varX), + pr(notFilterExpr(andFilterExpr(greaterEqFilterExpr(VocabId(0)), + lessEqFilterExpr(VocabId(10)))), + varY)); + // ?z >= 10 AND ?z <= 100 AND ?x >= 10 AND ?x != 50 AND !(?y <= 10) AND + // !(?city <= VocabId(1000) OR ?city == VocabId(1005)) + // expected prefilter pairs: + // {, <((>= 10) AND (!= + // 50)), ?x>, , <((>= 10) AND (<= 100)), ?z>} + evalAndEqualityCheck( + andSparqlExpr( + andSparqlExpr( + andSparqlExpr(greaterEqSparqlExpr(varZ, IntId(10)), + lessEqSparqlExpr(varZ, IntId(100))), + andSparqlExpr(andSparqlExpr(greaterEqSparqlExpr(varX, IntId(10)), + notEqSparqlExpr(varX, IntId(50))), + notSparqlExpr(lessEqSparqlExpr(varY, IntId(10))))), + notSparqlExpr( + orSparqlExpr(lessEqSparqlExpr(Variable{"?city"}, VocabId(1000)), + eqSparqlExpr(Variable{"?city"}, VocabId(1005))))), + pr(notFilterExpr(orFilterExpr(lessEqFilterExpr(VocabId(1000)), + eqFilterExpr(VocabId(1005)))), + Variable{"?city"}), + pr(andFilterExpr(greaterEqFilterExpr(IntId(10)), + notEqFilterExpr(IntId(50))), + varX), + pr(notFilterExpr(lessEqFilterExpr(IntId(10))), varY), + pr(andFilterExpr(greaterEqFilterExpr(IntId(10)), + lessEqFilterExpr(IntId(100))), + varZ)); + // ?x >= 10 OR (?x >= -10 AND ?x < 0.00) + // expected prefilter pairs: + // {<((>= 10) OR ((>= -10) AND (< 0.00))), ?x>} + evalAndEqualityCheck( + orSparqlExpr(greaterEqSparqlExpr(varX, IntId(10)), + andSparqlExpr(greaterEqSparqlExpr(varX, IntId(-10)), + lessThanSparqlExpr(DoubleId(0.00), varX))), + pr(orFilterExpr(greaterEqFilterExpr(IntId(10)), + andFilterExpr(greaterEqFilterExpr(IntId(-10)), + lessThanFilterExpr(DoubleId(0.00)))), + varX)); + // !(!(?x >= 10) OR !!(?x >= -10 AND ?x < 0.00)) + // expected prefilter pairs: + // {= 10) OR !!((>= -10) AND (< 0.00))), ?x>} + evalAndEqualityCheck(notSparqlExpr(orSparqlExpr( + notSparqlExpr(greaterEqSparqlExpr(varX, IntId(10))), + notSparqlExpr(notSparqlExpr(andSparqlExpr( + greaterEqSparqlExpr(varX, IntId(-10)), + lessThanSparqlExpr(DoubleId(0.00), varX)))))), + pr(notFilterExpr(orFilterExpr( + notFilterExpr(greaterEqFilterExpr(IntId(10))), + notFilterExpr(notFilterExpr(andFilterExpr( + greaterEqFilterExpr(IntId(-10)), + lessThanFilterExpr(DoubleId(0.00))))))), + varX)); +} + +//______________________________________________________________________________ +// For this test we expect that no PrefilterExpression is available. +TEST(SparqlExpression, getEmptyPrefilterFromSparqlRelational) { + const Variable var = Variable{"?x"}; + const Iri iri = I(""); + const Literal lit = L("\"lit\""); + evalToEmptyCheck(lessEqSparqlExpr(var, var)); + evalToEmptyCheck(notEqSparqlExpr(iri, var)); + evalToEmptyCheck(eqSparqlExpr(var, iri)); + evalToEmptyCheck(notEqSparqlExpr(IntId(10), DoubleId(23.3))); + evalToEmptyCheck(greaterThanSparqlExpr(DoubleId(10), lit)); + evalToEmptyCheck(lessThanSparqlExpr(VocabId(10), BoolId(10))); + evalToEmptyCheck(greaterEqSparqlExpr(lit, lit)); + evalToEmptyCheck(eqSparqlExpr(iri, iri)); +} + +//______________________________________________________________________________ +// For the following more complex SparqlExpression trees, we also expect an +// empty PrefilterExpression vector. +TEST(SparqlExpression, getEmptyPrefilterForMoreComplexSparqlExpressions) { + const Variable varX = Variable{"?x"}; + const Variable varY = Variable{"?y"}; + const Variable varZ = Variable{"?z"}; + // ?x <= 10.00 OR ?y > 10 + evalToEmptyCheck(orSparqlExpr(lessEqSparqlExpr(DoubleId(10), varX), + greaterThanSparqlExpr(IntId(10), varY))); + // ?x >= VocabId(23) OR ?z == VocabId(1) + evalToEmptyCheck(orSparqlExpr(greaterEqSparqlExpr(varX, VocabId(23)), + eqSparqlExpr(varZ, VocabId(1)))); + // (?x < VocabId(10) OR ?z <= VocabId(4)) OR ?z != 5.00 + evalToEmptyCheck( + orSparqlExpr(orSparqlExpr(lessThanSparqlExpr(varX, VocabId(10)), + lessEqSparqlExpr(VocabId(4), varZ)), + notEqSparqlExpr(varZ, DoubleId(5)))); + // !(?z > 10.20 AND ?x < 0.001) + // is equal to + // ?z <= 10.20 OR ?x >= 0.001 + evalToEmptyCheck( + notSparqlExpr(andSparqlExpr(greaterThanSparqlExpr(DoubleId(10.2), varZ), + lessThanSparqlExpr(DoubleId(0.001), varX)))); + // !(?x > 10.20 AND ?z != VocabId(22)) + // is equal to + // ?x <= 10.20 OR ?z == VocabId(22) + evalToEmptyCheck( + notSparqlExpr(andSparqlExpr(greaterThanSparqlExpr(DoubleId(10.2), varX), + notEqSparqlExpr(VocabId(22), varZ)))); + // !(!((?x < VocabId(10) OR ?x <= VocabId(4)) OR ?z != 5.00)) + // is equal to + // (?x < VocabId(10) OR ?x <= VocabId(4)) OR ?z != 5.00 + evalToEmptyCheck(notSparqlExpr(notSparqlExpr( + orSparqlExpr(orSparqlExpr(lessThanSparqlExpr(varX, VocabId(10)), + lessEqSparqlExpr(VocabId(4), varX)), + notEqSparqlExpr(varZ, DoubleId(5)))))); + // !(?x != 10 AND !(?y >= 10.00 OR ?z <= 10)) + // is equal to + // ?x == 10 OR ?y >= 10.00 OR ?z <= 10 + evalToEmptyCheck(notSparqlExpr(andSparqlExpr( + notEqSparqlExpr(varX, IntId(10)), + notSparqlExpr(orSparqlExpr(greaterEqSparqlExpr(varY, DoubleId(10.00)), + lessEqSparqlExpr(varZ, IntId(10))))))); + // !((?x != 10 AND ?z != 10) AND (?y == 10 AND ?x >= 20)) + // is equal to + //?x == 10 OR ?z == 10 OR ?y != 10 OR ?x < 20 + evalToEmptyCheck(notSparqlExpr( + andSparqlExpr(andSparqlExpr(notEqSparqlExpr(varX, IntId(10)), + notEqSparqlExpr(varZ, IntId(10))), + andSparqlExpr(eqSparqlExpr(varY, IntId(10)), + greaterEqSparqlExpr(varX, IntId(20)))))); + // !(?z >= 40 AND (?z != 10.00 AND ?y != VocabId(1))) + // is equal to + // ?z <= 40 OR ?z == 10.00 OR ?y == VocabId(1) + evalToEmptyCheck(notSparqlExpr( + andSparqlExpr(greaterEqSparqlExpr(varZ, IntId(40)), + andSparqlExpr(notEqSparqlExpr(varZ, DoubleId(10.00)), + notEqSparqlExpr(varY, VocabId(1)))))); + // ?z <= true OR !(?x == 10 AND ?y == 10) + // is equal to + // ?z <= true OR ?x != 10 OR ?y != 10 + evalToEmptyCheck(orSparqlExpr( + lessEqSparqlExpr(varZ, BoolId(true)), + notSparqlExpr(andSparqlExpr(eqSparqlExpr(varX, IntId(10)), + eqSparqlExpr(IntId(10), varY))))); + // !(!(?z <= true OR !(?x == 10 AND ?y == 10))) + // is equal to + // ?z <= true OR ?x != 10 OR ?y != 10 + evalToEmptyCheck(notSparqlExpr(notSparqlExpr(orSparqlExpr( + lessEqSparqlExpr(varZ, BoolId(true)), + notSparqlExpr(andSparqlExpr(eqSparqlExpr(varX, IntId(10)), + eqSparqlExpr(IntId(10), varY))))))); + // !(!(?x != 10 OR !(?y >= 10.00 AND ?z <= 10))) + // is equal to + // ?x != 10 OR ?y < 10.00 OR ?z > 10 + evalToEmptyCheck(notSparqlExpr(notSparqlExpr(orSparqlExpr( + notEqSparqlExpr(varX, IntId(10)), + notSparqlExpr(andSparqlExpr(greaterEqSparqlExpr(varY, DoubleId(10.00)), + lessEqSparqlExpr(varZ, IntId(10)))))))); + // !(!(?x == VocabId(10) OR ?y >= 25) AND !(!(?z == true AND ?country == + // VocabId(20)))) + // is equal to + // ?x == VocabId(10) OR ?y >= 25 OR ?z == true AND ?country == VocabId(20) + evalToEmptyCheck(notSparqlExpr(andSparqlExpr( + notSparqlExpr(orSparqlExpr(eqSparqlExpr(varX, VocabId(10)), + greaterEqSparqlExpr(varY, IntId(25)))), + notSparqlExpr(notSparqlExpr( + andSparqlExpr(eqSparqlExpr(varZ, BoolId(true)), + eqSparqlExpr(Variable{"?country"}, VocabId(20)))))))); +} From e4be1fe168eb1bc2a2f6b0d2d8d7c1c73cc0e323 Mon Sep 17 00:00:00 2001 From: realHannes Date: Tue, 22 Oct 2024 17:17:27 +0200 Subject: [PATCH 40/50] all builds should compile now --- .../sparqlExpressions/NumericBinaryExpressions.cpp | 2 +- .../sparqlExpressions/NumericUnaryExpressions.cpp | 2 +- .../sparqlExpressions/RelationalExpressions.cpp | 2 +- test/CompressedBlockPrefilteringTest.cpp | 14 +++++++++++++- 4 files changed, 16 insertions(+), 4 deletions(-) diff --git a/src/engine/sparqlExpressions/NumericBinaryExpressions.cpp b/src/engine/sparqlExpressions/NumericBinaryExpressions.cpp index 21df9e0f6d..04e3f536f4 100644 --- a/src/engine/sparqlExpressions/NumericBinaryExpressions.cpp +++ b/src/engine/sparqlExpressions/NumericBinaryExpressions.cpp @@ -236,7 +236,7 @@ class LogicalBinaryExpressionImpl : public NaryExpression { std::optional> getPrefilterExpressionForMetadata(bool isNegated) const override { - static_assert(this->N == 2); + AD_CORRECTNESS_CHECK(this->N == 2); auto leftChild = this->getNthChild(0).value()->getPrefilterExpressionForMetadata( isNegated); diff --git a/src/engine/sparqlExpressions/NumericUnaryExpressions.cpp b/src/engine/sparqlExpressions/NumericUnaryExpressions.cpp index f612cfc954..19a369ffd2 100644 --- a/src/engine/sparqlExpressions/NumericUnaryExpressions.cpp +++ b/src/engine/sparqlExpressions/NumericUnaryExpressions.cpp @@ -33,7 +33,7 @@ class UnaryNegateExpressionImpl : public NaryExpression { std::optional> getPrefilterExpressionForMetadata( [[maybe_unused]] bool isNegated) const override { - static_assert(this->N == 1); + AD_CORRECTNESS_CHECK(this->N == 1); auto optExprVarVec = this->getNthChild(0).value()->getPrefilterExpressionForMetadata( !isNegated); diff --git a/src/engine/sparqlExpressions/RelationalExpressions.cpp b/src/engine/sparqlExpressions/RelationalExpressions.cpp index e1d056ba7e..66180b41e5 100644 --- a/src/engine/sparqlExpressions/RelationalExpressions.cpp +++ b/src/engine/sparqlExpressions/RelationalExpressions.cpp @@ -434,7 +434,7 @@ RelationalExpression::getPrefilterExpressionForMetadata( }; const auto createPrefilterExprVariablePair = - [this](const std::pair& valuePair) + [](const std::pair& valuePair) -> std::vector { std::vector pairVec; pairVec.emplace_back( diff --git a/test/CompressedBlockPrefilteringTest.cpp b/test/CompressedBlockPrefilteringTest.cpp index 023bf5e752..f33398fa65 100644 --- a/test/CompressedBlockPrefilteringTest.cpp +++ b/test/CompressedBlockPrefilteringTest.cpp @@ -915,7 +915,8 @@ const auto makeLiteralSparqlExpr = } else if constexpr (std::is_same_v) { return std::make_unique(child); } else { - FAIL() << "Unexpected Type."; + throw std::runtime_error( + "Can't create a LiteralExpression from provided (input) type."); } }; @@ -1107,6 +1108,17 @@ TEST(SparqlExpression, getPrefilterExpressionsToComplexSparqlExpressions) { pr(andFilterExpr(greaterEqFilterExpr(IntId(10)), notEqFilterExpr(IntId(20))), varX)); + // ?z > VocabId(0) AND ?y > 0 AND ?x < 30.00 + // expected prefilter pairs + // {<(< 30.00), ?x>, <(> 0), ?y>, <(> VocabId(0)), ?z>} + evalAndEqualityCheck( + andSparqlExpr(andSparqlExpr(greaterThanSparqlExpr(varZ, VocabId(0)), + greaterThanSparqlExpr(varY, IntId(0))), + lessThanSparqlExpr(varX, DoubleId(30.00))), + pr(lessThanFilterExpr(DoubleId(30.00)), varX), + pr(greaterThanFilterExpr(IntId(0)), varY), + pr(greaterThanFilterExpr(VocabId(0)), varZ)); + // ?x == VocabId(10) AND ?y >= VocabId(10) // expected prefilter pairs: // {<(== VocabId(10)), ?x>, <(>= VocabId(10)), ?y>} From 74756fe679544b1e1b35c14fc6095cd4e2c832b9 Mon Sep 17 00:00:00 2001 From: realHannes Date: Tue, 22 Oct 2024 22:35:42 +0200 Subject: [PATCH 41/50] fix build issue and increase test coverage --- .../NumericBinaryExpressions.cpp | 4 +- test/CompressedBlockPrefilteringTest.cpp | 87 +++++++++++++++++-- 2 files changed, 83 insertions(+), 8 deletions(-) diff --git a/src/engine/sparqlExpressions/NumericBinaryExpressions.cpp b/src/engine/sparqlExpressions/NumericBinaryExpressions.cpp index 04e3f536f4..4ae1abc273 100644 --- a/src/engine/sparqlExpressions/NumericBinaryExpressions.cpp +++ b/src/engine/sparqlExpressions/NumericBinaryExpressions.cpp @@ -152,7 +152,7 @@ std::optional> mergeChildrenOrLogicImpl( // And because we can't definitely say that one child (and which of the two // children) must be true, pre-filtering w.r.t. underlying compressed blocks // is not possible here. - // In short: OR has multiple congifurations that yield true w.r.t. two child + // In short: OR has multiple configurations that yield true w.r.t. two child // expressions => we can't define a distinct PrefilterExpression. return std::nullopt; } @@ -184,7 +184,7 @@ std::optional> mergeChildrenOrLogicImpl( // We have to construct a PrefilterExpression for (?x >= 10 && ?y != 0) || // ?x <= 0. If this OR expression yields true, at least ?x >= 10 || ?x <= 0 // must be staisifed; for this objective we can construct a - // PrefiterExpression. We can't make a distinct predicition w.r.t. ?y != 0 + // PrefiterExpression. We can't make a distinct prediction w.r.t. ?y != 0 // => not relevant for the PrefilterExpression. Thus, we return the // PrefilterExpresion {((>= 10 OR <= 0), ?x)}. if (varLeft == varRight) { diff --git a/test/CompressedBlockPrefilteringTest.cpp b/test/CompressedBlockPrefilteringTest.cpp index f33398fa65..eff46e0902 100644 --- a/test/CompressedBlockPrefilteringTest.cpp +++ b/test/CompressedBlockPrefilteringTest.cpp @@ -24,7 +24,7 @@ using ad_utility::testing::UndefId; using ad_utility::testing::VocabId; using namespace prefilterExpressions; -// TEST SECTION 1 (PrefilterExpression) +// TEST SECTION 1 //______________________________________________________________________________ //______________________________________________________________________________ struct MetadataBlocks { @@ -58,7 +58,8 @@ struct MetadataBlocks { {VocabId10, DoubleId33, firstId}, // firstTriple {VocabId10, DoubleId33, lastId}, // lastTriple {}, - false}; + false, + 0}; }; const BlockMetadata b1 = makeBlock(IntId(0), IntId(0)); @@ -91,7 +92,8 @@ struct MetadataBlocks { {VocabId10, DoubleId33, IntId(0)}, {VocabId10, DoubleId(22), IntId(0)}, {}, - false}; + false, + 0}; std::vector blocksInvalidCol1 = {b1_1, b2, b3, b4, b5, b6, b7, b8, b9, b10, b11, b12, b13, b14, b15, b16, b17}; @@ -100,7 +102,8 @@ struct MetadataBlocks { {VocabId(11), DoubleId33, IntId(-10)}, {VocabId10, DoubleId33, IntId(-8)}, {}, - false}; + false, + 0}; std::vector blocksInvalidCol2 = {b1, b2, b3, b4, b5_1, b6, b7, b8, b9, b10, b11, b12, b13, b14, b15, b16, b17}; @@ -878,7 +881,7 @@ TEST(PrefilterExpression, testInputConditionCheck) { "The columns up to the evaluation column must contain the same values."); } -// TEST SECTION 2 (getPrefilterExpressionForMetadata) +// TEST SECTION 2 //______________________________________________________________________________ //______________________________________________________________________________ using namespace sparqlExpression; @@ -1054,7 +1057,64 @@ auto pr = [](std::unique_ptr expr, }; //______________________________________________________________________________ -// TEST SECTION +// TEST SECTION 2 + +//______________________________________________________________________________ +// Test PrefilterExpression equality operator. +TEST(PrefilterExpression, testEqualityOperator) { + MetadataBlocks blocks{}; + // Relational PrefilterExpressions + ASSERT_FALSE(*greaterEqFilterExpr(blocks.referenceDate1) == + *greaterEqFilterExpr(blocks.referenceDate2)); + ASSERT_FALSE(*notEqFilterExpr(BoolId(true)) == *eqFilterExpr(BoolId(true))); + ASSERT_TRUE(*eqFilterExpr(IntId(1)) == *eqFilterExpr(IntId(1))); + ASSERT_TRUE(*greaterEqFilterExpr(blocks.referenceDate1) == + *greaterEqFilterExpr(blocks.referenceDate1)); + // NotExpression + ASSERT_TRUE(*notFilterExpr(eqFilterExpr(IntId(0))) == + *notFilterExpr(eqFilterExpr(IntId(0)))); + ASSERT_TRUE(*notFilterExpr(notFilterExpr(greaterEqFilterExpr(VocabId(0)))) == + *notFilterExpr(notFilterExpr(greaterEqFilterExpr(VocabId(0))))); + ASSERT_FALSE(*notFilterExpr(greaterThanFilterExpr(IntId(0))) == + *eqFilterExpr(IntId(0))); + ASSERT_FALSE(*notFilterExpr(andFilterExpr(eqFilterExpr(IntId(1)), + eqFilterExpr(IntId(0)))) == + *notFilterExpr(greaterEqFilterExpr(VocabId(0)))); + // Binary PrefilterExpressions (AND and OR) + ASSERT_TRUE( + *orFilterExpr(eqFilterExpr(IntId(0)), lessEqFilterExpr(IntId(0))) == + *orFilterExpr(eqFilterExpr(IntId(0)), lessEqFilterExpr(IntId(0)))); + ASSERT_TRUE( + *andFilterExpr(lessEqFilterExpr(VocabId(1)), + lessEqFilterExpr(IntId(0))) == + *andFilterExpr(lessEqFilterExpr(VocabId(1)), lessEqFilterExpr(IntId(0)))); + ASSERT_FALSE( + *orFilterExpr(eqFilterExpr(IntId(0)), lessEqFilterExpr(IntId(0))) == + *andFilterExpr(lessEqFilterExpr(VocabId(1)), lessEqFilterExpr(IntId(0)))); + ASSERT_FALSE( + *notFilterExpr( + orFilterExpr(eqFilterExpr(IntId(0)), lessEqFilterExpr(IntId(0)))) == + *orFilterExpr(eqFilterExpr(IntId(0)), lessEqFilterExpr(IntId(0)))); +} + +//______________________________________________________________________________ +// Test PrefilterExpression content formatting for debugging. +TEST(PrefilterExpression, checkPrintFormattedPrefilterExpression) { + const auto& relExpr = lessThanFilterExpr(IntId(10)); + EXPECT_EQ((std::stringstream() << *relExpr).str(), + "Prefilter RelationalExpression<0>\nValueId: I:10\n.\n"); + const auto& notExpr = notFilterExpr(eqFilterExpr(VocabId(0))); + EXPECT_EQ((std::stringstream() << *notExpr).str(), + "Prefilter NotExpression:\nchild {Prefilter " + "RelationalExpression<3>\nValueId: V:0\n}\n.\n"); + const auto& orExpr = + orFilterExpr(lessEqFilterExpr(IntId(0)), eqFilterExpr(IntId(5))); + EXPECT_EQ((std::stringstream() << *orExpr).str(), + "Prefilter LogicalExpression<1>\nchild1 {Prefilter " + "RelationalExpression<1>\nValueId: I:0\n}child2 {Prefilter " + "RelationalExpression<2>\nValueId: I:5\n}\n.\n"); +} + //______________________________________________________________________________ // Test coverage for the default implementation of // getPrefilterExpressionForMetadata. @@ -1325,6 +1385,15 @@ TEST(SparqlExpression, getPrefilterExpressionsToComplexSparqlExpressions) { greaterEqFilterExpr(IntId(-10)), lessThanFilterExpr(DoubleId(0.00))))))), varX)); + // ?y != ?x AND ?x >= 10 + // expected prefilter pairs: + // {<(>= 10), ?x>} + evalAndEqualityCheck(andSparqlExpr(notEqSparqlExpr(varY, varX), + greaterEqSparqlExpr(varX, IntId(10))), + pr(greaterEqFilterExpr(IntId(10)), varX)); + evalAndEqualityCheck(andSparqlExpr(greaterEqSparqlExpr(varX, IntId(10)), + notEqSparqlExpr(varY, varX)), + pr(greaterEqFilterExpr(IntId(10)), varX)); } //______________________________________________________________________________ @@ -1341,6 +1410,12 @@ TEST(SparqlExpression, getEmptyPrefilterFromSparqlRelational) { evalToEmptyCheck(lessThanSparqlExpr(VocabId(10), BoolId(10))); evalToEmptyCheck(greaterEqSparqlExpr(lit, lit)); evalToEmptyCheck(eqSparqlExpr(iri, iri)); + evalToEmptyCheck(orSparqlExpr(eqSparqlExpr(var, var), + greaterThanSparqlExpr(var, IntId(0)))); + evalToEmptyCheck( + orSparqlExpr(eqSparqlExpr(var, var), greaterThanSparqlExpr(var, var))); + evalToEmptyCheck( + andSparqlExpr(eqSparqlExpr(var, var), greaterThanSparqlExpr(var, var))); } //______________________________________________________________________________ From 49de4d04d26860f46698f5efb5aa16c83aa5eaf8 Mon Sep 17 00:00:00 2001 From: realHannes Date: Fri, 25 Oct 2024 13:29:40 +0200 Subject: [PATCH 42/50] introduce test structure from PR1 (#1503) --- src/index/CompressedBlockPrefiltering.cpp | 8 +- test/CompressedBlockPrefilteringTest.cpp | 1776 ++++++++------------- 2 files changed, 683 insertions(+), 1101 deletions(-) diff --git a/src/index/CompressedBlockPrefiltering.cpp b/src/index/CompressedBlockPrefiltering.cpp index 4517c379e0..953e52026f 100644 --- a/src/index/CompressedBlockPrefiltering.cpp +++ b/src/index/CompressedBlockPrefiltering.cpp @@ -79,14 +79,12 @@ static auto checkEvalRequirements(const std::vector& input, // Check for equality of IDs up to the evaluation column. if (checkInvalid(std::not_equal_to<>{}, evaluationColumn)) { throwRuntimeError( - "The columns up to the evaluation column must contain the same " - "values."); + "The values in the columns up to the evaluation column must be " + "consistent."); } // Check for order of IDs on evaluation Column. if (checkInvalid(std::greater<>{}, evaluationColumn + 1)) { - throwRuntimeError( - "The data blocks must be provided in sorted order regarding the " - "evaluation column."); + throwRuntimeError("The blocks must be provided in sorted order."); } } }; diff --git a/test/CompressedBlockPrefilteringTest.cpp b/test/CompressedBlockPrefilteringTest.cpp index eff46e0902..c37b6cb2f2 100644 --- a/test/CompressedBlockPrefilteringTest.cpp +++ b/test/CompressedBlockPrefilteringTest.cpp @@ -7,14 +7,14 @@ #include #include "./SparqlExpressionTestHelpers.h" -#include "./util/GTestHelpers.h" #include "engine/sparqlExpressions/NaryExpression.h" #include "engine/sparqlExpressions/RelationalExpressions.h" #include "index/CompressedBlockPrefiltering.h" #include "util/DateYearDuration.h" +#include "util/GTestHelpers.h" #include "util/IdTestHelpers.h" -using ad_utility::source_location; +namespace { using ad_utility::testing::BlankNodeId; using ad_utility::testing::BoolId; using ad_utility::testing::DateId; @@ -22,868 +22,539 @@ using ad_utility::testing::DoubleId; using ad_utility::testing::IntId; using ad_utility::testing::UndefId; using ad_utility::testing::VocabId; +constexpr auto DateParser = &DateYearOrDuration::parseXsdDate; using namespace prefilterExpressions; -// TEST SECTION 1 +namespace makeFilterExpr { //______________________________________________________________________________ -//______________________________________________________________________________ -struct MetadataBlocks { - /* - Our pre-filtering procedure expects blocks that are in correct (ascending) - order w.r.t. their contained ValueIds given the first and last triple. - - The correct order of the ValueIds is dependent on their type and underlying - representation. +// Make RelationalExpression +template +auto relExpr = + [](const ValueId& referenceId) -> std::unique_ptr { + return std::make_unique(referenceId); +}; - Short overview on the ascending order logic for the underlying values: - Order ValueIds for (signed) integer values - [0... max, -max... -1] - Order ValueIds for (signed) doubles values - [0.0... max, -0.0... -max] - Order ValueIds for Vocab and LocalVocab values given the vocabulary with - indices (up to N) - [VocabId(0), .... VocabId(N)] +// Make AndExpression or OrExpression +template +auto logExpr = [](std::unique_ptr child1, + std::unique_ptr child2) + -> std::unique_ptr { + return std::make_unique(std::move(child1), std::move(child2)); +}; - COLUMN 0 and COLUMN 1 contain fixed values, this is a necessary condition - that is also checked during the pre-filtering procedure. The actual evaluation - column (we filter w.r.t. values of COLUMN 2) contains mixed types. - */ +// Make NotExpression +auto notExpr = [](std::unique_ptr child) + -> std::unique_ptr { + return std::make_unique(std::move(child)); +}; - // Values for fixed columns - const Id VocabId10 = VocabId(10); - const Id DoubleId33 = DoubleId(33); +} // namespace makeFilterExpr +//______________________________________________________________________________ +// instantiation relational +// LESS THAN (`<`, `PrefilterExpression`) +constexpr auto lt = makeFilterExpr::relExpr; +// LESS EQUAL (`<=`, `PrefilterExpression`) +constexpr auto le = makeFilterExpr::relExpr; +// GREATER EQUAL (`>=`, `PrefilterExpression`) +constexpr auto ge = makeFilterExpr::relExpr; +// GREATER THAN (`>`, `PrefiterExpression`) +constexpr auto gt = makeFilterExpr::relExpr; +// EQUAL (`==`, `PrefiterExpression`) +constexpr auto eq = makeFilterExpr::relExpr; +// NOT EQUAL (`!=`, `PrefiterExpression`) +constexpr auto neq = makeFilterExpr::relExpr; +// AND (`&&`, `PrefiterExpression`) +constexpr auto andExpr = makeFilterExpr::logExpr; +// OR (`||`, `PrefiterExpression`) +constexpr auto orExpr = makeFilterExpr::logExpr; +// NOT (`!`, `PrefiterExpression`) +constexpr auto notExpr = makeFilterExpr::notExpr; - auto makeBlock(const ValueId& firstId, const ValueId& lastId) - -> BlockMetadata { - return {{}, - 0, - // COLUMN 0 | COLUMN 1 | COLUMN 2 - {VocabId10, DoubleId33, firstId}, // firstTriple - {VocabId10, DoubleId33, lastId}, // lastTriple - {}, - false, - 0}; - }; +//______________________________________________________________________________ +/* +Our pre-filtering procedure expects blocks that are in correct (ascending) +order w.r.t. their contained ValueIds given the first and last triple. - const BlockMetadata b1 = makeBlock(IntId(0), IntId(0)); - const BlockMetadata b2 = makeBlock(IntId(0), IntId(5)); - const BlockMetadata b3 = makeBlock(IntId(5), IntId(6)); - const BlockMetadata b4 = makeBlock(IntId(8), IntId(9)); - const BlockMetadata b5 = makeBlock(IntId(-10), IntId(-8)); - const BlockMetadata b6 = makeBlock(IntId(-4), IntId(-4)); - // b7 contains mixed datatypes (COLUMN 2) - const BlockMetadata b7 = makeBlock(IntId(-4), DoubleId(2)); - const BlockMetadata b8 = makeBlock(DoubleId(2), DoubleId(2)); - const BlockMetadata b9 = makeBlock(DoubleId(4), DoubleId(4)); - const BlockMetadata b10 = makeBlock(DoubleId(4), DoubleId(10)); - const BlockMetadata b11 = makeBlock(DoubleId(-1.23), DoubleId(-6.25)); - const BlockMetadata b12 = makeBlock(DoubleId(-6.25), DoubleId(-6.25)); - const BlockMetadata b13 = makeBlock(DoubleId(-10.42), DoubleId(-12.00)); - // b14 contains mixed datatypes (COLUMN 2) - const BlockMetadata b14 = makeBlock(DoubleId(-14.01), VocabId(0)); - const BlockMetadata b15 = makeBlock(VocabId(10), VocabId(14)); - const BlockMetadata b16 = makeBlock(VocabId(14), VocabId(14)); - const BlockMetadata b17 = makeBlock(VocabId(14), VocabId(17)); - std::vector blocks = {b1, b2, b3, b4, b5, b6, - b7, b8, b9, b10, b11, b12, - b13, b14, b15, b16, b17}; +The correct order of the ValueIds is dependent on their type and underlying +representation. - // The following blocks will be swapped with their respective correct block, - // to test if the method evaluate checks the pre-conditions properly. - const BlockMetadata b1_1{{}, - 0, - {VocabId10, DoubleId33, IntId(0)}, - {VocabId10, DoubleId(22), IntId(0)}, - {}, - false, - 0}; - std::vector blocksInvalidCol1 = {b1_1, b2, b3, b4, b5, b6, - b7, b8, b9, b10, b11, b12, - b13, b14, b15, b16, b17}; - const BlockMetadata b5_1{{}, - 0, - {VocabId(11), DoubleId33, IntId(-10)}, - {VocabId10, DoubleId33, IntId(-8)}, - {}, - false, - 0}; - std::vector blocksInvalidCol2 = {b1, b2, b3, b4, b5_1, b6, - b7, b8, b9, b10, b11, b12, - b13, b14, b15, b16, b17}; - std::vector blocksInvalidOrder1 = { - b2, b1, b3, b4, b5, b6, b7, b8, b9, - b10, b11, b12, b13, b14, b15, b16, b17}; - std::vector blocksInvalidOrder2 = { - b1, b2, b3, b4, b5, b6, b7, b8, b9, - b10, b11, b12, b14, b13, b15, b16, b17}; - std::vector blocksWithDuplicate1 = { - b1, b1, b2, b3, b4, b5, b6, b7, b8, - b9, b10, b11, b12, b13, b14, b15, b16, b17}; - std::vector blocksWithDuplicate2 = { - b1, b2, b3, b4, b5, b6, b7, b8, b9, - b10, b11, b12, b13, b14, b15, b16, b17, b17}; +Short overview on the ascending order logic for the underlying values: +Order ValueIds for (signed) integer values - [0... max, -max... -1] +Order ValueIds for (signed) doubles values - [0.0... max, -0.0... -max] +Order ValueIds for Vocab and LocalVocab values given the vocabulary with +indices (up to N) - [VocabId(0), .... VocabId(N)] - //____________________________________________________________________________ - // Additional blocks for different datatype mix. +COLUMN 1 and COLUMN 2 contain fixed values, this is a necessary condition +that is also checked during the pre-filtering procedure. The actual evaluation +column (we filter w.r.t. values of COLUMN 0) contains mixed types. +*/ +//______________________________________________________________________________ +class TestPrefilterExprOnBlockMetadata : public ::testing::Test { + public: + const Id referenceDate1 = DateId(DateParser, "1999-11-11"); + const Id referenceDate2 = DateId(DateParser, "2005-02-27"); const Id undef = Id::makeUndefined(); const Id falseId = BoolId(false); const Id trueId = BoolId(true); - const Id referenceDate1 = - DateId(DateYearOrDuration::parseXsdDate, "1999-11-11"); - const Id referenceDate2 = - DateId(DateYearOrDuration::parseXsdDate, "2005-02-27"); - const Id referenceDateEqual = - DateId(DateYearOrDuration::parseXsdDate, "2000-01-01"); - const BlockMetadata bd1 = makeBlock(undef, undef); - const BlockMetadata bd2 = makeBlock(undef, falseId); - const BlockMetadata bd3 = makeBlock(falseId, falseId); - const BlockMetadata bd4 = makeBlock(trueId, trueId); - const BlockMetadata bd5 = - makeBlock(trueId, DateId(DateYearOrDuration::parseXsdDate, "1999-12-12")); - const BlockMetadata bd6 = - makeBlock(DateId(DateYearOrDuration::parseXsdDate, "2000-01-01"), - DateId(DateYearOrDuration::parseXsdDate, "2000-01-01")); - const BlockMetadata bd7 = makeBlock( - DateId(DateYearOrDuration::parseXsdDate, "2024-10-08"), BlankNodeId(10)); - std::vector otherBlocks = {bd1, bd2, bd3, bd4, bd5, bd6, bd7}; -}; + const Id referenceDateEqual = DateId(DateParser, "2000-01-01"); -// Static tests, they focus on corner case values for the given block triples. -//______________________________________________________________________________ -//______________________________________________________________________________ -// Test PrefilterExpressions + // Fixed column ValueIds + const Id VocabId10 = VocabId(10); + const Id DoubleId33 = DoubleId(33); + const Id GraphId = VocabId(0); -//______________________________________________________________________________ -static const auto testThrowError = - [](std::unique_ptr expression, size_t evaluationColumn, - const std::vector& input, - const std::string& expectedErrorMessage) { - try { - expression->evaluate(input, evaluationColumn); - FAIL() << "Expected thrown error message."; - } catch (const std::runtime_error& error) { - EXPECT_EQ(std::string(error.what()), expectedErrorMessage); - } catch (...) { - FAIL() << "Expected an error of type std::runtime_error, but a " - "different error was thrown."; - } - }; + // Define BlockMetadata + const BlockMetadata b1 = makeBlock(undef, undef); + const BlockMetadata b2 = makeBlock(undef, falseId); + const BlockMetadata b3 = makeBlock(falseId, falseId); + const BlockMetadata b4 = makeBlock(trueId, IntId(0)); + const BlockMetadata b5 = makeBlock(IntId(0), IntId(0)); + const BlockMetadata b6 = makeBlock(IntId(0), IntId(5)); + const BlockMetadata b7 = makeBlock(IntId(5), IntId(6)); + const BlockMetadata b8 = makeBlock(IntId(8), IntId(9)); + const BlockMetadata b9 = makeBlock(IntId(-10), IntId(-8)); + const BlockMetadata b10 = makeBlock(IntId(-4), IntId(-4)); + const BlockMetadata b11 = makeBlock(IntId(-4), DoubleId(2)); + const BlockMetadata b12 = makeBlock(DoubleId(2), DoubleId(2)); + const BlockMetadata b13 = makeBlock(DoubleId(4), DoubleId(4)); + const BlockMetadata b14 = makeBlock(DoubleId(4), DoubleId(10)); + const BlockMetadata b15 = makeBlock(DoubleId(-1.23), DoubleId(-6.25)); + const BlockMetadata b16 = makeBlock(DoubleId(-6.25), DoubleId(-6.25)); + const BlockMetadata b17 = makeBlock(DoubleId(-10.42), DoubleId(-12.00)); + const BlockMetadata b18 = makeBlock(DoubleId(-14.01), VocabId(0)); + const BlockMetadata b19 = makeBlock(VocabId(10), VocabId(14)); + const BlockMetadata b20 = makeBlock(VocabId(14), VocabId(14)); + const BlockMetadata b21 = makeBlock(VocabId(14), VocabId(17)); + const BlockMetadata b22 = + makeBlock(VocabId(20), DateId(DateParser, "1999-12-12")); + const BlockMetadata b23 = makeBlock(DateId(DateParser, "2000-01-01"), + DateId(DateParser, "2000-01-01")); + const BlockMetadata b24 = + makeBlock(DateId(DateParser, "2024-10-08"), BlankNodeId(10)); -//______________________________________________________________________________ -template -std::unique_ptr makeRelExpr(const ValueId& referenceId) { - return std::make_unique(referenceId); -} + // All blocks that contain mixed (ValueId) types over column 0 + const std::vector mixedBlocks = {b2, b4, b11, b18, b22, b24}; -//______________________________________________________________________________ -template -std::unique_ptr makeLogExpr(const ValueId& referenceId1, - const ValueId& referenceId2) { - return std::make_unique(makeRelExpr(referenceId1), - makeRelExpr(referenceId2)); -} + // Ordered and unique vector with BlockMetadata + const std::vector blocks = { + b1, b2, b3, b4, b5, b6, b7, b8, b9, b10, b11, b12, + b13, b14, b15, b16, b17, b18, b19, b20, b21, b22, b23, b24}; -//______________________________________________________________________________ -template -std::unique_ptr makeNotExpression( - const ValueId& referenceId1, const std::optional optId2) { - if constexpr (check_is_relational_v && - check_is_logical_v) { - assert(optId2.has_value() && "Logical Expressions require two ValueIds"); - return std::make_unique( - makeLogExpr(referenceId1, optId2.value())); - } else if constexpr (std::is_same_v) { - return std::make_unique( - makeNotExpression(referenceId1, optId2)); - } else { - return std::make_unique(makeRelExpr(referenceId1)); - } -} + const std::vector blocksInvalidOrder1 = { + b1, b2, b3, b4, b5, b6, b7, b8, b9, b10, b11, b12, + b13, b14, b15, b16, b17, b18, b19, b20, b21, b22, b24, b23}; -//______________________________________________________________________________ -template -struct TestRelationalExpression { - void operator()(size_t evaluationColumn, const ValueId& referenceId, - const std::vector& input, const ResT& expected) - requires check_is_relational_v { - auto expression = makeRelExpr(referenceId); - if constexpr (std::is_same_v) { - testThrowError(std::move(expression), evaluationColumn, input, expected); - } else { - static_assert(std::is_same_v>); - EXPECT_EQ(expression->evaluate(input, evaluationColumn), expected); - } + const std::vector blocksInvalidOrder2 = { + b1, b2, b3, b4, b5, b6, b7, b8, b9, b10, b11, b12, + b14, b10, b15, b16, b17, b18, b19, b20, b21, b22, b23, b24}; + + const std::vector blocksWithDuplicate1 = { + b1, b1, b2, b3, b4, b5, b6, b7, b8, b9, b10, b11, b12, + b13, b14, b15, b16, b17, b18, b19, b20, b21, b22, b23, b24}; + + const std::vector blocksWithDuplicate2 = { + b1, b2, b3, b4, b5, b6, b7, b8, b9, b10, b11, b12, b13, + b14, b15, b16, b17, b18, b19, b20, b21, b22, b23, b24, b24}; + + // Function to create BlockMetadata + const BlockMetadata makeBlock(const ValueId& firstId, const ValueId& lastId) { + assert(firstId <= lastId); + static size_t blockIdx = 0; + ++blockIdx; + return {{{}, + 0, + // COLUMN 0 | COLUMN 1 | COLUMN 2 + {firstId, VocabId10, DoubleId33, GraphId}, // firstTriple + {lastId, VocabId10, DoubleId33, GraphId}, // lastTriple + {}, + false}, + blockIdx}; } -}; -//______________________________________________________________________________ -template -struct TestLogicalExpression { - template - void test(size_t evaluationColumn, const ValueId& referenceIdChild1, - ValueId referenceIdChild2, const std::vector& input, - const ResT& expected) - requires(check_is_logical_v && check_is_relational_v && - check_is_relational_v) { - auto expression = makeLogExpr( - referenceIdChild1, referenceIdChild2); - if constexpr (std::is_same_v) { - testThrowError(std::move(expression), evaluationColumn, input, expected); - } else { - static_assert(std::is_same_v>); - EXPECT_EQ(expression->evaluate(input, evaluationColumn), expected); - } + // Check if expected error is thrown. + auto makeTestErrorCheck(std::unique_ptr expr, + const std::vector& input, + const std::string& expected, + size_t evaluationColumn = 0) { + AD_EXPECT_THROW_WITH_MESSAGE(expr->evaluate(input, evaluationColumn), + ::testing::HasSubstr(expected)); } -}; -//______________________________________________________________________________ -template -struct TestNotExpression { - template - void test(size_t evaluationColumn, const std::vector& input, - const ResT& expected, const ValueId& referenceId1, - std::optional optId2 = std::nullopt) - requires check_is_relational_v { - auto expression = - makeNotExpression(referenceId1, optId2); - if constexpr (std::is_same_v) { - testThrowError(std::move(expression), evaluationColumn, input, expected); - } else { - static_assert(std::is_same_v>); - EXPECT_EQ(expression->evaluate(input, evaluationColumn), expected); - } + // Check that the provided expression prefilters the correct blocks. + auto makeTest(std::unique_ptr expr, + std::vector&& expected) { + std::vector expectedAdjusted; + // This is for convenience, we automatically insert all mixed blocks + // which must be always returned. + std::ranges::set_union( + expected, mixedBlocks, std::back_inserter(expectedAdjusted), + [](const BlockMetadata& b1, const BlockMetadata& b2) { + return b1.blockIndex_ < b2.blockIndex_; + }); + ASSERT_EQ(expr->evaluate(blocks, 0), expectedAdjusted); } }; +} // namespace + //______________________________________________________________________________ -TEST(BlockMetadata, testBlockFormatForDebugging) { - MetadataBlocks blocks{}; +TEST_F(TestPrefilterExprOnBlockMetadata, testBlockFormatForDebugging) { EXPECT_EQ( - "#BlockMetadata\n(first) Triple: V:10 D:33.000000 I:0\n(last) Triple: " - "V:10 D:33.000000 I:0\nnum. rows: 0.\n", - (std::stringstream() << blocks.b1).str()); + "#BlockMetadata\n(first) Triple: I:0 V:10 D:33.000000 V:0\n(last) " + "Triple: I:0 V:10 D:33.000000 V:0\nnum. rows: 0.\n", + (std::stringstream() << b5).str()); EXPECT_EQ( - "#BlockMetadata\n(first) Triple: V:10 D:33.000000 I:-4\n(last) Triple: " - "V:10 D:33.000000 D:2.000000\nnum. rows: 0.\n", - (std::stringstream() << blocks.b7).str()); + "#BlockMetadata\n(first) Triple: I:-4 V:10 D:33.000000 V:0\n(last) " + "Triple: D:2.000000 V:10 D:33.000000 V:0\nnum. rows: 0.\n", + (std::stringstream() << b11).str()); EXPECT_EQ( - "#BlockMetadata\n(first) Triple: V:10 D:33.000000 V:14\n(last) Triple: " - "V:10 D:33.000000 V:17\nnum. rows: 0.\n", - (std::stringstream() << blocks.b17).str()); + "#BlockMetadata\n(first) Triple: V:14 V:10 D:33.000000 V:0\n(last) " + "Triple: V:17 V:10 D:33.000000 V:0\nnum. rows: 0.\n", + (std::stringstream() << b21).str()); } +// Test Relational Expressions //______________________________________________________________________________ // Test LessThanExpression -TEST(RelationalExpression, testLessThanExpressions) { - MetadataBlocks blocks{}; - TestRelationalExpression> - testExpression; - testExpression( - 2, IntId(5), blocks.blocks, - {blocks.b1, blocks.b2, blocks.b5, blocks.b6, blocks.b7, blocks.b8, - blocks.b9, blocks.b10, blocks.b11, blocks.b12, blocks.b13, blocks.b14}); - - testExpression(2, IntId(-12), blocks.blocks, {blocks.b7, blocks.b14}); - testExpression(2, IntId(0), blocks.blocks, - {blocks.b5, blocks.b6, blocks.b7, blocks.b11, blocks.b12, - blocks.b13, blocks.b14}); - testExpression(2, IntId(100), blocks.blocks, - {blocks.b1, blocks.b2, blocks.b3, blocks.b4, blocks.b5, - blocks.b6, blocks.b7, blocks.b8, blocks.b9, blocks.b10, - blocks.b11, blocks.b12, blocks.b13, blocks.b14}); - testExpression(2, DoubleId(-3), blocks.blocks, - {blocks.b5, blocks.b6, blocks.b7, blocks.b11, blocks.b12, - blocks.b13, blocks.b14}); - testExpression(2, DoubleId(-14.01), blocks.blocks, {blocks.b7, blocks.b14}); - testExpression(2, DoubleId(-11.22), blocks.blocks, - {blocks.b7, blocks.b13, blocks.b14}); - testExpression( - 2, DoubleId(-4.121), blocks.blocks, - {blocks.b5, blocks.b7, blocks.b11, blocks.b12, blocks.b13, blocks.b14}); - testExpression(2, VocabId(0), blocks.blocks, {blocks.b7, blocks.b14}); - testExpression(2, VocabId(12), blocks.blocks, - {blocks.b7, blocks.b14, blocks.b15}); - testExpression(2, VocabId(14), blocks.blocks, - {blocks.b7, blocks.b14, blocks.b15}); - testExpression(2, VocabId(16), blocks.blocks, - {blocks.b7, blocks.b14, blocks.b15, blocks.b16, blocks.b17}); - // test blocks.otherBlocks - testExpression(2, blocks.undef, blocks.otherBlocks, - {blocks.bd2, blocks.bd5, blocks.bd7}); - testExpression(2, blocks.falseId, blocks.otherBlocks, - {blocks.bd2, blocks.bd5, blocks.bd7}); - testExpression(2, blocks.trueId, blocks.otherBlocks, - {blocks.bd2, blocks.bd3, blocks.bd5, blocks.bd7}); - testExpression(2, blocks.referenceDate1, blocks.otherBlocks, - {blocks.bd2, blocks.bd5, blocks.bd7}); - testExpression(2, blocks.referenceDateEqual, blocks.otherBlocks, - {blocks.bd2, blocks.bd5, blocks.bd7}); - testExpression(2, blocks.referenceDate2, blocks.otherBlocks, - {blocks.bd2, blocks.bd5, blocks.bd6, blocks.bd7}); - testExpression(2, BlankNodeId(11), blocks.otherBlocks, - {blocks.bd2, blocks.bd5, blocks.bd7}); +TEST_F(TestPrefilterExprOnBlockMetadata, testLessThanExpressions) { + makeTest(lt(IntId(5)), + {b5, b6, b9, b10, b11, b12, b13, b14, b15, b16, b17, b18}); + makeTest(lt(IntId(-12)), {b18}); + makeTest(lt(IntId(0)), {b9, b10, b15, b16, b17, b18}); + makeTest(lt(DoubleId(-14.01)), {b18}); + makeTest(lt(DoubleId(-11.22)), {b17, b18}); + makeTest(lt(DoubleId(-4.121)), {b9, b15, b16, b17, b18}); + makeTest(lt(VocabId(0)), {b18}); + makeTest(lt(VocabId(12)), {b18, b19}); + makeTest(lt(VocabId(14)), {b18, b19}); + makeTest(lt(VocabId(16)), {b18, b19, b20, b21}); + makeTest(lt(IntId(100)), + {b5, b6, b7, b8, b9, b10, b12, b13, b14, b15, b16, b17, b18}); + makeTest(lt(undef), {}); + makeTest(lt(falseId), {}); + makeTest(lt(trueId), {b2, b3}); + makeTest(lt(referenceDate1), {}); + makeTest(lt(referenceDateEqual), {b22}); + makeTest(lt(referenceDate2), {b22, b23, b24}); + makeTest(lt(BlankNodeId(11)), {b24}); } //______________________________________________________________________________ // Test LessEqualExpression -TEST(RelationalExpression, testLessEqualExpressions) { - MetadataBlocks blocks{}; - TestRelationalExpression> - testExpression; - testExpression(2, IntId(0), blocks.blocks, - {blocks.b1, blocks.b2, blocks.b5, blocks.b6, blocks.b7, - blocks.b11, blocks.b12, blocks.b13, blocks.b14}); - testExpression( - 2, IntId(-6), blocks.blocks, - {blocks.b5, blocks.b7, blocks.b11, blocks.b12, blocks.b13, blocks.b14}); - testExpression(2, IntId(7), blocks.blocks, - {blocks.b1, blocks.b2, blocks.b3, blocks.b5, blocks.b6, - blocks.b7, blocks.b8, blocks.b9, blocks.b10, blocks.b11, - blocks.b12, blocks.b13, blocks.b14}); - testExpression(2, IntId(-9), blocks.blocks, - {blocks.b5, blocks.b7, blocks.b13, blocks.b14}); - testExpression(2, DoubleId(-9.131), blocks.blocks, - {blocks.b5, blocks.b7, blocks.b13, blocks.b14}); - testExpression(2, DoubleId(1.1415), blocks.blocks, - {blocks.b1, blocks.b2, blocks.b5, blocks.b6, blocks.b7, - blocks.b11, blocks.b12, blocks.b13, blocks.b14}); - testExpression(2, DoubleId(3.1415), blocks.blocks, - {blocks.b1, blocks.b2, blocks.b5, blocks.b6, blocks.b7, - blocks.b8, blocks.b11, blocks.b12, blocks.b13, blocks.b14}); - testExpression(2, DoubleId(-11.99999999999999), blocks.blocks, - {blocks.b7, blocks.b13, blocks.b14}); - testExpression(2, DoubleId(-14.03), blocks.blocks, {blocks.b7, blocks.b14}); - testExpression(2, VocabId(0), blocks.blocks, {blocks.b7, blocks.b14}); - testExpression(2, VocabId(11), blocks.blocks, - {blocks.b7, blocks.b14, blocks.b15}); - testExpression(2, VocabId(14), blocks.blocks, - {blocks.b7, blocks.b14, blocks.b15, blocks.b16, blocks.b17}); - // test block.otherBlocks - testExpression(2, blocks.undef, blocks.otherBlocks, - {blocks.bd2, blocks.bd5, blocks.bd7}); - testExpression(2, blocks.falseId, blocks.otherBlocks, - {blocks.bd2, blocks.bd3, blocks.bd5, blocks.bd7}); - testExpression(2, blocks.trueId, blocks.otherBlocks, - {blocks.bd2, blocks.bd3, blocks.bd4, blocks.bd5, blocks.bd7}); - testExpression(2, blocks.referenceDateEqual, blocks.otherBlocks, - {blocks.bd2, blocks.bd5, blocks.bd6, blocks.bd7}); - testExpression(2, BlankNodeId(11), blocks.otherBlocks, - {blocks.bd2, blocks.bd5, blocks.bd7}); +TEST_F(TestPrefilterExprOnBlockMetadata, testLessEqualExpressions) { + makeTest(le(IntId(0)), {b5, b6, b9, b10, b11, b15, b16, b17, b18}); + makeTest(le(IntId(-6)), {b9, b11, b15, b16, b17, b18}); + makeTest(le(IntId(7)), + {b5, b6, b7, b9, b10, b11, b12, b13, b14, b15, b16, b17, b18}); + makeTest(le(IntId(-9)), {b9, b11, b17, b18}); + makeTest(le(DoubleId(-9.131)), {b9, b11, b17, b18}); + makeTest(le(DoubleId(1.1415)), {b5, b6, b9, b10, b11, b15, b16, b17, b18}); + makeTest(le(DoubleId(3.1415)), + {b5, b6, b9, b10, b11, b12, b15, b16, b17, b18}); + makeTest(le(DoubleId(-11.99999999999999)), {b17, b18}); + makeTest(le(DoubleId(-14.03)), {b18}); + makeTest(le(VocabId(0)), {b18}); + makeTest(le(VocabId(11)), {b18, b19}); + makeTest(le(VocabId(14)), {b18, b19, b20, b21}); + makeTest(le(undef), {}); + makeTest(le(falseId), {b2, b3}); + makeTest(le(trueId), {b2, b3, b4}); + makeTest(le(referenceDateEqual), {b22, b23}); + makeTest(le(BlankNodeId(11)), {b24}); } //______________________________________________________________________________ // Test GreaterThanExpression -TEST(RelationalExpression, testGreaterThanExpression) { - MetadataBlocks blocks{}; - TestRelationalExpression> - testExpression; - testExpression(2, DoubleId(5.5375), blocks.blocks, - {blocks.b3, blocks.b4, blocks.b7, blocks.b10, blocks.b14}); - testExpression(2, DoubleId(9.9994), blocks.blocks, - {blocks.b7, blocks.b10, blocks.b14}); - testExpression( - 2, IntId(-5), blocks.blocks, - {blocks.b1, blocks.b2, blocks.b3, blocks.b4, blocks.b6, blocks.b7, - blocks.b8, blocks.b9, blocks.b10, blocks.b11, blocks.b14}); - testExpression( - 2, DoubleId(-5.5375), blocks.blocks, - {blocks.b1, blocks.b2, blocks.b3, blocks.b4, blocks.b6, blocks.b7, - blocks.b8, blocks.b9, blocks.b10, blocks.b11, blocks.b14}); - testExpression( - 2, DoubleId(-6.2499999), blocks.blocks, - {blocks.b1, blocks.b2, blocks.b3, blocks.b4, blocks.b6, blocks.b7, - blocks.b8, blocks.b9, blocks.b10, blocks.b11, blocks.b14}); - testExpression(2, IntId(1), blocks.blocks, - {blocks.b2, blocks.b3, blocks.b4, blocks.b7, blocks.b8, - blocks.b9, blocks.b10, blocks.b14}); - testExpression(2, IntId(3), blocks.blocks, - {blocks.b2, blocks.b3, blocks.b4, blocks.b7, blocks.b9, - blocks.b10, blocks.b14}); - testExpression( - 2, IntId(4), blocks.blocks, - {blocks.b2, blocks.b3, blocks.b4, blocks.b7, blocks.b10, blocks.b14}); - testExpression(2, IntId(-4), blocks.blocks, - {blocks.b1, blocks.b2, blocks.b3, blocks.b4, blocks.b7, - blocks.b8, blocks.b9, blocks.b10, blocks.b11, blocks.b14}); - testExpression(2, IntId(33), blocks.blocks, {blocks.b7, blocks.b14}); - testExpression(2, VocabId(22), blocks.blocks, {blocks.b7, blocks.b14}); - testExpression(2, VocabId(14), blocks.blocks, - {blocks.b7, blocks.b14, blocks.b17}); - testExpression(2, VocabId(12), blocks.blocks, - {blocks.b7, blocks.b14, blocks.b15, blocks.b16, blocks.b17}); - // test blocks.otherBlocks - testExpression(2, blocks.undef, blocks.otherBlocks, - {blocks.bd2, blocks.bd5, blocks.bd7}); - testExpression(2, blocks.falseId, blocks.otherBlocks, - {blocks.bd2, blocks.bd4, blocks.bd5, blocks.bd7}); - testExpression(2, blocks.trueId, blocks.otherBlocks, - {blocks.bd2, blocks.bd5, blocks.bd7}); - testExpression(2, blocks.referenceDateEqual, blocks.otherBlocks, - {blocks.bd2, blocks.bd5, blocks.bd7}); - testExpression(2, blocks.referenceDate1, blocks.otherBlocks, - {blocks.bd2, blocks.bd5, blocks.bd6, blocks.bd7}); - testExpression(2, IntId(5), blocks.otherBlocks, - {blocks.bd2, blocks.bd5, blocks.bd7}); +TEST_F(TestPrefilterExprOnBlockMetadata, testGreaterThanExpression) { + makeTest(gt(DoubleId(5.5375)), {b7, b8, b11, b14, b18}); + makeTest(gt(DoubleId(9.9994)), {b14}); + makeTest(gt(IntId(-5)), {b5, b6, b7, b8, b10, b11, b12, b13, b14, b15}); + makeTest(gt(DoubleId(-5.5375)), + {b5, b6, b7, b8, b10, b11, b12, b13, b14, b15}); + makeTest(gt(DoubleId(-6.2499999)), + {b5, b6, b7, b8, b10, b11, b12, b13, b14, b15}); + makeTest(gt(IntId(1)), {b6, b7, b8, b11, b12, b13, b14}); + makeTest(gt(IntId(3)), {b6, b7, b8, b11, b13, b14}); + makeTest(gt(IntId(4)), {b6, b7, b8, b11, b14}); + makeTest(gt(IntId(-4)), {b5, b6, b7, b8, b11, b12, b13, b14, b15}); + makeTest(gt(IntId(33)), {}); + makeTest(gt(VocabId(22)), {b22}); + makeTest(gt(VocabId(14)), {b21, b22}); + makeTest(gt(VocabId(12)), {b19, b20, b21, b22}); + makeTest(gt(undef), {}); + makeTest(gt(falseId), {b4}); + makeTest(gt(trueId), {}); + makeTest(gt(referenceDateEqual), {b24}); + makeTest(gt(referenceDate1), {b22, b23, b24}); + makeTest(gt(referenceDate2), {b24}); } //______________________________________________________________________________ // Test GreaterEqualExpression -TEST(RelationalExpression, testGreaterEqualExpression) { - MetadataBlocks blocks{}; - TestRelationalExpression> - testExpression; - testExpression(2, IntId(0), blocks.blocks, - {blocks.b1, blocks.b2, blocks.b3, blocks.b4, blocks.b7, - blocks.b8, blocks.b9, blocks.b10, blocks.b14}); - testExpression(2, IntId(8), blocks.blocks, - {blocks.b4, blocks.b7, blocks.b10, blocks.b14}); - testExpression(2, DoubleId(9.98), blocks.blocks, - {blocks.b7, blocks.b10, blocks.b14}); - testExpression(2, IntId(-3), blocks.blocks, - {blocks.b1, blocks.b2, blocks.b3, blocks.b4, blocks.b7, - blocks.b8, blocks.b9, blocks.b10, blocks.b11, blocks.b14}); - testExpression(2, IntId(-10), blocks.blocks, - {blocks.b1, blocks.b2, blocks.b3, blocks.b4, blocks.b5, - blocks.b6, blocks.b7, blocks.b8, blocks.b9, blocks.b10, - blocks.b11, blocks.b12, blocks.b14}); - testExpression(2, DoubleId(-3.1415), blocks.blocks, - {blocks.b1, blocks.b2, blocks.b3, blocks.b4, blocks.b7, - blocks.b8, blocks.b9, blocks.b10, blocks.b11, blocks.b14}); - testExpression( - 2, DoubleId(-4.000001), blocks.blocks, - {blocks.b1, blocks.b2, blocks.b3, blocks.b4, blocks.b6, blocks.b7, - blocks.b8, blocks.b9, blocks.b10, blocks.b11, blocks.b14}); - testExpression(2, DoubleId(10.000), blocks.blocks, - {blocks.b7, blocks.b10, blocks.b14}); - testExpression(2, DoubleId(-15.22), blocks.blocks, - {blocks.b1, blocks.b2, blocks.b3, blocks.b4, blocks.b5, - blocks.b6, blocks.b7, blocks.b8, blocks.b9, blocks.b10, - blocks.b11, blocks.b12, blocks.b13, blocks.b14}); - testExpression(2, DoubleId(7.999999), blocks.blocks, - {blocks.b4, blocks.b7, blocks.b10, blocks.b14}); - testExpression(2, DoubleId(10.0001), blocks.blocks, {blocks.b7, blocks.b14}); - testExpression(2, VocabId(14), blocks.blocks, - {blocks.b7, blocks.b14, blocks.b15, blocks.b16, blocks.b17}); - testExpression(2, VocabId(10), blocks.blocks, - {blocks.b7, blocks.b14, blocks.b15, blocks.b16, blocks.b17}); - testExpression(2, VocabId(17), blocks.blocks, - {blocks.b7, blocks.b14, blocks.b17}); - // test blocks.otherBlocks - testExpression(2, blocks.undef, blocks.otherBlocks, - {blocks.bd2, blocks.bd5, blocks.bd7}); - testExpression(2, blocks.falseId, blocks.otherBlocks, - {blocks.bd2, blocks.bd3, blocks.bd4, blocks.bd5, blocks.bd7}); - testExpression(2, blocks.trueId, blocks.otherBlocks, - {blocks.bd2, blocks.bd4, blocks.bd5, blocks.bd7}); - testExpression(2, blocks.referenceDateEqual, blocks.otherBlocks, - {blocks.bd2, blocks.bd5, blocks.bd6, blocks.bd7}); - testExpression(2, VocabId(0), blocks.otherBlocks, - {blocks.bd2, blocks.bd5, blocks.bd7}); +TEST_F(TestPrefilterExprOnBlockMetadata, testGreaterEqualExpression) { + makeTest(ge(IntId(0)), {b5, b6, b7, b8, b11, b12, b13, b14}); + makeTest(ge(IntId(8)), {b8, b11, b14}); + makeTest(ge(DoubleId(9.98)), {b11, b14}); + makeTest(ge(IntId(-3)), {b5, b6, b7, b8, b11, b12, b13, b14, b15}); + makeTest(ge(IntId(-10)), + {b5, b6, b7, b8, b9, b10, b11, b12, b13, b14, b15, b16}); + makeTest(ge(DoubleId(-3.1415)), {b5, b6, b7, b8, b11, b12, b13, b14, b15}); + makeTest(ge(DoubleId(-4.000001)), + {b5, b6, b7, b8, b10, b11, b12, b13, b14, b15}); + makeTest(ge(DoubleId(10.000)), {b11, b14}); + makeTest(ge(DoubleId(-15.22)), + {b5, b6, b7, b8, b9, b10, b11, b12, b13, b14, b15, b16, b17, b18}); + makeTest(ge(DoubleId(7.999999)), {b8, b11, b14}); + makeTest(ge(DoubleId(10.0001)), {}); + makeTest(ge(VocabId(14)), {b18, b19, b20, b21, b22}); + makeTest(ge(VocabId(10)), {b18, b19, b20, b21, b22}); + makeTest(ge(VocabId(17)), {b18, b21, b22}); + makeTest(ge(undef), {}); + makeTest(ge(falseId), {b2, b3, b4}); + makeTest(ge(trueId), {b4}); + makeTest(ge(referenceDateEqual), {b23, b24}); } //______________________________________________________________________________ // Test EqualExpression -TEST(RelationalExpression, testEqualExpression) { - MetadataBlocks blocks{}; - TestRelationalExpression> - testExpression; - testExpression(2, IntId(0), blocks.blocks, - {blocks.b1, blocks.b2, blocks.b7, blocks.b14}); - testExpression(2, IntId(5), blocks.blocks, - {blocks.b2, blocks.b3, blocks.b7, blocks.b10, blocks.b14}); - testExpression(2, IntId(22), blocks.blocks, {blocks.b7, blocks.b14}); - testExpression(2, IntId(-10), blocks.blocks, - {blocks.b5, blocks.b7, blocks.b14}); - testExpression(2, DoubleId(-6.25), blocks.blocks, - {blocks.b7, blocks.b11, blocks.b12, blocks.b14}); - testExpression(2, IntId(-11), blocks.blocks, - {blocks.b7, blocks.b13, blocks.b14}); - testExpression(2, DoubleId(-14.02), blocks.blocks, {blocks.b7, blocks.b14}); - testExpression(2, DoubleId(-0.001), blocks.blocks, {blocks.b7, blocks.b14}); - testExpression(2, DoubleId(0), blocks.blocks, - {blocks.b1, blocks.b2, blocks.b7, blocks.b14}); - testExpression(2, IntId(2), blocks.blocks, - {blocks.b2, blocks.b7, blocks.b8, blocks.b14}); - testExpression(2, DoubleId(5.5), blocks.blocks, - {blocks.b3, blocks.b7, blocks.b10, blocks.b14}); - testExpression(2, DoubleId(1.5), blocks.blocks, - {blocks.b2, blocks.b7, blocks.b14}); - testExpression(2, VocabId(1), blocks.blocks, {blocks.b7, blocks.b14}); - testExpression(2, VocabId(14), blocks.blocks, - {blocks.b7, blocks.b14, blocks.b15, blocks.b16, blocks.b17}); - testExpression(2, VocabId(11), blocks.blocks, - {blocks.b7, blocks.b14, blocks.b15}); - testExpression(2, VocabId(17), blocks.blocks, - {blocks.b7, blocks.b14, blocks.b17}); - testExpression(2, IntId(-4), blocks.blocks, - {blocks.b6, blocks.b7, blocks.b11, blocks.b14}); - // test blocks.otherBlocks - testExpression(2, blocks.trueId, blocks.otherBlocks, - {blocks.bd2, blocks.bd4, blocks.bd5, blocks.bd7}); - testExpression(2, blocks.referenceDate1, blocks.otherBlocks, - {blocks.bd2, blocks.bd5, blocks.bd7}); - testExpression(2, blocks.referenceDateEqual, blocks.otherBlocks, - {blocks.bd2, blocks.bd5, blocks.bd6, blocks.bd7}); - testExpression(2, blocks.referenceDate2, blocks.otherBlocks, - {blocks.bd2, blocks.bd5, blocks.bd7}); +TEST_F(TestPrefilterExprOnBlockMetadata, testEqualExpression) { + makeTest(eq(IntId(0)), {b4, b5, b6, b11}); + makeTest(eq(IntId(5)), {b6, b7, b11, b14}); + makeTest(eq(IntId(22)), {}); + makeTest(eq(IntId(-10)), {b9, b11, b18}); + makeTest(eq(DoubleId(-6.25)), {b15, b16}); + makeTest(eq(IntId(-11)), {b17}); + makeTest(eq(DoubleId(-14.02)), {b18}); + makeTest(eq(DoubleId(-0.001)), {b11}); + makeTest(eq(DoubleId(0)), {b4, b5, b6, b11}); + makeTest(eq(IntId(2)), {b6, b11, b12}); + makeTest(eq(DoubleId(5.5)), {b7, b11, b14}); + makeTest(eq(DoubleId(1.5)), {b6, b11}); + makeTest(eq(VocabId(1)), {b18}); + makeTest(eq(VocabId(14)), {b18, b19, b20, b21}); + makeTest(eq(VocabId(11)), {b18, b19}); + makeTest(eq(VocabId(17)), {b18, b21}); + makeTest(eq(IntId(-4)), {b10, b11, b15}); + makeTest(eq(trueId), {b4}); + makeTest(eq(referenceDate1), {b22}); + makeTest(eq(referenceDateEqual), {b23}); + makeTest(eq(referenceDate2), {}); } //______________________________________________________________________________ // Test NotEqualExpression -TEST(RelationalExpression, testNotEqualExpression) { - MetadataBlocks blocks{}; - TestRelationalExpression> - testExpression; - testExpression(2, DoubleId(0.00), blocks.blocks, - {blocks.b2, blocks.b3, blocks.b4, blocks.b5, blocks.b6, - blocks.b7, blocks.b8, blocks.b9, blocks.b10, blocks.b11, - blocks.b12, blocks.b13, blocks.b14}); - testExpression(2, IntId(-4), blocks.blocks, - {blocks.b1, blocks.b2, blocks.b3, blocks.b4, blocks.b5, - blocks.b7, blocks.b8, blocks.b9, blocks.b10, blocks.b11, - blocks.b12, blocks.b13, blocks.b14}); - testExpression(2, DoubleId(0.001), blocks.blocks, - {blocks.b1, blocks.b2, blocks.b3, blocks.b4, blocks.b5, - blocks.b6, blocks.b7, blocks.b8, blocks.b9, blocks.b10, - blocks.b11, blocks.b12, blocks.b13, blocks.b14}); - testExpression(2, IntId(2), blocks.blocks, - {blocks.b1, blocks.b2, blocks.b3, blocks.b4, blocks.b5, - blocks.b6, blocks.b7, blocks.b9, blocks.b10, blocks.b11, - blocks.b12, blocks.b13, blocks.b14}); - testExpression(2, DoubleId(-6.2500), blocks.blocks, - {blocks.b1, blocks.b2, blocks.b3, blocks.b4, blocks.b5, - blocks.b6, blocks.b7, blocks.b8, blocks.b9, blocks.b10, - blocks.b11, blocks.b13, blocks.b14}); - testExpression(2, IntId(5), blocks.blocks, - {blocks.b1, blocks.b2, blocks.b3, blocks.b4, blocks.b5, - blocks.b6, blocks.b7, blocks.b8, blocks.b9, blocks.b10, - blocks.b11, blocks.b12, blocks.b13, blocks.b14}); - testExpression(2, DoubleId(-101.23), blocks.blocks, - {blocks.b1, blocks.b2, blocks.b3, blocks.b4, blocks.b5, - blocks.b6, blocks.b7, blocks.b8, blocks.b9, blocks.b10, - blocks.b11, blocks.b12, blocks.b13, blocks.b14}); - testExpression(2, VocabId(0), blocks.blocks, - {blocks.b7, blocks.b14, blocks.b15, blocks.b16, blocks.b17}); - testExpression(2, VocabId(7), blocks.blocks, - {blocks.b7, blocks.b14, blocks.b15, blocks.b16, blocks.b17}); - testExpression(2, VocabId(14), blocks.blocks, - {blocks.b7, blocks.b14, blocks.b15, blocks.b17}); - testExpression(2, VocabId(17), blocks.blocks, - {blocks.b7, blocks.b14, blocks.b15, blocks.b16, blocks.b17}); - // test blocks.otherBlocks - testExpression(2, blocks.undef, blocks.otherBlocks, - {blocks.bd2, blocks.bd5, blocks.bd7}); - testExpression(2, blocks.falseId, blocks.otherBlocks, - {blocks.bd2, blocks.bd4, blocks.bd5, blocks.bd7}); - testExpression(2, blocks.referenceDateEqual, blocks.otherBlocks, - {blocks.bd2, blocks.bd5, blocks.bd7}); - testExpression(2, blocks.referenceDate1, blocks.otherBlocks, - {blocks.bd2, blocks.bd5, blocks.bd6, blocks.bd7}); +TEST_F(TestPrefilterExprOnBlockMetadata, testNotEqualExpression) { + makeTest(neq(DoubleId(0.00)), + {b6, b7, b8, b9, b10, b11, b12, b13, b14, b15, b16, b17, b18}); + makeTest(neq(IntId(-4)), + {b5, b6, b7, b8, b9, b11, b12, b13, b14, b15, b16, b17, b18}); + makeTest(neq(DoubleId(0.001)), + {b5, b6, b7, b8, b9, b10, b11, b12, b13, b14, b15, b16, b17, b18}); + makeTest(neq(IntId(2)), + {b5, b6, b7, b8, b9, b10, b11, b13, b14, b15, b16, b17, b18}); + makeTest(neq(DoubleId(-6.2500)), + {b5, b6, b7, b8, b9, b10, b11, b12, b13, b14, b15, b17, b18}); + makeTest(neq(IntId(5)), + {b5, b6, b7, b8, b9, b10, b11, b12, b13, b14, b15, b16, b17, b18}); + makeTest(neq(DoubleId(-101.23)), + {b5, b6, b7, b8, b9, b10, b11, b12, b13, b14, b15, b16, b17, b18}); + makeTest(neq(VocabId(0)), {b19, b20, b21, b22}); + makeTest(neq(VocabId(7)), {b18, b19, b20, b21, b22}); + makeTest(neq(VocabId(14)), {b18, b19, b21, b22}); + makeTest(neq(VocabId(17)), {b18, b19, b20, b21, b22}); + makeTest(neq(undef), {}); + makeTest(neq(falseId), {b4}); + makeTest(neq(referenceDateEqual), {b22, b24}); + makeTest(neq(referenceDate1), {b22, b23, b24}); } -//______________________________________________________________________________ -//______________________________________________________________________________ // Test Logical Expressions - +//______________________________________________________________________________ // Test AndExpression -TEST(LogicalExpression, testAndExpression) { - MetadataBlocks blocks{}; - TestLogicalExpression> - testExpression; - testExpression.test( - 2, VocabId(10), VocabId(10), blocks.blocks, - {blocks.b7, blocks.b14, blocks.b15, blocks.b16, blocks.b17}); - testExpression.test( - 2, VocabId(0), VocabId(17), blocks.blocks, - {blocks.b7, blocks.b14, blocks.b17}); - testExpression.test( - 2, VocabId(12), VocabId(17), blocks.blocks, {blocks.b7, blocks.b14}); - testExpression.test( - 2, VocabId(10), VocabId(14), blocks.blocks, - {blocks.b7, blocks.b14, blocks.b15}); - testExpression.test( - 2, VocabId(0), VocabId(10), blocks.blocks, {blocks.b7, blocks.b14}); - testExpression.test( - 2, VocabId(17), VocabId(17), blocks.blocks, - {blocks.b7, blocks.b14, blocks.b15, blocks.b16, blocks.b17}); - testExpression.test( - 2, DoubleId(-6.25), IntId(-7), blocks.blocks, {blocks.b7, blocks.b14}); - testExpression.test( - 2, DoubleId(-6.25), DoubleId(-6.25), blocks.blocks, - {blocks.b7, blocks.b14}); - testExpression.test( - 2, IntId(0), IntId(0), blocks.blocks, {blocks.b7, blocks.b14}); - testExpression.test( - 2, IntId(-10), DoubleId(0.00), blocks.blocks, - {blocks.b5, blocks.b6, blocks.b7, blocks.b11, blocks.b12, blocks.b14}); - // Also a corner case. - testExpression.test( - 2, IntId(0), DoubleId(0), blocks.blocks, - {blocks.b2, blocks.b7, blocks.b14}); - testExpression.test( - 2, IntId(0), IntId(0), blocks.blocks, - {blocks.b1, blocks.b2, blocks.b7, blocks.b14}); - testExpression.test( - 2, DoubleId(-34.23), DoubleId(15.1), blocks.blocks, - {blocks.b7, blocks.b14}); - testExpression.test( - 2, IntId(0), DoubleId(-4), blocks.blocks, - {blocks.b5, blocks.b6, blocks.b7, blocks.b11, blocks.b12, blocks.b13, - blocks.b14}); - testExpression.test( - 2, IntId(0), IntId(-4), blocks.blocks, - {blocks.b2, blocks.b3, blocks.b4, blocks.b5, blocks.b7, blocks.b8, - blocks.b9, blocks.b10, blocks.b11, blocks.b12, blocks.b13, blocks.b14}); - testExpression.test( - 2, DoubleId(-3.1415), DoubleId(4.5), blocks.blocks, - {blocks.b2, blocks.b7, blocks.b10, blocks.b14}); - testExpression.test( - 2, DoubleId(-6.25), IntId(0), blocks.blocks, - {blocks.b5, blocks.b6, blocks.b7, blocks.b11, blocks.b13, blocks.b14}); - testExpression.test( - 2, DoubleId(-4), DoubleId(1), blocks.blocks, {blocks.b7, blocks.b14}); - testExpression.test( - 2, DoubleId(-2), IntId(-3), blocks.blocks, - {blocks.b7, blocks.b11, blocks.b14}); +TEST_F(TestPrefilterExprOnBlockMetadata, testAndExpression) { + makeTest(andExpr(ge(VocabId(10)), gt(VocabId(10))), {b19, b20, b21, b22}); + makeTest(andExpr(ge(VocabId(10)), ge(VocabId(10))), {b19, b20, b21, b22}); + makeTest(andExpr(ge(VocabId(12)), gt(VocabId(17))), {b22}); + makeTest(andExpr(ge(VocabId(12)), gt(VocabId(17))), {b22}); + makeTest(andExpr(ge(VocabId(10)), lt(VocabId(14))), {b19}); + makeTest(andExpr(le(VocabId(0)), lt(VocabId(10))), {b18}); + makeTest(andExpr(le(VocabId(17)), lt(VocabId(17))), {b18, b19, b20, b21}); + makeTest(andExpr(ge(DoubleId(-6.25)), lt(IntId(-7))), {}); + makeTest(andExpr(gt(DoubleId(-6.25)), lt(DoubleId(-6.25))), {}); + makeTest(andExpr(gt(IntId(0)), lt(IntId(0))), {}); + makeTest(andExpr(gt(IntId(-10)), lt(DoubleId(0))), {b9, b10, b11, b15, b16}); + makeTest(andExpr(gt(IntId(0)), eq(DoubleId(0))), {b6, b11}); + makeTest(andExpr(ge(IntId(0)), eq(IntId(0))), {b5, b6, b11}); + makeTest(andExpr(gt(DoubleId(-34.23)), ge(DoubleId(15.1))), {}); + makeTest(andExpr(lt(IntId(0)), le(DoubleId(-4))), + {b9, b10, b11, b15, b16, b17, b18}); + makeTest(andExpr(neq(IntId(0)), neq(IntId(-4))), + {b6, b7, b8, b9, b11, b12, b13, b14, b15, b16, b17, b18}); + makeTest(andExpr(neq(DoubleId(-3.141)), eq(DoubleId(4.5))), + {b6, b11, b14, b18}); + makeTest(andExpr(neq(DoubleId(-6.25)), lt(IntId(0))), + {b9, b10, b11, b15, b17, b18}); + makeTest(andExpr(le(DoubleId(-4)), ge(DoubleId(1))), {}); + makeTest(andExpr(le(DoubleId(-2)), eq(IntId(-3))), {b11, b15}); + makeTest(andExpr(andExpr(le(IntId(10)), gt(DoubleId(0))), eq(undef)), {}); + makeTest(andExpr(gt(referenceDate1), le(IntId(10))), {}); + makeTest(andExpr(gt(IntId(4)), andExpr(gt(DoubleId(8)), lt(IntId(10)))), + {b8, b14}); + makeTest(andExpr(eq(IntId(0)), andExpr(lt(IntId(-20)), gt(IntId(30)))), {}); + makeTest(andExpr(eq(IntId(0)), andExpr(le(IntId(0)), ge(IntId(0)))), + {b4, b5, b6, b11}); } //______________________________________________________________________________ // Test OrExpression -TEST(LogicalExpression, testOrExpression) { - MetadataBlocks blocks{}; - TestLogicalExpression> - testExpression; - testExpression.test( - 2, VocabId(22), VocabId(0), blocks.blocks, - {blocks.b7, blocks.b14, blocks.b15, blocks.b16, blocks.b17}); - testExpression.test( - 2, VocabId(0), VocabId(16), blocks.blocks, - {blocks.b7, blocks.b14, blocks.b17}); - testExpression.test( - 2, VocabId(17), VocabId(242), blocks.blocks, {blocks.b7, blocks.b14}); - testExpression.test( - 2, DoubleId(-5.95), VocabId(14), blocks.blocks, - {blocks.b5, blocks.b7, blocks.b11, blocks.b12, blocks.b13, blocks.b14, - blocks.b15, blocks.b16, blocks.b17}); - testExpression.test( - 2, DoubleId(0), VocabId(14), blocks.blocks, - {blocks.b1, blocks.b2, blocks.b7, blocks.b14, blocks.b15, blocks.b17}); - testExpression.test( - 2, DoubleId(0.0), DoubleId(-6.25), blocks.blocks, - {blocks.b1, blocks.b2, blocks.b7, blocks.b11, blocks.b12, blocks.b14}); - testExpression.test( - 2, DoubleId(-11.99), DoubleId(-15.22), blocks.blocks, - {blocks.b7, blocks.b13, blocks.b14}); - testExpression.test( - 2, DoubleId(7.99), DoubleId(-7.99), blocks.blocks, - {blocks.b4, blocks.b5, blocks.b7, blocks.b10, blocks.b13, blocks.b14}); - testExpression.test( - 2, IntId(-15), IntId(2), blocks.blocks, - {blocks.b1, blocks.b2, blocks.b3, blocks.b4, blocks.b5, blocks.b6, - blocks.b7, blocks.b8, blocks.b9, blocks.b10, blocks.b11, blocks.b12, - blocks.b13, blocks.b14}); - testExpression.test( - 2, IntId(0), IntId(-4), blocks.blocks, - {blocks.b1, blocks.b2, blocks.b6, blocks.b7, blocks.b11, blocks.b14}); - testExpression.test( - 2, VocabId(14), IntId(2), blocks.blocks, - {blocks.b2, blocks.b7, blocks.b8, blocks.b14, blocks.b15, blocks.b17}); - testExpression.test( - 2, DoubleId(-1), IntId(1), blocks.blocks, - {blocks.b2, blocks.b3, blocks.b4, blocks.b5, blocks.b6, blocks.b7, - blocks.b8, blocks.b9, blocks.b10, blocks.b11, blocks.b12, blocks.b13, - blocks.b14}); - testExpression.test( - 2, DoubleId(-4), IntId(-4), blocks.blocks, - {blocks.b5, blocks.b6, blocks.b7, blocks.b11, blocks.b12, blocks.b13, - blocks.b14}); +TEST_F(TestPrefilterExprOnBlockMetadata, testOrExpression) { + makeTest(orExpr(lt(VocabId(22)), le(VocabId(0))), {b18, b19, b20, b21}); + makeTest(orExpr(le(VocabId(0)), ge(VocabId(16))), {b18, b21, b22}); + makeTest(orExpr(gt(VocabId(17)), ge(VocabId(17))), {b21, b22}); + makeTest(orExpr(lt(DoubleId(-5.95)), eq(VocabId(14))), + {b9, b15, b16, b17, b18, b19, b20, b21}); + makeTest(orExpr(eq(DoubleId(0)), neq(VocabId(14))), + {b5, b6, b11, b18, b19, b21}); + makeTest(orExpr(eq(DoubleId(0)), eq(DoubleId(-6.25))), + {b5, b6, b11, b15, b16, b18}); + makeTest(orExpr(gt(undef), le(IntId(-6))), {b9, b15, b16, b17, b18}); + makeTest(orExpr(le(trueId), gt(referenceDate1)), {b2, b3, b4, b22, b23, b24}); + makeTest(orExpr(eq(IntId(0)), orExpr(lt(IntId(-10)), gt(IntId(8)))), + {b5, b6, b8, b11, b14, b17, b18}); + makeTest(orExpr(gt(referenceDate2), eq(trueId)), {b4}); + makeTest(orExpr(eq(VocabId(17)), orExpr(lt(VocabId(0)), gt(VocabId(20)))), + {b21, b22}); + makeTest(orExpr(eq(undef), gt(referenceDateEqual)), {b24}); + makeTest(orExpr(gt(IntId(8)), gt(DoubleId(22.1))), {b8, b14}); + makeTest(orExpr(lt(DoubleId(-8.25)), le(IntId(-10))), {b9, b17, b18}); + makeTest(orExpr(eq(IntId(0)), neq(DoubleId(0.25))), + {b5, b6, b7, b8, b9, b10, b11, b12, b13, b14, b15, b16, b17, b18}); + makeTest(orExpr(gt(referenceDate1), orExpr(gt(trueId), eq(IntId(0)))), + {b4, b5, b6, b11, b22, b23, b24}); + makeTest(orExpr(gt(DoubleId(-6.25)), lt(DoubleId(-6.25))), + {b5, b6, b7, b8, b9, b10, b11, b12, b13, b14, b15, b17, b18}); + makeTest(orExpr(orExpr(eq(IntId(0)), eq(IntId(5))), + orExpr(eq(DoubleId(-6.25)), lt(DoubleId(-12)))), + {b4, b5, b6, b7, b11, b14, b15, b16, b18}); + makeTest(orExpr(le(trueId), gt(falseId)), {b2, b3, b4}); + makeTest(orExpr(eq(VocabId(0)), eq(DoubleId(0.25))), {b6, b11, b18}); } //______________________________________________________________________________ // Test NotExpression -TEST(LogicalExpression, testNotExpression) { - MetadataBlocks blocks{}; - TestNotExpression> testExpression{}; - testExpression.test( - 2, blocks.blocks, - {blocks.b7, blocks.b14, blocks.b15, blocks.b16, blocks.b17}, VocabId(2)); - testExpression.test( - 2, blocks.blocks, {blocks.b7, blocks.b14, blocks.b15, blocks.b17}, - VocabId(14)); - testExpression.test( - 2, blocks.blocks, - {blocks.b7, blocks.b14, blocks.b15, blocks.b16, blocks.b17}, VocabId(14)); - testExpression.test( - 2, blocks.blocks, - {blocks.b7, blocks.b14, blocks.b15, blocks.b16, blocks.b17}, VocabId(0)); - testExpression.test( - 2, blocks.blocks, - {blocks.b1, blocks.b2, blocks.b3, blocks.b4, blocks.b5, blocks.b6, - blocks.b7, blocks.b8, blocks.b9, blocks.b10, blocks.b11, blocks.b12, - blocks.b13, blocks.b14}, - DoubleId(-14.01)); - testExpression.test( - 2, blocks.blocks, {blocks.b7, blocks.b14}, DoubleId(-14.01)); - testExpression.test( - 2, blocks.blocks, - {blocks.b5, blocks.b6, blocks.b7, blocks.b11, blocks.b12, blocks.b13, - blocks.b14}, - DoubleId(-4.00)); - testExpression.test( - 2, blocks.blocks, {blocks.b7, blocks.b14}, DoubleId(-24.4)); - testExpression.test( - 2, blocks.blocks, - {blocks.b2, blocks.b3, blocks.b4, blocks.b7, blocks.b8, blocks.b9, - blocks.b10, blocks.b14}, - IntId(0)); - testExpression.test( - 2, blocks.blocks, - {blocks.b1, blocks.b2, blocks.b3, blocks.b4, blocks.b5, blocks.b6, - blocks.b7, blocks.b8, blocks.b9, blocks.b10, blocks.b11, blocks.b13, - blocks.b14}, - DoubleId(-6.25)); - testExpression.test( - 2, blocks.blocks, - {blocks.b2, blocks.b7, blocks.b9, blocks.b10, blocks.b14}, DoubleId(4)); - testExpression.test( - 2, blocks.blocks, - {blocks.b1, blocks.b2, blocks.b5, blocks.b6, blocks.b7, blocks.b11, - blocks.b12, blocks.b13, blocks.b14}, - DoubleId(0)); - testExpression.test(0, blocks.blocks, {}, blocks.VocabId10); - testExpression.test(1, blocks.blocks, {}, blocks.DoubleId33); - testExpression.test(0, blocks.blocks, blocks.blocks, - blocks.VocabId10); - testExpression.test(1, blocks.blocks, {}, - blocks.DoubleId33); - testExpression.test( - 2, blocks.blocks, {blocks.b1, blocks.b2, blocks.b7, blocks.b14}, - IntId(0)); - testExpression.test( - 2, blocks.blocks, - {blocks.b1, blocks.b2, blocks.b3, blocks.b4, blocks.b5, blocks.b6, - blocks.b7, blocks.b8, blocks.b9, blocks.b10, blocks.b11, blocks.b13, - blocks.b14}, - DoubleId(-6.25)); - testExpression.test( - 2, blocks.blocks, {blocks.b7, blocks.b14}, VocabId(10)); - testExpression.test( - 2, blocks.blocks, - {blocks.b2, blocks.b3, blocks.b4, blocks.b7, blocks.b9, blocks.b10, - blocks.b14}, - DoubleId(3.99)); - testExpression - .test( - 2, blocks.blocks, - {blocks.b2, blocks.b3, blocks.b4, blocks.b5, blocks.b6, blocks.b7, - blocks.b8, blocks.b9, blocks.b10, blocks.b11, blocks.b12, blocks.b13, - blocks.b14}, - IntId(0), IntId(0)); - testExpression.test( - 2, blocks.blocks, {blocks.b5, blocks.b7, blocks.b14}, IntId(-10), - DoubleId(-14.02)); - testExpression - .test( - 2, blocks.blocks, - {blocks.b1, blocks.b2, blocks.b3, blocks.b4, blocks.b5, blocks.b6, - blocks.b7, blocks.b8, blocks.b9, blocks.b10, blocks.b11, blocks.b12, - blocks.b13, blocks.b14}, - IntId(10), DoubleId(-6.25)); - testExpression - .test( - 2, blocks.blocks, - {blocks.b5, blocks.b6, blocks.b7, blocks.b11, blocks.b12, blocks.b13, - blocks.b14}, - IntId(-4), DoubleId(-6.25)); - testExpression - .test( - 2, blocks.blocks, - {blocks.b1, blocks.b2, blocks.b3, blocks.b4, blocks.b5, blocks.b6, - blocks.b7, blocks.b8, blocks.b9, blocks.b10, blocks.b11, blocks.b12, - blocks.b13, blocks.b14}, - DoubleId(-7), IntId(6)); - testExpression - .test( - 2, blocks.blocks, - {blocks.b2, blocks.b3, blocks.b7, blocks.b8, blocks.b9, blocks.b10, - blocks.b14}, - IntId(0), DoubleId(6)); - testExpression - .test( - 2, blocks.blocks, {blocks.b5, blocks.b7, blocks.b13, blocks.b14}, - DoubleId(0), IntId(-10)); - testExpression.test( - 2, blocks.blocks, {blocks.b7, blocks.b14, blocks.b15}, VocabId(10), - VocabId(10)); - testExpression.test( - 2, blocks.blocks, {blocks.b6, blocks.b7, blocks.b11, blocks.b14}, - DoubleId(-4), IntId(-4)); - testExpression - .test( - 2, blocks.blocks, {blocks.b7, blocks.b14}, IntId(-42), VocabId(0)); - testExpression - .test( - 2, blocks.blocks, {blocks.b7, blocks.b14, blocks.b15}, VocabId(14), - VocabId(15)); - testExpression.test( - 2, blocks.blocks, {blocks.b7, blocks.b11, blocks.b12, blocks.b14}, - DoubleId(-7.25), DoubleId(-6.25)); +TEST_F(TestPrefilterExprOnBlockMetadata, testNotExpression) { + makeTest(notExpr(eq(VocabId(2))), {b18, b19, b20, b21, b22}); + makeTest(notExpr(eq(VocabId(14))), {b18, b19, b21, b22}); + makeTest(notExpr(neq(VocabId(14))), {b19, b20, b21}); + makeTest(notExpr(gt(VocabId(2))), {b18}); + makeTest(notExpr(lt(DoubleId(-14.01))), + {b5, b6, b7, b8, b9, b10, b11, b12, b13, b14, b15, b16, b17, b18}); + makeTest(notExpr(ge(DoubleId(-14.01))), {b18}); + makeTest(notExpr(gt(DoubleId(-4.00))), {b9, b10, b11, b15, b16, b17, b18}); + makeTest(notExpr(ge(DoubleId(-24.4))), {b18}); + makeTest(notExpr(gt(referenceDate2)), {b22, b23}); + makeTest(notExpr(le(trueId)), {}); + makeTest(notExpr(le(IntId(0))), {b6, b7, b8, b11, b12, b13, b14}); + makeTest(notExpr(gt(undef)), {}); + makeTest(notExpr(eq(DoubleId(-6.25))), + {b5, b6, b7, b8, b9, b10, b11, b12, b13, b14, b15, b17, b18}); + makeTest(notExpr(neq(DoubleId(4))), {b6, b11, b13, b14, b18}); + makeTest(notExpr(gt(DoubleId(0))), + {b4, b5, b6, b9, b10, b11, b15, b16, b17, b18}); + makeTest(notExpr(notExpr(eq(IntId(0)))), {b4, b5, b6, b11}); + makeTest(notExpr(notExpr(neq(DoubleId(-6.25)))), + {b4, b5, b6, b7, b8, b9, b10, b11, b12, b13, b14, b15, b17, b18}); + makeTest(notExpr(notExpr(lt(VocabId(10)))), {b18}); + makeTest(notExpr(notExpr(ge(DoubleId(3.99)))), {b6, b7, b8, b11, b13, b14}); + makeTest(notExpr(andExpr(le(IntId(0)), ge(IntId(0)))), + {b6, b7, b8, b9, b10, b11, b12, b13, b14, b15, b16, b17, b18}); + makeTest(notExpr(andExpr(neq(IntId(-10)), neq(DoubleId(-14.02)))), {b9, b18}); + makeTest( + notExpr(andExpr(gt(IntId(10)), ge(DoubleId(-6.25)))), + {b4, b5, b6, b7, b8, b9, b10, b11, b12, b13, b14, b15, b16, b17, b18}); + makeTest( + notExpr(andExpr(lt(DoubleId(-7)), ge(IntId(6)))), + {b4, b5, b6, b7, b8, b9, b10, b11, b12, b13, b14, b15, b16, b17, b18}); + makeTest(notExpr(orExpr(le(IntId(0)), ge(DoubleId(6)))), + {b6, b7, b11, b12, b13, b14}); + makeTest(notExpr(orExpr(ge(DoubleId(0)), gt(IntId(-10)))), + {b9, b11, b17, b18}); + makeTest(notExpr(orExpr(lt(VocabId(10)), gt(VocabId(10)))), {b19}); + makeTest(notExpr(orExpr(lt(DoubleId(-4)), gt(IntId(-4)))), {b10, b11, b15}); + makeTest(notExpr(orExpr(gt(IntId(-42)), ge(VocabId(0)))), {b11}); + makeTest(notExpr(orExpr(ge(VocabId(14)), gt(VocabId(15)))), {b18, b19}); } //______________________________________________________________________________ -// Test that correct errors are thrown for invalid input (condition checking). -TEST(PrefilterExpression, testInputConditionCheck) { - MetadataBlocks blocks{}; - TestRelationalExpression - testRelationalExpression{}; - testRelationalExpression( - 2, DoubleId(10), blocks.blocksInvalidCol1, - "The columns up to the evaluation column must contain the same values."); - testRelationalExpression( - 1, DoubleId(10), blocks.blocksInvalidCol1, - "The data blocks must be provided in sorted order regarding the " - "evaluation column."); - testRelationalExpression(2, DoubleId(10), blocks.blocksWithDuplicate2, - "The provided data blocks must be unique."); +// Test PrefilterExpressions mixed +TEST_F(TestPrefilterExprOnBlockMetadata, testGeneralPrefilterExprCombinations) { + makeTest(andExpr(notExpr(gt(DoubleId(-14.01))), lt(IntId(0))), {b18}); + makeTest( + orExpr(andExpr(gt(DoubleId(8.25)), le(IntId(10))), eq(DoubleId(-6.25))), + {b8, b14, b15, b16}); + makeTest( + orExpr(andExpr(gt(DoubleId(8.25)), le(IntId(10))), lt(DoubleId(-6.25))), + {b8, b9, b14, b17, b18}); + makeTest(andExpr(orExpr(ge(trueId), le(falseId)), eq(referenceDate1)), {}); + makeTest(andExpr(eq(IntId(0)), orExpr(lt(IntId(-11)), le(IntId(-12)))), {}); + makeTest( + andExpr(eq(DoubleId(-4)), orExpr(gt(IntId(-4)), lt(DoubleId(-1.25)))), + {b10, b11, b15}); + makeTest(orExpr(notExpr(andExpr(lt(IntId(10)), gt(IntId(5)))), eq(IntId(0))), + {b4, b5, b6, b7, b9, b10, b11, b12, b13, b14, b15, b16, b17, b18}); + makeTest(andExpr(orExpr(gt(VocabId(16)), le(VocabId(5))), gt(DoubleId(7.25))), + {}); + makeTest(andExpr(lt(falseId), orExpr(lt(IntId(10)), gt(DoubleId(17.25)))), + {}); + makeTest( + orExpr(andExpr(gt(VocabId(16)), ge(VocabId(17))), gt(DoubleId(7.25))), + {b8, b14, b18, b21, b22}); + makeTest(orExpr(eq(trueId), andExpr(gt(referenceDate1), lt(referenceDate2))), + {b4, b22, b23}); +} - TestNotExpression testNotExpression{}; - testNotExpression.test( - 2, blocks.blocksWithDuplicate1, - "The provided data blocks must be unique.", VocabId(2)); - testNotExpression.test( - 2, blocks.blocksInvalidOrder1, - "The data blocks must be provided in sorted order regarding the " - "evaluation column.", - DoubleId(-14.1)); - testNotExpression.test( - 0, blocks.blocksInvalidCol2, - "The data blocks must be provided in sorted order regarding the " - "evaluation column.", - IntId(0)); - testNotExpression.test( - 1, blocks.blocksInvalidCol2, - "The columns up to the evaluation column must contain the same values.", - IntId(0)); - testNotExpression.test( - 2, blocks.blocksInvalidCol2, - "The columns up to the evaluation column must contain the same values.", - IntId(0)); +//______________________________________________________________________________ +// Test that correct errors are thrown for invalid input (condition +TEST_F(TestPrefilterExprOnBlockMetadata, testInputConditionCheck) { + makeTestErrorCheck(le(IntId(5)), blocksWithDuplicate1, + "The provided data blocks must be unique."); + makeTestErrorCheck(andExpr(gt(VocabId(10)), le(VocabId(20))), + blocksWithDuplicate2, + "The provided data blocks must be unique."); + makeTestErrorCheck(gt(DoubleId(2)), blocksInvalidOrder1, + "The blocks must be provided in sorted order."); + makeTestErrorCheck(andExpr(gt(VocabId(10)), le(VocabId(20))), + blocksInvalidOrder2, + "The blocks must be provided in sorted order."); + makeTestErrorCheck( + gt(DoubleId(2)), blocks, + "The values in the columns up to the evaluation column must be " + "consistent.", + 1); + makeTestErrorCheck( + gt(DoubleId(2)), blocks, + "The values in the columns up to the evaluation column must be " + "consistent.", + 2); +} - TestLogicalExpression testAndExpression{}; - testAndExpression.test( - 2, DoubleId(-4.24), IntId(5), blocks.blocksWithDuplicate2, - "The provided data blocks must be unique."); - testAndExpression.test( - 2, DoubleId(-4.24), IntId(5), blocks.blocksInvalidOrder1, - "The data blocks must be provided in sorted order regarding the " - "evaluation column."); - testAndExpression.test( - 2, DoubleId(-4.24), IntId(5), blocks.blocksInvalidCol2, - "The columns up to the evaluation column must contain the same values."); +//______________________________________________________________________________ +// Check for correctness given only one BlockMetadata value is provided. +TEST_F(TestPrefilterExprOnBlockMetadata, testWithOneBlockMetadataValue) { + auto expr = orExpr(eq(DoubleId(-6.25)), eq(IntId(0))); + std::vector input = {b16}; + EXPECT_EQ(expr->evaluate(input, 0), input); + EXPECT_EQ(expr->evaluate(input, 1), std::vector{}); + EXPECT_EQ(expr->evaluate(input, 2), std::vector{}); } +//______________________________________________________________________________ +//______________________________________________________________________________ // TEST SECTION 2 //______________________________________________________________________________ //______________________________________________________________________________ +namespace { + using namespace sparqlExpression; using optPrefilterVec = std::optional>; using Literal = ad_utility::triple_component::Literal; @@ -905,6 +576,12 @@ const auto I = [](const std::string& content) -> Iri { return Iri::fromIriref(content); }; +//______________________________________________________________________________ +struct TestDates { + const Id referenceDate1 = DateId(DateParser, "1999-11-11"); + const Id referenceDate2 = DateId(DateParser, "2005-02-27"); +}; + //______________________________________________________________________________ const auto makeLiteralSparqlExpr = [](const auto& child) -> std::unique_ptr { @@ -944,45 +621,30 @@ std::unique_ptr makeLogicalBinaryPrefilterExprImpl( }; //______________________________________________________________________________ -std::unique_ptr makeNotPrefilterExprImpl( - std::unique_ptr child) { - return std::make_unique( - std::move(child)); -}; - -//______________________________________________________________________________ -constexpr auto lessThanSparqlExpr = +// LESS THAN (`<`, `SparqlExpression`) +constexpr auto ltSprql = &makeRelationalSparqlExprImpl; -constexpr auto lessThanFilterExpr = - &makeRelExpr; -constexpr auto lessEqSparqlExpr = +// LESS EQUAL (`<=`, `SparqlExpression`) +constexpr auto leSprql = &makeRelationalSparqlExprImpl; -constexpr auto lessEqFilterExpr = - &makeRelExpr; -constexpr auto eqSparqlExpr = +// EQUAL (`==`, `SparqlExpression`) +constexpr auto eqSprql = &makeRelationalSparqlExprImpl; -constexpr auto eqFilterExpr = - &makeRelExpr; -constexpr auto notEqSparqlExpr = +// NOT EQUAL (`!=`, `SparqlExpression`) +constexpr auto neqSprql = &makeRelationalSparqlExprImpl; -constexpr auto notEqFilterExpr = - &makeRelExpr; -constexpr auto greaterEqSparqlExpr = +// GREATER EQUAL (`>=`, `SparqlExpression`) +constexpr auto geSprql = &makeRelationalSparqlExprImpl; -constexpr auto greaterEqFilterExpr = - &makeRelExpr; -constexpr auto greaterThanSparqlExpr = +// GREATER THAN (`>`, `SparqlExpression`) +constexpr auto gtSprql = &makeRelationalSparqlExprImpl; -constexpr auto greaterThanFilterExpr = - &makeRelExpr; -constexpr auto andSparqlExpr = &makeAndExpression; -constexpr auto orSparqlExpr = &makeOrExpression; -constexpr auto notSparqlExpr = &makeUnaryNegateExpression; -constexpr auto andFilterExpr = - &makeLogicalBinaryPrefilterExprImpl; -constexpr auto orFilterExpr = - &makeLogicalBinaryPrefilterExprImpl; -constexpr auto notFilterExpr = &makeNotPrefilterExprImpl; +// AND (`&&`, `SparqlExpression`) +constexpr auto andSprqlExpr = &makeAndExpression; +// OR (`||`, `SparqlExpression`) +constexpr auto orSprqlExpr = &makeOrExpression; +// NOT (`!`, `SparqlExpression`) +constexpr auto notSprqlExpr = &makeUnaryNegateExpression; //______________________________________________________________________________ const auto checkVectorPrefilterExprVariablePair = @@ -1050,66 +712,52 @@ const auto evalAndEqualityCheck = }; //______________________________________________________________________________ -// Construct a pair with the given `PrefilterExpression` and `Variable` value. +// Construct a `PAIR` with the given `PrefilterExpression` and `Variable` value. auto pr = [](std::unique_ptr expr, const Variable& var) -> PrefilterExprVariablePair { return {std::move(expr), var}; }; - -//______________________________________________________________________________ -// TEST SECTION 2 +} // namespace //______________________________________________________________________________ // Test PrefilterExpression equality operator. TEST(PrefilterExpression, testEqualityOperator) { - MetadataBlocks blocks{}; + const TestDates dt{}; // Relational PrefilterExpressions - ASSERT_FALSE(*greaterEqFilterExpr(blocks.referenceDate1) == - *greaterEqFilterExpr(blocks.referenceDate2)); - ASSERT_FALSE(*notEqFilterExpr(BoolId(true)) == *eqFilterExpr(BoolId(true))); - ASSERT_TRUE(*eqFilterExpr(IntId(1)) == *eqFilterExpr(IntId(1))); - ASSERT_TRUE(*greaterEqFilterExpr(blocks.referenceDate1) == - *greaterEqFilterExpr(blocks.referenceDate1)); + ASSERT_FALSE(*ge(dt.referenceDate1) == *ge(dt.referenceDate2)); + ASSERT_FALSE(*neq(BoolId(true)) == *eq(BoolId(true))); + ASSERT_TRUE(*eq(IntId(1)) == *eq(IntId(1))); + ASSERT_TRUE(*ge(dt.referenceDate1) == *ge(dt.referenceDate1)); // NotExpression - ASSERT_TRUE(*notFilterExpr(eqFilterExpr(IntId(0))) == - *notFilterExpr(eqFilterExpr(IntId(0)))); - ASSERT_TRUE(*notFilterExpr(notFilterExpr(greaterEqFilterExpr(VocabId(0)))) == - *notFilterExpr(notFilterExpr(greaterEqFilterExpr(VocabId(0))))); - ASSERT_FALSE(*notFilterExpr(greaterThanFilterExpr(IntId(0))) == - *eqFilterExpr(IntId(0))); - ASSERT_FALSE(*notFilterExpr(andFilterExpr(eqFilterExpr(IntId(1)), - eqFilterExpr(IntId(0)))) == - *notFilterExpr(greaterEqFilterExpr(VocabId(0)))); + ASSERT_TRUE(*notExpr(eq(IntId(0))) == *notExpr(eq(IntId(0)))); + ASSERT_TRUE(*notExpr(notExpr(ge(VocabId(0)))) == + *notExpr(notExpr(ge(VocabId(0))))); + ASSERT_FALSE(*notExpr(gt(IntId(0))) == *eq(IntId(0))); + ASSERT_FALSE(*notExpr(andExpr(eq(IntId(1)), eq(IntId(0)))) == + *notExpr(ge(VocabId(0)))); // Binary PrefilterExpressions (AND and OR) - ASSERT_TRUE( - *orFilterExpr(eqFilterExpr(IntId(0)), lessEqFilterExpr(IntId(0))) == - *orFilterExpr(eqFilterExpr(IntId(0)), lessEqFilterExpr(IntId(0)))); - ASSERT_TRUE( - *andFilterExpr(lessEqFilterExpr(VocabId(1)), - lessEqFilterExpr(IntId(0))) == - *andFilterExpr(lessEqFilterExpr(VocabId(1)), lessEqFilterExpr(IntId(0)))); - ASSERT_FALSE( - *orFilterExpr(eqFilterExpr(IntId(0)), lessEqFilterExpr(IntId(0))) == - *andFilterExpr(lessEqFilterExpr(VocabId(1)), lessEqFilterExpr(IntId(0)))); - ASSERT_FALSE( - *notFilterExpr( - orFilterExpr(eqFilterExpr(IntId(0)), lessEqFilterExpr(IntId(0)))) == - *orFilterExpr(eqFilterExpr(IntId(0)), lessEqFilterExpr(IntId(0)))); + ASSERT_TRUE(*orExpr(eq(IntId(0)), le(IntId(0))) == + *orExpr(eq(IntId(0)), le(IntId(0)))); + ASSERT_TRUE(*andExpr(le(VocabId(1)), le(IntId(0))) == + *andExpr(le(VocabId(1)), le(IntId(0)))); + ASSERT_FALSE(*orExpr(eq(IntId(0)), le(IntId(0))) == + *andExpr(le(VocabId(1)), le(IntId(0)))); + ASSERT_FALSE(*notExpr(orExpr(eq(IntId(0)), le(IntId(0)))) == + *orExpr(eq(IntId(0)), le(IntId(0)))); } //______________________________________________________________________________ // Test PrefilterExpression content formatting for debugging. TEST(PrefilterExpression, checkPrintFormattedPrefilterExpression) { - const auto& relExpr = lessThanFilterExpr(IntId(10)); - EXPECT_EQ((std::stringstream() << *relExpr).str(), + auto expr = lt(IntId(10)); + EXPECT_EQ((std::stringstream() << *expr).str(), "Prefilter RelationalExpression<0>\nValueId: I:10\n.\n"); - const auto& notExpr = notFilterExpr(eqFilterExpr(VocabId(0))); - EXPECT_EQ((std::stringstream() << *notExpr).str(), + expr = notExpr(eq(VocabId(0))); + EXPECT_EQ((std::stringstream() << *expr).str(), "Prefilter NotExpression:\nchild {Prefilter " "RelationalExpression<3>\nValueId: V:0\n}\n.\n"); - const auto& orExpr = - orFilterExpr(lessEqFilterExpr(IntId(0)), eqFilterExpr(IntId(5))); - EXPECT_EQ((std::stringstream() << *orExpr).str(), + expr = orExpr(le(IntId(0)), eq(IntId(5))); + EXPECT_EQ((std::stringstream() << *expr).str(), "Prefilter LogicalExpression<1>\nchild1 {Prefilter " "RelationalExpression<1>\nValueId: I:0\n}child2 {Prefilter " "RelationalExpression<2>\nValueId: I:5\n}\n.\n"); @@ -1136,20 +784,17 @@ TEST(SparqlExpression, testGetPrefilterExpressionDefault) { // Check that the (Sparql) RelationalExpression returns the expected // PrefilterExpression. TEST(SparqlExpression, getPrefilterExpressionFromSparqlRelational) { + const TestDates dt{}; const Variable var = Variable{"?x"}; - MetadataBlocks blocks{}; - evalAndEqualityCheck(eqSparqlExpr(var, BoolId(true)), - pr(eqFilterExpr(BoolId(true)), var)); - evalAndEqualityCheck(greaterEqSparqlExpr(var, IntId(1)), - pr(greaterEqFilterExpr(IntId(1)), var)); - evalAndEqualityCheck(notEqSparqlExpr(VocabId(10), var), - pr(notEqFilterExpr(VocabId(10)), var)); - evalAndEqualityCheck(greaterEqSparqlExpr(BlankNodeId(1), var), - pr(greaterEqFilterExpr(BlankNodeId(1)), var)); - evalAndEqualityCheck(lessEqSparqlExpr(var, blocks.referenceDate1), - pr(lessEqFilterExpr(blocks.referenceDate1), var)); - evalAndEqualityCheck(lessThanSparqlExpr(DoubleId(10.2), var), - pr(lessThanFilterExpr(DoubleId(10.2)), var)); + evalAndEqualityCheck(eqSprql(var, BoolId(true)), pr(eq(BoolId(true)), var)); + evalAndEqualityCheck(geSprql(var, IntId(1)), pr(ge(IntId(1)), var)); + evalAndEqualityCheck(neqSprql(VocabId(10), var), pr(neq(VocabId(10)), var)); + evalAndEqualityCheck(geSprql(BlankNodeId(1), var), + pr(ge(BlankNodeId(1)), var)); + evalAndEqualityCheck(leSprql(var, dt.referenceDate1), + pr(le(dt.referenceDate1), var)); + evalAndEqualityCheck(ltSprql(DoubleId(10.2), var), + pr(lt(DoubleId(10.2)), var)); } //______________________________________________________________________________ @@ -1163,237 +808,186 @@ TEST(SparqlExpression, getPrefilterExpressionsToComplexSparqlExpressions) { // ?x >= 10 AND ?x != 20 // expected prefilter pairs: // {<((>= 10) AND (!= 20)), ?x>} - evalAndEqualityCheck(andSparqlExpr(greaterEqSparqlExpr(varX, IntId(10)), - notEqSparqlExpr(varX, IntId(20))), - pr(andFilterExpr(greaterEqFilterExpr(IntId(10)), - notEqFilterExpr(IntId(20))), - varX)); + evalAndEqualityCheck( + andSprqlExpr(geSprql(varX, IntId(10)), neqSprql(varX, IntId(20))), + pr(andExpr(ge(IntId(10)), neq(IntId(20))), varX)); // ?z > VocabId(0) AND ?y > 0 AND ?x < 30.00 // expected prefilter pairs // {<(< 30.00), ?x>, <(> 0), ?y>, <(> VocabId(0)), ?z>} - evalAndEqualityCheck( - andSparqlExpr(andSparqlExpr(greaterThanSparqlExpr(varZ, VocabId(0)), - greaterThanSparqlExpr(varY, IntId(0))), - lessThanSparqlExpr(varX, DoubleId(30.00))), - pr(lessThanFilterExpr(DoubleId(30.00)), varX), - pr(greaterThanFilterExpr(IntId(0)), varY), - pr(greaterThanFilterExpr(VocabId(0)), varZ)); + evalAndEqualityCheck(andSprqlExpr(andSprqlExpr(gtSprql(varZ, VocabId(0)), + gtSprql(varY, IntId(0))), + ltSprql(varX, DoubleId(30.00))), + pr(lt(DoubleId(30.00)), varX), pr(gt(IntId(0)), varY), + pr(gt(VocabId(0)), varZ)); // ?x == VocabId(10) AND ?y >= VocabId(10) // expected prefilter pairs: // {<(== VocabId(10)), ?x>, <(>= VocabId(10)), ?y>} - evalAndEqualityCheck(andSparqlExpr(eqSparqlExpr(varX, VocabId(10)), - greaterEqSparqlExpr(varY, VocabId(10))), - pr(eqFilterExpr(VocabId(10)), varX), - pr(greaterEqFilterExpr(VocabId(10)), varY)); + evalAndEqualityCheck( + andSprqlExpr(eqSprql(varX, VocabId(10)), geSprql(varY, VocabId(10))), + pr(eq(VocabId(10)), varX), pr(ge(VocabId(10)), varY)); // !(?x >= 10 OR ?x <= 0) // expected prefilter pairs: // {= 10 OR ?x <= 0), ?x>} - evalAndEqualityCheck( - notSparqlExpr(orSparqlExpr(greaterEqSparqlExpr(varX, IntId(10)), - lessEqSparqlExpr(varX, IntId(0)))), - pr(notFilterExpr(orFilterExpr(greaterEqFilterExpr(IntId(10)), - lessEqFilterExpr(IntId(0)))), - varX)); + evalAndEqualityCheck(notSprqlExpr(orSprqlExpr(geSprql(varX, IntId(10)), + leSprql(varX, IntId(0)))), + pr(notExpr(orExpr(ge(IntId(10)), le(IntId(0)))), varX)); // !(?z == VocabId(10) AND ?z >= VocabId(20)) // expected prefilter pairs: // {= VocabId(20)) , ?z>} evalAndEqualityCheck( - notSparqlExpr(andSparqlExpr(eqSparqlExpr(varZ, VocabId(10)), - greaterEqSparqlExpr(varZ, VocabId(20)))), - pr(notFilterExpr(andFilterExpr(eqFilterExpr(VocabId(10)), - greaterEqFilterExpr(VocabId(20)))), - varZ)); + notSprqlExpr( + andSprqlExpr(eqSprql(varZ, VocabId(10)), geSprql(varZ, VocabId(20)))), + pr(notExpr(andExpr(eq(VocabId(10)), ge(VocabId(20)))), varZ)); // (?x == VocabId(10) AND ?z == VocabId(0)) AND ?y != DoubleId(22.1) // expected prefilter pairs: // {<(==VocabId(10)) , ?x>, <(!=DoubleId(22.1)), ?y>, <(==VocabId(0)), ?z>} - evalAndEqualityCheck( - andSparqlExpr(andSparqlExpr(eqSparqlExpr(VocabId(10), varX), - eqSparqlExpr(varZ, VocabId(0))), - notEqSparqlExpr(DoubleId(22.1), varY)), - pr(eqFilterExpr(VocabId(10)), varX), - pr(notEqFilterExpr(DoubleId(22.1)), varY), - pr(eqFilterExpr(VocabId(0)), varZ)); + evalAndEqualityCheck(andSprqlExpr(andSprqlExpr(eqSprql(VocabId(10), varX), + eqSprql(varZ, VocabId(0))), + neqSprql(DoubleId(22.1), varY)), + pr(eq(VocabId(10)), varX), pr(neq(DoubleId(22.1)), varY), + pr(eq(VocabId(0)), varZ)); // (?z >= 1000 AND ?x == VocabId(10)) OR ?z >= 10000 // expected prefilter pairs: // {<((>=1000) OR (>= 10000)), ?z>} - evalAndEqualityCheck( - orSparqlExpr(andSparqlExpr(greaterEqSparqlExpr(varZ, IntId(1000)), - eqSparqlExpr(varX, VocabId(10))), - greaterEqSparqlExpr(varZ, IntId(10000))), - pr(orFilterExpr(greaterEqFilterExpr(IntId(1000)), - greaterEqFilterExpr(IntId(10000))), - varZ)); + evalAndEqualityCheck(orSprqlExpr(andSprqlExpr(geSprql(varZ, IntId(1000)), + eqSprql(varX, VocabId(10))), + geSprql(varZ, IntId(10000))), + pr(orExpr(ge(IntId(1000)), ge(IntId(10000))), varZ)); // !((?z <= VocabId(10) OR ?y <= VocabId(10)) OR ?x <= VocabId(10)) // expected prefilter pairs: // {, , } - evalAndEqualityCheck(notSparqlExpr(orSparqlExpr( - orSparqlExpr(lessEqSparqlExpr(varZ, VocabId(10)), - lessEqSparqlExpr(varY, VocabId(10))), - lessEqSparqlExpr(varX, VocabId(10)))), - pr(notFilterExpr(lessEqFilterExpr(VocabId(10))), varX), - pr(notFilterExpr(lessEqFilterExpr(VocabId(10))), varY), - pr(notFilterExpr(lessEqFilterExpr(VocabId(10))), varZ)); + evalAndEqualityCheck( + notSprqlExpr(orSprqlExpr( + orSprqlExpr(leSprql(varZ, VocabId(10)), leSprql(varY, VocabId(10))), + leSprql(varX, VocabId(10)))), + pr(notExpr(le(VocabId(10))), varX), pr(notExpr(le(VocabId(10))), varY), + pr(notExpr(le(VocabId(10))), varZ)); // ?x >= 10 AND ?y >= 10 // expected prefilter pairs: // {<(>= 10), ?x>, <(>= 10), ?y>} - evalAndEqualityCheck(andSparqlExpr(greaterEqSparqlExpr(varX, IntId(10)), - greaterEqSparqlExpr(varY, IntId(10))), - pr(greaterEqFilterExpr(IntId(10)), varX), - pr(greaterEqFilterExpr(IntId(10)), varY)); + evalAndEqualityCheck( + andSprqlExpr(geSprql(varX, IntId(10)), geSprql(varY, IntId(10))), + pr(ge(IntId(10)), varX), pr(ge(IntId(10)), varY)); // ?x <= 0 AND ?y <= 0 // expected prefilter pairs: // {<(<= 0), ?x>, <(<= 0), ?y>} - evalAndEqualityCheck(andSparqlExpr(lessEqSparqlExpr(varX, IntId(0)), - lessEqSparqlExpr(varY, IntId(0))), - pr(lessEqFilterExpr(IntId(0)), varX), - pr(lessEqFilterExpr(IntId(0)), varY)); + evalAndEqualityCheck( + andSprqlExpr(leSprql(varX, IntId(0)), leSprql(varY, IntId(0))), + pr(le(IntId(0)), varX), pr(le(IntId(0)), varY)); // (?x >= 10 AND ?y >= 10) OR (?x <= 0 AND ?y <= 0) // expected prefilter pairs: // {<((>= 10) OR (<= 0)), ?x> <(>= 10) OR (<= 0)), ?y>} evalAndEqualityCheck( - orSparqlExpr(andSparqlExpr(greaterEqSparqlExpr(varX, IntId(10)), - greaterEqSparqlExpr(varY, IntId(10))), - andSparqlExpr(lessEqSparqlExpr(varX, IntId(0)), - lessEqSparqlExpr(varY, IntId(0)))), - pr(orFilterExpr(greaterEqFilterExpr(IntId(10)), - lessEqFilterExpr(IntId(0))), - varX), - pr(orFilterExpr(greaterEqFilterExpr(IntId(10)), - lessEqFilterExpr(IntId(0))), - varY)); + orSprqlExpr( + andSprqlExpr(geSprql(varX, IntId(10)), geSprql(varY, IntId(10))), + andSprqlExpr(leSprql(varX, IntId(0)), leSprql(varY, IntId(0)))), + pr(orExpr(ge(IntId(10)), le(IntId(0))), varX), + pr(orExpr(ge(IntId(10)), le(IntId(0))), varY)); // !(?x >= 10 OR ?y >= 10) OR !(?x <= 0 OR ?y <= 0) // expected prefilter pairs: // {<((!(>= 10) OR !(<= 0))), ?x> <(!(>= 10) OR !(<= 0))), ?y>} evalAndEqualityCheck( - orSparqlExpr( - notSparqlExpr(orSparqlExpr(greaterEqSparqlExpr(varX, IntId(10)), - greaterEqSparqlExpr(varY, IntId(10)))), - notSparqlExpr(orSparqlExpr(lessEqSparqlExpr(varX, IntId(0)), - lessEqSparqlExpr(varY, IntId(0))))), - pr(orFilterExpr(notFilterExpr(greaterEqFilterExpr(IntId(10))), - notFilterExpr(lessEqFilterExpr(IntId(0)))), - varX), - pr(orFilterExpr(notFilterExpr(greaterEqFilterExpr(IntId(10))), - notFilterExpr(lessEqFilterExpr(IntId(0)))), - varY)); + orSprqlExpr(notSprqlExpr(orSprqlExpr(geSprql(varX, IntId(10)), + geSprql(varY, IntId(10)))), + notSprqlExpr(orSprqlExpr(leSprql(varX, IntId(0)), + leSprql(varY, IntId(0))))), + pr(orExpr(notExpr(ge(IntId(10))), notExpr(le(IntId(0)))), varX), + pr(orExpr(notExpr(ge(IntId(10))), notExpr(le(IntId(0)))), varY)); // !(?x == VocabId(10) OR ?x == VocabId(20)) AND !(?z >= 10.00 OR ?y == false) // expected prefilter pairs: // {, , // = 10), ?z>} evalAndEqualityCheck( - andSparqlExpr( - notSparqlExpr(orSparqlExpr(eqSparqlExpr(varX, VocabId(10)), - eqSparqlExpr(varX, VocabId(20)))), - notSparqlExpr(orSparqlExpr(greaterEqSparqlExpr(varZ, DoubleId(10)), - eqSparqlExpr(varY, BoolId(false))))), - pr(notFilterExpr(orFilterExpr(eqFilterExpr(VocabId(10)), - eqFilterExpr(VocabId(20)))), - varX), - pr(notFilterExpr(eqFilterExpr(BoolId(false))), varY), - pr(notFilterExpr(greaterEqFilterExpr(DoubleId(10))), varZ)); + andSprqlExpr(notSprqlExpr(orSprqlExpr(eqSprql(varX, VocabId(10)), + eqSprql(varX, VocabId(20)))), + notSprqlExpr(orSprqlExpr(geSprql(varZ, DoubleId(10)), + eqSprql(varY, BoolId(false))))), + pr(notExpr(orExpr(eq(VocabId(10)), eq(VocabId(20)))), varX), + pr(notExpr(eq(BoolId(false))), varY), + pr(notExpr(ge(DoubleId(10))), varZ)); // !(!(?x >= 10 AND ?y >= 10)) OR !(!(?x <= 0 AND ?y <= 0)) // expected prefilter pairs: // {<(!!(>= 10) OR !!(<= 0)), ?x>, <(!!(>= 10) OR !!(<= 0)) ,?y>} evalAndEqualityCheck( - orSparqlExpr(notSparqlExpr(notSparqlExpr( - andSparqlExpr(greaterEqSparqlExpr(varX, IntId(10)), - greaterEqSparqlExpr(varY, IntId(10))))), - notSparqlExpr(notSparqlExpr( - andSparqlExpr(lessEqSparqlExpr(varX, IntId(0)), - lessEqSparqlExpr(varY, IntId(0)))))), - pr(orFilterExpr( - notFilterExpr(notFilterExpr(greaterEqFilterExpr(IntId(10)))), - notFilterExpr(notFilterExpr(lessEqFilterExpr(IntId(0))))), + orSprqlExpr(notSprqlExpr(notSprqlExpr(andSprqlExpr( + geSprql(varX, IntId(10)), geSprql(varY, IntId(10))))), + notSprqlExpr(notSprqlExpr(andSprqlExpr( + leSprql(varX, IntId(0)), leSprql(varY, IntId(0)))))), + pr(orExpr(notExpr(notExpr(ge(IntId(10)))), + notExpr(notExpr(le(IntId(0))))), varX), - pr(orFilterExpr( - notFilterExpr(notFilterExpr(greaterEqFilterExpr(IntId(10)))), - notFilterExpr(notFilterExpr(lessEqFilterExpr(IntId(0))))), + pr(orExpr(notExpr(notExpr(ge(IntId(10)))), + notExpr(notExpr(le(IntId(0))))), varY)); // !((?x >= VocabId(0) AND ?x <= VocabId(10)) OR !(?x != VocabId(99))) // expected prefilter pairs: // {= VocabId(0)) AND (<= VocabId(10))) OR !(!= VocabId(99))) , ?x>} - evalAndEqualityCheck(notSparqlExpr(orSparqlExpr( - andSparqlExpr(greaterEqSparqlExpr(varX, VocabId(0)), - lessEqSparqlExpr(varX, VocabId(10))), - notSparqlExpr(notEqSparqlExpr(varX, VocabId(99))))), - pr(notFilterExpr(orFilterExpr( - andFilterExpr(greaterEqFilterExpr(VocabId(0)), - lessEqFilterExpr(VocabId(10))), - notFilterExpr(notEqFilterExpr(VocabId(99))))), - varX)); + evalAndEqualityCheck( + notSprqlExpr(orSprqlExpr( + andSprqlExpr(geSprql(varX, VocabId(0)), leSprql(varX, VocabId(10))), + notSprqlExpr(neqSprql(varX, VocabId(99))))), + pr(notExpr(orExpr(andExpr(ge(VocabId(0)), le(VocabId(10))), + notExpr(neq(VocabId(99))))), + varX)); // !((?y >= 10 AND ?y <= 100) OR !(?x >= VocabId(99))) // expected prefilter pairs: // {= VocabId(0)) AND (<= VocabId(10)), ?y>, = VocabId(99))), ?x>} evalAndEqualityCheck( - notSparqlExpr( - orSparqlExpr(andSparqlExpr(greaterEqSparqlExpr(varY, VocabId(0)), - lessEqSparqlExpr(varY, VocabId(10))), - notSparqlExpr(greaterEqSparqlExpr(varX, VocabId(99))))), - pr(notFilterExpr(notFilterExpr(greaterEqFilterExpr(VocabId(99)))), varX), - pr(notFilterExpr(andFilterExpr(greaterEqFilterExpr(VocabId(0)), - lessEqFilterExpr(VocabId(10)))), - varY)); + notSprqlExpr(orSprqlExpr( + andSprqlExpr(geSprql(varY, VocabId(0)), leSprql(varY, VocabId(10))), + notSprqlExpr(geSprql(varX, VocabId(99))))), + pr(notExpr(notExpr(ge(VocabId(99)))), varX), + pr(notExpr(andExpr(ge(VocabId(0)), le(VocabId(10)))), varY)); // ?z >= 10 AND ?z <= 100 AND ?x >= 10 AND ?x != 50 AND !(?y <= 10) AND // !(?city <= VocabId(1000) OR ?city == VocabId(1005)) // expected prefilter pairs: // {, <((>= 10) AND (!= // 50)), ?x>, , <((>= 10) AND (<= 100)), ?z>} evalAndEqualityCheck( - andSparqlExpr( - andSparqlExpr( - andSparqlExpr(greaterEqSparqlExpr(varZ, IntId(10)), - lessEqSparqlExpr(varZ, IntId(100))), - andSparqlExpr(andSparqlExpr(greaterEqSparqlExpr(varX, IntId(10)), - notEqSparqlExpr(varX, IntId(50))), - notSparqlExpr(lessEqSparqlExpr(varY, IntId(10))))), - notSparqlExpr( - orSparqlExpr(lessEqSparqlExpr(Variable{"?city"}, VocabId(1000)), - eqSparqlExpr(Variable{"?city"}, VocabId(1005))))), - pr(notFilterExpr(orFilterExpr(lessEqFilterExpr(VocabId(1000)), - eqFilterExpr(VocabId(1005)))), + andSprqlExpr( + andSprqlExpr( + andSprqlExpr(geSprql(varZ, IntId(10)), leSprql(varZ, IntId(100))), + andSprqlExpr(andSprqlExpr(geSprql(varX, IntId(10)), + neqSprql(varX, IntId(50))), + notSprqlExpr(leSprql(varY, IntId(10))))), + notSprqlExpr(orSprqlExpr(leSprql(Variable{"?city"}, VocabId(1000)), + eqSprql(Variable{"?city"}, VocabId(1005))))), + pr(notExpr(orExpr(le(VocabId(1000)), eq(VocabId(1005)))), Variable{"?city"}), - pr(andFilterExpr(greaterEqFilterExpr(IntId(10)), - notEqFilterExpr(IntId(50))), - varX), - pr(notFilterExpr(lessEqFilterExpr(IntId(10))), varY), - pr(andFilterExpr(greaterEqFilterExpr(IntId(10)), - lessEqFilterExpr(IntId(100))), - varZ)); + pr(andExpr(ge(IntId(10)), neq(IntId(50))), varX), + pr(notExpr(le(IntId(10))), varY), + pr(andExpr(ge(IntId(10)), le(IntId(100))), varZ)); // ?x >= 10 OR (?x >= -10 AND ?x < 0.00) // expected prefilter pairs: // {<((>= 10) OR ((>= -10) AND (< 0.00))), ?x>} evalAndEqualityCheck( - orSparqlExpr(greaterEqSparqlExpr(varX, IntId(10)), - andSparqlExpr(greaterEqSparqlExpr(varX, IntId(-10)), - lessThanSparqlExpr(DoubleId(0.00), varX))), - pr(orFilterExpr(greaterEqFilterExpr(IntId(10)), - andFilterExpr(greaterEqFilterExpr(IntId(-10)), - lessThanFilterExpr(DoubleId(0.00)))), + orSprqlExpr(geSprql(varX, IntId(10)), + andSprqlExpr(geSprql(varX, IntId(-10)), + ltSprql(DoubleId(0.00), varX))), + pr(orExpr(ge(IntId(10)), andExpr(ge(IntId(-10)), lt(DoubleId(0.00)))), varX)); // !(!(?x >= 10) OR !!(?x >= -10 AND ?x < 0.00)) // expected prefilter pairs: // {= 10) OR !!((>= -10) AND (< 0.00))), ?x>} - evalAndEqualityCheck(notSparqlExpr(orSparqlExpr( - notSparqlExpr(greaterEqSparqlExpr(varX, IntId(10))), - notSparqlExpr(notSparqlExpr(andSparqlExpr( - greaterEqSparqlExpr(varX, IntId(-10)), - lessThanSparqlExpr(DoubleId(0.00), varX)))))), - pr(notFilterExpr(orFilterExpr( - notFilterExpr(greaterEqFilterExpr(IntId(10))), - notFilterExpr(notFilterExpr(andFilterExpr( - greaterEqFilterExpr(IntId(-10)), - lessThanFilterExpr(DoubleId(0.00))))))), - varX)); + evalAndEqualityCheck( + notSprqlExpr(orSprqlExpr( + notSprqlExpr(geSprql(varX, IntId(10))), + notSprqlExpr(notSprqlExpr(andSprqlExpr( + geSprql(varX, IntId(-10)), ltSprql(DoubleId(0.00), varX)))))), + pr(notExpr(orExpr( + notExpr(ge(IntId(10))), + notExpr(notExpr(andExpr(ge(IntId(-10)), lt(DoubleId(0.00))))))), + varX)); // ?y != ?x AND ?x >= 10 // expected prefilter pairs: // {<(>= 10), ?x>} - evalAndEqualityCheck(andSparqlExpr(notEqSparqlExpr(varY, varX), - greaterEqSparqlExpr(varX, IntId(10))), - pr(greaterEqFilterExpr(IntId(10)), varX)); - evalAndEqualityCheck(andSparqlExpr(greaterEqSparqlExpr(varX, IntId(10)), - notEqSparqlExpr(varY, varX)), - pr(greaterEqFilterExpr(IntId(10)), varX)); + evalAndEqualityCheck( + andSprqlExpr(neqSprql(varY, varX), geSprql(varX, IntId(10))), + pr(ge(IntId(10)), varX)); + evalAndEqualityCheck( + andSprqlExpr(geSprql(varX, IntId(10)), neqSprql(varY, varX)), + pr(ge(IntId(10)), varX)); } //______________________________________________________________________________ @@ -1402,20 +996,17 @@ TEST(SparqlExpression, getEmptyPrefilterFromSparqlRelational) { const Variable var = Variable{"?x"}; const Iri iri = I(""); const Literal lit = L("\"lit\""); - evalToEmptyCheck(lessEqSparqlExpr(var, var)); - evalToEmptyCheck(notEqSparqlExpr(iri, var)); - evalToEmptyCheck(eqSparqlExpr(var, iri)); - evalToEmptyCheck(notEqSparqlExpr(IntId(10), DoubleId(23.3))); - evalToEmptyCheck(greaterThanSparqlExpr(DoubleId(10), lit)); - evalToEmptyCheck(lessThanSparqlExpr(VocabId(10), BoolId(10))); - evalToEmptyCheck(greaterEqSparqlExpr(lit, lit)); - evalToEmptyCheck(eqSparqlExpr(iri, iri)); - evalToEmptyCheck(orSparqlExpr(eqSparqlExpr(var, var), - greaterThanSparqlExpr(var, IntId(0)))); - evalToEmptyCheck( - orSparqlExpr(eqSparqlExpr(var, var), greaterThanSparqlExpr(var, var))); - evalToEmptyCheck( - andSparqlExpr(eqSparqlExpr(var, var), greaterThanSparqlExpr(var, var))); + evalToEmptyCheck(leSprql(var, var)); + evalToEmptyCheck(neqSprql(iri, var)); + evalToEmptyCheck(eqSprql(var, iri)); + evalToEmptyCheck(neqSprql(IntId(10), DoubleId(23.3))); + evalToEmptyCheck(gtSprql(DoubleId(10), lit)); + evalToEmptyCheck(ltSprql(VocabId(10), BoolId(10))); + evalToEmptyCheck(geSprql(lit, lit)); + evalToEmptyCheck(eqSprql(iri, iri)); + evalToEmptyCheck(orSprqlExpr(eqSprql(var, var), gtSprql(var, IntId(0)))); + evalToEmptyCheck(orSprqlExpr(eqSprql(var, var), gtSprql(var, var))); + evalToEmptyCheck(andSprqlExpr(eqSprql(var, var), gtSprql(var, var))); } //______________________________________________________________________________ @@ -1426,86 +1017,79 @@ TEST(SparqlExpression, getEmptyPrefilterForMoreComplexSparqlExpressions) { const Variable varY = Variable{"?y"}; const Variable varZ = Variable{"?z"}; // ?x <= 10.00 OR ?y > 10 - evalToEmptyCheck(orSparqlExpr(lessEqSparqlExpr(DoubleId(10), varX), - greaterThanSparqlExpr(IntId(10), varY))); + evalToEmptyCheck( + orSprqlExpr(leSprql(DoubleId(10), varX), gtSprql(IntId(10), varY))); // ?x >= VocabId(23) OR ?z == VocabId(1) - evalToEmptyCheck(orSparqlExpr(greaterEqSparqlExpr(varX, VocabId(23)), - eqSparqlExpr(varZ, VocabId(1)))); - // (?x < VocabId(10) OR ?z <= VocabId(4)) OR ?z != 5.00 evalToEmptyCheck( - orSparqlExpr(orSparqlExpr(lessThanSparqlExpr(varX, VocabId(10)), - lessEqSparqlExpr(VocabId(4), varZ)), - notEqSparqlExpr(varZ, DoubleId(5)))); + orSprqlExpr(geSprql(varX, VocabId(23)), eqSprql(varZ, VocabId(1)))); + // (?x < VocabId(10) OR ?z <= VocabId(4)) OR ?z != 5.00 + evalToEmptyCheck(orSprqlExpr( + orSprqlExpr(ltSprql(varX, VocabId(10)), leSprql(VocabId(4), varZ)), + neqSprql(varZ, DoubleId(5)))); // !(?z > 10.20 AND ?x < 0.001) // is equal to // ?z <= 10.20 OR ?x >= 0.001 - evalToEmptyCheck( - notSparqlExpr(andSparqlExpr(greaterThanSparqlExpr(DoubleId(10.2), varZ), - lessThanSparqlExpr(DoubleId(0.001), varX)))); + evalToEmptyCheck(notSprqlExpr(andSprqlExpr(gtSprql(DoubleId(10.2), varZ), + ltSprql(DoubleId(0.001), varX)))); // !(?x > 10.20 AND ?z != VocabId(22)) // is equal to // ?x <= 10.20 OR ?z == VocabId(22) - evalToEmptyCheck( - notSparqlExpr(andSparqlExpr(greaterThanSparqlExpr(DoubleId(10.2), varX), - notEqSparqlExpr(VocabId(22), varZ)))); + evalToEmptyCheck(notSprqlExpr(andSprqlExpr(gtSprql(DoubleId(10.2), varX), + neqSprql(VocabId(22), varZ)))); // !(!((?x < VocabId(10) OR ?x <= VocabId(4)) OR ?z != 5.00)) // is equal to // (?x < VocabId(10) OR ?x <= VocabId(4)) OR ?z != 5.00 - evalToEmptyCheck(notSparqlExpr(notSparqlExpr( - orSparqlExpr(orSparqlExpr(lessThanSparqlExpr(varX, VocabId(10)), - lessEqSparqlExpr(VocabId(4), varX)), - notEqSparqlExpr(varZ, DoubleId(5)))))); + evalToEmptyCheck(notSprqlExpr(notSprqlExpr(orSprqlExpr( + orSprqlExpr(ltSprql(varX, VocabId(10)), leSprql(VocabId(4), varX)), + neqSprql(varZ, DoubleId(5)))))); // !(?x != 10 AND !(?y >= 10.00 OR ?z <= 10)) // is equal to // ?x == 10 OR ?y >= 10.00 OR ?z <= 10 - evalToEmptyCheck(notSparqlExpr(andSparqlExpr( - notEqSparqlExpr(varX, IntId(10)), - notSparqlExpr(orSparqlExpr(greaterEqSparqlExpr(varY, DoubleId(10.00)), - lessEqSparqlExpr(varZ, IntId(10))))))); + evalToEmptyCheck(notSprqlExpr( + andSprqlExpr(neqSprql(varX, IntId(10)), + notSprqlExpr(orSprqlExpr(geSprql(varY, DoubleId(10.00)), + leSprql(varZ, IntId(10))))))); // !((?x != 10 AND ?z != 10) AND (?y == 10 AND ?x >= 20)) // is equal to //?x == 10 OR ?z == 10 OR ?y != 10 OR ?x < 20 - evalToEmptyCheck(notSparqlExpr( - andSparqlExpr(andSparqlExpr(notEqSparqlExpr(varX, IntId(10)), - notEqSparqlExpr(varZ, IntId(10))), - andSparqlExpr(eqSparqlExpr(varY, IntId(10)), - greaterEqSparqlExpr(varX, IntId(20)))))); + evalToEmptyCheck(notSprqlExpr(andSprqlExpr( + andSprqlExpr(neqSprql(varX, IntId(10)), neqSprql(varZ, IntId(10))), + andSprqlExpr(eqSprql(varY, IntId(10)), geSprql(varX, IntId(20)))))); // !(?z >= 40 AND (?z != 10.00 AND ?y != VocabId(1))) // is equal to // ?z <= 40 OR ?z == 10.00 OR ?y == VocabId(1) - evalToEmptyCheck(notSparqlExpr( - andSparqlExpr(greaterEqSparqlExpr(varZ, IntId(40)), - andSparqlExpr(notEqSparqlExpr(varZ, DoubleId(10.00)), - notEqSparqlExpr(varY, VocabId(1)))))); + evalToEmptyCheck(notSprqlExpr(andSprqlExpr( + geSprql(varZ, IntId(40)), andSprqlExpr(neqSprql(varZ, DoubleId(10.00)), + neqSprql(varY, VocabId(1)))))); // ?z <= true OR !(?x == 10 AND ?y == 10) // is equal to // ?z <= true OR ?x != 10 OR ?y != 10 - evalToEmptyCheck(orSparqlExpr( - lessEqSparqlExpr(varZ, BoolId(true)), - notSparqlExpr(andSparqlExpr(eqSparqlExpr(varX, IntId(10)), - eqSparqlExpr(IntId(10), varY))))); + evalToEmptyCheck( + orSprqlExpr(leSprql(varZ, BoolId(true)), + notSprqlExpr(andSprqlExpr(eqSprql(varX, IntId(10)), + eqSprql(IntId(10), varY))))); // !(!(?z <= true OR !(?x == 10 AND ?y == 10))) // is equal to // ?z <= true OR ?x != 10 OR ?y != 10 - evalToEmptyCheck(notSparqlExpr(notSparqlExpr(orSparqlExpr( - lessEqSparqlExpr(varZ, BoolId(true)), - notSparqlExpr(andSparqlExpr(eqSparqlExpr(varX, IntId(10)), - eqSparqlExpr(IntId(10), varY))))))); + evalToEmptyCheck(notSprqlExpr(notSprqlExpr( + orSprqlExpr(leSprql(varZ, BoolId(true)), + notSprqlExpr(andSprqlExpr(eqSprql(varX, IntId(10)), + eqSprql(IntId(10), varY))))))); // !(!(?x != 10 OR !(?y >= 10.00 AND ?z <= 10))) // is equal to // ?x != 10 OR ?y < 10.00 OR ?z > 10 - evalToEmptyCheck(notSparqlExpr(notSparqlExpr(orSparqlExpr( - notEqSparqlExpr(varX, IntId(10)), - notSparqlExpr(andSparqlExpr(greaterEqSparqlExpr(varY, DoubleId(10.00)), - lessEqSparqlExpr(varZ, IntId(10)))))))); + evalToEmptyCheck(notSprqlExpr(notSprqlExpr( + orSprqlExpr(neqSprql(varX, IntId(10)), + notSprqlExpr(andSprqlExpr(geSprql(varY, DoubleId(10.00)), + leSprql(varZ, IntId(10)))))))); // !(!(?x == VocabId(10) OR ?y >= 25) AND !(!(?z == true AND ?country == // VocabId(20)))) // is equal to // ?x == VocabId(10) OR ?y >= 25 OR ?z == true AND ?country == VocabId(20) - evalToEmptyCheck(notSparqlExpr(andSparqlExpr( - notSparqlExpr(orSparqlExpr(eqSparqlExpr(varX, VocabId(10)), - greaterEqSparqlExpr(varY, IntId(25)))), - notSparqlExpr(notSparqlExpr( - andSparqlExpr(eqSparqlExpr(varZ, BoolId(true)), - eqSparqlExpr(Variable{"?country"}, VocabId(20)))))))); + evalToEmptyCheck(notSprqlExpr(andSprqlExpr( + notSprqlExpr( + orSprqlExpr(eqSprql(varX, VocabId(10)), geSprql(varY, IntId(25)))), + notSprqlExpr(notSprqlExpr( + andSprqlExpr(eqSprql(varZ, BoolId(true)), + eqSprql(Variable{"?country"}, VocabId(20)))))))); } From 9f0efc6298c9a4b783b0d2b4fab2f41c21aab15f Mon Sep 17 00:00:00 2001 From: realHannes Date: Mon, 28 Oct 2024 12:49:13 +0100 Subject: [PATCH 43/50] fix build --- src/index/CompressedBlockPrefiltering.cpp | 2 +- src/index/CompressedBlockPrefiltering.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/index/CompressedBlockPrefiltering.cpp b/src/index/CompressedBlockPrefiltering.cpp index 72d4488df2..75182fd61f 100644 --- a/src/index/CompressedBlockPrefiltering.cpp +++ b/src/index/CompressedBlockPrefiltering.cpp @@ -224,7 +224,7 @@ std::vector RelationalExpression::evaluateImpl( relevantBlocks.shrink_to_fit(); // Merge mixedDatatypeBlocks into relevantBlocks while maintaining order and // avoiding duplicates. - return getSetUnion(relevantBlocks, mixedDatatypeBlocks, evaluationColumn); + return getSetUnion(relevantBlocks, mixedDatatypeBlocks); }; //______________________________________________________________________________ diff --git a/src/index/CompressedBlockPrefiltering.h b/src/index/CompressedBlockPrefiltering.h index e64eaa3b12..08fa9092c7 100644 --- a/src/index/CompressedBlockPrefiltering.h +++ b/src/index/CompressedBlockPrefiltering.h @@ -15,7 +15,7 @@ namespace prefilterExpressions { //______________________________________________________________________________ // The maximum recursion depth for `info()` / `operator<<()`. A depth of `3` -// should be sufficient for most `PrefilterExpressions` with our use case. +// should be sufficient for most `PrefilterExpressions` in our use case. constexpr size_t maxInfoRecursion = 3; //______________________________________________________________________________ From 25f325a5b102051286f834cae736b1e90445ffc2 Mon Sep 17 00:00:00 2001 From: realHannes Date: Wed, 30 Oct 2024 16:01:12 +0100 Subject: [PATCH 44/50] implement proposed changes (2) --- .../NumericBinaryExpressions.cpp | 265 +++++++++++------- .../NumericUnaryExpressions.cpp | 44 ++- .../RelationalExpressions.cpp | 89 +++--- src/index/CompressedBlockPrefiltering.cpp | 24 ++ src/index/CompressedBlockPrefiltering.h | 27 +- src/parser/data/Variable.h | 4 +- test/CompressedBlockPrefilteringTest.cpp | 83 +++++- 7 files changed, 369 insertions(+), 167 deletions(-) diff --git a/src/engine/sparqlExpressions/NumericBinaryExpressions.cpp b/src/engine/sparqlExpressions/NumericBinaryExpressions.cpp index 4ae1abc273..7ffa773a53 100644 --- a/src/engine/sparqlExpressions/NumericBinaryExpressions.cpp +++ b/src/engine/sparqlExpressions/NumericBinaryExpressions.cpp @@ -68,50 +68,53 @@ inline auto andLambda = [](TernaryBool a, TernaryBool b) { }; namespace constructPrefilterExpr { +namespace { +// MERGE AND CONJUNCTION // _____________________________________________________________________________ +// For our pre-filtering logic over index scans, we need exactly one +// corresponding PrefilterExpression for each relavant Variable. Thus, if the +// left and right child contain a PrefilterExpression w.r.t. the same Variable, +// combine them here for an AND conjunction. In the following, three examples +// are given on how the following function merges the content of the left and +// right child. +// +// EXAMPLE 1 +// left child: {<(>= 10), ?x>, <(!= 5), ?y>}; (?x >= 10 && ?y != 5) +// right child: {}; (std::nullopt) +// The AND conjunction can only evaluate to true, if both child expressions +// evaluate to true. For the given example, we have no PrefilterExpression +// for the right child, but we can certainly assume that the left child +// expression must evaluate to true. +// Thus, return {<(>= 10), ?x>, <(!= 5), ?y>}. +// +// EXAMPLE 2 +// left child: {<(= 5), ?x>}; (?x = 5) +// right child: {<(= VocabId(10)), ?y>}; (?y = VocabId(10)) +// Returns {<(= 5), ?x>, <(= VocabId(10)), ?y>}. +// +// EXAMPLE 3 +// left child: {<(>= 10 AND <= 20), ?x>}; (?x >= 10 && ?x <= 20) +// right child: {<(!= 15), ?x>, <(= 10), ?y>}; (?x != 15 && ?y = 10) +// Returns {<((>= 10 AND <= 20) AND != 15), ?x>, <(= 10), ?y>} +//______________________________________________________________________________ template -std::optional> mergeChildrenAndLogicImpl( - std::optional>&& leftChildExprs, - std::optional>&& rightChildExprs) { - if (!leftChildExprs.has_value() || !rightChildExprs.has_value()) { - // If we have two children that yield no PrefilterExpressions, then there - // is also no PrefilterExpression over the AND (&&) conjunction available. - if (!leftChildExprs.has_value() && !rightChildExprs.has_value()) { - return std::nullopt; - } - // Given that only one of the children returned a PrefilterExpression - // vector and the pre-filter construction logic of AND (&&), we can just - // return the corresponding (non-empty) vector. - // - // EXAMPLE why this works: - // Assume that left is - {(>=5, ?x), (<=6, ?y)} - // and right is - std::nullopt ({undefinable prefilter}) - // Remark: left represents the expression ?x >= 5 && ?y <= 6 - // - // For this AND conjunction {(>=5, ?x), (<=6, ?y)} && {undef. - // prefilter}, we can at least (partly) pre-define the plausible true - // evaluation range with the left child. This is because the respective - // expressions contained in left child must always yield true - // for making this AND conjunction expression true. - return leftChildExprs.has_value() ? std::move(leftChildExprs.value()) - : std::move(rightChildExprs.value()); - } +std::optional> +mergeChildrenForAndExpressionImpl( + std::vector&& leftChild, + std::vector&& rightChild) { + namespace pd = prefilterExpressions::detail; + pd::checkPropertiesForPrefilterConstruction(leftChild); + pd::checkPropertiesForPrefilterConstruction(rightChild); // Merge the PrefilterExpression vectors from the left and right child. // Remark: The vectors contain std::pairs, sorted by their respective - // variable to the PrefilterExpression. - auto& expressionsLeft = leftChildExprs.value(); - auto& expressionsRight = rightChildExprs.value(); - auto itLeft = expressionsLeft.begin(); - auto itRight = expressionsRight.begin(); + // Variable to the PrefilterExpression. + auto itLeft = leftChild.begin(); + auto itRight = rightChild.begin(); std::vector resPairs; - while (itLeft != expressionsLeft.end() && itRight != expressionsRight.end()) { + while (itLeft != leftChild.end() && itRight != rightChild.end()) { auto& [exprLeft, varLeft] = *itLeft; auto& [exprRight, varRight] = *itRight; - // For our pre-filtering logic over index scans, we need exactly one - // PrefilterExpression for each of the variables. Thus if the left and right - // child contain a PrefilterExpression w.r.t. the same variable, combine - // them here over a (new) prefilter AndExpression. if (varLeft == varRight) { resPairs.emplace_back(std::make_unique( std::move(exprLeft), std::move(exprRight)), @@ -126,67 +129,65 @@ std::optional> mergeChildrenAndLogicImpl( ++itRight; } } - std::ranges::move(itLeft, expressionsLeft.end(), - std::back_inserter(resPairs)); - std::ranges::move(itRight, expressionsRight.end(), - std::back_inserter(resPairs)); + std::ranges::move(itLeft, leftChild.end(), std::back_inserter(resPairs)); + std::ranges::move(itRight, rightChild.end(), std::back_inserter(resPairs)); + if (resPairs.empty()) { + // No PrefilterExpression(s) available with this conjunction merge on the + // contents of left and right child. + return std::nullopt; + } + pd::checkPropertiesForPrefilterConstruction(resPairs); return resPairs; } +// MERGE OR CONJUNCTION +//______________________________________________________________________________ +// Four examples on how this function (OR) merges the content of two children. +// +// EXAMPLE 1 +// left child: {} (std::nullopt) +// right child: {<(<= 10), ?y>} +// Given that it is not possible to make a fixed value assumption (true or +// false) for the left (empty) child, it is also unclear if the right child +// must evaluate to true for this OR expression to become true. +// Hence, no PrefilterExpression (std::nullopt) will be returned. +// +// EXAMPLE 2 +// left child: {<(= 5), ?y>} +// right child: {<(<= 3), ?x>} +// We want to pre-filter the blocks for the expression: ?y >= 5 || ?x <= 3 +// No PrefilterExpression will be returned. +// +// EXAMPLE 3 +// left child: {<(>= 5), ?x>} +// right child: {<(= 0), ?x>} +// We want to pre-filter the blocks to the expression: ?x >= 5 || ?x = 0 +// The resulting PrefilterExpression is {<(>= 5 OR = 0), ?x>} +// +// EXAMPLE 4 +// left child: {<(= 10), ?x), <(!= 0), ?y>} +// right child: {<(<= 0), ?x>} +// We have to construct a PrefilterExpression for (?x >= 10 && ?y != 0) || +// ?x <= 0. If this OR expression yields true, at least ?x >= 10 || ?x <= 0 +// must be staisifed; for this objective we can construct a +// PrefiterExpression. We can't make a distinct prediction w.r.t. ?y != 0 +// => not relevant for the PrefilterExpression. Thus, we return the +// PrefilterExpresion {<(>= 10 OR <= 0), ?x>}. //______________________________________________________________________________ template -std::optional> mergeChildrenOrLogicImpl( - std::optional>&& leftChildExprs, - std::optional>&& rightChildExprs) { - if (!leftChildExprs.has_value() || !rightChildExprs.has_value()) { - // If one of the children yields no PrefilterExpressions, we simply can't - // further define a PrefilterExpression with this OR (||) conjunction. - // - // EXAMPLE / REASON: - // Assume that left is - {(=10, ?x), (=10, ?y)} - // and right is - std::nullopt ({undefinable prefilter}) - // Remark: left represents the expression ?x = 10 && ?y = 10. - // We can't define a PrefilterExpression here because left must not always - // yield true when this OR expression evaluates to true (because right can - // plausibly yield true). - // And because we can't definitely say that one child (and which of the two - // children) must be true, pre-filtering w.r.t. underlying compressed blocks - // is not possible here. - // In short: OR has multiple configurations that yield true w.r.t. two child - // expressions => we can't define a distinct PrefilterExpression. - return std::nullopt; - } - auto& expressionsLeft = leftChildExprs.value(); - auto& expressionsRight = rightChildExprs.value(); - auto itLeft = expressionsLeft.begin(); - auto itRight = expressionsRight.begin(); +std::optional> +mergeChildrenForOrExpressionImpl( + std::vector&& leftChild, + std::vector&& rightChild) { + namespace pd = prefilterExpressions::detail; + pd::checkPropertiesForPrefilterConstruction(leftChild); + pd::checkPropertiesForPrefilterConstruction(rightChild); + auto itLeft = leftChild.begin(); + auto itRight = rightChild.begin(); std::vector resPairs; - while (itLeft != expressionsLeft.end() && itRight != expressionsRight.end()) { + while (itLeft != leftChild.end() && itRight != rightChild.end()) { auto& [exprLeft, varLeft] = *itLeft; auto& [exprRight, varRight] = *itRight; - // The logic that is implemented with this loop. - // - // EXAMPLE 1 - // left child: {(>=5, ?y)} - // right child: {(<=3, ?x)} - // We want to pre-filter the blocks for the expression: ?y >= 5 || ?x <= 3 - // No PrefilterExpression will be returned. - // - // EXAMPLE 2 - // left child: {(>=5, ?x)} - // right child: {(=0, ?x)} - // We want to pre-filter the blocks to the expression: ?x >= 5 || ?x = 0 - // The resulting PrefilterExpression is {((>=5 OR =0), ?x)} - // - // EXAMPLE 3 - // left child: {(>=10, ?x), (!=0, ?y)} - // right child: {(<= 0, ?x)} - // We have to construct a PrefilterExpression for (?x >= 10 && ?y != 0) || - // ?x <= 0. If this OR expression yields true, at least ?x >= 10 || ?x <= 0 - // must be staisifed; for this objective we can construct a - // PrefiterExpression. We can't make a distinct prediction w.r.t. ?y != 0 - // => not relevant for the PrefilterExpression. Thus, we return the - // PrefilterExpresion {((>= 10 OR <= 0), ?x)}. if (varLeft == varRight) { resPairs.emplace_back(std::make_unique( std::move(exprLeft), std::move(exprRight)), @@ -200,36 +201,80 @@ std::optional> mergeChildrenOrLogicImpl( } } if (resPairs.empty()) { + // No PrefilterExpression(s) available with this conjunction merge on the + // contents of left and right child. return std::nullopt; } + pd::checkPropertiesForPrefilterConstruction(resPairs); return resPairs; } +// GET TEMPLATED MERGE FUNCTION (CONJUNCTION AND / OR) +//______________________________________________________________________________ +// getMergeFunction (get the respective merging function) follows the +// principles of De Morgan's law. If this is a child of a NotExpression +// (NOT(!)), we have to swap the combination procedure (swap AND(&&) and +// OR(||) respectively). This equals a partial application of De Morgan's +// law. On the resulting PrefilterExpressions, NOT is applied in +// UnaryNegateExpression(Impl). For more details take a look at the +// implementation in NumericUnaryExpressions.cpp (see +// UnaryNegateExpressionImpl). +// +// EXAMPLE +// - How De-Morgan's law is applied in steps +// !((?y != 10 || ?x = 0) || (?x = 5 || ?x >= 10)) is the complete (Sparql +// expression). With De-Morgan's law applied: +// (?y = 10 && ?x != 0) && (?x != 5 && ?x < 10) +// +// {, ....} represents the PrefilterExpressions +// with their corresponding Variable. +// +// At the current step we have: isNegated = true; because of !(...) (NOT) +// left child: {<(!= 10), ?y>, <(!= 0), ?x>}; (?y != 10 || ?x != 0) +// +// Remark: The (child) expressions of the left and right child have been +// combined with the merging procedure AND +// (mergeChildrenForAndExpressionImpl), +// because with isNegated = true, it is implied that De-Morgan's law is applied. +// Thus, the OR is logically an AND conjunction with its application. +// +// right child: {<((= 5) OR (>= 10)), ?x>} (?x = 5 || ?x >= 10) +// +// isNegated is true, hence we know that we have to partially apply +// De-Morgan's law. Given that, OR switches to an AND. Therefore we use +// mergeChildrenForAndExpressionImpl with OrExpression (PrefilterExpression) +// as a template value (BinaryPrefilterExpr). +// Call mergeChildrenAndExpressionImpl +// with left and right child as args., we get their merged combination: +// {<(!= 10), ?y>, <((!= 0) OR ((= 5) OR (>= 10))), ?x>} +// +// Next their merged value is returned to UnaryNegateExpressionImpl +// (defined in NumericUnaryExpressions.cpp), where for each expression of +// the pairs, NOT (NotExpression) is +// applied. +// {<(!= 10), ?y>, <((!= 0) OR ((= 5) OR (>= 10))), ?x>} +// apply NotExpr.: {, = 10))), ?x>} +// This yields: {<(= 10), ?y>, <((= 0) AND ((!= 5) AND (< 10))), ?x>} +//______________________________________________________________________________ template -constexpr auto getCombineLogic(bool isNegated) { +constexpr auto getMergeFunction(bool isNegated) { if constexpr (std::is_same_v) { - // This follows the principles of De Morgan's law. - // If this is a child of a NotExpression, we have to swap the combination - // procedure (swap AND(&&) and OR(||) respectively). This equals a partial - // application of De Morgan's law. On the resulting PrefilterExpressions, - // NOT is applied in UnaryNegateExpression(Impl). For more details take a - // look at the implementation in NumericUnaryExpressions.cpp - return !isNegated ? mergeChildrenAndLogicImpl - : mergeChildrenOrLogicImpl; + return !isNegated ? mergeChildrenForAndExpressionImpl + : mergeChildrenForOrExpressionImpl; } else { static_assert(std::is_same_v); - return !isNegated ? mergeChildrenOrLogicImpl - : mergeChildrenAndLogicImpl; + return !isNegated ? mergeChildrenForOrExpressionImpl + : mergeChildrenForAndExpressionImpl; } } -} // namespace constructPrefilterExpr + +} // namespace //______________________________________________________________________________ template -requires(isOperation && - prefilterExpressions::check_is_logical_v) +requires isOperation class LogicalBinaryExpressionImpl : public NaryExpression { public: using NaryExpression::NaryExpression; @@ -243,18 +288,24 @@ class LogicalBinaryExpressionImpl : public NaryExpression { auto rightChild = this->getNthChild(1).value()->getPrefilterExpressionForMetadata( isNegated); - return constructPrefilterExpr::getCombineLogic( - isNegated)(std::move(leftChild), std::move(rightChild)); + return constructPrefilterExpr::getMergeFunction( + isNegated)( + leftChild.has_value() ? std::move(leftChild.value()) + : std::vector{}, + rightChild.has_value() ? std::move(rightChild.value()) + : std::vector{}); } }; +} // namespace constructPrefilterExpr + //______________________________________________________________________________ -using AndExpression = LogicalBinaryExpressionImpl< +using AndExpression = constructPrefilterExpr::LogicalBinaryExpressionImpl< prefilterExpressions::AndExpression, Operation<2, FV, SET>>; -using OrExpression = LogicalBinaryExpressionImpl< +using OrExpression = constructPrefilterExpr::LogicalBinaryExpressionImpl< prefilterExpressions::OrExpression, Operation<2, FV, SET>>; diff --git a/src/engine/sparqlExpressions/NumericUnaryExpressions.cpp b/src/engine/sparqlExpressions/NumericUnaryExpressions.cpp index 19a369ffd2..21a606c020 100644 --- a/src/engine/sparqlExpressions/NumericUnaryExpressions.cpp +++ b/src/engine/sparqlExpressions/NumericUnaryExpressions.cpp @@ -31,9 +31,40 @@ class UnaryNegateExpressionImpl : public NaryExpression { using NaryExpression::NaryExpression; std::optional> - getPrefilterExpressionForMetadata( - [[maybe_unused]] bool isNegated) const override { + getPrefilterExpressionForMetadata(bool isNegated) const override { AD_CORRECTNESS_CHECK(this->N == 1); + namespace p = prefilterExpressions; + // The bool flag isNegated (by default false) acts as decision variable + // to select the correct merging procedure while constructing the + // PrefilterExpression(s) for a binary expression (AND or OR). For the + // negation procedure, we apply (partially over multiple Variables) + // De-Morgans law w.r.t. the affected (lower) expression parts, and + // isNegated indicates if we should apply it (or not). For more detailed + // information see NumericBinaryExpressions.cpp. For UnaryNegate we have to + // toggle the value of isNegated to pass the respective negation information + // down the expression tree. + // Remark: + // - Expression sub-tree has an even number of NOT expressions as + // parents: the negation cancels out (isNegated = false). + // - For an uneven number of NOT expressions as parent nodes: the + // sub-tree is actually negated (isNegated = true). + // + // Example - Apply De-Morgans law in two steps (see (1) and (2)) on + // expression !(?x >= IntId(10) || ?y >= IntId(10)) (SparqlExpression) + // With De-Morgan's rule we retrieve: ?x < IntId(10) && ?y < IntId(10) + // + // (1) Merge {<(>= IntId(10)), ?x>} and {<(>= IntId(10)), ?y>} + // with mergeChildrenForAndExpressionImpl (defined in + // NumericBinaryExpressions.cpp), which we select based on isNegated = true + // (first part of De-Morgans law). + // Result (1): {<(>= IntId(10)), ?x>, <(>= IntId(10)), ?y>} + // + // (2) On each pair given the result from + // (1), apply NotExpression (see the following implementation part). + // Step by step for the given example: + // {<(>= IntId(10)), ?x>, <(>= IntId(10)), ?y>} (apply NotExpression) => + // {<(!(>= IntId(10))), ?x>, <(!(>= IntId(10))), ?y>} + // => Result (2): {<(< IntId(10)), ?x>, <(< IntId(10)), ?y>} auto optExprVarVec = this->getNthChild(0).value()->getPrefilterExpressionForMetadata( !isNegated); @@ -41,11 +72,12 @@ class UnaryNegateExpressionImpl : public NaryExpression { return std::nullopt; } std::ranges::for_each( - optExprVarVec.value(), [](PrefilterExprVariablePair& exprVarPair) { - exprVarPair.first = - std::make_unique( - std::move(exprVarPair.first)); + optExprVarVec.value() | std::views::keys, + [](std::unique_ptr& expression) { + expression = + std::make_unique(std::move(expression)); }); + p::detail::checkPropertiesForPrefilterConstruction(optExprVarVec.value()); return optExprVarVec; } }; diff --git a/src/engine/sparqlExpressions/RelationalExpressions.cpp b/src/engine/sparqlExpressions/RelationalExpressions.cpp index 66180b41e5..a7c615aab8 100644 --- a/src/engine/sparqlExpressions/RelationalExpressions.cpp +++ b/src/engine/sparqlExpressions/RelationalExpressions.cpp @@ -414,46 +414,69 @@ template std::optional> RelationalExpression::getPrefilterExpressionForMetadata( [[maybe_unused]] bool isNegated) const { + namespace p = prefilterExpressions; AD_CORRECTNESS_CHECK(children_.size() == 2); const SparqlExpression::Ptr& child0 = children_.at(0); const SparqlExpression::Ptr& child1 = children_.at(1); - const auto checkBinarySearchEvaluable = - [](const SparqlExpression::Ptr& child0, - const SparqlExpression::Ptr& child1) - -> std::optional> { - if (const auto* literalValueId = - dynamic_cast(child0.get())) { - if (const auto* literalVariable = - dynamic_cast(child1.get())) { - return std::make_pair(literalValueId->value(), - literalVariable->value()); + const auto getPrefilterExprVariablePairVec = + [](const Variable& variable, const ValueId valueId, + bool reverse) -> std::vector { + const auto mirroredExpression = + [](const ValueId valueId) -> std::unique_ptr { + using enum Comparison; + switch (comp) { + case LT: + // Id < ?var -> ?var > Id + return std::make_unique(valueId); + case LE: + // Id <= ?var -> ?var >= Id + return std::make_unique(valueId); + case GE: + // Id >= ?var -> ?var <= Id + return std::make_unique(valueId); + case GT: + // Id > ?var -> ?var < Id + return std::make_unique(valueId); + default: + // EQ(==) or NE(!=) + // Given that these two relations are symmetric w.r.t. ?var and Id, + // no swap regarding the relational operator is neccessary. + return std::make_unique>(valueId); } - } - return std::nullopt; - }; - - const auto createPrefilterExprVariablePair = - [](const std::pair& valuePair) - -> std::vector { - std::vector pairVec; - pairVec.emplace_back( - std::make_unique>( - valuePair.first), - valuePair.second); - return pairVec; + }; + std::vector resVec; + resVec.emplace_back(std::make_pair( + reverse ? mirroredExpression(valueId) + : std::make_unique>(valueId), + variable)); + return resVec; }; - // Option 1: child0 is a (constant) reference ValueId, while child1 contains a - // Variable to the respective column on which we want to filter. - const auto& optionalPair1 = checkBinarySearchEvaluable(child0, child1); - if (optionalPair1.has_value()) { - return createPrefilterExprVariablePair(optionalPair1.value()); + // Option 1: + // RelationalExpression containing a VariableExpression as the first child + // and an IdExpression as the second child. + // E.g. for ?x >= 10 (RelationalExpression Sparql), we obtain the following + // pair with PrefilterExpression and Variable: <(>= 10), ?x> + if (const auto* variable = + dynamic_cast(child0.get())) { + if (const auto* valueId = dynamic_cast(child1.get())) { + return getPrefilterExprVariablePairVec(variable->value(), + valueId->value(), false); + } } - // Option 2: child1 is a (constant) reference ValueId, while child0 contains a - // Variable to the respective column on which we want to filter. - const auto& optionalPair2 = checkBinarySearchEvaluable(child1, child0); - if (optionalPair2.has_value()) { - return createPrefilterExprVariablePair(optionalPair2.value()); + // Option 2: + // RelationalExpression containing a IdExpression as the first child and an + // VariableExpression as the second child. + // (1) 10 >= ?x (RelationalExpression Sparql), we obtain the following + // pair with PrefilterExpression and Variable: <(<= 10), ?x> + // (2) 10 != ?x (RelationalExpression Sparql), we obtain the following + // pair with PrefilterExpression and Variable: <(!= 10), ?x> + if (const auto* valueId = dynamic_cast(child0.get())) { + if (const auto* variable = + dynamic_cast(child1.get())) { + return getPrefilterExprVariablePairVec(variable->value(), + valueId->value(), true); + } } return std::nullopt; } diff --git a/src/index/CompressedBlockPrefiltering.cpp b/src/index/CompressedBlockPrefiltering.cpp index 75182fd61f..ab75064463 100644 --- a/src/index/CompressedBlockPrefiltering.cpp +++ b/src/index/CompressedBlockPrefiltering.cpp @@ -4,6 +4,8 @@ #include "index/CompressedBlockPrefiltering.h" +#include + #include "global/ValueIdComparators.h" namespace prefilterExpressions { @@ -347,4 +349,26 @@ std::string NotExpression::info(size_t depth) const { return stream.str(); } +namespace detail { +//______________________________________________________________________________ +void checkPropertiesForPrefilterConstruction( + const std::vector& vec) { + if (!std::ranges::is_sorted(vec, [](const auto& pair1, const auto& pair2) { + return pair1.second < pair2.second; + })) { + throw std::runtime_error( + "The vector must contain the pairs in " + "sorted order w.r.t. Variable value."); + }; + if (auto it = std::ranges::adjacent_find( + vec, [](const auto& pair1, + const auto& pair2) { return pair1.second == pair2.second; }); + it != vec.end()) { + throw std::runtime_error( + "For each relevant Variable must exist exactly one " + " pair."); + } +}; + +} // namespace detail } // namespace prefilterExpressions diff --git a/src/index/CompressedBlockPrefiltering.h b/src/index/CompressedBlockPrefiltering.h index 08fa9092c7..998b302f35 100644 --- a/src/index/CompressedBlockPrefiltering.h +++ b/src/index/CompressedBlockPrefiltering.h @@ -196,19 +196,18 @@ using AndExpression = prefilterExpressions::LogicalExpression< using OrExpression = prefilterExpressions::LogicalExpression< prefilterExpressions::LogicalOperators::OR>; +namespace detail { //______________________________________________________________________________ -// Helpers to check for the respective type of `PrefilterExpression` -template -struct check_is : std::false_type {}; - -template -struct check_is> : std::true_type {}; -template -struct check_is> : std::true_type {}; - -template -constexpr bool check_is_logical_v = check_is::value; -template -constexpr bool check_is_relational_v = check_is::value; - +// Pair containing a `PrefilterExpression` and its corresponding `Variable`. +using PrefilterExprVariablePair = + std::pair, Variable>; +//______________________________________________________________________________ +// Helper function to perform a check regarding the following conditions. +// (1) For each relevant Variable, the vector contains exactly one pair. +// (2) The vector contains those pairs (``) in +// sorted order w.r.t. the Variable value. +void checkPropertiesForPrefilterConstruction( + const std::vector& vec); + +} // namespace detail } // namespace prefilterExpressions diff --git a/src/parser/data/Variable.h b/src/parser/data/Variable.h index ffa39b5e12..a378cfd903 100644 --- a/src/parser/data/Variable.h +++ b/src/parser/data/Variable.h @@ -57,9 +57,7 @@ class Variable { bool operator==(const Variable&) const = default; // The construction of PrefilterExpressions requires a defined < order. - bool operator<(const Variable& other) const { - return this->_name < other._name; - }; + bool operator<(const Variable& other) const { return _name < other._name; }; // Make the type hashable for absl, see // https://abseil.io/docs/cpp/guides/hash. diff --git a/test/CompressedBlockPrefilteringTest.cpp b/test/CompressedBlockPrefilteringTest.cpp index 8529b68df2..80412184fe 100644 --- a/test/CompressedBlockPrefilteringTest.cpp +++ b/test/CompressedBlockPrefilteringTest.cpp @@ -806,15 +806,54 @@ TEST(SparqlExpression, testGetPrefilterExpressionDefault) { TEST(SparqlExpression, getPrefilterExpressionFromSparqlRelational) { const TestDates dt{}; const Variable var = Variable{"?x"}; + // ?x == BooldId(true) (RelationalExpression Sparql) + // expected: <(== Boold(true)), ?x> (PrefilterExpression, Variable) evalAndEqualityCheck(eqSprql(var, BoolId(true)), pr(eq(BoolId(true)), var)); + // For BoolId(true) == ?x we expect the same PrefilterExpression pair. + evalAndEqualityCheck(eqSprql(BoolId(true), var), pr(eq(BoolId(true)), var)); + // ?x != BooldId(true) (RelationalExpression Sparql) + // expected: <(!= Boold(true)), ?x> (PrefilterExpression, Variable) + evalAndEqualityCheck(neqSprql(var, BoolId(false)), + pr(neq(BoolId(false)), var)); + // Same expected value for BoolId(true) != ?x. + evalAndEqualityCheck(neqSprql(BoolId(false), var), + pr(neq(BoolId(false)), var)); + // ?x >= IntId(1) + // expected: <(>= IntId(1)), ?x> evalAndEqualityCheck(geSprql(var, IntId(1)), pr(ge(IntId(1)), var)); + // IntId(1) <= ?x + // expected: <(>= IntId(1)), ?x> + evalAndEqualityCheck(leSprql(IntId(1), var), pr(ge(IntId(1)), var)); + // ?x > IntId(1) + // expected: <(> IntId(1)), ?x> + evalAndEqualityCheck(gtSprql(var, IntId(1)), pr(gt(IntId(1)), var)); + // VocabId(10) != ?x + // expected: <(!= VocabId(10)), ?x> evalAndEqualityCheck(neqSprql(VocabId(10), var), pr(neq(VocabId(10)), var)); + // BlankNodeId(1) > ?x + // expected: <(< BlankNodeId(1)), ?x> evalAndEqualityCheck(geSprql(BlankNodeId(1), var), - pr(ge(BlankNodeId(1)), var)); + pr(le(BlankNodeId(1)), var)); + // ?x < BlankNodeId(1) + // expected: <(< BlankNodeId(1)), ?x> + evalAndEqualityCheck(ltSprql(var, BlankNodeId(1)), + pr(lt(BlankNodeId(1)), var)); + // ?x <= referenceDate1 + // expected: <(<= referenceDate1), ?x> evalAndEqualityCheck(leSprql(var, dt.referenceDate1), pr(le(dt.referenceDate1), var)); + // referenceDate1 >= ?x + // expected: <(<= referenceDate1), ?x> + evalAndEqualityCheck(geSprql(dt.referenceDate1, var), + pr(le(dt.referenceDate1), var)); + // DoubleId(10.2) < ?x + // expected: <(> DoubleId(10.2)), ?x> evalAndEqualityCheck(ltSprql(DoubleId(10.2), var), - pr(lt(DoubleId(10.2)), var)); + pr(gt(DoubleId(10.2)), var)); + // ?x > DoubleId(10.2) + // expected: <(> DoubleId(10.2)), ?x> + evalAndEqualityCheck(gtSprql(var, DoubleId(10.2)), + pr(gt(DoubleId(10.2)), var)); } //______________________________________________________________________________ @@ -984,7 +1023,7 @@ TEST(SparqlExpression, getPrefilterExpressionsToComplexSparqlExpressions) { evalAndEqualityCheck( orSprqlExpr(geSprql(varX, IntId(10)), andSprqlExpr(geSprql(varX, IntId(-10)), - ltSprql(DoubleId(0.00), varX))), + ltSprql(varX, DoubleId(0.00)))), pr(orExpr(ge(IntId(10)), andExpr(ge(IntId(-10)), lt(DoubleId(0.00)))), varX)); // !(!(?x >= 10) OR !!(?x >= -10 AND ?x < 0.00)) @@ -994,7 +1033,7 @@ TEST(SparqlExpression, getPrefilterExpressionsToComplexSparqlExpressions) { notSprqlExpr(orSprqlExpr( notSprqlExpr(geSprql(varX, IntId(10))), notSprqlExpr(notSprqlExpr(andSprqlExpr( - geSprql(varX, IntId(-10)), ltSprql(DoubleId(0.00), varX)))))), + geSprql(varX, IntId(-10)), ltSprql(varX, DoubleId(0.00))))))), pr(notExpr(orExpr( notExpr(ge(IntId(10))), notExpr(notExpr(andExpr(ge(IntId(-10)), lt(DoubleId(0.00))))))), @@ -1113,3 +1152,39 @@ TEST(SparqlExpression, getEmptyPrefilterForMoreComplexSparqlExpressions) { andSprqlExpr(eqSprql(varZ, BoolId(true)), eqSprql(Variable{"?country"}, VocabId(20)))))))); } + +// Test that the conditions required for a correct merge of child +// PrefilterExpressions are properly checked during the PrefilterExpression +// construction procedure. This check is applied in the SparqlExpression (for +// NOT, AND and OR) counter-expressions, while constructing their corresponding +// PrefilterExpression. +//______________________________________________________________________________ +TEST(SparqlExpression, checkPropertiesForPrefilterConstruction) { + namespace pd = prefilterExpressions::detail; + const Variable varX = Variable{"?x"}; + const Variable varY = Variable{"?y"}; + const Variable varZ = Variable{"?z"}; + const Variable varW = Variable{"?w"}; + std::vector vec{}; + vec.push_back(pr(andExpr(lt(IntId(5)), gt(DoubleId(-0.01))), varX)); + vec.push_back(pr(gt(VocabId(0)), varY)); + EXPECT_NO_THROW(pd::checkPropertiesForPrefilterConstruction(vec)); + vec.push_back(pr(eq(VocabId(33)), varZ)); + EXPECT_NO_THROW(pd::checkPropertiesForPrefilterConstruction(vec)); + // Add a pair with duplicate Variable. + vec.push_back(pr(gt(VocabId(0)), varZ)); + AD_EXPECT_THROW_WITH_MESSAGE( + pd::checkPropertiesForPrefilterConstruction(vec), + ::testing::HasSubstr("For each relevant Variable must exist exactly " + "one pair.")); + // Remove the last two pairs and add a pair + // which violates the order on Variable(s). + vec.pop_back(); + vec.pop_back(); + vec.push_back(pr(eq(VocabId(0)), varW)); + AD_EXPECT_THROW_WITH_MESSAGE( + pd::checkPropertiesForPrefilterConstruction(vec), + ::testing::HasSubstr( + "The vector must contain the " + "pairs in sorted order w.r.t. Variable value.")); +} From d65acc34392e92c52979c0eef68024a8f63a83e0 Mon Sep 17 00:00:00 2001 From: realHannes Date: Wed, 30 Oct 2024 16:05:46 +0100 Subject: [PATCH 45/50] correction for codespell check --- src/engine/sparqlExpressions/NumericBinaryExpressions.cpp | 2 +- src/engine/sparqlExpressions/RelationalExpressions.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/engine/sparqlExpressions/NumericBinaryExpressions.cpp b/src/engine/sparqlExpressions/NumericBinaryExpressions.cpp index 7ffa773a53..8afb6b7593 100644 --- a/src/engine/sparqlExpressions/NumericBinaryExpressions.cpp +++ b/src/engine/sparqlExpressions/NumericBinaryExpressions.cpp @@ -73,7 +73,7 @@ namespace { // MERGE AND CONJUNCTION // _____________________________________________________________________________ // For our pre-filtering logic over index scans, we need exactly one -// corresponding PrefilterExpression for each relavant Variable. Thus, if the +// corresponding PrefilterExpression for each relevant Variable. Thus, if the // left and right child contain a PrefilterExpression w.r.t. the same Variable, // combine them here for an AND conjunction. In the following, three examples // are given on how the following function merges the content of the left and diff --git a/src/engine/sparqlExpressions/RelationalExpressions.cpp b/src/engine/sparqlExpressions/RelationalExpressions.cpp index a7c615aab8..4324a2cc06 100644 --- a/src/engine/sparqlExpressions/RelationalExpressions.cpp +++ b/src/engine/sparqlExpressions/RelationalExpressions.cpp @@ -441,7 +441,7 @@ RelationalExpression::getPrefilterExpressionForMetadata( default: // EQ(==) or NE(!=) // Given that these two relations are symmetric w.r.t. ?var and Id, - // no swap regarding the relational operator is neccessary. + // no swap regarding the relational operator is necessary. return std::make_unique>(valueId); } }; From f85a96f11564be55bffefd062d149d4392f3e53d Mon Sep 17 00:00:00 2001 From: realHannes Date: Wed, 30 Oct 2024 16:08:08 +0100 Subject: [PATCH 46/50] fix for codespell --- test/CompressedBlockPrefilteringTest.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/CompressedBlockPrefilteringTest.cpp b/test/CompressedBlockPrefilteringTest.cpp index 80412184fe..b9d17b3713 100644 --- a/test/CompressedBlockPrefilteringTest.cpp +++ b/test/CompressedBlockPrefilteringTest.cpp @@ -807,12 +807,12 @@ TEST(SparqlExpression, getPrefilterExpressionFromSparqlRelational) { const TestDates dt{}; const Variable var = Variable{"?x"}; // ?x == BooldId(true) (RelationalExpression Sparql) - // expected: <(== Boold(true)), ?x> (PrefilterExpression, Variable) + // expected: <(== BoolId(true)), ?x> (PrefilterExpression, Variable) evalAndEqualityCheck(eqSprql(var, BoolId(true)), pr(eq(BoolId(true)), var)); // For BoolId(true) == ?x we expect the same PrefilterExpression pair. evalAndEqualityCheck(eqSprql(BoolId(true), var), pr(eq(BoolId(true)), var)); // ?x != BooldId(true) (RelationalExpression Sparql) - // expected: <(!= Boold(true)), ?x> (PrefilterExpression, Variable) + // expected: <(!= BoolId(true)), ?x> (PrefilterExpression, Variable) evalAndEqualityCheck(neqSprql(var, BoolId(false)), pr(neq(BoolId(false)), var)); // Same expected value for BoolId(true) != ?x. From 5f4e586fb7c6d7689ede18cf218fee564ad57d1f Mon Sep 17 00:00:00 2001 From: realHannes Date: Thu, 31 Oct 2024 13:10:58 +0100 Subject: [PATCH 47/50] remove optionality (std::optional) for return value --- .../NumericBinaryExpressions.cpp | 26 +--- .../NumericUnaryExpressions.cpp | 15 +-- .../RelationalExpressions.cpp | 4 +- .../sparqlExpressions/RelationalExpressions.h | 3 +- .../sparqlExpressions/SparqlExpression.cpp | 4 +- .../sparqlExpressions/SparqlExpression.h | 2 +- .../SparqlExpressionPimpl.cpp | 2 +- .../sparqlExpressions/SparqlExpressionPimpl.h | 4 +- test/CompressedBlockPrefilteringTest.cpp | 127 ++++++++---------- 9 files changed, 73 insertions(+), 114 deletions(-) diff --git a/src/engine/sparqlExpressions/NumericBinaryExpressions.cpp b/src/engine/sparqlExpressions/NumericBinaryExpressions.cpp index 8afb6b7593..7f8319e17c 100644 --- a/src/engine/sparqlExpressions/NumericBinaryExpressions.cpp +++ b/src/engine/sparqlExpressions/NumericBinaryExpressions.cpp @@ -99,8 +99,7 @@ namespace { // Returns {<((>= 10 AND <= 20) AND != 15), ?x>, <(= 10), ?y>} //______________________________________________________________________________ template -std::optional> -mergeChildrenForAndExpressionImpl( +std::vector mergeChildrenForAndExpressionImpl( std::vector&& leftChild, std::vector&& rightChild) { namespace pd = prefilterExpressions::detail; @@ -131,11 +130,6 @@ mergeChildrenForAndExpressionImpl( } std::ranges::move(itLeft, leftChild.end(), std::back_inserter(resPairs)); std::ranges::move(itRight, rightChild.end(), std::back_inserter(resPairs)); - if (resPairs.empty()) { - // No PrefilterExpression(s) available with this conjunction merge on the - // contents of left and right child. - return std::nullopt; - } pd::checkPropertiesForPrefilterConstruction(resPairs); return resPairs; } @@ -175,8 +169,7 @@ mergeChildrenForAndExpressionImpl( // PrefilterExpresion {<(>= 10 OR <= 0), ?x>}. //______________________________________________________________________________ template -std::optional> -mergeChildrenForOrExpressionImpl( +std::vector mergeChildrenForOrExpressionImpl( std::vector&& leftChild, std::vector&& rightChild) { namespace pd = prefilterExpressions::detail; @@ -200,11 +193,6 @@ mergeChildrenForOrExpressionImpl( ++itRight; } } - if (resPairs.empty()) { - // No PrefilterExpression(s) available with this conjunction merge on the - // contents of left and right child. - return std::nullopt; - } pd::checkPropertiesForPrefilterConstruction(resPairs); return resPairs; } @@ -279,8 +267,8 @@ class LogicalBinaryExpressionImpl : public NaryExpression { public: using NaryExpression::NaryExpression; - std::optional> - getPrefilterExpressionForMetadata(bool isNegated) const override { + std::vector getPrefilterExpressionForMetadata( + bool isNegated) const override { AD_CORRECTNESS_CHECK(this->N == 2); auto leftChild = this->getNthChild(0).value()->getPrefilterExpressionForMetadata( @@ -289,11 +277,7 @@ class LogicalBinaryExpressionImpl : public NaryExpression { this->getNthChild(1).value()->getPrefilterExpressionForMetadata( isNegated); return constructPrefilterExpr::getMergeFunction( - isNegated)( - leftChild.has_value() ? std::move(leftChild.value()) - : std::vector{}, - rightChild.has_value() ? std::move(rightChild.value()) - : std::vector{}); + isNegated)(std::move(leftChild), std::move(rightChild)); } }; diff --git a/src/engine/sparqlExpressions/NumericUnaryExpressions.cpp b/src/engine/sparqlExpressions/NumericUnaryExpressions.cpp index 21a606c020..cb72da969a 100644 --- a/src/engine/sparqlExpressions/NumericUnaryExpressions.cpp +++ b/src/engine/sparqlExpressions/NumericUnaryExpressions.cpp @@ -30,8 +30,8 @@ class UnaryNegateExpressionImpl : public NaryExpression { public: using NaryExpression::NaryExpression; - std::optional> - getPrefilterExpressionForMetadata(bool isNegated) const override { + std::vector getPrefilterExpressionForMetadata( + bool isNegated) const override { AD_CORRECTNESS_CHECK(this->N == 1); namespace p = prefilterExpressions; // The bool flag isNegated (by default false) acts as decision variable @@ -65,20 +65,17 @@ class UnaryNegateExpressionImpl : public NaryExpression { // {<(>= IntId(10)), ?x>, <(>= IntId(10)), ?y>} (apply NotExpression) => // {<(!(>= IntId(10))), ?x>, <(!(>= IntId(10))), ?y>} // => Result (2): {<(< IntId(10)), ?x>, <(< IntId(10)), ?y>} - auto optExprVarVec = + auto child = this->getNthChild(0).value()->getPrefilterExpressionForMetadata( !isNegated); - if (!optExprVarVec.has_value()) { - return std::nullopt; - } std::ranges::for_each( - optExprVarVec.value() | std::views::keys, + child | std::views::keys, [](std::unique_ptr& expression) { expression = std::make_unique(std::move(expression)); }); - p::detail::checkPropertiesForPrefilterConstruction(optExprVarVec.value()); - return optExprVarVec; + p::detail::checkPropertiesForPrefilterConstruction(child); + return child; } }; diff --git a/src/engine/sparqlExpressions/RelationalExpressions.cpp b/src/engine/sparqlExpressions/RelationalExpressions.cpp index 4324a2cc06..fde2ad1490 100644 --- a/src/engine/sparqlExpressions/RelationalExpressions.cpp +++ b/src/engine/sparqlExpressions/RelationalExpressions.cpp @@ -411,7 +411,7 @@ ExpressionResult InExpression::evaluate( // _____________________________________________________________________________ template -std::optional> +std::vector RelationalExpression::getPrefilterExpressionForMetadata( [[maybe_unused]] bool isNegated) const { namespace p = prefilterExpressions; @@ -478,7 +478,7 @@ RelationalExpression::getPrefilterExpressionForMetadata( valueId->value(), true); } } - return std::nullopt; + return {}; } // _____________________________________________________________________________ diff --git a/src/engine/sparqlExpressions/RelationalExpressions.h b/src/engine/sparqlExpressions/RelationalExpressions.h index 6448435c0c..d406afc936 100644 --- a/src/engine/sparqlExpressions/RelationalExpressions.h +++ b/src/engine/sparqlExpressions/RelationalExpressions.h @@ -42,8 +42,7 @@ class RelationalExpression : public SparqlExpression { // corresponding `PrefilterExpression` for the pre-filtering procedure on // `CompressedBlockMetadata`. In addition we return the `Variable` that // corresponds to the sorted column. - std::optional> - getPrefilterExpressionForMetadata( + std::vector getPrefilterExpressionForMetadata( [[maybe_unused]] bool isNegated) const override; // These expressions are typically used inside `FILTER` clauses, so we need diff --git a/src/engine/sparqlExpressions/SparqlExpression.cpp b/src/engine/sparqlExpressions/SparqlExpression.cpp index 4482bf7182..dab49835a8 100644 --- a/src/engine/sparqlExpressions/SparqlExpression.cpp +++ b/src/engine/sparqlExpressions/SparqlExpression.cpp @@ -109,10 +109,10 @@ Estimates SparqlExpression::getEstimatesForFilterExpression( } // _____________________________________________________________________________ -std::optional> +std::vector SparqlExpression::getPrefilterExpressionForMetadata( [[maybe_unused]] bool isNegated) const { - return std::nullopt; + return {}; }; // _____________________________________________________________________________ diff --git a/src/engine/sparqlExpressions/SparqlExpression.h b/src/engine/sparqlExpressions/SparqlExpression.h index a864a809b5..83b3ff89ad 100644 --- a/src/engine/sparqlExpressions/SparqlExpression.h +++ b/src/engine/sparqlExpressions/SparqlExpression.h @@ -101,7 +101,7 @@ class SparqlExpression { // column is required to be sorted, and hence is as a consequence also binary // evaluable regarding the relational (e.g. `>=`) / logical (`&&`, `||` and // `!`) expressions. - virtual std::optional> + virtual std::vector getPrefilterExpressionForMetadata( [[maybe_unused]] bool isNegated = false) const; diff --git a/src/engine/sparqlExpressions/SparqlExpressionPimpl.cpp b/src/engine/sparqlExpressions/SparqlExpressionPimpl.cpp index 8a05ec147d..39dd932231 100644 --- a/src/engine/sparqlExpressions/SparqlExpressionPimpl.cpp +++ b/src/engine/sparqlExpressions/SparqlExpressionPimpl.cpp @@ -93,7 +93,7 @@ auto SparqlExpressionPimpl::getEstimatesForFilterExpression( } //_____________________________________________________________________________ -std::optional> +std::vector SparqlExpressionPimpl::getPrefilterExpressionForMetadata(bool isNegated) const { return _pimpl->getPrefilterExpressionForMetadata(isNegated); } diff --git a/src/engine/sparqlExpressions/SparqlExpressionPimpl.h b/src/engine/sparqlExpressions/SparqlExpressionPimpl.h index 80a7766260..48ee2b2407 100644 --- a/src/engine/sparqlExpressions/SparqlExpressionPimpl.h +++ b/src/engine/sparqlExpressions/SparqlExpressionPimpl.h @@ -118,8 +118,8 @@ class SparqlExpressionPimpl { uint64_t inputSizeEstimate, const std::optional& primarySortKeyVariable); - std::optional> - getPrefilterExpressionForMetadata(bool isNegated = false) const; + std::vector getPrefilterExpressionForMetadata( + bool isNegated = false) const; SparqlExpression* getPimpl() { return _pimpl.get(); } [[nodiscard]] const SparqlExpression* getPimpl() const { diff --git a/test/CompressedBlockPrefilteringTest.cpp b/test/CompressedBlockPrefilteringTest.cpp index b9d17b3713..995a627791 100644 --- a/test/CompressedBlockPrefilteringTest.cpp +++ b/test/CompressedBlockPrefilteringTest.cpp @@ -576,7 +576,6 @@ TEST_F(TestPrefilterExprOnBlockMetadata, testWithOneBlockMetadataValue) { namespace { using namespace sparqlExpression; -using optPrefilterVec = std::optional>; using Literal = ad_utility::triple_component::Literal; using Iri = ad_utility::triple_component::Iri; using MakeBinarySprqlExprFunc = std::function& result, - const std::vector& expected) -> bool { - if (result.size() != expected.size()) { - ADD_FAILURE() << "Expected vectors (result vs. expected) of equal length"; - return false; - }; + const std::vector& expected) -> void { + ASSERT_EQ(result.size(), expected.size()); const auto isEqualImpl = [](const PrefilterExprVariablePair& resPair, const PrefilterExprVariablePair& expPair) { if (*resPair.first != *expPair.first || resPair.second != expPair.second) { @@ -687,46 +684,26 @@ const auto checkVectorPrefilterExprVariablePair = } return true; }; - return std::equal(result.begin(), result.end(), expected.begin(), - isEqualImpl); -}; - -//______________________________________________________________________________ -const auto checkEqualityPrefilterMethodT = - [](const optPrefilterVec& result, const optPrefilterVec& expected) { - const auto testEquality = - [](const optPrefilterVec& result, const optPrefilterVec& expected) { - if (!result.has_value() && !expected.has_value()) { - return true; - } else if (result.has_value() && expected.has_value()) { - return checkVectorPrefilterExprVariablePair(result.value(), - expected.value()); - } else { - ADD_FAILURE() - << "Expected both values to either contain a value or to be " - "std::optional."; - return false; - } - }; - ASSERT_TRUE(testEquality(result, expected)); - }; - -//______________________________________________________________________________ -const auto evalToEmptyCheck = [](std::unique_ptr sparqlExpr) { - std::vector prefilterVarPair; - checkEqualityPrefilterMethodT(sparqlExpr->getPrefilterExpressionForMetadata(), - std::nullopt); + ASSERT_TRUE( + std::equal(result.begin(), result.end(), expected.begin(), isEqualImpl)); }; //______________________________________________________________________________ +// `evalAndEqualityCheck` evaluates the provided `SparqlExpression` and checks +// in the following if the resulting vector contains the same +// `` pairs in the correct order. If no +// `` pair is provided, the expected value for +// the `SparqlExpression` is an empty vector. const auto evalAndEqualityCheck = [](std::unique_ptr sparqlExpr, std::convertible_to auto&&... prefilterArgs) { - std::vector prefilterVarPair; - (prefilterVarPair.emplace_back( - std::forward(prefilterArgs)), - ...); - checkEqualityPrefilterMethodT( + std::vector prefilterVarPair = {}; + if constexpr (sizeof...(prefilterArgs) > 0) { + (prefilterVarPair.emplace_back( + std::forward(prefilterArgs)), + ...); + } + equalityCheckPrefilterVectors( sparqlExpr->getPrefilterExpressionForMetadata(), std::move(prefilterVarPair)); }; @@ -787,15 +764,17 @@ TEST(PrefilterExpression, checkPrintFormattedPrefilterExpression) { // Test coverage for the default implementation of // getPrefilterExpressionForMetadata. TEST(SparqlExpression, testGetPrefilterExpressionDefault) { - evalToEmptyCheck(makeUnaryMinusExpression(makeLiteralSparqlExpr(IntId(0)))); - evalToEmptyCheck(makeMultiplyExpression(makeLiteralSparqlExpr(DoubleId(11)), - makeLiteralSparqlExpr(DoubleId(3)))); - evalToEmptyCheck( + evalAndEqualityCheck( + makeUnaryMinusExpression(makeLiteralSparqlExpr(IntId(0)))); + evalAndEqualityCheck(makeMultiplyExpression( + makeLiteralSparqlExpr(DoubleId(11)), makeLiteralSparqlExpr(DoubleId(3)))); + evalAndEqualityCheck( makeStrEndsExpression(makeLiteralSparqlExpr(L("\"Freiburg\"")), makeLiteralSparqlExpr(L("\"burg\"")))); - evalToEmptyCheck(makeIsIriExpression(makeLiteralSparqlExpr(I("")))); - evalToEmptyCheck(makeLogExpression(makeLiteralSparqlExpr(DoubleId(8)))); - evalToEmptyCheck( + evalAndEqualityCheck( + makeIsIriExpression(makeLiteralSparqlExpr(I("")))); + evalAndEqualityCheck(makeLogExpression(makeLiteralSparqlExpr(DoubleId(8)))); + evalAndEqualityCheck( makeStrIriDtExpression(makeLiteralSparqlExpr(L("\"test\"")), makeLiteralSparqlExpr(I("")))); } @@ -1055,17 +1034,17 @@ TEST(SparqlExpression, getEmptyPrefilterFromSparqlRelational) { const Variable var = Variable{"?x"}; const Iri iri = I(""); const Literal lit = L("\"lit\""); - evalToEmptyCheck(leSprql(var, var)); - evalToEmptyCheck(neqSprql(iri, var)); - evalToEmptyCheck(eqSprql(var, iri)); - evalToEmptyCheck(neqSprql(IntId(10), DoubleId(23.3))); - evalToEmptyCheck(gtSprql(DoubleId(10), lit)); - evalToEmptyCheck(ltSprql(VocabId(10), BoolId(10))); - evalToEmptyCheck(geSprql(lit, lit)); - evalToEmptyCheck(eqSprql(iri, iri)); - evalToEmptyCheck(orSprqlExpr(eqSprql(var, var), gtSprql(var, IntId(0)))); - evalToEmptyCheck(orSprqlExpr(eqSprql(var, var), gtSprql(var, var))); - evalToEmptyCheck(andSprqlExpr(eqSprql(var, var), gtSprql(var, var))); + evalAndEqualityCheck(leSprql(var, var)); + evalAndEqualityCheck(neqSprql(iri, var)); + evalAndEqualityCheck(eqSprql(var, iri)); + evalAndEqualityCheck(neqSprql(IntId(10), DoubleId(23.3))); + evalAndEqualityCheck(gtSprql(DoubleId(10), lit)); + evalAndEqualityCheck(ltSprql(VocabId(10), BoolId(10))); + evalAndEqualityCheck(geSprql(lit, lit)); + evalAndEqualityCheck(eqSprql(iri, iri)); + evalAndEqualityCheck(orSprqlExpr(eqSprql(var, var), gtSprql(var, IntId(0)))); + evalAndEqualityCheck(orSprqlExpr(eqSprql(var, var), gtSprql(var, var))); + evalAndEqualityCheck(andSprqlExpr(eqSprql(var, var), gtSprql(var, var))); } //______________________________________________________________________________ @@ -1076,68 +1055,68 @@ TEST(SparqlExpression, getEmptyPrefilterForMoreComplexSparqlExpressions) { const Variable varY = Variable{"?y"}; const Variable varZ = Variable{"?z"}; // ?x <= 10.00 OR ?y > 10 - evalToEmptyCheck( + evalAndEqualityCheck( orSprqlExpr(leSprql(DoubleId(10), varX), gtSprql(IntId(10), varY))); // ?x >= VocabId(23) OR ?z == VocabId(1) - evalToEmptyCheck( + evalAndEqualityCheck( orSprqlExpr(geSprql(varX, VocabId(23)), eqSprql(varZ, VocabId(1)))); // (?x < VocabId(10) OR ?z <= VocabId(4)) OR ?z != 5.00 - evalToEmptyCheck(orSprqlExpr( + evalAndEqualityCheck(orSprqlExpr( orSprqlExpr(ltSprql(varX, VocabId(10)), leSprql(VocabId(4), varZ)), neqSprql(varZ, DoubleId(5)))); // !(?z > 10.20 AND ?x < 0.001) // is equal to // ?z <= 10.20 OR ?x >= 0.001 - evalToEmptyCheck(notSprqlExpr(andSprqlExpr(gtSprql(DoubleId(10.2), varZ), - ltSprql(DoubleId(0.001), varX)))); + evalAndEqualityCheck(notSprqlExpr(andSprqlExpr( + gtSprql(DoubleId(10.2), varZ), ltSprql(DoubleId(0.001), varX)))); // !(?x > 10.20 AND ?z != VocabId(22)) // is equal to // ?x <= 10.20 OR ?z == VocabId(22) - evalToEmptyCheck(notSprqlExpr(andSprqlExpr(gtSprql(DoubleId(10.2), varX), - neqSprql(VocabId(22), varZ)))); + evalAndEqualityCheck(notSprqlExpr(andSprqlExpr(gtSprql(DoubleId(10.2), varX), + neqSprql(VocabId(22), varZ)))); // !(!((?x < VocabId(10) OR ?x <= VocabId(4)) OR ?z != 5.00)) // is equal to // (?x < VocabId(10) OR ?x <= VocabId(4)) OR ?z != 5.00 - evalToEmptyCheck(notSprqlExpr(notSprqlExpr(orSprqlExpr( + evalAndEqualityCheck(notSprqlExpr(notSprqlExpr(orSprqlExpr( orSprqlExpr(ltSprql(varX, VocabId(10)), leSprql(VocabId(4), varX)), neqSprql(varZ, DoubleId(5)))))); // !(?x != 10 AND !(?y >= 10.00 OR ?z <= 10)) // is equal to // ?x == 10 OR ?y >= 10.00 OR ?z <= 10 - evalToEmptyCheck(notSprqlExpr( + evalAndEqualityCheck(notSprqlExpr( andSprqlExpr(neqSprql(varX, IntId(10)), notSprqlExpr(orSprqlExpr(geSprql(varY, DoubleId(10.00)), leSprql(varZ, IntId(10))))))); // !((?x != 10 AND ?z != 10) AND (?y == 10 AND ?x >= 20)) // is equal to //?x == 10 OR ?z == 10 OR ?y != 10 OR ?x < 20 - evalToEmptyCheck(notSprqlExpr(andSprqlExpr( + evalAndEqualityCheck(notSprqlExpr(andSprqlExpr( andSprqlExpr(neqSprql(varX, IntId(10)), neqSprql(varZ, IntId(10))), andSprqlExpr(eqSprql(varY, IntId(10)), geSprql(varX, IntId(20)))))); // !(?z >= 40 AND (?z != 10.00 AND ?y != VocabId(1))) // is equal to // ?z <= 40 OR ?z == 10.00 OR ?y == VocabId(1) - evalToEmptyCheck(notSprqlExpr(andSprqlExpr( + evalAndEqualityCheck(notSprqlExpr(andSprqlExpr( geSprql(varZ, IntId(40)), andSprqlExpr(neqSprql(varZ, DoubleId(10.00)), neqSprql(varY, VocabId(1)))))); // ?z <= true OR !(?x == 10 AND ?y == 10) // is equal to // ?z <= true OR ?x != 10 OR ?y != 10 - evalToEmptyCheck( + evalAndEqualityCheck( orSprqlExpr(leSprql(varZ, BoolId(true)), notSprqlExpr(andSprqlExpr(eqSprql(varX, IntId(10)), eqSprql(IntId(10), varY))))); // !(!(?z <= true OR !(?x == 10 AND ?y == 10))) // is equal to // ?z <= true OR ?x != 10 OR ?y != 10 - evalToEmptyCheck(notSprqlExpr(notSprqlExpr( + evalAndEqualityCheck(notSprqlExpr(notSprqlExpr( orSprqlExpr(leSprql(varZ, BoolId(true)), notSprqlExpr(andSprqlExpr(eqSprql(varX, IntId(10)), eqSprql(IntId(10), varY))))))); // !(!(?x != 10 OR !(?y >= 10.00 AND ?z <= 10))) // is equal to // ?x != 10 OR ?y < 10.00 OR ?z > 10 - evalToEmptyCheck(notSprqlExpr(notSprqlExpr( + evalAndEqualityCheck(notSprqlExpr(notSprqlExpr( orSprqlExpr(neqSprql(varX, IntId(10)), notSprqlExpr(andSprqlExpr(geSprql(varY, DoubleId(10.00)), leSprql(varZ, IntId(10)))))))); @@ -1145,7 +1124,7 @@ TEST(SparqlExpression, getEmptyPrefilterForMoreComplexSparqlExpressions) { // VocabId(20)))) // is equal to // ?x == VocabId(10) OR ?y >= 25 OR ?z == true AND ?country == VocabId(20) - evalToEmptyCheck(notSprqlExpr(andSprqlExpr( + evalAndEqualityCheck(notSprqlExpr(andSprqlExpr( notSprqlExpr( orSprqlExpr(eqSprql(varX, VocabId(10)), geSprql(varY, IntId(25)))), notSprqlExpr(notSprqlExpr( From c06de607c9408d1d159bb3c616567062fa1c61fd Mon Sep 17 00:00:00 2001 From: realHannes Date: Mon, 4 Nov 2024 20:39:03 +0100 Subject: [PATCH 48/50] implement proposed improvements (4) --- .../RelationalExpressions.cpp | 104 +++++++++--------- src/index/CompressedBlockPrefiltering.cpp | 67 ++++++++--- src/index/CompressedBlockPrefiltering.h | 12 +- test/CompressedBlockPrefilteringTest.cpp | 20 +++- 4 files changed, 125 insertions(+), 78 deletions(-) diff --git a/src/engine/sparqlExpressions/RelationalExpressions.cpp b/src/engine/sparqlExpressions/RelationalExpressions.cpp index fde2ad1490..de035dce71 100644 --- a/src/engine/sparqlExpressions/RelationalExpressions.cpp +++ b/src/engine/sparqlExpressions/RelationalExpressions.cpp @@ -416,69 +416,75 @@ RelationalExpression::getPrefilterExpressionForMetadata( [[maybe_unused]] bool isNegated) const { namespace p = prefilterExpressions; AD_CORRECTNESS_CHECK(children_.size() == 2); - const SparqlExpression::Ptr& child0 = children_.at(0); - const SparqlExpression::Ptr& child1 = children_.at(1); - - const auto getPrefilterExprVariablePairVec = - [](const Variable& variable, const ValueId valueId, - bool reverse) -> std::vector { - const auto mirroredExpression = - [](const ValueId valueId) -> std::unique_ptr { - using enum Comparison; - switch (comp) { - case LT: - // Id < ?var -> ?var > Id - return std::make_unique(valueId); - case LE: - // Id <= ?var -> ?var >= Id - return std::make_unique(valueId); - case GE: - // Id >= ?var -> ?var <= Id - return std::make_unique(valueId); - case GT: - // Id > ?var -> ?var < Id - return std::make_unique(valueId); - default: - // EQ(==) or NE(!=) - // Given that these two relations are symmetric w.r.t. ?var and Id, - // no swap regarding the relational operator is necessary. - return std::make_unique>(valueId); + const SparqlExpression* child0 = children_.at(0).get(); + const SparqlExpression* child1 = children_.at(1).get(); + + const auto mirroredExpression = + [](const ValueId valueId) -> std::unique_ptr { + using enum Comparison; + switch (comp) { + case LT: + // Id < ?var -> ?var > Id + return std::make_unique(valueId); + case LE: + // Id <= ?var -> ?var >= Id + return std::make_unique(valueId); + case GE: + // Id >= ?var -> ?var <= Id + return std::make_unique(valueId); + case GT: + // Id > ?var -> ?var < Id + return std::make_unique(valueId); + case EQ: + case NE: + // EQ(==) or NE(!=) + // Given that these two relations are symmetric w.r.t. ?var and Id, + // no swap regarding the relational operator is necessary. + return std::make_unique>(valueId); + default: + // Unchecked / new valueIdComparators::Comparison case. + AD_FAIL(); + } + }; + + const auto tryGetPrefilterExprVariablePairVec = + [&mirroredExpression]( + const SparqlExpression* child0, const SparqlExpression* child1, + bool reversed) -> std::vector { + std::vector resVec{}; + if (const auto* variable = + dynamic_cast(child0)) { + if (const auto* valueId = dynamic_cast(child1)) { + resVec.emplace_back(std::make_pair( + reversed ? mirroredExpression(valueId->value()) + : std::make_unique>( + valueId->value()), + variable->value())); } - }; - std::vector resVec; - resVec.emplace_back(std::make_pair( - reverse ? mirroredExpression(valueId) - : std::make_unique>(valueId), - variable)); + } return resVec; }; + std::vector resVec; // Option 1: // RelationalExpression containing a VariableExpression as the first child // and an IdExpression as the second child. // E.g. for ?x >= 10 (RelationalExpression Sparql), we obtain the following // pair with PrefilterExpression and Variable: <(>= 10), ?x> - if (const auto* variable = - dynamic_cast(child0.get())) { - if (const auto* valueId = dynamic_cast(child1.get())) { - return getPrefilterExprVariablePairVec(variable->value(), - valueId->value(), false); - } + resVec = tryGetPrefilterExprVariablePairVec(child0, child1, false); + if (!resVec.empty()) { + return resVec; } // Option 2: - // RelationalExpression containing a IdExpression as the first child and an + // RelationalExpression containing an IdExpression as the first child and a // VariableExpression as the second child. // (1) 10 >= ?x (RelationalExpression Sparql), we obtain the following // pair with PrefilterExpression and Variable: <(<= 10), ?x> // (2) 10 != ?x (RelationalExpression Sparql), we obtain the following - // pair with PrefilterExpression and Variable: <(!= 10), ?x> - if (const auto* valueId = dynamic_cast(child0.get())) { - if (const auto* variable = - dynamic_cast(child1.get())) { - return getPrefilterExprVariablePairVec(variable->value(), - valueId->value(), true); - } - } - return {}; + // pair with PrefilterExpression and Variable: <(!= 10), ?x>; + // Option 3: + // If no PrefilterExpressions could be constructed for this + // RelationalExpression, just return the empty vector. + return tryGetPrefilterExprVariablePairVec(child1, child0, true); } // _____________________________________________________________________________ diff --git a/src/index/CompressedBlockPrefiltering.cpp b/src/index/CompressedBlockPrefiltering.cpp index ab75064463..51e20d6bc3 100644 --- a/src/index/CompressedBlockPrefiltering.cpp +++ b/src/index/CompressedBlockPrefiltering.cpp @@ -123,6 +123,42 @@ static auto getSetUnion(const std::vector& blocks1, return mergedVectors; } +//______________________________________________________________________________ +// Return `CompOp`s as string. +static std::string getRelationalOpStr(const CompOp relOp) { + using enum CompOp; + switch (relOp) { + case LT: + return "LT(<)"; + case LE: + return "LE(<=)"; + case EQ: + return "EQ(=)"; + case NE: + return "NE(!=)"; + case GE: + return "GE(>=)"; + case GT: + return "GT(>)"; + default: + AD_FAIL(); + } +} + +//______________________________________________________________________________ +// Return `LogicalOperator`s as string. +static std::string getLogicalOpStr(const LogicalOperator logOp) { + using enum LogicalOperator; + switch (logOp) { + case AND: + return "AND(&&)"; + case OR: + return "OR(||)"; + default: + AD_FAIL(); + } +} + // SECTION PREFILTER EXPRESSION (BASE CLASS) //______________________________________________________________________________ std::vector PrefilterExpression::evaluate( @@ -246,17 +282,17 @@ template std::string RelationalExpression::info( [[maybe_unused]] size_t depth) const { std::stringstream stream; - stream << "Prefilter RelationalExpression<" << static_cast(Comparison) + stream << "Prefilter RelationalExpression<" << getRelationalOpStr(Comparison) << ">\nValueId: " << referenceId_ << std::endl; return stream.str(); }; // SECTION LOGICAL OPERATIONS //______________________________________________________________________________ -template +template std::unique_ptr LogicalExpression::logicalComplement() const { - using enum LogicalOperators; + using enum LogicalOperator; // Source De-Morgan's laws: De Morgan's laws, Wikipedia. // Reference: https://en.wikipedia.org/wiki/De_Morgan%27s_laws if constexpr (Operation == OR) { @@ -272,10 +308,10 @@ LogicalExpression::logicalComplement() const { }; //______________________________________________________________________________ -template +template std::vector LogicalExpression::evaluateImpl( const std::vector& input, size_t evaluationColumn) const { - using enum LogicalOperators; + using enum LogicalOperator; if constexpr (Operation == AND) { auto resultChild1 = child1_->evaluate(input, evaluationColumn); return child2_->evaluate(resultChild1, evaluationColumn); @@ -287,7 +323,7 @@ std::vector LogicalExpression::evaluateImpl( }; //______________________________________________________________________________ -template +template bool LogicalExpression::operator==( const PrefilterExpression& other) const { const LogicalExpression* otherlogical = @@ -300,14 +336,14 @@ bool LogicalExpression::operator==( }; //______________________________________________________________________________ -template +template std::string LogicalExpression::info(size_t depth) const { std::string child1Info = depth < maxInfoRecursion ? child1_->info(depth + 1) : "MAX_DEPTH"; std::string child2Info = depth < maxInfoRecursion ? child2_->info(depth + 1) : "MAX_DEPTH"; std::stringstream stream; - stream << "Prefilter LogicalExpression<" << static_cast(Operation) + stream << "Prefilter LogicalExpression<" << getLogicalOpStr(Operation) << ">\n" << "child1 {" << child1Info << "}" << "child2 {" << child2Info << "}" << std::endl; @@ -353,22 +389,19 @@ namespace detail { //______________________________________________________________________________ void checkPropertiesForPrefilterConstruction( const std::vector& vec) { - if (!std::ranges::is_sorted(vec, [](const auto& pair1, const auto& pair2) { - return pair1.second < pair2.second; - })) { + auto viewVariable = vec | std::views::values; + if (!std::ranges::is_sorted(viewVariable, std::less<>{})) { throw std::runtime_error( "The vector must contain the pairs in " "sorted order w.r.t. Variable value."); - }; - if (auto it = std::ranges::adjacent_find( - vec, [](const auto& pair1, - const auto& pair2) { return pair1.second == pair2.second; }); - it != vec.end()) { + } + if (auto it = std::ranges::adjacent_find(viewVariable); + it != std::ranges::end(viewVariable)) { throw std::runtime_error( "For each relevant Variable must exist exactly one " " pair."); } -}; +} } // namespace detail } // namespace prefilterExpressions diff --git a/src/index/CompressedBlockPrefiltering.h b/src/index/CompressedBlockPrefiltering.h index 998b302f35..d7d158d01f 100644 --- a/src/index/CompressedBlockPrefiltering.h +++ b/src/index/CompressedBlockPrefiltering.h @@ -118,10 +118,10 @@ class RelationalExpression : public PrefilterExpression { // Helper struct for a compact class implementation regarding the logical // operations `AND` and `OR`. `NOT` is implemented separately given that the // expression is unary (single child expression). -enum struct LogicalOperators { AND, OR }; +enum struct LogicalOperator { AND, OR }; //______________________________________________________________________________ -template +template class LogicalExpression : public PrefilterExpression { private: std::unique_ptr child1_; @@ -171,8 +171,8 @@ template class RelationalExpression; template class RelationalExpression; template class RelationalExpression; -template class LogicalExpression; -template class LogicalExpression; +template class LogicalExpression; +template class LogicalExpression; //______________________________________________________________________________ // Definition of the RelationalExpression for LT, LE, EQ, NE, GE and GT. @@ -192,9 +192,9 @@ using GreaterThanExpression = prefilterExpressions::RelationalExpression< //______________________________________________________________________________ // Definition of the LogicalExpression for AND and OR. using AndExpression = prefilterExpressions::LogicalExpression< - prefilterExpressions::LogicalOperators::AND>; + prefilterExpressions::LogicalOperator::AND>; using OrExpression = prefilterExpressions::LogicalExpression< - prefilterExpressions::LogicalOperators::OR>; + prefilterExpressions::LogicalOperator::OR>; namespace detail { //______________________________________________________________________________ diff --git a/test/CompressedBlockPrefilteringTest.cpp b/test/CompressedBlockPrefilteringTest.cpp index 995a627791..72d7c6754f 100644 --- a/test/CompressedBlockPrefilteringTest.cpp +++ b/test/CompressedBlockPrefilteringTest.cpp @@ -748,16 +748,24 @@ TEST(PrefilterExpression, testEqualityOperator) { TEST(PrefilterExpression, checkPrintFormattedPrefilterExpression) { auto expr = lt(IntId(10)); EXPECT_EQ((std::stringstream() << *expr).str(), - "Prefilter RelationalExpression<0>\nValueId: I:10\n.\n"); + "Prefilter RelationalExpression\nValueId: I:10\n.\n"); + expr = neq(DoubleId(8.21)); + EXPECT_EQ((std::stringstream() << *expr).str(), + "Prefilter RelationalExpression\nValueId: D:8.210000\n.\n"); expr = notExpr(eq(VocabId(0))); EXPECT_EQ((std::stringstream() << *expr).str(), "Prefilter NotExpression:\nchild {Prefilter " - "RelationalExpression<3>\nValueId: V:0\n}\n.\n"); - expr = orExpr(le(IntId(0)), eq(IntId(5))); + "RelationalExpression\nValueId: V:0\n}\n.\n"); + expr = orExpr(le(IntId(0)), ge(IntId(5))); + EXPECT_EQ((std::stringstream() << *expr).str(), + "Prefilter LogicalExpression\nchild1 {Prefilter " + "RelationalExpression\nValueId: I:0\n}child2 {Prefilter " + "RelationalExpression=)>\nValueId: I:5\n}\n.\n"); + expr = andExpr(lt(IntId(20)), gt(IntId(10))); EXPECT_EQ((std::stringstream() << *expr).str(), - "Prefilter LogicalExpression<1>\nchild1 {Prefilter " - "RelationalExpression<1>\nValueId: I:0\n}child2 {Prefilter " - "RelationalExpression<2>\nValueId: I:5\n}\n.\n"); + "Prefilter LogicalExpression\nchild1 {Prefilter " + "RelationalExpression\nValueId: I:20\n}child2 {Prefilter " + "RelationalExpression)>\nValueId: I:10\n}\n.\n"); } //______________________________________________________________________________ From 95e9027e06f0cb470be81c738ec3a1c388ef4fcc Mon Sep 17 00:00:00 2001 From: realHannes Date: Mon, 11 Nov 2024 16:53:52 +0100 Subject: [PATCH 49/50] further adjustments + apply PrefilterExpressions in IndexScan --- src/engine/Filter.cpp | 17 +- src/engine/Filter.h | 5 +- src/engine/IndexScan.cpp | 104 ++++++++---- src/engine/IndexScan.h | 33 +++- src/engine/Operation.cpp | 26 ++- src/engine/Operation.h | 16 +- src/engine/QueryExecutionTree.cpp | 7 + src/engine/QueryExecutionTree.h | 15 +- .../NumericBinaryExpressions.cpp | 155 ++++++++++++------ .../NumericUnaryExpressions.cpp | 2 +- .../RelationalExpressions.cpp | 3 +- .../sparqlExpressions/StringExpressions.cpp | 60 ++++++- src/index/CompressedBlockPrefiltering.cpp | 20 +++ src/index/CompressedBlockPrefiltering.h | 14 +- src/index/Permutation.cpp | 18 +- src/index/Permutation.h | 4 +- test/CompressedBlockPrefilteringTest.cpp | 59 ++++++- 17 files changed, 432 insertions(+), 126 deletions(-) diff --git a/src/engine/Filter.cpp b/src/engine/Filter.cpp index 79ef7572ad..265fb4b89c 100644 --- a/src/engine/Filter.cpp +++ b/src/engine/Filter.cpp @@ -27,7 +27,9 @@ Filter::Filter(QueryExecutionContext* qec, sparqlExpression::SparqlExpressionPimpl expression) : Operation(qec), _subtree(std::move(subtree)), - _expression{std::move(expression)} {} + _expression{std::move(expression)} { + setPrefilterExpressionForIndexScanChildren(); +} // _____________________________________________________________________________ string Filter::getCacheKeyImpl() const { @@ -37,10 +39,23 @@ string Filter::getCacheKeyImpl() const { return std::move(os).str(); } +//______________________________________________________________________________ string Filter::getDescriptor() const { return absl::StrCat("Filter ", _expression.getDescriptor()); } +//______________________________________________________________________________ +void Filter::setPrefilterExpressionForIndexScanChildren() { + const std::vector& prefilterVec = + _expression.getPrefilterExpressionForMetadata(); + this->forAllDescendants( + [&prefilterVec](const QueryExecutionTree* ptr) -> void { + if (ptr) { + ptr->setPrefilterExpression(prefilterVec); + } + }); +} + // _____________________________________________________________________________ ProtoResult Filter::computeResult(bool requestLaziness) { LOG(DEBUG) << "Getting sub-result for Filter result computation..." << endl; diff --git a/src/engine/Filter.h b/src/engine/Filter.h index eaa9303bb7..a475758e0f 100644 --- a/src/engine/Filter.h +++ b/src/engine/Filter.h @@ -10,10 +10,11 @@ #include "engine/Operation.h" #include "engine/QueryExecutionTree.h" -#include "engine/sparqlExpressions/SparqlExpressionPimpl.h" #include "parser/ParsedQuery.h" class Filter : public Operation { + using PrefilterVariablePair = sparqlExpression::PrefilterExprVariablePair; + private: std::shared_ptr _subtree; sparqlExpression::SparqlExpressionPimpl _expression; @@ -58,6 +59,8 @@ class Filter : public Operation { return _subtree->getVariableColumns(); } + void setPrefilterExpressionForIndexScanChildren(); + ProtoResult computeResult(bool requestLaziness) override; // Perform the actual filter operation of the data provided. diff --git a/src/engine/IndexScan.cpp b/src/engine/IndexScan.cpp index 9217e91ac0..8a0066b4d5 100644 --- a/src/engine/IndexScan.cpp +++ b/src/engine/IndexScan.cpp @@ -17,13 +17,15 @@ using std::string; // _____________________________________________________________________________ IndexScan::IndexScan(QueryExecutionContext* qec, Permutation::Enum permutation, - const SparqlTripleSimple& triple, Graphs graphsToFilter) + const SparqlTripleSimple& triple, Graphs graphsToFilter, + PrefilterIndexPairs prefilters) : Operation(qec), permutation_(permutation), subject_(triple.s_), predicate_(triple.p_), object_(triple.o_), graphsToFilter_{std::move(graphsToFilter)}, + prefilters_{std::move(prefilters)}, numVariables_(static_cast(subject_.isVariable()) + static_cast(predicate_.isVariable()) + static_cast(object_.isVariable())) { @@ -51,9 +53,10 @@ IndexScan::IndexScan(QueryExecutionContext* qec, Permutation::Enum permutation, // _____________________________________________________________________________ IndexScan::IndexScan(QueryExecutionContext* qec, Permutation::Enum permutation, - const SparqlTriple& triple, Graphs graphsToFilter) - : IndexScan(qec, permutation, triple.getSimple(), - std::move(graphsToFilter)) {} + const SparqlTriple& triple, Graphs graphsToFilter, + PrefilterIndexPairs prefilters) + : IndexScan(qec, permutation, triple.getSimple(), std::move(graphsToFilter), + std::move(prefilters)) {} // _____________________________________________________________________________ string IndexScan::getCacheKeyImpl() const { @@ -115,6 +118,24 @@ vector IndexScan::resultSortedOn() const { return result; } +// _____________________________________________________________________________ +void IndexScan::setPrefilterExpression( + const std::vector& prefilterVariablePairs) { + const std::vector& sortedColumns = resultSortedOn(); + VariableToColumnMap varToColMap = computeVariableToColumnMap(); + + const auto addPrefilterIfSorted = [&](const PrefilterVariablePair& pair) { + const Variable& variable = pair.second; + if (varToColMap.find(variable) != varToColMap.end()) { + const ColumnIndex colIdx = varToColMap[variable].columnIndex_; + if (std::ranges::find(sortedColumns, colIdx) != sortedColumns.end()) { + prefilters_.push_back(std::make_pair(pair.first->clone(), colIdx)); + } + } + }; + std::ranges::for_each(prefilterVariablePairs, addPrefilterIfSorted); +} + // _____________________________________________________________________________ VariableToColumnMap IndexScan::computeVariableToColumnMap() const { VariableToColumnMap variableToColumnMap; @@ -134,40 +155,63 @@ VariableToColumnMap IndexScan::computeVariableToColumnMap() const { return variableToColumnMap; } +//______________________________________________________________________________ +std::vector IndexScan::applyFilterBlockMetadata( + const std::vector& blocks) const { + std::ranges::for_each(prefilters_, [&blocks](const PrefilterIndexPair& pair) { + pair.first->evaluate(blocks, pair.second); + }); + return blocks; +}; + // _____________________________________________________________________________ -Result::Generator IndexScan::scanInChunks() const { +Result::Generator IndexScan::chunkedIndexScan() const { auto metadata = getMetadataForScan(); if (!metadata.has_value()) { co_return; } auto blocksSpan = CompressedRelationReader::getBlocksFromMetadata(metadata.value()); - std::vector blocks{blocksSpan.begin(), - blocksSpan.end()}; - for (IdTable& idTable : getLazyScan(std::move(blocks))) { + for (IdTable& idTable : getLazyScan({blocksSpan.begin(), blocksSpan.end()})) { co_yield {std::move(idTable), LocalVocab{}}; } } // _____________________________________________________________________________ -ProtoResult IndexScan::computeResult(bool requestLaziness) { - LOG(DEBUG) << "IndexScan result computation...\n"; - if (requestLaziness) { - return {scanInChunks(), resultSortedOn()}; - } - IdTable idTable{getExecutionContext()->getAllocator()}; - +IdTable IndexScan::completeIndexScan() const { + // Get the blocks. + auto metadata = getMetadataForScan(); + auto blockSpan = + metadata.has_value() + ? CompressedRelationReader::getBlocksFromMetadata(metadata.value()) + : std::span{}; + auto optFilteredBlocks = getLimit().isUnconstrained() + ? std::optional{applyFilterBlockMetadata( + {blockSpan.begin(), blockSpan.end()})} + : std::nullopt; + // Create IdTable, fill it with content by performing scan(). using enum Permutation::Enum; + IdTable idTable{getExecutionContext()->getAllocator()}; idTable.setNumColumns(numVariables_); const auto& index = _executionContext->getIndex(); - idTable = - index.scan(getScanSpecification(), permutation_, additionalColumns(), - cancellationHandle_, deltaTriples(), getLimit()); + idTable = index.getImpl() + .getPermutation(permutation_) + .scan(getScanSpecification(), additionalColumns(), + cancellationHandle_, deltaTriples(), getLimit(), + optFilteredBlocks); AD_CORRECTNESS_CHECK(idTable.numColumns() == getResultWidth()); LOG(DEBUG) << "IndexScan result computation done.\n"; checkCancellation(); + return idTable; +} - return {std::move(idTable), resultSortedOn(), LocalVocab{}}; +// _____________________________________________________________________________ +ProtoResult IndexScan::computeResult(bool requestLaziness) { + LOG(DEBUG) << "IndexScan result computation...\n"; + if (requestLaziness) { + return {chunkedIndexScan(), resultSortedOn()}; + } + return {completeIndexScan(), getResultSortedOn(), LocalVocab{}}; } // _____________________________________________________________________________ @@ -208,7 +252,7 @@ void IndexScan::determineMultiplicities() { AD_CONTRACT_CHECK(multiplicity_.size() == getResultWidth()); } -// ___________________________________________________________________________ +// _____________________________________________________________________________ std::array IndexScan::getPermutedTriple() const { std::array triple{&subject_, &predicate_, &object_}; @@ -217,30 +261,30 @@ std::array IndexScan::getPermutedTriple() triple[permutation[2]]}; } -// ___________________________________________________________________________ +// _____________________________________________________________________________ ScanSpecification IndexScan::getScanSpecification() const { const IndexImpl& index = getIndex().getImpl(); return getScanSpecificationTc().toScanSpecification(index); } -// ___________________________________________________________________________ +// _____________________________________________________________________________ ScanSpecificationAsTripleComponent IndexScan::getScanSpecificationTc() const { auto permutedTriple = getPermutedTriple(); return {*permutedTriple[0], *permutedTriple[1], *permutedTriple[2], graphsToFilter_}; } -// ___________________________________________________________________________ +// _____________________________________________________________________________ Permutation::IdTableGenerator IndexScan::getLazyScan( std::vector blocks) const { // If there is a LIMIT or OFFSET clause that constrains the scan // (which can happen with an explicit subquery), we cannot use the prefiltered // blocks, as we currently have no mechanism to include limits and offsets // into the prefiltering (`std::nullopt` means `scan all blocks`). - auto actualBlocks = getLimit().isUnconstrained() - ? std::optional{std::move(blocks)} - : std::nullopt; - + auto actualBlocks = + getLimit().isUnconstrained() + ? std::optional{applyFilterBlockMetadata(std::move(blocks))} + : std::nullopt; return getIndex() .getImpl() .getPermutation(permutation()) @@ -249,7 +293,7 @@ Permutation::IdTableGenerator IndexScan::getLazyScan( getLimit()); }; -// ________________________________________________________________ +// _____________________________________________________________________________ std::optional IndexScan::getMetadataForScan() const { const auto& index = getExecutionContext()->getIndex().getImpl(); @@ -257,7 +301,7 @@ std::optional IndexScan::getMetadataForScan() .getMetadataAndBlocks(getScanSpecification(), deltaTriples()); }; -// ________________________________________________________________ +// _____________________________________________________________________________ std::array IndexScan::lazyScanForJoinOfTwoScans(const IndexScan& s1, const IndexScan& s2) { AD_CONTRACT_CHECK(s1.numVariables_ <= 3 && s2.numVariables_ <= 3); @@ -306,7 +350,7 @@ IndexScan::lazyScanForJoinOfTwoScans(const IndexScan& s1, const IndexScan& s2) { return result; } -// ________________________________________________________________ +// _____________________________________________________________________________ Permutation::IdTableGenerator IndexScan::lazyScanForJoinOfColumnWithScan( std::span joinColumn) const { AD_EXPENSIVE_CHECK(std::ranges::is_sorted(joinColumn)); diff --git a/src/engine/IndexScan.h b/src/engine/IndexScan.h index 9e75c89d35..6055b5a1a0 100644 --- a/src/engine/IndexScan.h +++ b/src/engine/IndexScan.h @@ -11,13 +11,21 @@ class SparqlTriple; class SparqlTripleSimple; class IndexScan final : public Operation { + using Graphs = ScanSpecificationAsTripleComponent::Graphs; + // Pair containing a `PrefilterExpression` with `ColumnIndex` (eval. index) + using PrefilterIndexPair = + std::pair, + ColumnIndex>; + // Vector with `PrefilterIndexPair` values. + using PrefilterIndexPairs = std::vector; + private: Permutation::Enum permutation_; TripleComponent subject_; TripleComponent predicate_; TripleComponent object_; - using Graphs = ScanSpecificationAsTripleComponent::Graphs; Graphs graphsToFilter_; + PrefilterIndexPairs prefilters_; size_t numVariables_; size_t sizeEstimate_; vector multiplicity_; @@ -30,10 +38,12 @@ class IndexScan final : public Operation { public: IndexScan(QueryExecutionContext* qec, Permutation::Enum permutation, - const SparqlTriple& triple, Graphs graphsToFilter = std::nullopt); + const SparqlTriple& triple, Graphs graphsToFilter = std::nullopt, + PrefilterIndexPairs prefilters = {}); IndexScan(QueryExecutionContext* qec, Permutation::Enum permutation, const SparqlTripleSimple& triple, - Graphs graphsToFilter = std::nullopt); + Graphs graphsToFilter = std::nullopt, + PrefilterIndexPairs prefilters = {}); ~IndexScan() override = default; @@ -55,6 +65,10 @@ class IndexScan final : public Operation { vector resultSortedOn() const override; + // Set `PrefilterExpression`s. + void setPrefilterExpression(const std::vector& + prefilterVariablePairs) override; + size_t numVariables() const { return numVariables_; } // Return the exact result size of the index scan. This is always known as it @@ -130,9 +144,18 @@ class IndexScan final : public Operation { VariableToColumnMap computeVariableToColumnMap() const override; - Result::Generator scanInChunks() const; + // Filter relevant `CompressedBlockMetadata` blocks by applying the + // `PrefilterExpression`s from `prefilters_`. + std::vector applyFilterBlockMetadata( + const std::vector& blocks) const; + + // Return the (lazy) `IdTable` for this `IndexScan` in chunks. + Result::Generator chunkedIndexScan() const; + // Get the `IdTable` for this `IndexScan` in one piece. + IdTable completeIndexScan() const; - // Helper functions for the public `getLazyScanFor...` functions (see above). + // Helper functions for the public `getLazyScanFor...` methods and + // `chunkedIndexScan` (see above). Permutation::IdTableGenerator getLazyScan( std::vector blocks) const; std::optional getMetadataForScan() const; diff --git a/src/engine/Operation.cpp b/src/engine/Operation.cpp index bb7d8fb0af..4d10cffc87 100644 --- a/src/engine/Operation.cpp +++ b/src/engine/Operation.cpp @@ -10,31 +10,39 @@ using namespace std::chrono_literals; +//______________________________________________________________________________ template -void Operation::forAllDescendants(F f) { +void Operation::forAllDescendantsImpl(F&& f) { static_assert( std::is_same_v>); for (auto ptr : getChildren()) { if (ptr) { - f(ptr); - ptr->forAllDescendants(f); + std::forward(f)(ptr); + ptr->forAllDescendants(std::forward(f)); } } } +//______________________________________________________________________________ template -void Operation::forAllDescendants(F f) const { +void Operation::forAllDescendantsImpl(F&& f) const { static_assert( std::is_same_v>); for (auto ptr : getChildren()) { if (ptr) { - f(ptr); - ptr->forAllDescendants(f); + std::forward(f)(ptr); + ptr->forAllDescendants(std::forward(f)); } } } -// __________________________________________________________________________________________________________ +// _____________________________________________________________________________ +void Operation::forAllDescendants( + std::function&& func) { + forAllDescendantsImpl(std::move(func)); +} + +// _____________________________________________________________________________ vector Operation::collectWarnings() const { vector res = getWarnings(); for (auto child : getChildren()) { @@ -53,7 +61,7 @@ vector Operation::collectWarnings() const { void Operation::recursivelySetCancellationHandle( SharedCancellationHandle cancellationHandle) { AD_CORRECTNESS_CHECK(cancellationHandle); - forAllDescendants([&cancellationHandle](auto child) { + forAllDescendantsImpl([&cancellationHandle](auto child) { child->getRootOperation()->cancellationHandle_ = cancellationHandle; }); cancellationHandle_ = std::move(cancellationHandle); @@ -64,7 +72,7 @@ void Operation::recursivelySetCancellationHandle( void Operation::recursivelySetTimeConstraint( std::chrono::steady_clock::time_point deadline) { deadline_ = deadline; - forAllDescendants([deadline](auto child) { + forAllDescendantsImpl([deadline](auto child) { child->getRootOperation()->deadline_ = deadline; }); } diff --git a/src/engine/Operation.h b/src/engine/Operation.h index 61702c7766..10f7df07a0 100644 --- a/src/engine/Operation.h +++ b/src/engine/Operation.h @@ -14,6 +14,7 @@ #include "engine/Result.h" #include "engine/RuntimeInformation.h" #include "engine/VariableToColumnMap.h" +#include "engine/sparqlExpressions/SparqlExpressionPimpl.h" #include "parser/data/LimitOffsetClause.h" #include "parser/data/Variable.h" #include "util/CancellationHandle.h" @@ -34,6 +35,9 @@ class Operation { using Milliseconds = std::chrono::milliseconds; public: + // Holds a `PrefilterExpression` with its corresponding `Variable`. + using PrefilterVariablePair = sparqlExpression::PrefilterExprVariablePair; + // Default Constructor. Operation() : _executionContext(nullptr) {} @@ -73,6 +77,14 @@ class Operation { return _executionContext->deltaTriples(); } + // Set `PrefilterExpression`s (for `IndexScan`). + virtual void setPrefilterExpression( + const std::vector&){}; + + // Apply the provided void function for all descendants. The given function + // must be invocable with a `const QueryExecutionTree*` object. + void forAllDescendants(std::function&& func); + // Get a unique, not ambiguous string representation for a subtree. // This should act like an ID for each subtree. // Calls `getCacheKeyImpl` and adds the information about the `LIMIT` clause. @@ -354,11 +366,11 @@ class Operation { // Recursively call a function on all children. template - void forAllDescendants(F f); + void forAllDescendantsImpl(F&& f); // Recursively call a function on all children. template - void forAllDescendants(F f) const; + void forAllDescendantsImpl(F&& f) const; // Holds a precomputed Result of this operation if it is the sibling of a // Service operation. diff --git a/src/engine/QueryExecutionTree.cpp b/src/engine/QueryExecutionTree.cpp index aed58d4fde..65642ae5af 100644 --- a/src/engine/QueryExecutionTree.cpp +++ b/src/engine/QueryExecutionTree.cpp @@ -96,6 +96,13 @@ size_t QueryExecutionTree::getSizeEstimate() { return sizeEstimate_.value(); } +//_____________________________________________________________________________ +void QueryExecutionTree::setPrefilterExpression( + const std::vector& prefilterVec) const { + AD_CONTRACT_CHECK(rootOperation_); + rootOperation_->setPrefilterExpression(prefilterVec); +} + // _____________________________________________________________________________ bool QueryExecutionTree::knownEmptyResult() { if (cachedResult_) { diff --git a/src/engine/QueryExecutionTree.h b/src/engine/QueryExecutionTree.h index 0519082b78..7df9a6bed4 100644 --- a/src/engine/QueryExecutionTree.h +++ b/src/engine/QueryExecutionTree.h @@ -85,6 +85,9 @@ class QueryExecutionTree { return rootOperation_->getMultiplicity(col); } + void setPrefilterExpression( + const std::vector& prefilterVec) const; + size_t getDistinctEstimate(size_t col) const { return static_cast(rootOperation_->getSizeEstimate() / rootOperation_->getMultiplicity(col)); @@ -106,26 +109,26 @@ class QueryExecutionTree { } template - void forAllDescendants(F f) { + void forAllDescendants(F&& f) { static_assert( std::is_same_v>); for (auto ptr : rootOperation_->getChildren()) { if (ptr) { - f(ptr); - ptr->forAllDescendants(f); + std::forward(f)(ptr); + ptr->forAllDescendants(std::forward(f)); } } } template - void forAllDescendants(F f) const { + void forAllDescendants(F&& f) const { static_assert( std::is_same_v>); for (auto ptr : rootOperation_->getChildren()) { if (ptr) { - f(ptr); - ptr->forAllDescendants(f); + std::forward(f)(ptr); + ptr->forAllDescendants(std::forward(f)); } } } diff --git a/src/engine/sparqlExpressions/NumericBinaryExpressions.cpp b/src/engine/sparqlExpressions/NumericBinaryExpressions.cpp index 7f8319e17c..e8bf9a450f 100644 --- a/src/engine/sparqlExpressions/NumericBinaryExpressions.cpp +++ b/src/engine/sparqlExpressions/NumericBinaryExpressions.cpp @@ -70,7 +70,10 @@ inline auto andLambda = [](TernaryBool a, TernaryBool b) { namespace constructPrefilterExpr { namespace { -// MERGE AND CONJUNCTION +// `BinaryOperator` defines the operations `AND` and `OR`. +using BinaryOperator = prefilterExpressions::LogicalOperator; + +// MERGE // _____________________________________________________________________________ // For our pre-filtering logic over index scans, we need exactly one // corresponding PrefilterExpression for each relevant Variable. Thus, if the @@ -97,44 +100,34 @@ namespace { // left child: {<(>= 10 AND <= 20), ?x>}; (?x >= 10 && ?x <= 20) // right child: {<(!= 15), ?x>, <(= 10), ?y>}; (?x != 15 && ?y = 10) // Returns {<((>= 10 AND <= 20) AND != 15), ?x>, <(= 10), ?y>} +// +// +// MERGE +// (Partial De-Morgan applied w.r.t. Or-conjunction) //______________________________________________________________________________ -template -std::vector mergeChildrenForAndExpressionImpl( - std::vector&& leftChild, - std::vector&& rightChild) { - namespace pd = prefilterExpressions::detail; - pd::checkPropertiesForPrefilterConstruction(leftChild); - pd::checkPropertiesForPrefilterConstruction(rightChild); - // Merge the PrefilterExpression vectors from the left and right child. - // Remark: The vectors contain std::pairs, sorted by their respective - // Variable to the PrefilterExpression. - auto itLeft = leftChild.begin(); - auto itRight = rightChild.begin(); - std::vector resPairs; - while (itLeft != leftChild.end() && itRight != rightChild.end()) { - auto& [exprLeft, varLeft] = *itLeft; - auto& [exprRight, varRight] = *itRight; - if (varLeft == varRight) { - resPairs.emplace_back(std::make_unique( - std::move(exprLeft), std::move(exprRight)), - varLeft); - ++itLeft; - ++itRight; - } else if (varLeft < varRight) { - resPairs.emplace_back(std::move(*itLeft)); - ++itLeft; - } else { - resPairs.emplace_back(std::move(*itRight)); - ++itRight; - } - } - std::ranges::move(itLeft, leftChild.end(), std::back_inserter(resPairs)); - std::ranges::move(itRight, rightChild.end(), std::back_inserter(resPairs)); - pd::checkPropertiesForPrefilterConstruction(resPairs); - return resPairs; -} - -// MERGE OR CONJUNCTION +// Merge the pair values of leftChild and rightChild with the combine logic for +// AND. Given we have a respective pair from leftChild and rightChild for which the Variable value is +// equal, conjunct the two PrefilterExpressions over an OrExpression. For the +// resulting pairs, NOT (NotExpression) is applied later on. (As a result we +// have fully applied De-Morgan on the available PrefilterExpression) +// +// EXAMPLE 1 +// !...(?x = 5 OR ?y = VocabId(10)) +// partial De-Morgan: ...(?x = 5 AND ?y = VocabId(10)) +// (complete De-Morgan later on with NotExpression: +// ...(?x != 5 AND ?y != VocabId(10))) (in NumericUnaryExpression.cpp) +// The merge function implements the following: +// left child: {<(= 5), ?x>} +// right child: {<(= VocabId(10)), ?y>} +// merged: {<(= 5), ?x>, <(= VocabId(10)), ?y>} +// +// EXAMPLE 2 +// left child: {<(>= 10 OR <= 20), ?x>} +// right child: {<(!= 15), ?x>, <(= 10), ?y>} +// merged: {<((>= 10 OR <= 20) OR ?x != 15), ?x>, <(= 10), ?y>} +// +// MERGE //______________________________________________________________________________ // Four examples on how this function (OR) merges the content of two children. // @@ -167,14 +160,44 @@ std::vector mergeChildrenForAndExpressionImpl( // PrefiterExpression. We can't make a distinct prediction w.r.t. ?y != 0 // => not relevant for the PrefilterExpression. Thus, we return the // PrefilterExpresion {<(>= 10 OR <= 0), ?x>}. +// +// +// MERGE +// (Partial De-Morgan applied w.r.t. And-conjunction) //______________________________________________________________________________ -template -std::vector mergeChildrenForOrExpressionImpl( +// Merge the pair values of leftChild and rightChild with the combine logic for +// OR. The difference, given we have a respective pair from leftChild and rightChild for which the Variable value is +// equal, conjunct the two PrefilterExpressions over an AndExpression. For the +// resulting pairs, NOT (NotExpression) is applied later on. As a result we +// have fully applied De-Morgan on the available PrefilterExpression. +// +// EXAMPLE 1 +// !...(?y = 5 AND ?x <= 3); apply partial De-Morgan given NOT(!): Thus OR +// instead of AND merge (here). +// left child: {<(= 5), ?y>} +// right child: {<(<= 3), ?x>} +// merged result: {} +// +// EXAMPLE 2 +// !...((?x = 10 OR ?y != 0) AND ?x <= 0); apply partial De-Morgan given NOT(!): +// Thus OR instead of AND merge (here). +// left child: {<(= 10), ?x), <(!= 0), ?y>}; +// right child: {<(<= 0), ?x>} +// merged result: {<((= 10) AND (<= 0)), ?x>} +// (with NOT applied later on: {<((!= 10) OR (> 0)), ?x>}) +//______________________________________________________________________________ +template +std::vector mergeChildrenForBinaryOpExpressionImpl( std::vector&& leftChild, std::vector&& rightChild) { + using enum BinaryOperator; namespace pd = prefilterExpressions::detail; pd::checkPropertiesForPrefilterConstruction(leftChild); pd::checkPropertiesForPrefilterConstruction(rightChild); + // Merge the PrefilterExpression vectors from the left and right child. + // Remark: The vectors contain std::pairs, sorted by their respective + // Variable to the PrefilterExpression. auto itLeft = leftChild.begin(); auto itRight = rightChild.begin(); std::vector resPairs; @@ -188,15 +211,42 @@ std::vector mergeChildrenForOrExpressionImpl( ++itLeft; ++itRight; } else if (varLeft < varRight) { + if constexpr (binOp == AND) { + resPairs.emplace_back(std::move(*itLeft)); + } ++itLeft; } else { + if constexpr (binOp == AND) { + resPairs.emplace_back(std::move(*itRight)); + } ++itRight; } } + if constexpr (binOp == AND) { + std::ranges::move(itLeft, leftChild.end(), std::back_inserter(resPairs)); + std::ranges::move(itRight, rightChild.end(), std::back_inserter(resPairs)); + } pd::checkPropertiesForPrefilterConstruction(resPairs); return resPairs; } +// TEMPLATED STANDARD MERGE +//______________________________________________________________________________ +constexpr auto makeAndMergeWithAndConjunction = + mergeChildrenForBinaryOpExpressionImpl; +constexpr auto makeOrMergeWithOrConjunction = + mergeChildrenForBinaryOpExpressionImpl; +// TEMPLATED (PARTIAL) DE-MORGAN MERGE +//______________________________________________________________________________ +constexpr auto makeAndMergeWithOrConjunction = + mergeChildrenForBinaryOpExpressionImpl; +constexpr auto makeOrMergeWithAndConjunction = + mergeChildrenForBinaryOpExpressionImpl; + // GET TEMPLATED MERGE FUNCTION (CONJUNCTION AND / OR) //______________________________________________________________________________ // getMergeFunction (get the respective merging function) follows the @@ -221,8 +271,7 @@ std::vector mergeChildrenForOrExpressionImpl( // left child: {<(!= 10), ?y>, <(!= 0), ?x>}; (?y != 10 || ?x != 0) // // Remark: The (child) expressions of the left and right child have been -// combined with the merging procedure AND -// (mergeChildrenForAndExpressionImpl), +// combined with the merging procedure AND (makeAndMergeWithOrConjunction), // because with isNegated = true, it is implied that De-Morgan's law is applied. // Thus, the OR is logically an AND conjunction with its application. // @@ -230,10 +279,10 @@ std::vector mergeChildrenForOrExpressionImpl( // // isNegated is true, hence we know that we have to partially apply // De-Morgan's law. Given that, OR switches to an AND. Therefore we use -// mergeChildrenForAndExpressionImpl with OrExpression (PrefilterExpression) -// as a template value (BinaryPrefilterExpr). -// Call mergeChildrenAndExpressionImpl -// with left and right child as args., we get their merged combination: +// mergeChildrenForBinaryOpExpressionImpl with OrExpression +// (PrefilterExpression) as a template value (BinaryPrefilterExpr). Call +// makeAndMergeWithOrConjunction with left and right child as arguments, we get +// their merged combination: // {<(!= 10), ?y>, <((!= 0) OR ((= 5) OR (>= 10))), ?x>} // // Next their merged value is returned to UnaryNegateExpressionImpl @@ -242,19 +291,23 @@ std::vector mergeChildrenForOrExpressionImpl( // applied. // {<(!= 10), ?y>, <((!= 0) OR ((= 5) OR (>= 10))), ?x>} // apply NotExpr.: {, = 10))), ?x>} -// This yields: {<(= 10), ?y>, <((= 0) AND ((!= 5) AND (< 10))), ?x>} +// This procedure yields: {<(= 10), ?y>, <((= 0) AND ((!= 5) AND (< 10))), ?x>} //______________________________________________________________________________ template constexpr auto getMergeFunction(bool isNegated) { if constexpr (std::is_same_v) { - return !isNegated ? mergeChildrenForAndExpressionImpl - : mergeChildrenForOrExpressionImpl; + return !isNegated ? makeAndMergeWithAndConjunction + // negated, partially apply De-Morgan: + // change AND to OR + : makeOrMergeWithAndConjunction; } else { static_assert(std::is_same_v); - return !isNegated ? mergeChildrenForOrExpressionImpl - : mergeChildrenForAndExpressionImpl; + return !isNegated ? makeOrMergeWithOrConjunction + // negated, partially apply De-Morgan: + // change OR to AND + : makeAndMergeWithOrConjunction; } } diff --git a/src/engine/sparqlExpressions/NumericUnaryExpressions.cpp b/src/engine/sparqlExpressions/NumericUnaryExpressions.cpp index cb72da969a..a9d46d645e 100644 --- a/src/engine/sparqlExpressions/NumericUnaryExpressions.cpp +++ b/src/engine/sparqlExpressions/NumericUnaryExpressions.cpp @@ -54,7 +54,7 @@ class UnaryNegateExpressionImpl : public NaryExpression { // With De-Morgan's rule we retrieve: ?x < IntId(10) && ?y < IntId(10) // // (1) Merge {<(>= IntId(10)), ?x>} and {<(>= IntId(10)), ?y>} - // with mergeChildrenForAndExpressionImpl (defined in + // with mergeChildrenForBinaryOpExpressionImpl (defined in // NumericBinaryExpressions.cpp), which we select based on isNegated = true // (first part of De-Morgans law). // Result (1): {<(>= IntId(10)), ?x>, <(>= IntId(10)), ?y>} diff --git a/src/engine/sparqlExpressions/RelationalExpressions.cpp b/src/engine/sparqlExpressions/RelationalExpressions.cpp index de035dce71..4f2f28f80e 100644 --- a/src/engine/sparqlExpressions/RelationalExpressions.cpp +++ b/src/engine/sparqlExpressions/RelationalExpressions.cpp @@ -464,13 +464,12 @@ RelationalExpression::getPrefilterExpressionForMetadata( } return resVec; }; - std::vector resVec; // Option 1: // RelationalExpression containing a VariableExpression as the first child // and an IdExpression as the second child. // E.g. for ?x >= 10 (RelationalExpression Sparql), we obtain the following // pair with PrefilterExpression and Variable: <(>= 10), ?x> - resVec = tryGetPrefilterExprVariablePairVec(child0, child1, false); + auto resVec = tryGetPrefilterExprVariablePairVec(child0, child1, false); if (!resVec.empty()) { return resVec; } diff --git a/src/engine/sparqlExpressions/StringExpressions.cpp b/src/engine/sparqlExpressions/StringExpressions.cpp index 47ee97bd23..85753e96f8 100644 --- a/src/engine/sparqlExpressions/StringExpressions.cpp +++ b/src/engine/sparqlExpressions/StringExpressions.cpp @@ -4,6 +4,7 @@ #include +#include "engine/sparqlExpressions/LiteralExpression.h" #include "engine/sparqlExpressions/NaryExpressionImpl.h" #include "engine/sparqlExpressions/VariadicExpression.h" @@ -231,9 +232,62 @@ using SubstrExpression = return Id::makeFromBool(text.starts_with(pattern)); }; -using StrStartsExpression = - StringExpressionImpl<2, LiftStringFunction, - StringValueGetter>; +namespace { + +template +requires(isOperation) +class StrStartsExpressionImpl : public NaryExpression { + public: + using NaryExpression::NaryExpression; + std::vector getPrefilterExpressionForMetadata( + [[maybe_unused]] bool isNegated) const override { + AD_CORRECTNESS_CHECK(this->N == 2); + const SparqlExpression* child0 = this->getNthChild(0).value(); + const SparqlExpression* child1 = this->getNthChild(1).value(); + const auto getPrefilterExprVariableVec = + [](const SparqlExpression* child0, const SparqlExpression* child1, + bool startsWithVar) -> std::vector { + using namespace prefilterExpressions; + std::vector resVec{}; + if (const auto* variable = + dynamic_cast(child0)) { + if (const auto* valueId = dynamic_cast(child1)) { + !startsWithVar + // make {<(>= VocabId(n)), ?var>} (Option 1) + ? resVec.emplace_back(std::make_pair( + std::make_unique(valueId->value()), + variable->value())) + // make {<(<= VocabId(n)), ?var>} (Option 2) + : resVec.emplace_back(std::make_pair( + std::make_unique(valueId->value()), + variable->value())); + } + } + return resVec; + }; + // Remark: With the current implementation we only prefilter w.r.t. one + // bound. + // TODO: It is technically possible to pre-filter more precisely by + // introducing a second bound. + // + // Option 1: STRSTARTS(?var, VocabId(n)); startsWithVar = false + // Return PrefilterExpression vector: {<(>= VocabId(n)), ?var>} + auto resVec = getPrefilterExprVariableVec(child0, child1, false); + if (!resVec.empty()) { + return resVec; + } + // Option 2: STRTSTARTS(VocabId(n), ?var); startsWithVar = true + // Return PrefilterExpression vector: {<(<= VocabId(n)), ?var>} + // Option 3: + // child0 or/and child1 are unsuitable SparqlExpression types, return {}. + return getPrefilterExprVariableVec(child1, child0, true); + } +}; + +} // namespace + +using StrStartsExpression = StrStartsExpressionImpl, StringValueGetter>>>; // STRENDS [[maybe_unused]] auto strEndsImpl = [](std::string_view text, diff --git a/src/index/CompressedBlockPrefiltering.cpp b/src/index/CompressedBlockPrefiltering.cpp index 51e20d6bc3..6300d12307 100644 --- a/src/index/CompressedBlockPrefiltering.cpp +++ b/src/index/CompressedBlockPrefiltering.cpp @@ -277,6 +277,13 @@ bool RelationalExpression::operator==( return referenceId_ == otherRelational->referenceId_; }; +//______________________________________________________________________________ +template +std::unique_ptr RelationalExpression::clone() + const { + return std::make_unique>(referenceId_); +}; + //______________________________________________________________________________ template std::string RelationalExpression::info( @@ -335,6 +342,14 @@ bool LogicalExpression::operator==( *child2_ == *otherlogical->child2_; }; +//______________________________________________________________________________ +template +std::unique_ptr LogicalExpression::clone() + const { + return std::make_unique>(child1_->clone(), + child2_->clone()); +}; + //______________________________________________________________________________ template std::string LogicalExpression::info(size_t depth) const { @@ -375,6 +390,11 @@ bool NotExpression::operator==(const PrefilterExpression& other) const { return *child_ == *otherNotExpression->child_; } +//______________________________________________________________________________ +std::unique_ptr NotExpression::clone() const { + return std::make_unique((child_->clone()), true); +}; + //______________________________________________________________________________ std::string NotExpression::info(size_t depth) const { std::string childInfo = diff --git a/src/index/CompressedBlockPrefiltering.h b/src/index/CompressedBlockPrefiltering.h index d7d158d01f..2419d9aea9 100644 --- a/src/index/CompressedBlockPrefiltering.h +++ b/src/index/CompressedBlockPrefiltering.h @@ -45,6 +45,9 @@ class PrefilterExpression { virtual bool operator==(const PrefilterExpression& other) const = 0; + // Create a copy of this `PrefilterExpression`. + virtual std::unique_ptr clone() const = 0; + // Format content for debugging. virtual std::string info(size_t depth) const = 0; @@ -106,6 +109,7 @@ class RelationalExpression : public PrefilterExpression { std::unique_ptr logicalComplement() const override; bool operator==(const PrefilterExpression& other) const override; + std::unique_ptr clone() const override; std::string info([[maybe_unused]] size_t depth) const override; private: @@ -135,6 +139,7 @@ class LogicalExpression : public PrefilterExpression { std::unique_ptr logicalComplement() const override; bool operator==(const PrefilterExpression& other) const override; + std::unique_ptr clone() const override; std::string info(size_t depth) const override; private: @@ -149,11 +154,16 @@ class NotExpression : public PrefilterExpression { std::unique_ptr child_; public: - explicit NotExpression(std::unique_ptr child) - : child_(child->logicalComplement()) {} + explicit NotExpression(std::unique_ptr child, + bool makeCopy = false) + // If we create a copy, the child expression has already been + // complemented while creating the original expression. Thus, just move + // the child. + : child_(makeCopy ? std::move(child) : child->logicalComplement()) {} std::unique_ptr logicalComplement() const override; bool operator==(const PrefilterExpression& other) const override; + std::unique_ptr clone() const override; std::string info(size_t depth) const override; private: diff --git a/src/index/Permutation.cpp b/src/index/Permutation.cpp index 16f5113d68..64a6290216 100644 --- a/src/index/Permutation.cpp +++ b/src/index/Permutation.cpp @@ -52,11 +52,11 @@ void Permutation::loadFromDisk(const std::string& onDiskBase, } // _____________________________________________________________________ -IdTable Permutation::scan(const ScanSpecification& scanSpec, - ColumnIndicesRef additionalColumns, - const CancellationHandle& cancellationHandle, - const DeltaTriples& deltaTriples, - const LimitOffsetClause& limitOffset) const { +IdTable Permutation::scan( + const ScanSpecification& scanSpec, ColumnIndicesRef additionalColumns, + const CancellationHandle& cancellationHandle, + const DeltaTriples& deltaTriples, const LimitOffsetClause& limitOffset, + std::optional> blocks) const { if (!isLoaded_) { throw std::runtime_error("This query requires the permutation " + readableName_ + ", which was not loaded"); @@ -64,9 +64,11 @@ IdTable Permutation::scan(const ScanSpecification& scanSpec, const auto& p = getActualPermutation(scanSpec); - return p.reader().scan(scanSpec, p.meta_.blockData(), additionalColumns, - cancellationHandle, locatedTriples(deltaTriples), - limitOffset); + return p.reader().scan( + scanSpec, + blocks.has_value() ? std::move(blocks.value()) : p.meta_.blockData(), + additionalColumns, cancellationHandle, locatedTriples(deltaTriples), + limitOffset); } // _____________________________________________________________________ diff --git a/src/index/Permutation.h b/src/index/Permutation.h index 93cad7e775..5512fe2a5a 100644 --- a/src/index/Permutation.h +++ b/src/index/Permutation.h @@ -67,7 +67,9 @@ class Permutation { ColumnIndicesRef additionalColumns, const CancellationHandle& cancellationHandle, const DeltaTriples& deltaTriples, - const LimitOffsetClause& limitOffset = {}) const; + const LimitOffsetClause& limitOffset = {}, + std::optional> blocks = + std::nullopt) const; // For a given relation, determine the `col1Id`s and their counts. This is // used for `computeGroupByObjectWithCount`. The `col0Id` must have metadata diff --git a/test/CompressedBlockPrefilteringTest.cpp b/test/CompressedBlockPrefilteringTest.cpp index 72d7c6754f..6089ba4ca0 100644 --- a/test/CompressedBlockPrefilteringTest.cpp +++ b/test/CompressedBlockPrefilteringTest.cpp @@ -180,6 +180,12 @@ class TestPrefilterExprOnBlockMetadata : public ::testing::Test { ::testing::HasSubstr(expected)); } + // Assert that the PrefilterExpression tree is properly copied when calling + // method clone. + auto makeTestClone(std::unique_ptr expr) { + ASSERT_EQ(*expr, *expr->clone()); + } + // Check that the provided expression prefilters the correct blocks. auto makeTest(std::unique_ptr expr, std::vector&& expected) { @@ -568,6 +574,26 @@ TEST_F(TestPrefilterExprOnBlockMetadata, testWithOneBlockMetadataValue) { EXPECT_EQ(expr->evaluate(input, 2), std::vector{}); } +//______________________________________________________________________________ +// Test method clone. clone() creates a copy of the complete PrefilterExpression +// tree. +TEST_F(TestPrefilterExprOnBlockMetadata, testMethodClonePrefilterExpression) { + makeTestClone(lt(VocabId(10))); + makeTestClone(gt(referenceDate2)); + makeTestClone(andExpr(lt(VocabId(20)), gt(VocabId(10)))); + makeTestClone(neq(IntId(10))); + makeTestClone(orExpr(eq(IntId(10)), neq(DoubleId(10)))); + makeTestClone(notExpr(ge(referenceDate1))); + makeTestClone(notExpr(notExpr(neq(VocabId(0))))); + makeTestClone(notExpr(andExpr(eq(IntId(10)), neq(DoubleId(10))))); + makeTestClone(orExpr(orExpr(eq(VocabId(101)), lt(IntId(100))), + andExpr(gt(referenceDate1), lt(referenceDate2)))); + makeTestClone(andExpr(andExpr(neq(IntId(10)), neq(DoubleId(100.23))), + orExpr(gt(DoubleId(0.001)), lt(IntId(250))))); + makeTestClone(orExpr(orExpr(eq(VocabId(101)), lt(IntId(100))), + notExpr(andExpr(lt(VocabId(0)), neq(IntId(100)))))); +} + //______________________________________________________________________________ //______________________________________________________________________________ // TEST SECTION 2 @@ -619,18 +645,26 @@ const auto makeLiteralSparqlExpr = } }; +//______________________________________________________________________________ +auto getExpr = [](const auto& variantVal) -> std::unique_ptr { + return makeLiteralSparqlExpr(variantVal); +}; + //______________________________________________________________________________ template std::unique_ptr makeRelationalSparqlExprImpl( const RelValues& child0, const RelValues& child1) { - auto getExpr = - [](const auto& variantVal) -> std::unique_ptr { - return makeLiteralSparqlExpr(variantVal); - }; return std::make_unique(std::array{ std::visit(getExpr, child0), std::visit(getExpr, child1)}); }; +//______________________________________________________________________________ +std::unique_ptr makeStringStartsWithSparqlExpression( + const RelValues& child0, const RelValues& child1) { + return makeStrStartsExpression(std::visit(getExpr, child0), + std::visit(getExpr, child1)); +}; + //______________________________________________________________________________ template std::unique_ptr makeLogicalBinaryPrefilterExprImpl( @@ -665,6 +699,10 @@ constexpr auto orSprqlExpr = &makeOrExpression; // NOT (`!`, `SparqlExpression`) constexpr auto notSprqlExpr = &makeUnaryNegateExpression; +//______________________________________________________________________________ +// Create SparqlExpression `STRSTARTS`. +constexpr auto strStartsSprql = &makeStringStartsWithSparqlExpression; + // ASSERT EQUALITY //______________________________________________________________________________ const auto equalityCheckPrefilterVectors = @@ -1140,6 +1178,19 @@ TEST(SparqlExpression, getEmptyPrefilterForMoreComplexSparqlExpressions) { eqSprql(Variable{"?country"}, VocabId(20)))))))); } +// Test PrefilterExpression creation for SparqlExpression STRSTARTS +//______________________________________________________________________________ +TEST(SparqlExpression, getPrefilterExprForStrStartsExpr) { + const auto varX = Variable{"?x"}; + const auto varY = Variable{"?y"}; + evalAndEqualityCheck(strStartsSprql(varX, VocabId(0)), + pr(ge(VocabId(0)), varX)); + evalAndEqualityCheck(strStartsSprql(VocabId(0), varX), + pr(le(VocabId(0)), varX)); + evalAndEqualityCheck(strStartsSprql(varX, varY)); + evalAndEqualityCheck(strStartsSprql(VocabId(0), VocabId(10))); +} + // Test that the conditions required for a correct merge of child // PrefilterExpressions are properly checked during the PrefilterExpression // construction procedure. This check is applied in the SparqlExpression (for From f489b247f12b29c3a556f39fdb40ff6583d1fa47 Mon Sep 17 00:00:00 2001 From: realHannes Date: Mon, 11 Nov 2024 23:42:30 +0100 Subject: [PATCH 50/50] few additional adaptations --- src/engine/Filter.cpp | 11 +++---- src/engine/IndexScan.cpp | 9 +++--- src/engine/IndexScan.h | 4 +-- src/engine/Operation.h | 10 +++++- .../RelationalExpressions.cpp | 31 +++++++++---------- .../sparqlExpressions/StringExpressions.cpp | 18 +++++------ src/index/CompressedBlockPrefiltering.cpp | 7 ++--- src/index/Permutation.cpp | 3 +- 8 files changed, 49 insertions(+), 44 deletions(-) diff --git a/src/engine/Filter.cpp b/src/engine/Filter.cpp index 1e0dd2b443..be8009b617 100644 --- a/src/engine/Filter.cpp +++ b/src/engine/Filter.cpp @@ -48,12 +48,11 @@ string Filter::getDescriptor() const { void Filter::setPrefilterExpressionForIndexScanChildren() { const std::vector& prefilterVec = _expression.getPrefilterExpressionForMetadata(); - this->forAllDescendants( - [&prefilterVec](const QueryExecutionTree* ptr) -> void { - if (ptr) { - ptr->setPrefilterExpression(prefilterVec); - } - }); + this->forAllDescendants([&prefilterVec](const QueryExecutionTree* ptr) { + if (ptr) { + ptr->setPrefilterExpression(prefilterVec); + } + }); } // _____________________________________________________________________________ diff --git a/src/engine/IndexScan.cpp b/src/engine/IndexScan.cpp index 80f593d4d9..650fc3adc3 100644 --- a/src/engine/IndexScan.cpp +++ b/src/engine/IndexScan.cpp @@ -157,7 +157,7 @@ VariableToColumnMap IndexScan::computeVariableToColumnMap() const { //______________________________________________________________________________ std::vector IndexScan::applyFilterBlockMetadata( - const std::vector& blocks) const { + std::vector&& blocks) const { std::ranges::for_each(prefilters_, [&blocks](const PrefilterIndexPair& pair) { pair.first->evaluate(blocks, pair.second); }); @@ -276,7 +276,7 @@ ScanSpecificationAsTripleComponent IndexScan::getScanSpecificationTc() const { // _____________________________________________________________________________ Permutation::IdTableGenerator IndexScan::getLazyScan( - std::vector blocks) const { + std::vector&& blocks) const { // If there is a LIMIT or OFFSET clause that constrains the scan // (which can happen with an explicit subquery), we cannot use the prefiltered // blocks, as we currently have no mechanism to include limits and offsets @@ -344,7 +344,8 @@ IndexScan::lazyScanForJoinOfTwoScans(const IndexScan& s1, const IndexScan& s2) { auto [blocks1, blocks2] = CompressedRelationReader::getBlocksForJoin( metaBlocks1.value(), metaBlocks2.value()); - std::array result{s1.getLazyScan(blocks1), s2.getLazyScan(blocks2)}; + std::array result{s1.getLazyScan(std::move(blocks1)), + s2.getLazyScan(std::move(blocks2))}; result[0].details().numBlocksAll_ = metaBlocks1.value().blockMetadata_.size(); result[1].details().numBlocksAll_ = metaBlocks2.value().blockMetadata_.size(); return result; @@ -365,7 +366,7 @@ Permutation::IdTableGenerator IndexScan::lazyScanForJoinOfColumnWithScan( auto blocks = CompressedRelationReader::getBlocksForJoin(joinColumn, metaBlocks1.value()); - auto result = getLazyScan(blocks); + auto result = getLazyScan(std::move(blocks)); result.details().numBlocksAll_ = metaBlocks1.value().blockMetadata_.size(); return result; } diff --git a/src/engine/IndexScan.h b/src/engine/IndexScan.h index 6055b5a1a0..540acd5fa1 100644 --- a/src/engine/IndexScan.h +++ b/src/engine/IndexScan.h @@ -147,7 +147,7 @@ class IndexScan final : public Operation { // Filter relevant `CompressedBlockMetadata` blocks by applying the // `PrefilterExpression`s from `prefilters_`. std::vector applyFilterBlockMetadata( - const std::vector& blocks) const; + std::vector&& blocks) const; // Return the (lazy) `IdTable` for this `IndexScan` in chunks. Result::Generator chunkedIndexScan() const; @@ -157,6 +157,6 @@ class IndexScan final : public Operation { // Helper functions for the public `getLazyScanFor...` methods and // `chunkedIndexScan` (see above). Permutation::IdTableGenerator getLazyScan( - std::vector blocks) const; + std::vector&& blocks) const; std::optional getMetadataForScan() const; }; diff --git a/src/engine/Operation.h b/src/engine/Operation.h index d1004ceace..8e642ed442 100644 --- a/src/engine/Operation.h +++ b/src/engine/Operation.h @@ -79,7 +79,15 @@ class Operation { // Set `PrefilterExpression`s (for `IndexScan`). virtual void setPrefilterExpression( - const std::vector&){}; + const std::vector&){ + // The prefiltering procedure is implemented by applying the + // PrefilterExpressions directly on the CompressedBlockMetadata. + // This is currently done while performing the result computation for + // IndexScan. + // (1) Overwrite this method for the derived IndexScan class. + // (2) The default method for all other derived classes is implemented + // here, no PrefilterExpressions need to be set. + }; // Apply the provided void function for all descendants. The given function // must be invocable with a `const QueryExecutionTree*` object. diff --git a/src/engine/sparqlExpressions/RelationalExpressions.cpp b/src/engine/sparqlExpressions/RelationalExpressions.cpp index 4f2f28f80e..bb349878da 100644 --- a/src/engine/sparqlExpressions/RelationalExpressions.cpp +++ b/src/engine/sparqlExpressions/RelationalExpressions.cpp @@ -448,22 +448,21 @@ RelationalExpression::getPrefilterExpressionForMetadata( }; const auto tryGetPrefilterExprVariablePairVec = - [&mirroredExpression]( - const SparqlExpression* child0, const SparqlExpression* child1, - bool reversed) -> std::vector { - std::vector resVec{}; - if (const auto* variable = - dynamic_cast(child0)) { - if (const auto* valueId = dynamic_cast(child1)) { - resVec.emplace_back(std::make_pair( - reversed ? mirroredExpression(valueId->value()) - : std::make_unique>( - valueId->value()), - variable->value())); - } - } - return resVec; - }; + [&mirroredExpression](const SparqlExpression* child0, + const SparqlExpression* child1, bool reversed) { + std::vector resVec{}; + if (const auto* variable = + dynamic_cast(child0)) { + if (const auto* valueId = dynamic_cast(child1)) { + resVec.emplace_back( + reversed ? mirroredExpression(valueId->value()) + : std::make_unique>( + valueId->value()), + variable->value()); + } + } + return resVec; + }; // Option 1: // RelationalExpression containing a VariableExpression as the first child // and an IdExpression as the second child. diff --git a/src/engine/sparqlExpressions/StringExpressions.cpp b/src/engine/sparqlExpressions/StringExpressions.cpp index 85753e96f8..2392ec2b18 100644 --- a/src/engine/sparqlExpressions/StringExpressions.cpp +++ b/src/engine/sparqlExpressions/StringExpressions.cpp @@ -244,23 +244,23 @@ class StrStartsExpressionImpl : public NaryExpression { AD_CORRECTNESS_CHECK(this->N == 2); const SparqlExpression* child0 = this->getNthChild(0).value(); const SparqlExpression* child1 = this->getNthChild(1).value(); - const auto getPrefilterExprVariableVec = - [](const SparqlExpression* child0, const SparqlExpression* child1, - bool startsWithVar) -> std::vector { + const auto getPrefilterExprVariableVec = [](const SparqlExpression* child0, + const SparqlExpression* child1, + bool startsWithVar) { using namespace prefilterExpressions; std::vector resVec{}; if (const auto* variable = dynamic_cast(child0)) { if (const auto* valueId = dynamic_cast(child1)) { !startsWithVar - // make {<(>= VocabId(n)), ?var>} (Option 1) - ? resVec.emplace_back(std::make_pair( + // Will return: {<(>= VocabId(n)), ?var>} (Option 1) + ? resVec.emplace_back( std::make_unique(valueId->value()), - variable->value())) - // make {<(<= VocabId(n)), ?var>} (Option 2) - : resVec.emplace_back(std::make_pair( + variable->value()) + // Will return: {<(<= VocabId(n)), ?var>} (Option 2) + : resVec.emplace_back( std::make_unique(valueId->value()), - variable->value())); + variable->value()); } } return resVec; diff --git a/src/index/CompressedBlockPrefiltering.cpp b/src/index/CompressedBlockPrefiltering.cpp index 6300d12307..f7b7af5381 100644 --- a/src/index/CompressedBlockPrefiltering.cpp +++ b/src/index/CompressedBlockPrefiltering.cpp @@ -269,7 +269,7 @@ std::vector RelationalExpression::evaluateImpl( template bool RelationalExpression::operator==( const PrefilterExpression& other) const { - const RelationalExpression* otherRelational = + const auto* otherRelational = dynamic_cast*>(&other); if (!otherRelational) { return false; @@ -333,7 +333,7 @@ std::vector LogicalExpression::evaluateImpl( template bool LogicalExpression::operator==( const PrefilterExpression& other) const { - const LogicalExpression* otherlogical = + const auto* otherlogical = dynamic_cast*>(&other); if (!otherlogical) { return false; @@ -382,8 +382,7 @@ std::vector NotExpression::evaluateImpl( //______________________________________________________________________________ bool NotExpression::operator==(const PrefilterExpression& other) const { - const NotExpression* otherNotExpression = - dynamic_cast(&other); + const auto* otherNotExpression = dynamic_cast(&other); if (!otherNotExpression) { return false; } diff --git a/src/index/Permutation.cpp b/src/index/Permutation.cpp index cbae8ce499..c331c3a296 100644 --- a/src/index/Permutation.cpp +++ b/src/index/Permutation.cpp @@ -66,8 +66,7 @@ IdTable Permutation::scan( const auto& p = getActualPermutation(scanSpec); return p.reader().scan( - scanSpec, - blocks.has_value() ? std::move(blocks.value()) : p.meta_.blockData(), + scanSpec, blocks.has_value() ? blocks.value() : p.meta_.blockData(), additionalColumns, cancellationHandle, getLocatedTriplesForPermutation(locatedTriplesSnapshot), limitOffset); }