add arango package with aql generation
This commit is contained in:
@@ -0,0 +1,22 @@
|
|||||||
|
package arango
|
||||||
|
|
||||||
|
import (
|
||||||
|
"regexp"
|
||||||
|
)
|
||||||
|
|
||||||
|
// https://docs.arangodb.com/3.11/aql/fundamentals/bind-parameters/#syntax
|
||||||
|
var paramRegexp = regexp.MustCompile("@{1,2}([A-z0-9_]+)")
|
||||||
|
|
||||||
|
// ReadParams reads out named parameters from an AQL string.
|
||||||
|
func ReadParams(input string) []string {
|
||||||
|
params := []string{}
|
||||||
|
|
||||||
|
matches := paramRegexp.FindAllStringSubmatch(input, -1)
|
||||||
|
if matches != nil {
|
||||||
|
for _, match := range matches {
|
||||||
|
params = append(params, match[1])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return params
|
||||||
|
}
|
||||||
@@ -0,0 +1,35 @@
|
|||||||
|
package arango
|
||||||
|
|
||||||
|
import "testing"
|
||||||
|
|
||||||
|
func TestReadParams(t *testing.T) {
|
||||||
|
type TestCase struct {
|
||||||
|
Input string
|
||||||
|
Output []string
|
||||||
|
}
|
||||||
|
|
||||||
|
testCases := []TestCase{
|
||||||
|
{
|
||||||
|
Input: "FOR doc IN @@collection FILTER doc.title == @title RETURN doc",
|
||||||
|
Output: []string{"collection", "title"},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, testCase := range testCases {
|
||||||
|
t.Log("Input:", testCase.Input)
|
||||||
|
t.Log("Expected output:", testCase.Output)
|
||||||
|
|
||||||
|
params := ReadParams(testCase.Input)
|
||||||
|
|
||||||
|
if len(params) != len(testCase.Output) {
|
||||||
|
t.Errorf("Expected %d parameters", len(testCase.Output))
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
for i, name := range testCase.Output {
|
||||||
|
if params[i] != name {
|
||||||
|
t.Errorf("Expected parameter %d to be %q", i, name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,87 @@
|
|||||||
|
package arango
|
||||||
|
|
||||||
|
import (
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Query provides a simple way to build ArangoDB queries.
|
||||||
|
type Query struct {
|
||||||
|
Lines []string
|
||||||
|
Params map[string]any
|
||||||
|
}
|
||||||
|
|
||||||
|
// Append adds a line to the query, attaching (optional) values to bind parameters.
|
||||||
|
//
|
||||||
|
// Any variables given are assigned to bind parameters in the same order as they appear in the line.
|
||||||
|
// Keep in mind the following behaviours:
|
||||||
|
//
|
||||||
|
// - If more variables are provided than there are parameters in the line, leftover variables are discarded
|
||||||
|
// - If more parameters are used than variables are given, remaining parameters are left unmapped
|
||||||
|
// - Reused parameters are overwritten
|
||||||
|
func (query *Query) Append(line string, values ...any) *Query {
|
||||||
|
var params map[string]any = nil
|
||||||
|
|
||||||
|
names := ReadParams(line)
|
||||||
|
if len(names) > 0 {
|
||||||
|
params = map[string]any{}
|
||||||
|
for i, name := range names {
|
||||||
|
if i > len(values) {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
params[name] = values[i]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
query.Lines = append(query.Lines, line)
|
||||||
|
return query.AssignMap(params)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Assign assigns a value to a single bind parameter.
|
||||||
|
func (query *Query) Assign(name string, value any) *Query {
|
||||||
|
query.Params[name] = value
|
||||||
|
return query
|
||||||
|
}
|
||||||
|
|
||||||
|
// AssignMap assigns values to bind parameters.
|
||||||
|
func (query *Query) AssignMap(params map[string]any) *Query {
|
||||||
|
if params != nil {
|
||||||
|
for name, value := range params {
|
||||||
|
query.Params[name] = value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return query
|
||||||
|
}
|
||||||
|
|
||||||
|
// Copy creates a copy of the query.
|
||||||
|
func (query *Query) Copy() *Query {
|
||||||
|
newQuery := NewQuery()
|
||||||
|
for _, line := range query.Lines {
|
||||||
|
newQuery.Lines = append(newQuery.Lines, line)
|
||||||
|
}
|
||||||
|
for name, value := range query.Params {
|
||||||
|
newQuery.Params[name] = value
|
||||||
|
}
|
||||||
|
return newQuery
|
||||||
|
}
|
||||||
|
|
||||||
|
// L (for "Line") is a shorthand for Append.
|
||||||
|
func (query *Query) L(line string, values ...any) *Query {
|
||||||
|
return query.Append(line, values...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// P (for "Parameter") is a shorthand for Assign.
|
||||||
|
func (query *Query) P(name string, value any) *Query {
|
||||||
|
return query.Assign(name, value)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (query *Query) String() string {
|
||||||
|
return strings.Join(query.Lines, "\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewQuery creates a new Query.
|
||||||
|
func NewQuery() *Query {
|
||||||
|
return &Query{
|
||||||
|
Lines: []string{},
|
||||||
|
Params: map[string]any{},
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,51 @@
|
|||||||
|
package arango
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestQueryAppend(t *testing.T) {
|
||||||
|
type TestCase struct {
|
||||||
|
Input *Query
|
||||||
|
ExpectedStr string
|
||||||
|
ExpectedParams map[string]any
|
||||||
|
}
|
||||||
|
|
||||||
|
testCases := []TestCase{
|
||||||
|
{
|
||||||
|
Input: NewQuery().
|
||||||
|
Append("FOR doc IN @@collection", "recipes").
|
||||||
|
Append("FILTER doc.title == @title", "Spaghetti").
|
||||||
|
Append("RETURN doc"),
|
||||||
|
ExpectedStr: `FOR doc IN @@collection
|
||||||
|
FILTER doc.title == @title
|
||||||
|
RETURN doc`,
|
||||||
|
ExpectedParams: map[string]any{
|
||||||
|
"collection": "recipes",
|
||||||
|
"title": "Spaghetti",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, testCase := range testCases {
|
||||||
|
actualStr := testCase.Input.String()
|
||||||
|
|
||||||
|
if actualStr != testCase.ExpectedStr {
|
||||||
|
t.Logf("Expected: %q", testCase.ExpectedStr)
|
||||||
|
t.Logf("Actual: %q", actualStr)
|
||||||
|
t.Fail()
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(testCase.Input.Params) != len(testCase.ExpectedParams) {
|
||||||
|
t.Errorf("Expected %d parameters; got %d", len(testCase.ExpectedParams), len(testCase.Input.Params))
|
||||||
|
}
|
||||||
|
|
||||||
|
for name, value := range testCase.ExpectedParams {
|
||||||
|
if testCase.Input.Params[name] == nil {
|
||||||
|
t.Errorf("Expected parameter %q to be %q; got nil", name, value)
|
||||||
|
} else if testCase.Input.Params[name] != value {
|
||||||
|
t.Errorf("Expected parameter %q to be %q; got %q", name, value, testCase.Input.Params[name])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user