solher / arangolite Goto Github PK
View Code? Open in Web Editor NEWLightweight Golang driver for ArangoDB
License: MIT License
Lightweight Golang driver for ArangoDB
License: MIT License
I've just started using your library in a project and I really like the API. I can definitely understand wanting to avoid creating an overly complex API as it makes it complicated to maintain and becomes a barrier to adoption.
My use case for Arango is a bit different than the original intent of arangolite in that I need to be able to create documents and perform a few other API calls. Instead of adding bloat to your library, I would like to propose a change to the Runnable interface to allow for extensions to be created instead.
The change would be to export the methods in Runnable as follows:
type Runnable interface {
Description() string // Description shown in the logger
Generate() []byte // The body of the request
Path() string // The path where to send the request
Method() string // The HTTP method to use
}
This would require changing all the existing Runnables in your library but would then allow others to extend the library by simple creating new Runnable implementations and passing them to DB.Run
.
Let me know if this would be acceptable and I can create a PR to make the changes.
I trying to use this library as the basis for an ArangoDB migration tool. Creating a new database is harder than it should be since the DB properties are hidden. I'm sure I could get them with reflect, but I don't think that should be necessary. Would you be open to making the fields for NewDatabase public via an interface or just regular public?
can connect multiple coordinator, and polling to send requests?
Is this even possible using AQL only?
From here it says that currently there is no way to do this via AQL. But arangolite doesn't seems to have a way to remove edge using graph management interface?
When performing a CreateDatabase
where the database already exists, the database returns error code 1207 (https://docs.arangodb.com/3.0/Manual/Appendix/ErrorCodes.html, ERROR_ARANGO_DUPLICATE_NAME).
This error code is wrapped in a numberedError
at https://github.com/solher/arangolite/blob/v2.0.1/database.go#L207
However, at the next step, the numberedError
is wrapped in a statusCodedError
at https://github.com/solher/arangolite/blob/v2.0.1/database.go#L213
The statusCodedError
is what is returned by Send
.
My question is how should the error code be accessed?
HasErrorNum
will not work, since it requires a numberedError
as input, and casting to statusCodedError
to access the error
attribute doesn't work, since statusCodedError
is not exported.
Current result of arangolite.HasStatusCode(err)
is 409, but arangolite.HasErrorNum(e_status, 1207)
gives false.
Response unmarshalling requires wrapping the unmarshalled interface in a result tag.
`json"result"`
I believe this is because both the Unmarshal and UnmarshalResult methods are both operating on the raw response which is enclosed in a result tag.
func (r *response) Unmarshal(v interface{}) error {
if err := json.Unmarshal(r.raw, v); err != nil {
return errors.Wrap(err, "response unmarshalling failed")
}
return nil
}
func (r *response) UnmarshalResult(v interface{}) error {
if err := json.Unmarshal(r.raw, v); err != nil {
return errors.Wrap(err, "response result unmarshalling failed")
}
return nil
}
I believe UnmarshalResult should be operating on the parsedResponse instead.
Hi Fabien
When inserting a struct represeting a user, if any field of this user contains a simple quote, the resulting AQL query is invalid:
My code is the following one
userJsonBytes, err := json.Marshal(user)
if err != nil {
return err
}
userJson := string(userJsonBytes)
query := requests.NewAQL(` UPSERT { "id":"%s" } INSERT %s UPDATE %s IN %s `, user.ID, userJson, userJson, CollectionName)
I probably could find a workaround by creating a dedicated json marshaller; but shouldn't the driver prevent this issue?
The API changed between the version. For example Collects returns "results". The current library can't read that.
To prevent AQL injection in transaction, my understanding of the documentation is that we could use javascript variables.
I created a modified version of transaction.
Its usage:
transaction := arangolite.NewTransaction([]string{}, []string{}).
AddQuery("var1", "FOR d IN nodes FILTER d._key == {{.key}} RETURN d").
AddQuery("var2", "FOR d IN nodes FILTER d._id == {{.var1}}._id RETURN d.key").Return("var2")
transaction.Bind("key", 123)
result, err := db.Run(transaction)
But the result is not what I expect; It returns an empty array instead of [123].
The reason is that the replacement of var1 in the second query is not working.
Do you have any idea to fix this?
BTW, this solution does not seem to work with collection names. It looks like they cannot by defined by a javascript variable. But I assume that's a minor limitation.
We are using this lib and extracting large chunks of json. The problem is that these jsons does not have strict structure so they should be unmarshaled only to map[string]interface{}
for latter use, but in our scenario we do not need to unmarshal json at all and raw []byte
of json is ok. It would be nice:
func (db *Database) GetRaw(ctx context.Context, q Runnable) (json.RawMessage, error)
or equivalent.Or
followCursor
as exported so this can be easily achieved with Send
.Any opinions?
Creating something like pagination seems expensive with this way.
Say I run a query like FOR n IN nodes LIMIT 1000 RETURN 20
, by using Run
, all 1000 results if found in the DB are returned, and no cursor key to manage the request.
Using RunAsync
means having a goroutine followCursor
created everytime there is more results in the cursor, and I am forced to get everything by checking if there is more just not to have hanging goroutine(s) in the background, or close the channel(meaning I have to begin going through the cursor again).
If I had multiple clients in an application running the same query, I have to keep these goroutines even though none of them might be interested in going through the list of results.
I propose we return the result.ID
and let the user(developer) decide a proper method for getting the results.
When running the example below (derived from the readme example), I have two problems:
package main
import (
"encoding/json"
"fmt"
"github.com/solher/arangolite"
)
type Node struct {
arangolite.Document
}
type MyNode struct {
// The document handle. Format: ':collection/:key'
ID string `json:"_id,omitempty"`
// The document's revision token. Changes at each update.
Rev string `json:"_rev,omitempty"`
// The document's unique key.
Key string `json:"_key,omitempty"`
}
func main() {
db := arangolite.New().
LoggerOptions(false, false, false).
Connect("http://localhost:8529", "_system", "root", "")
db.Run(&arangolite.CreateDatabase{
Name: "testDB",
Users: []map[string]interface{}{
{"username": "root", "passwd": ""},
{"username": "user", "passwd": ""},
},
})
db.SwitchDatabase("testDB").SwitchUser("user", "")
db.Run(&arangolite.CreateCollection{Name: "nodes"})
// The Run method returns all the query results of every batches
// available in the cursor as a slice of byte.
key1 := "48765564346"
key2 := "48765564347"
_, err := remove(db, key1)
_, err = remove(db, key2)
_, err = insert(db, key1)
_, err = insert(db, key2)
if (err != nil) {
return
}
q := arangolite.NewQuery(`
FOR n
IN nodes
FILTER n._key == "%s"
LIMIT 1
RETURN n
`, key1)
async, asyncErr := db.RunAsync(q)
if(asyncErr != nil){
fmt.Printf("asyncErr %v", asyncErr)
}
nodes := []Node{}
decoder := json.NewDecoder(async.Buffer())
for async.HasMore() {
batch := []Node{}
decoder.Decode(batch)
fmt.Printf("%v", decoder)
nodes = append(nodes, batch...)
}
fmt.Printf("%v", nodes)
}
func remove(db *arangolite.DB, key string) ([]byte, error) {
removeQuery := arangolite.NewQuery(`
REMOVE { _key: '%s' } IN nodes
`, key)
res, err := db.Run(removeQuery)
if(err != nil) {
fmt.Printf("Remove error %v", err)
}
return res, err
}
func insert(db *arangolite.DB, key string) ([]byte, error) {
node := MyNode{
ID: "nodes/" + key,
Rev: key,
Key: key,
}
value, marshallErr := json.Marshal(node)
if(marshallErr != nil) {
fmt.Printf("Insertion error %v. Cannot convert %v to JSON", marshallErr, value)
return nil, marshallErr
}
insertQuery := arangolite.NewQuery(`
INSERT %s
IN nodes
`, value)
insertResult, err := db.Run(insertQuery)
if (err != nil) {
fmt.Printf("Insertion error %v", err)
}
return insertResult, err
}
What did I get wrong?
The response
object returned by database.Send()
is passed to database.followCursor()
in the database.Run()
function.
When the response
object is created in senders.Send()
, the response's parsed (parsedResponse) attribute is unmarshalled
.
It seem the parsedResponse.Result
attribute is never populated, and is simply an empty array.
The problem then occurs in database.followCursor()
where the call to r.RawResult() always returns an empty array, and hence, the result is not unmarshalled into the v
input argument.
I'm I doing something wrong?
Hi
In the basic example, in the readme, I believe you should switch these 2 lines:
_, _ := db.Run(&arangolite.CreateCollection{Name: "nodes"})
db.SwitchDatabase("testDB").SwitchUser("user", "password")
Otherwise you create the collection in the wrong database
The new http api has a jwt auth method, it would be cool to add it. ๐
https://docs.arangodb.com/3.2/HTTP/BulkImports/
How can I use BulkImport feature in arangolite?
I will extend the driver. I already started with an edge creation feature. But something worries me: in your readme you suggest to add everything in requests.go. So this file could become very long...
I believe the code should be organised by around the business domain, and not the technical notions. By example the create edge request definition should be in edge.go.
Do you agree with this?
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.