diff --git a/src/engine/ExportQueryExecutionTrees.cpp b/src/engine/ExportQueryExecutionTrees.cpp
index 873bd21ae7..515e08830f 100644
--- a/src/engine/ExportQueryExecutionTrees.cpp
+++ b/src/engine/ExportQueryExecutionTrees.cpp
@@ -784,16 +784,17 @@ ExportQueryExecutionTrees::computeResultAsQLeverJSON(
qet, query.constructClause().triples_, query._limitOffset,
std::move(result), std::move(cancellationHandle));
- size_t resultSize = 0;
+ size_t numBindingsYielded = 0;
for (const std::string& b : bindings) {
- if (resultSize > 0) [[likely]] {
+ if (numBindingsYielded > 0) [[likely]] {
co_yield ",";
}
co_yield b;
- ++resultSize;
+ ++numBindingsYielded;
}
RuntimeInformation runtimeInformation = qet.getRootOperation()->runtimeInfo();
+ size_t resultSizeBeforeImplicitLimit = runtimeInformation.numRows_;
runtimeInformation.addLimitOffsetRow(query._limitOffset, false);
auto timeResultComputation =
@@ -805,7 +806,16 @@ ExportQueryExecutionTrees::computeResultAsQLeverJSON(
qet.getRootOperation()->getRuntimeInfoWholeQuery());
jsonSuffix["runtimeInformation"]["query_execution_tree"] =
nlohmann::ordered_json(runtimeInformation);
- jsonSuffix["resultsize"] = resultSize;
+ // TODO: This is a simple hack to get the result size for typical SELECT
+ // queries. When the final operation is lazy, it will be the size of the
+ // blocks produced by that operation (which is a lower bound on the size of
+ // the full result). For CONSTRUCT queries, it will be at most the implicit
+ // limit. The proper solution is to reinstate the `send` parameter
+ // (independently from the LIMIT), and if it is set, send only that many
+ // results but compute the full result size.
+ jsonSuffix["resultsize"] = query.hasSelectClause()
+ ? resultSizeBeforeImplicitLimit
+ : numBindingsYielded;
jsonSuffix["time"]["total"] =
absl::StrCat(requestTimer.msecs().count(), "ms");
jsonSuffix["time"]["computeResult"] =
diff --git a/test/ExportQueryExecutionTreesTest.cpp b/test/ExportQueryExecutionTreesTest.cpp
index ed8482d66c..ce10671092 100644
--- a/test/ExportQueryExecutionTreesTest.cpp
+++ b/test/ExportQueryExecutionTreesTest.cpp
@@ -1067,7 +1067,7 @@ TEST(ExportQueryExecutionTrees, LimitOffset) {
g
)" + xmlTrailer;
TestCaseSelectQuery testCaseLimitOffset{
- kg, objectQuery, 2,
+ kg, objectQuery, 4,
// TSV
"?s\n"
"\n"
@@ -1421,7 +1421,8 @@ TEST(ExportQueryExecutionTrees, verifyQleverJsonContainsValidMetadata) {
auto& runtimeInformation = runtimeInformationWrapper["query_execution_tree"];
EXPECT_EQ(runtimeInformation["result_cols"], 3);
EXPECT_EQ(runtimeInformation["result_rows"], 4);
- EXPECT_EQ(json["resultsize"], 4);
+ // This number is an implementation detail for lazy results and may change.
+ EXPECT_EQ(json["resultsize"], 6);
auto& timingInformation = json["time"];
EXPECT_GE(toChrono(timingInformation["total"].get()), 1ms);
// Ensure result is not returned in microseconds and subsequently interpreted