From 097ccfe3ce043f39fff7516aea67e7fddeca37ce Mon Sep 17 00:00:00 2001 From: Eike Kettner Date: Thu, 7 Mar 2024 14:51:12 +0100 Subject: [PATCH] Add delete-by-id to solr client --- .../renku/solr/client/DeleteByIdRequest.scala | 32 +++++++++++++++++++ .../io/renku/solr/client/SolrClient.scala | 2 ++ .../io/renku/solr/client/SolrClientImpl.scala | 7 ++++ .../io/renku/solr/client/SolrClientSpec.scala | 26 +++++++++++++-- 4 files changed, 65 insertions(+), 2 deletions(-) create mode 100644 modules/solr-client/src/main/scala/io/renku/solr/client/DeleteByIdRequest.scala diff --git a/modules/solr-client/src/main/scala/io/renku/solr/client/DeleteByIdRequest.scala b/modules/solr-client/src/main/scala/io/renku/solr/client/DeleteByIdRequest.scala new file mode 100644 index 00000000..a156e785 --- /dev/null +++ b/modules/solr-client/src/main/scala/io/renku/solr/client/DeleteByIdRequest.scala @@ -0,0 +1,32 @@ +/* + * Copyright 2024 Swiss Data Science Center (SDSC) + * A partnership between École Polytechnique Fédérale de Lausanne (EPFL) and + * Eidgenössische Technische Hochschule Zürich (ETHZ). + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.renku.solr.client + +import io.bullet.borer.derivation.MapBasedCodecs.deriveEncoder +import io.bullet.borer.{Encoder, Writer} + +final private[client] case class DeleteByIdRequest(id: String) + +private[client] object DeleteByIdRequest: + given Encoder[DeleteByIdRequest] = { + val e: Encoder[DeleteByIdRequest] = deriveEncoder[DeleteByIdRequest] + new Encoder[DeleteByIdRequest]: + override def write(w: Writer, value: DeleteByIdRequest) = + w.writeMap(Map("delete" -> value))(Encoder[String], e) + } diff --git a/modules/solr-client/src/main/scala/io/renku/solr/client/SolrClient.scala b/modules/solr-client/src/main/scala/io/renku/solr/client/SolrClient.scala index 5c43ad15..46de2fa0 100644 --- a/modules/solr-client/src/main/scala/io/renku/solr/client/SolrClient.scala +++ b/modules/solr-client/src/main/scala/io/renku/solr/client/SolrClient.scala @@ -38,6 +38,8 @@ trait SolrClient[F[_]]: def delete(q: QueryString): F[Unit] + def deleteById(id: String): F[Unit] + def insert[A: Encoder](docs: Seq[A]): F[InsertResponse] object SolrClient: diff --git a/modules/solr-client/src/main/scala/io/renku/solr/client/SolrClientImpl.scala b/modules/solr-client/src/main/scala/io/renku/solr/client/SolrClientImpl.scala index ba03ff68..f007c0ca 100644 --- a/modules/solr-client/src/main/scala/io/renku/solr/client/SolrClientImpl.scala +++ b/modules/solr-client/src/main/scala/io/renku/solr/client/SolrClientImpl.scala @@ -60,6 +60,13 @@ private class SolrClientImpl[F[_]: Async](config: SolrConfig, underlying: Client .flatTap(r => logger.trace(s"Solr delete response: $r")) .void + def deleteById(id: String): F[Unit] = + val req = Method.POST(DeleteByIdRequest(id), makeUpdateUrl).withBasicAuth(credentials) + underlying + .expectOr[InsertResponse](req)(ResponseLogging.Error(logger, req)) + .flatTap(r => logger.trace(s"Solr delete response: $r")) + .void + def insert[A: Encoder](docs: Seq[A]): F[InsertResponse] = val req = Method.POST(docs, makeUpdateUrl).withBasicAuth(credentials) underlying diff --git a/modules/solr-client/src/test/scala/io/renku/solr/client/SolrClientSpec.scala b/modules/solr-client/src/test/scala/io/renku/solr/client/SolrClientSpec.scala index bf68a458..1284e9cf 100644 --- a/modules/solr-client/src/test/scala/io/renku/solr/client/SolrClientSpec.scala +++ b/modules/solr-client/src/test/scala/io/renku/solr/client/SolrClientSpec.scala @@ -20,7 +20,7 @@ package io.renku.solr.client import cats.effect.IO import cats.syntax.all.* -import io.bullet.borer.derivation.MapBasedCodecs.deriveDecoder +import io.bullet.borer.derivation.MapBasedCodecs import io.bullet.borer.{Decoder, Encoder} import io.renku.solr.client.SolrClientSpec.Room import io.renku.solr.client.schema.* @@ -31,6 +31,8 @@ import org.scalacheck.effect.PropF import io.bullet.borer.Reader import org.scalacheck.Gen import io.renku.solr.client.facet.{Facet, Facets} +import io.bullet.borer.derivation.key +import scala.concurrent.duration.* class SolrClientSpec extends CatsEffectSuite @@ -92,6 +94,21 @@ class SolrClientSpec } yield () } + test("delete by id"): + withSolrClient().use { client => + for { + _ <- client.delete(QueryString("*:*")) + _ <- client.insert(Seq(SolrClientSpec.Person("p1", "John"))) + r <- client.query[SolrClientSpec.Person](QueryData(QueryString("*:*"))) + p = r.responseBody.docs.head + _ = assertEquals(p.id, "p1") + _ <- client.deleteById("p1") + r2 <- client.query[SolrClientSpec.Person](QueryData(QueryString("*:*"))) + _ <- IO.sleep(50.millis) // seems to be necessary on ci + _ = assert(r2.responseBody.docs.isEmpty) + } yield () + } + object SolrClientSpec: case class Room(roomName: String, roomDescription: String, roomSeats: Int) object Room: @@ -104,5 +121,10 @@ object SolrClientSpec: seats <- Gen.choose(15, 350) } yield Room(name, descr, seats) - given Decoder[Room] = deriveDecoder + given Decoder[Room] = MapBasedCodecs.deriveDecoder given Encoder[Room] = EncoderSupport.deriveWithDiscriminator[Room] + + case class Person(id: String, @key("name_s") name: String) + object Person: + given Decoder[Person] = MapBasedCodecs.deriveDecoder + given Encoder[Person] = MapBasedCodecs.deriveEncoder