Skip to content

Commit

Permalink
refactor: Introduce accessors for SPARQL SELECT results (#3238)
Browse files Browse the repository at this point in the history
  • Loading branch information
BalduinLandolt authored May 8, 2024
1 parent bee3b08 commit fafe48a
Show file tree
Hide file tree
Showing 16 changed files with 76 additions and 58 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -5783,7 +5783,7 @@ class OntologyResponderV2Spec extends CoreSpec with ImplicitSender {
)

assert(
actual.results.bindings.map(_.rowMap("cardinalityProp")).sorted == Seq(
actual.getColOrThrow("cardinalityProp").sorted == Seq(
"http://www.knora.org/ontology/0001/anything#testIntProp",
"http://www.knora.org/ontology/0001/anything#testTextProp",
),
Expand Down Expand Up @@ -6089,7 +6089,7 @@ class OntologyResponderV2Spec extends CoreSpec with ImplicitSender {
)

assert(
actual.results.bindings.map(_.rowMap("cardinalityProp")).sorted == Seq(
actual.getColOrThrow("cardinalityProp").sorted == Seq(
"http://www.knora.org/ontology/0001/freetest#hasBlueTestIntProp",
),
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -531,7 +531,7 @@ class ResourcesResponderV2Spec extends CoreSpec with ImplicitSender {
val actual = UnsafeZioRun.runOrThrow(
ZIO.serviceWithZIO[TriplestoreService](_.query(Select(sparql.v2.txt.getStandoffTagByUUID(uuid)))),
)
actual.results.bindings.map(_.rowMap("standoffTag")).toSet
actual.getColOrThrow("standoffTag").toSet
}

// The default timeout for receiving reply messages from actors.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -144,36 +144,34 @@ class TriplestoreServiceLiveSpec extends CoreSpec with ImplicitSender {
)

val msg = UnsafeZioRun.runOrThrow(ZIO.serviceWithZIO[TriplestoreService](_.query(Select(countTriplesQuery))))
afterLoadCount = msg.results.bindings.head.rowMap("no").toInt
afterLoadCount = msg.getFirstOrThrow("no").toInt
(afterLoadCount > 0) should ===(true)
}

"provide data receiving a Named Graph request" in {
val actual = UnsafeZioRun.runOrThrow(ZIO.serviceWithZIO[TriplestoreService](_.query(Select(namedGraphQuery))))
actual.results.bindings.nonEmpty should ===(true)
actual.nonEmpty should ===(true)
}

"execute an update" in {
val countTriplesBefore = UnsafeZioRun.runOrThrow(
ZIO
.serviceWithZIO[TriplestoreService](_.query(Select(countTriplesQuery)))
.map(_.results.bindings.head.rowMap("no").toInt),
.map(_.getFirstOrThrow("no").toInt),
)
countTriplesBefore should ===(afterLoadCount)

UnsafeZioRun.runOrThrow(ZIO.serviceWithZIO[TriplestoreService](_.query(Update(insertQuery))))

val checkInsertActual = UnsafeZioRun.runOrThrow(
ZIO
.serviceWithZIO[TriplestoreService](_.query(Select(checkInsertQuery)))
.map(_.results.bindings.size),
ZIO.serviceWithZIO[TriplestoreService](_.query(Select(checkInsertQuery))).map(_.size),
)
checkInsertActual should ===(3)

afterChangeCount = UnsafeZioRun.runOrThrow(
ZIO
.serviceWithZIO[TriplestoreService](_.query(Select(countTriplesQuery)))
.map(_.results.bindings.head.rowMap("no").toInt),
.map(_.getFirstOrThrow("no").toInt),
)
(afterChangeCount - afterLoadCount) should ===(3)
}
Expand All @@ -182,7 +180,7 @@ class TriplestoreServiceLiveSpec extends CoreSpec with ImplicitSender {
val countTriplesBefore = UnsafeZioRun.runOrThrow(
ZIO
.serviceWithZIO[TriplestoreService](_.query(Select(countTriplesQuery)))
.map(_.results.bindings.head.rowMap("no").toInt),
.map(_.getFirstOrThrow("no").toInt),
)
countTriplesBefore should ===(afterChangeCount)

Expand All @@ -191,14 +189,14 @@ class TriplestoreServiceLiveSpec extends CoreSpec with ImplicitSender {
val countTriplesQueryActual = UnsafeZioRun.runOrThrow(
ZIO
.serviceWithZIO[TriplestoreService](_.query(Select(countTriplesQuery)))
.map(_.results.bindings.head.rowMap("no").toInt),
.map(_.getFirstOrThrow("no").toInt),
)
countTriplesQueryActual should ===(afterLoadCount)

val checkInsertActual = UnsafeZioRun.runOrThrow(
ZIO
.serviceWithZIO[TriplestoreService](_.query(Select(checkInsertQuery)))
.map(_.results.bindings.size),
.map(_.size),
)
checkInsertActual should ===(0)
}
Expand All @@ -208,7 +206,7 @@ class TriplestoreServiceLiveSpec extends CoreSpec with ImplicitSender {
val actual = UnsafeZioRun.runOrThrow(
ZIO
.serviceWithZIO[TriplestoreService](_.query(Select(textSearchQueryFusekiValueHasString)))
.map(_.results.bindings.size),
.map(_.size),
)
actual should ===(3)
}
Expand All @@ -219,7 +217,7 @@ class TriplestoreServiceLiveSpec extends CoreSpec with ImplicitSender {
val actual = UnsafeZioRun.runOrThrow(
ZIO
.serviceWithZIO[TriplestoreService](_.query(Select(textSearchQueryFusekiDRFLabel)))
.map(_.results.bindings.size),
.map(_.size),
)
actual should ===(1)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ class UpgradePluginPR1615Spec extends UpgradePluginSpec {
|""".stripMargin

val queryResult1: SparqlSelectResult = repository.doSelect(selectQuery = query1)
assert(queryResult1.results.bindings.isEmpty)
assert(queryResult1.isEmpty)

// Check that other data is still there.

Expand All @@ -45,7 +45,7 @@ class UpgradePluginPR1615Spec extends UpgradePluginSpec {
|""".stripMargin

val queryResult2: SparqlSelectResult = repository.doSelect(selectQuery = query2)
assert(queryResult2.results.bindings.nonEmpty)
assert(queryResult2.nonEmpty)

repository.shutDown()
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,15 +13,45 @@ import dsp.errors.InconsistentRepositoryDataException
* @param head the header of the response, containing the variable names.
* @param results the body of the response, containing rows of query results.
*/
case class SparqlSelectResult(head: SparqlSelectResultHeader, results: SparqlSelectResultBody) {
case class SparqlSelectResult(head: SparqlSelectResultHeader, results: SparqlSelectResultBody) { self =>

def getFirstRow: Option[VariableResultsRow] =
results.bindings.headOption

def getFirst(v: String): Option[String] =
results.bindings.headOption.flatMap(_.rowMap.get(v))

def getFirstOrThrow(v: String): String =
results.bindings.head.rowMap(v)

def getCol(v: String): Seq[String] =
self.flatMap(_.rowMap.get(v))

def getColOrThrow(v: String): Seq[String] =
self.map(_.rowMap(v))

def map[B](f: VariableResultsRow => B): Seq[B] =
results.bindings.map(f)

def flatMap[B](f: VariableResultsRow => Iterable[B]): Seq[B] =
results.bindings.flatMap(f)

def isEmpty: Boolean =
results.bindings.isEmpty

def nonEmpty: Boolean =
results.bindings.nonEmpty

def size: Int =
results.bindings.size

/**
* Returns the contents of the first row of results.
*
* @return a [[Map]] representing the contents of the first row of results.
*/
def getFirstRow: VariableResultsRow =
results.bindings.headOption match {
def getFirstRowOrThrow: VariableResultsRow =
getFirstRow match {
case Some(row: VariableResultsRow) => row
case None => throw InconsistentRepositoryDataException(s"A SPARQL query unexpectedly returned an empty result")
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ final case class IriService(
* @return `true` if the class is used.
*/
def isClassUsedInData(classIri: SmartIri): Task[Boolean] =
triplestore.query(Select(sparql.v2.txt.isClassUsedInData(classIri))).map(_.results.bindings.nonEmpty)
triplestore.query(Select(sparql.v2.txt.isClassUsedInData(classIri))).map(_.nonEmpty)

def checkOrCreateNewUserIri(entityIri: Option[UserIri]): Task[UserIri] =
for {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1083,7 +1083,7 @@ final case class ListsResponder(
def canDeleteListRequestADM(iri: ListIri): Task[CanDeleteListResponseADM] =
triplestore
.query(Select(sparql.admin.txt.canDeleteList(iri.value)))
.map(_.results.bindings.isEmpty)
.map(_.isEmpty)
.map(CanDeleteListResponseADM(iri.value, _))

/**
Expand Down Expand Up @@ -1129,7 +1129,7 @@ final case class ListsResponder(
def isNodeOrItsChildrenUsed(nodeIri: IRI, children: Seq[ListChildNodeADM]): Task[Unit] = {
def failIfInUse(nodeIri: IRI, failReason: String) = ZIO
.fail(BadRequestException(failReason))
.whenZIO(triplestore.query(Select(sparql.admin.txt.isNodeUsed(nodeIri))).map(_.results.bindings.nonEmpty))
.whenZIO(triplestore.query(Select(sparql.admin.txt.isNodeUsed(nodeIri))).map(_.nonEmpty))
failIfInUse(nodeIri, s"Node $nodeIri cannot be deleted, because it is in use.") *> {
ZIO.foreachDiscard(children) { child =>
failIfInUse(child.id, s"Node $nodeIri cannot be deleted, because its child ${child.id} is in use.")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -521,7 +521,7 @@ final case class PermissionsResponder(
)

/* get the iri of the retrieved permission */
val permissionIri = result.getFirstRow.rowMap("s")
val permissionIri = result.getFirstRowOrThrow.rowMap("s")

val groupedPermissionsQueryResponse: Map[IRI, Seq[IRI]] =
rows.groupBy(_.rowMap("p")).map { case (predicate, rows) =>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1565,7 +1565,7 @@ final case class ResourcesResponderV2(
// Do a SELECT prequery to get the IRIs of the resources that belong to the project.
prequery = sparql.v2.txt.getAllResourcesInProjectPrequery(projectId.value)
sparqlSelectResponse <- triplestore.query(Select(prequery))
mainResourceIris = sparqlSelectResponse.results.bindings.map(_.rowMap("resource"))
mainResourceIris = sparqlSelectResponse.getColOrThrow("resource")
// For each resource IRI return history events
historyOfResourcesAsSeqOfFutures: Seq[Task[Seq[ResourceAndValueHistoryEvent]]] =
mainResourceIris.map { resourceIri =>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -334,9 +334,7 @@ final case class SearchResponderV2Live(

// a sequence of resource IRIs that match the search criteria
// attention: no permission checking has been done so far
resourceIris: Seq[IRI] = prequeryResponse.results.bindings.map { (resultRow: VariableResultsRow) =>
resultRow.rowMap(FullTextSearchConstants.resourceVar.variableName)
}
resourceIris: Seq[IRI] = prequeryResponse.getColOrThrow(FullTextSearchConstants.resourceVar.variableName)

// If the prequery returned some results, prepare a main query.
mainResourcesAndValueRdfData <-
Expand Down Expand Up @@ -472,13 +470,11 @@ final case class SearchResponderV2Live(
_ <- // query response should contain one result with one row with the name "count"
ZIO
.fail(
GravsearchException(
s"Count query is expected to return exactly one row, but ${countResponse.results.bindings.size} given",
),
GravsearchException(s"Count query is expected to return exactly one row, but ${countResponse.size} given"),
)
.when(countResponse.results.bindings.size != 1)
.when(countResponse.size != 1)

count: String = countResponse.results.bindings.head.rowMap("count")
count: String = countResponse.getFirstOrThrow("count")

} yield ResourceCountV2(numberOfResources = count.toInt)

Expand Down Expand Up @@ -546,7 +542,7 @@ final case class SearchResponderV2Live(
.query(Select.gravsearch(prequerySparql))
.logError(s"Gravsearch timed out for prequery:\n$prequerySparql")

pageSizeBeforeFiltering: Int = prequeryResponseNotMerged.results.bindings.size
pageSizeBeforeFiltering: Int = prequeryResponseNotMerged.size

// Merge rows with the same main resource IRI. This could happen if there are unbound variables in a UNION.
prequeryResponse =
Expand All @@ -557,10 +553,7 @@ final case class SearchResponderV2Live(

// a sequence of resource IRIs that match the search criteria
// attention: no permission checking has been done so far
mainResourceIris: Seq[IRI] =
prequeryResponse.results.bindings.map { (resultRow: VariableResultsRow) =>
resultRow.rowMap(mainResourceVar.variableName)
}
mainResourceIris: Seq[IRI] = prequeryResponse.getColOrThrow(mainResourceVar.variableName)

mainQueryResults <-
if (mainResourceIris.nonEmpty) {
Expand Down Expand Up @@ -791,7 +784,7 @@ final case class SearchResponderV2Live(
offset = page * appConfig.v2.resourcesSequence.resultsPerPage,
)
sparqlSelectResponse <- triplestore.query(Select(prequery))
mainResourceIris: Seq[IRI] = sparqlSelectResponse.results.bindings.map(_.rowMap("resource"))
mainResourceIris: Seq[IRI] = sparqlSelectResponse.getColOrThrow("resource")

// Find out whether to query standoff along with text values. This boolean value will be passed to
// ConstructResponseUtilV2.makeTextValueContentV2.
Expand Down Expand Up @@ -876,11 +869,11 @@ final case class SearchResponderV2Live(
ZIO
.fail(
GravsearchException(
s"Fulltext count query is expected to return exactly one row, but ${countResponse.results.bindings.size} given",
s"Fulltext count query is expected to return exactly one row, but ${countResponse.size} given",
),
)
.when(countResponse.results.bindings.length != 1)
.as(countResponse.results.bindings.head.rowMap("count"))
.when(countResponse.size != 1)
.as(countResponse.getFirstOrThrow("count"))

} yield ResourceCountV2(count.toInt)

Expand Down Expand Up @@ -1042,9 +1035,7 @@ final case class SearchResponderV2Live(

// Construct a sequence of the distinct main resource IRIs in the query results, preserving the
// order of the result rows.
val mainResourceIris: Seq[IRI] = prequeryResponseNotMerged.results.bindings.map { (resultRow: VariableResultsRow) =>
resultRow.rowMap(mainResourceVar.variableName)
}.distinct
val mainResourceIris: Seq[IRI] = prequeryResponseNotMerged.getColOrThrow(mainResourceVar.variableName).distinct

// Arrange the merged rows in the same order.
val prequeryRowsMerged: Seq[VariableResultsRow] = mainResourceIris.map { resourceIri =>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1713,7 +1713,7 @@ final case class OntologyHelpersLive(

isOntologyUsedResponse <- triplestore.query(Select(isOntologyUsedSparql))

subjects = isOntologyUsedResponse.results.bindings.map(row => row.rowMap("s")).toSet
subjects = isOntologyUsedResponse.getColOrThrow("s").toSet
} yield subjects

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ final case class TopLeftCorrectionAction[A <: ProjectsWithBakfilesReport](
): IO[Option[Throwable], (Dimensions, InternalIri)] =
for {
result <- triplestoreService.query(checkDimensionsQuery(project, asset.id)).asSomeError
rowMap <- ZIO.fromOption(result.results.bindings.headOption.map(_.rowMap))
rowMap <- ZIO.fromOption(result.getFirstRow.map(_.rowMap))
iri <- ZIO.fromOption(rowMap.get("valueIri")).map(InternalIri.apply)
width <- ZIO.fromOption(rowMap.get("dimX").flatMap(_.toIntOption))
height <- ZIO.fromOption(rowMap.get("dimY").flatMap(_.toIntOption))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ final case class PredicateRepositoryLive(private val tripleStore: TriplestoreSer
): Task[List[(InternalIri, Int)]] =
tripleStore
.query(Select(countPropertyUsedWithClass(propertyIri, classIri)))
.map(_.results.bindings.map(row => (InternalIri(row.rowMap("subject")), row.rowMap("count").toInt)).toList)
.map(_.map(row => (InternalIri(row.rowMap("subject")), row.rowMap("count").toInt)).toList)
}

object PredicateRepositoryLive {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ final case class ResourceInfoRepoLive(triplestore: TriplestoreService) extends R
)

private def toResourceInfoList(result: SparqlSelectResult) =
ZIO.attempt(result.results.bindings.map(toResourceInfo).toList)
ZIO.attempt(result.map(toResourceInfo).toList)

private def toResourceInfo(row: VariableResultsRow) = {
val rowMap = row.rowMap
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -95,14 +95,13 @@ final case class RepositoryUpdater(triplestoreService: TriplestoreService) {
|}""".stripMargin,
),
)
.map(_.results.bindings.headOption.flatMap(_.rowMap.get("knoraBaseVersion")))
.map(_.getFirst("knoraBaseVersion"))
.flatMap {
case Some(knoraBaseVersion: String) =>
ZIO.foreach(_) { kb =>
ZIO
.fromOption(knoraBaseVersionFrom(knoraBaseVersion))
.orDieWith(_ => new InconsistentRepositoryDataException(s"Invalid repository version: $knoraBaseVersion"))
.map(Some(_))
case None => ZIO.none
.fromOption(knoraBaseVersionFrom(kb))
.orDieWith(_ => new InconsistentRepositoryDataException(s"Invalid repository version: $kb"))
}
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -188,7 +188,7 @@ object TriplestoreServiceInMemorySpec extends ZIOSpecDefault {
|""".stripMargin
for {
result <- ZIO.serviceWithZIO[TriplestoreService](_.query(Select(query)))
} yield assert(result.results.bindings.flatMap(_.rowMap.get("entity")))(
} yield assert(result.getCol("entity"))(
hasSameElements(List("http://anArticle")),
)
},
Expand All @@ -206,7 +206,7 @@ object TriplestoreServiceInMemorySpec extends ZIOSpecDefault {
|""".stripMargin
for {
result <- ZIO.serviceWithZIO[TriplestoreService](_.query(Select(query)))
} yield assert(result.results.bindings.flatMap(_.rowMap.get("entity")))(
} yield assert(result.getCol("entity"))(
hasSameElements(List("http://anArticle", "http://aJournalArticle")),
)
},
Expand All @@ -222,7 +222,7 @@ object TriplestoreServiceInMemorySpec extends ZIOSpecDefault {
|""".stripMargin
for {
result <- ZIO.serviceWithZIO[TriplestoreService](_.query(Select(query)))
} yield assertTrue(result.results.bindings.isEmpty)
} yield assertTrue(result.isEmpty)
},
test("find an existing thing") {
val query = """
Expand Down

0 comments on commit fafe48a

Please sign in to comment.