Skip to content

Instantly share code, notes, and snippets.

@dacr
Last active April 2, 2023 10:11
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save dacr/9063bf7c788c4f0872b33e9547f93af4 to your computer and use it in GitHub Desktop.
Save dacr/9063bf7c788c4f0872b33e9547f93af4 to your computer and use it in GitHub Desktop.
learning arangodb serialization / deserialization using the java client driver / published by https://github.com/dacr/code-examples-manager #6f65b437-59bf-49fd-acf1-ec32ae86d3fc/117bdc4db208752a098cda491263c2d19175ca4a
// summary : learning arangodb serialization / deserialization using the java client driver
// keywords : arangodb, graphdb, javadriver, @testable, serdes, serialization, deserialization
// publish : gist, corporate
// authors : David Crosson
// license : Apache NON-AI License Version 2.0 (https://raw.githubusercontent.com/non-ai-licenses/non-ai-licenses/main/NON-AI-APACHE2)
// id : 6f65b437-59bf-49fd-acf1-ec32ae86d3fc
// created-on : 2021-03-05T09:25:00Z
// managed-by : https://github.com/dacr/code-examples-manager
// execution : scala ammonite script (http://ammonite.io/) - run as follow 'amm scriptname.sc'
import $ivy.`com.fasterxml.jackson.core:jackson-databind:2.12.2`
import $ivy.`com.arangodb:arangodb-java-driver:6.9.1`
import $ivy.`com.arangodb:jackson-dataformat-velocypack:1.0.0`
import $ivy.`com.whisk::docker-testkit-impl-spotify:0.9.9`
import $ivy.`org.json4s::json4s-jackson:3.6.11`
import $ivy.`org.json4s::json4s-ext:3.6.11`
import $ivy.`org.scalatest::scalatest:3.2.6`
import $ivy.`org.slf4j:slf4j-simple:1.7.30`
import $ivy.`javax.activation:activation:1.1.1`
import org.scalatest._
import matchers.should.Matchers
import wordspec.AnyWordSpec
import OptionValues._
import com.arangodb._
import com.arangodb.mapping.ArangoJack
import com.arangodb.entity.DocumentField.Type
import com.arangodb.entity._
import com.arangodb.model.{AqlQueryOptions, CollectionCreateOptions, DocumentCreateOptions, StreamTransactionOptions, TransactionOptions}
import com.arangodb.util.MapBuilder
import com.fasterxml.jackson.annotation.JsonIgnoreProperties
import com.whisk.docker.{DockerContainer, DockerKit, DockerPortMapping, DockerReadyChecker}
import org.json4s.{DefaultFormats, FieldSerializer, Formats}
import org.json4s.jackson.Serialization.{read, write}
import org.json4s.FieldSerializer._
import java.io.Serializable
import scala.jdk.CollectionConverters._
import scala.beans.BeanProperty
// ==================================================================================================
// Provided DockerTestKit allow to have control over the used scalatest release
trait DockerTestKit extends BeforeAndAfterAll with org.scalatest.concurrent.ScalaFutures with DockerKit {
self: Suite =>
import org.scalatest.time.{Span, Seconds, Millis}
def dockerInitPatienceInterval =
PatienceConfig(scaled(Span(20, Seconds)), scaled(Span(10, Millis)))
def dockerPullImagesPatienceInterval =
PatienceConfig(scaled(Span(1200, Seconds)), scaled(Span(250, Millis)))
override def beforeAll(): Unit = {
super.beforeAll();
startAllOrFail()
}
override def afterAll(): Unit = {
stopAllQuietly();
super.afterAll()
}
}
// ==================================================================================================
trait DockerArangoDBService extends com.whisk.docker.impl.spotify.DockerKitSpotify {
val arangoUsername = "root"
val arangoPassword = "changeit"
val mappedArangoPort = 9529
lazy val arangoContainer = {
val env = Array(
s"ARANGO_ROOT_PASSWORD=$arangoPassword",
)
DockerContainer("arangodb:3.7.3")
.withEnv(env: _*)
.withPortMapping(8529 -> DockerPortMapping(Some(mappedArangoPort), "127.0.0.1"))
.withReadyChecker(DockerReadyChecker.LogLineContains("is ready for business"))
}
override def dockerContainers: List[DockerContainer] = arangoContainer :: Nil
}
// ==================================================================================================
@JsonIgnoreProperties(ignoreUnknown = true)
class Someone(
@BeanProperty var name: String,
@BeanProperty var age: Int,
) extends Serializable {
def this() = this(name = "", age = 0)
}
trait ArangoDocument {
val key: Option[String]
}
trait ArangoEdgeDocument extends ArangoDocument {
val from: String
val to: String
}
case class SomeoneAlt(key: Option[String], name: String, age: Int) extends ArangoDocument
trait Pet extends ArangoDocument {
val name:String
val birthYear:Int
}
case class Dog(key: Option[String], name:String, birthYear:Int) extends Pet
case class Cat(key: Option[String], name:String, birthYear:Int, lifeCount:Int) extends Pet
case class Animals(animals:List[Pet])
trait ArangoDatabaseAccessForTest extends DockerArangoDBService {
val documentRenamer = FieldSerializer[ArangoDocument](
renameTo("key", "_key"),renameFrom("_key", "key")
)
val edgeFromRenamer = FieldSerializer[ArangoEdgeDocument](
renameTo("from", "_from"), renameFrom("_from", "from")
)
val edgeToRenamer = FieldSerializer[ArangoEdgeDocument](
renameTo("to", "_to"), renameFrom("_to", "to")
)
implicit val formats = DefaultFormats.lossless + documentRenamer + edgeFromRenamer + edgeToRenamer
def uuid = java.util.UUID.randomUUID().toString
// raw queries just return json strings
def rawQueryCursor(query: String, vars: (String, Object)*)(implicit db: ArangoDatabase): ArangoCursor[String] = {
db.query(query, vars.toMap.asJava, null, classOf[String])
}
def rawQuery(query: String, vars: (String, Object)*)(implicit db: ArangoDatabase): Iterable[String] = {
db.query(query, vars.toMap.asJava, null, classOf[String]).asListRemaining().asScala
}
// normal queries returns json4s mapped json
def query[T](query: String, vars: (String, Object)*)(implicit db: ArangoDatabase, formats: Formats, mf: Manifest[T]): Iterable[T] = {
db.query(query, vars.toMap.asJava, null, classOf[String]).asListRemaining().asScala.map(read[T])
}
def withArango(testCode: ArangoDB => Any): Unit = {
val arango = {
new ArangoDB.Builder()
.host("127.0.0.1", mappedArangoPort)
.serializer(new ArangoJack)
.user(arangoUsername)
.password(arangoPassword)
.build()
}
try {
testCode(arango)
} finally {
arango.shutdown()
}
}
def withArangoAndDatabaseCleanup(testCode: (ArangoDB, String) => Any): Unit = {
withArango { arango =>
val databaseName = "random-" + java.util.UUID.randomUUID().toString
try {
testCode(arango, databaseName)
} finally {
val db: ArangoDatabase = arango.db(databaseName)
if (db.exists()) db.drop()
}
}
}
def withDatabase(testCode: ArangoDatabase => Any): Unit = {
withArango { arango =>
val databaseName = s"test-database-$uuid"
val db: ArangoDatabase = arango.db(databaseName)
if (db.exists()) db.drop()
arango.createDatabase(databaseName)
try {
testCode(db)
} finally {
db.drop()
}
}
}
def withCollection(testCode: ArangoCollection => Any): Unit = {
withDatabase { db =>
def collectionName = s"test-collection-$uuid".replaceAll("-", "_")
val collection = db.collection(collectionName)
if (!collection.exists) collection.create()
try {
testCode(collection)
} finally {
collection.drop()
}
}
}
}
class ArangoDbLearningTest extends AnyWordSpec with Matchers with ArangoDatabaseAccessForTest with DockerTestKit {
override def suiteName: String = "ArangoDbLearningTest"
"ArangoDb serialization deserialization (serdes)" can {
// -----------------------------------------------------------------------------------------------------------------
"when using no serdes, just raw strings" should {
"insert and get a document using raw json strings" in withCollection { collection =>
val result = collection.insertDocument("""{"name":"joe","age":42}""")
val doc = collection.getDocument(result.getKey, classOf[String])
info("using classOf[String] tells the driver to just work with raw json strings")
doc should include regex """"name":"joe""""
doc should include regex """"age":42"""
}
}
// -----------------------------------------------------------------------------------------------------------------
"when using unstructured documents" should {
"insert and get a unstructured document by using the BaseDocument approach" in withCollection { collection =>
info("using classOf[BaseDocument] tells the driver to use this generic data structure")
val doc = new BaseDocument()
doc.setKey("a")
doc.addAttribute("name", "joe")
doc.addAttribute("age", 42)
collection.insertDocument(doc)
collection.documentExists("a") shouldBe true
val gottenDoc = collection.getDocument("a", classOf[BaseDocument])
gottenDoc.getAttribute("name") shouldBe "joe"
gottenDoc.getAttribute("age") shouldBe 42
}
}
// -----------------------------------------------------------------------------------------------------------------
"when using java beans based documents (and so velocitypack)" should {
"insert and get a javabean document" in withCollection { collection =>
val doc = new Someone("joe", 42)
val result = collection.insertDocument(doc)
val gottenDoc = collection.getDocument(result.getKey, classOf[Someone])
gottenDoc.name shouldBe doc.name
gottenDoc.age shouldBe doc.age
//gottenDoc shouldEqual doc // issue here !
}
}
// -----------------------------------------------------------------------------------------------------------------
"when using an third party JSON library while relying on scala simplificication mechanisms" should {
"insert and get a JSON4S AST based document" in withCollection { collection =>
info("As the driver is able to work with raw json string, json4s json library integration becomes obvious")
val doc = SomeoneAlt(Some("a"), "joe", 42)
collection.insertDocument(write(doc))
val gottenDoc = read[SomeoneAlt](collection.getDocument("a", classOf[String]))
gottenDoc shouldEqual doc
info("note here that this code can be simplified thanks to advanced scala features such as implicits")
}
}
}
}
org.scalatest.tools.Runner.main(Array("-oDF", "-s", classOf[ArangoDbLearningTest].getName))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment