Main repo for zio-arangodb
ArangoDB supports 3 type of API's:
- Velocypack over Velocystream (sockets)
- Velocypack over HTTP
- JSON over HTTP
This library is built on top of ZIO and is designed in a way it can (potentially) implement all protocols. Current version supports JSON over HTTP only using zio-http as underlying client.
Experimental: trying to support zio-schema, instead of using JSON encoder/decoder you would be able to use any type that has the typeclass Schema
Install:
import sbt._
object Dependencies {
val zioArangoV = "0.0.3"
}
object Libraries {
val zioArangoHttp = "io.funkode" %% "zio-arangodb-http" % zioArangoV
}
Example of usage:
import io.funkode.arangodb.*
import io.funkode.arangodb.http.*
import zio.*
import zio.Console.*
import zio.http.Client
import zio.http.Middleware.*
import zio.json.*
object Main extends ZIOAppDefault:
import model.* // arango API types
import JsonCodecs.given // json codecs for arango API types
val testDb = DatabaseName("test")
def app =
for
db <- ArangoClientJson.database(testDb).createIfNotExist()
dbInfo <- db.info
_ <- printLine(s"""Database info: $dbInfo""")
collections <- db.collections(true)
_ <- printLine(s"""Database collections: ${collections.map(_.name).mkString(", ")}""")
_ <- printLine(s"""Press any key to exit""")
_ <- readLine
yield ()
def run = app.provide(
ArangoConfiguration.default, // check reference.conf (HOCON)
ArangoClientJson.live, // JSON over HTTP
Client.default, // zio-http client
Scope.default
)
To do IT testing you can use ArangoClientJson.testcontainers
instead of live
.
It will add an ArangoContainer
layer running on a random port and a client already configured against the instance
Example from it tests (check code for more examples):
import io.funkode.arangodb.*
import io.funkode.arangodb.http.*
import io.funkode.arangodb.model.*
import io.funkode.velocypack.VPack
import zio.*
import zio.http.Client
import zio.json.*
import zio.test.*
object ArangoDatabaseIT extends ZIOSpecDefault:
import JsonCodecs.given
import VPack.*
import docker.ArangoContainer
override def spec: Spec[TestEnvironment, Any] =
suite("ArangoDB client should")(
test("Create and drop a database") {
for
container <- ZIO.service[ArangoContainer]
_ <- Console.printLine(s"ArangoDB container running on port ${container.container.getFirstMappedPort.nn}")
databaseApi <- ArangoClientJson.database("pets").create()
dataInfo <- databaseApi.info
deleteResult <- databaseApi.drop
yield assertTrue(dataInfo.name == testDatabase.name) &&
assertTrue(!dataInfo.isSystem) &&
assertTrue(deleteResult)
}).provideShared(
Scope.default,
ArangoConfiguration.default,
Client.default,
ArangoClientJson.testContainers
)
ServerInfo API link
High level methods:
def databases: List[DatabaseName]
- list of server databasesdef version(details: Boolean = false): ServerVersion
- server version, license type, etc
Example of usage:
for
databases <- ArangoClientJson.serverInfo().databases
yield databases
Database API link
High level methods:
def name: DatabaseName
- name of current databasedef info: DatabaseInfo
- get database infodef create(users: List[DatabaseCreate.User]): ArangoDatabase
- create this database, will fail if already existdef createIfNotExist(users: List[DatabaseCreate.User]): ArangoDatabase
def drop: Boolean
- drop current databasedef collections: List[CollectionInfo]
- list of database collectionsdef graphs: List[GraphInfo]
- list of database graphsdef collection(name: CollectionName): ArangoCollection
- access to collection API
Example of usage:
val testDb = DatabaseName("test")
for
dbApi <- ArangoClientJson.database(testDb).createIfNotExist()
databaseInfo <- dbApi.info
collections <- dbApi.collections()
yield (databaseInfo, collections)
Collection API link
High level methods:
def database: DatabaseName
- current database namedef name: CollectionName
- current collection namedef info: CollectionInfo
- collection infodef create(setup: CollectionCreate => CollectionCreate)
- create collectiondef createIfNotExist(setup: CollectionCreate => CollectionCreate)
- create collection if not existdef drop(isSystem: Boolean = false): DeleteResult
- drop collection
Example of usage:
for
collection <- ArangoClientJson.collection("pets").createIfNotExist
collectionInfo <- collection.info
yield collectionInfo
Documents API link
High level methods:
def database: DatabaseName
- current database namedef collection: CollectionName
- current collection namedef count: Long
- number of documents for this collectiondef insert[T](document: T, ...): Document[T]
- insert new document, retrievesDocument[T]
which has internal id, key and revisiondef create[T](documents: List[T], ...): List[Document[T]]
- insert list of documentsdef replace[T](documents: List[T], ...): List[Document[T]]
- replace list of documentsdef update[T, P](patch: List[P], ...): List[Document[T]]
- patch list of documentsdef remove[T, K](keys: List[K], ...): List[Document[T]]
- remove list of documents by key
Example of usage:
for
documents <- ArangoClientJson.collection(petsCollection).createIfNotExist().documents
document <- documents.insert(pet1, true, true)
yield document
`
Document API link
High level methods:
def database: DatabaseName
- current database namedef handle: DocumentHandle
- current document handle (collection and key)def read[T: Decoder]: T
- retrieve current documentdef head: ArangoMessage.Header
- retrieve document metadata headersdef remove[T: Decoder]: Document[T]
- delete documentdef update[T, P](patch: P): Document[T]
- patch documentdef replate[T](document: T): Document[T]
- replace document
Example of usage:
for
collection <- ArangoClientJson.collection(petsCollection).createIfNotExist()
document = collection.document(petKey)
pet <- document.read[Pet]
yield pet
Query API link
High level methods:
def database: DatabaseName
- current database namebatchSize(value: Long): ArangoQuery
- setup batch size and return new querycount(value: Boolean): ArangoQuery
- set if query returns counttransaction(id: TransactionId): ArangoQuery
- setup transaction id for the querydef execute[T: Decoder]: QueryResults[T]
- execute query and return results (no pagination)def cursor[T: Decoder]: ArangoCursor[T]
- execute query and return results with cursordef stream[T: Decoder]: ZStream[Any, ArangoError, T]
- execute query and return streamed results
Examples of usage:
for
db <- ArangoClientJson.database(DatabaseName("test"))
queryCountries =
db
.query(
Query("FOR c IN @@col SORT c RETURN c")
.bindVar("@col", VString("countries"))
)
.count(true)
.batchSize(2) // setup the batch size
// query with cursor
cursor <- queryCountries.cursor[Country]
firstResults = cursor.body
// cursor has a next method to get next batch
more <- cursor.next
secondResults = more.body
// another approach is to use directly streams
firstStreamResults <- queryCountries.stream[Country].run(ZSink.take(4))
streamResultsCount <- queryCountries.stream[Country].run(ZSink.count)
streamedResults <- queryCountries.stream[Country]
yield streamedResults
Graph API link
High level methods:
def name: GraphName
- graph namedef create: GraphInfo
- create graphdef info: GraphInfo
- graph infodef vertexCollections: List[CollectionName]
- retrieves vertex collectionsdef addVertexCollection: GrahInfo
- add a vertex collectiondef removeVertexCollection: GrahInfo
- remove a vertex collectiondef collection: ArangoGraphCollection
- access to graph collection apidef vertex(handle: DocumentHandle): ArangoGraphVertex
- access to vertex apidef edge(handle: DocumentHandle): ArangoGraphEdge
- access to edge api
Example of usage:
val politics = GraphName("politics")
val allies = CollectionName("allies")
val countries = CollectionName("countries")
val graphEdgeDefinitions = List(GraphEdgeDefinition(allies, List(countries), List(countries)))
for
graph <- ArangoClientJson.graph(politics)
graphCreated <- graph.create(graphEdgeDefinitions)
alliesCol = ArangoClientJson.db.collection(allies)
_ <- alliesCol.documents.create(alliesOfEs)
queryAlliesOfSpain =
ArangoClientJson.db
.query(
Query("FOR c IN OUTBOUND @startVertex @@edge RETURN c")
.bindVar("startVertex", VString(es.unwrap))
.bindVar("@edge", VString(allies.unwrap))
)
resultQuery <- queryAlliesOfSpain.execute[Country].map(_.result)
yield resultQuery
Start ArangoDB with docker:
make startDockerArango
Run local test versus running ArangoDB instance (default port 8529):
make run