Skip to content

Commit

Permalink
Initial commit
Browse files Browse the repository at this point in the history
  • Loading branch information
iDevelopThings committed Oct 4, 2022
0 parents commit c82bd00
Show file tree
Hide file tree
Showing 12 changed files with 550 additions and 0 deletions.
8 changes: 8 additions & 0 deletions .idea/.gitignore

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 8 additions & 0 deletions .idea/modules.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

9 changes: 9 additions & 0 deletions .idea/surrealdb.go.utils.iml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 6 additions & 0 deletions .idea/vcs.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

65 changes: 65 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
# SurrealDB Go Utils

A basic little package with some useful structs for working with surreal

### Thing:

```go
package main

import (
"github.com/idevelopthings/surrealdb_go_utils"
)

type User struct {
ID surrealdb_go_utils.Thing `json:"id"`
}
```

Thing will allow you to the regular surrealdb id formatting, and take out the Table or Value with ease.
We can also use this for situations in queries.

It will also unescape the value for you, so when it's passed via an api endpoint as a normal id, like "user:u4y127h7fh78h1", it will automatically be unescaped.

### Record:

This is useful for when you have a type for your Table, which will have a pointer value, for example. A Book with an Author:

```go
package main

import (
"github.com/idevelopthings/surrealdb_go_utils"
)

type Author struct {
ID surrealdb_go_utils.Thing `json:"id"`
}
type Book struct {
ID surrealdb_go_utils.Thing `json:"id"`
Author surrealdb_go_utils.Record[Author] `json:"author"`
}

```

When you pull the Book from the db as normal, it will only return an id string, something like `{"id": "book:u4y127h7fh78h1", "author": "author:jsdfhf8f2h7fh"}`.

When you also fetch "author", this usually will cause issues unmarsalling the json.

But this "Record" will handle both situations, and marshal to json as the correct type

```go
var book *Book

book.Author.IsId() // Returns true if the value is an id(record pointer)
book.Author.Id() // Returns the ID of the record
book.Author.IsRecord() // Returns true if it's the Author value/record
book.Author.Record() // Returns the Author record(if the query included `fetch author` for example)

```

But when you use the Record, it will automatically unescape the id, and then pull the Author from the db, and return the Author struct.

### RecordList

This works the same as "Record" above, but for an array instead.
9 changes: 9 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
module github.com/idevelopthings/surrealdb_go_utils

go 1.19

require (
github.com/buger/jsonparser v1.1.1
github.com/goccy/go-json v0.9.11
golang.org/x/exp v0.0.0-20221002003631-540bb7301a08
)
6 changes: 6 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
github.com/buger/jsonparser v1.1.1 h1:2PnMjfWD7wBILjqQbt530v576A/cAbQvEW9gGIpYMUs=
github.com/buger/jsonparser v1.1.1/go.mod h1:6RYKKt7H4d4+iWqouImQ9R2FZql3VbhNgx27UK13J/0=
github.com/goccy/go-json v0.9.11 h1:/pAaQDLHEoCq/5FFmSKBswWmK6H0e8g4159Kc/X/nqk=
github.com/goccy/go-json v0.9.11/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
golang.org/x/exp v0.0.0-20221002003631-540bb7301a08 h1:LtBIgSqNhkuC9gA3BFjGy5obHQT1lnmNsMDFSqWzQ5w=
golang.org/x/exp v0.0.0-20221002003631-540bb7301a08/go.mod h1:cyybsKvd6eL0RnXn6p/Grxp8F5bW7iYuBgsNCOHpMYE=
87 changes: 87 additions & 0 deletions record-list.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
package surrealdb_go_utils

import (
"github.com/buger/jsonparser"
"github.com/goccy/go-json"
"golang.org/x/exp/maps"
)

type ListType string

var (
ListTypeIds ListType = "ids"
ListTypeObjects ListType = "objects"
)

type RecordList[T any | string] struct {
items []T
ids map[string]*T
listType ListType
}

func (d *RecordList[T]) MarshalJSON() ([]byte, error) {
if d.IsResolved() {
return json.Marshal(d.items)
}

return json.Marshal(d.Ids())
}

func (d *RecordList[T]) UnmarshalJSON(data []byte) error {
_, dataType, _, err := jsonparser.Get(data)
if err != nil {
return err
}

if dataType == jsonparser.Array {
dat := *new(RecordList[T])
dat.ids = make(map[string]*T)
dat.items = make([]T, 0)
dat.listType = ""

jsonparser.ArrayEach(data, func(value []byte, dataType jsonparser.ValueType, offset int, err error) {
if dataType == jsonparser.String {
val, err := jsonparser.ParseString(value)
if err != nil {
panic(err)
}
dat.ids[val] = nil
dat.listType = ListTypeIds
} else {
var item T = *new(T)
err = json.Unmarshal(value, &item)
if err != nil {
return
}
dat.items = append(dat.items, item)
dat.listType = ListTypeObjects
}
})

*d = dat

return nil
}

return nil
}

// IsResolved check if the list has been resolved/initiated
func (d *RecordList[T]) IsResolved() bool {
return d.listType != ListTypeIds
}

// Items returns the items in the list
func (d *RecordList[T]) Items() []T {
return d.items
}

// IdMap returns the ids in the list(this would be used when we haven't fetched the records)
func (d *RecordList[T]) IdMap() map[string]*T {
return d.ids
}

// Ids return an array of all ids in the list
func (d *RecordList[T]) Ids() []string {
return maps.Keys(d.ids)
}
91 changes: 91 additions & 0 deletions record.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
package surrealdb_go_utils

import (
"github.com/buger/jsonparser"
"github.com/goccy/go-json"
)

type RecordType string

var (
RecordTypeId RecordType = "id"
RecordTypeRecord RecordType = "record"
)

type Record[T any] struct {
id string
value T
isSet bool
kind RecordType
}

func (record *Record[T]) UnmarshalJSON(data []byte) error {
_, dataType, _, err := jsonparser.Get(data)
if err != nil {
return err
}

dat := *new(Record[T])

if dataType == jsonparser.String {
dat.kind = RecordTypeId
dat.id, err = jsonparser.GetString(data)
if err != nil {
return err
}
dat.isSet = true

*record = dat

return nil
}
if dataType == jsonparser.Object {
var item T = *new(T)
err = json.Unmarshal(data, &item)
if err != nil {
return err
}
dat.value = item
dat.kind = RecordTypeRecord
dat.isSet = true

dat.id, err = jsonparser.GetString(data, "id")
if err != nil {
return err
}

*record = dat

return nil
}

return nil
}

func (record *Record[T]) MarshalJSON() ([]byte, error) {
if record.IsId() {
return []byte(`"` + record.id + `"`), nil
}

return json.Marshal(record.value)
}

// IsId returns true if the record is an id
func (record *Record[T]) IsId() bool {
return record.kind == RecordTypeId
}

// Id returns the id of the record(used when we haven't fetched the record yet)
func (record *Record[T]) Id() string {
return record.id
}

// IsRecord returns true if we're using record rather than id
func (record *Record[T]) IsRecord() bool {
return record.kind == RecordTypeRecord
}

// Record returns the record
func (record *Record[T]) Record() T {
return record.value
}
61 changes: 61 additions & 0 deletions record_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
package surrealdb_go_utils

import (
"testing"

"github.com/goccy/go-json"
)

func TestFetchedRecordUnmarshal(t *testing.T) {
type subrec struct {
Id *Thing `json:"id"`
}
type rec struct {
Id *Thing `json:"id"`
Value *Record[subrec]
}

jsonWithFetch := `{"id": "user:one", "value": {"id": "book:one"}}`
var r rec
err := json.Unmarshal([]byte(jsonWithFetch), &r)
if err != nil {
t.Error(err)
}

if r.Value.IsRecord() == false {
t.Error("Invalid value")
}

if r.Value.Id() != "book:one" {
t.Error("Invalid value")
}

if r.Value.Record().Id.Value() != "book:one" {
t.Error("Invalid value")
}
}

func TestRecordPointerUnmarshal(t *testing.T) {
type subrec struct {
Id *Thing `json:"id"`
}
type rec struct {
Id *Thing `json:"id"`
Value *Record[subrec]
}

jsonWithFetch := `{"id": "user:one", "value": "book:one"}`
var r rec
err := json.Unmarshal([]byte(jsonWithFetch), &r)
if err != nil {
t.Error(err)
}

if r.Value.IsId() == false {
t.Error("Invalid value")
}

if r.Value.Id() != "book:one" {
t.Error("Invalid value")
}
}
Loading

0 comments on commit c82bd00

Please sign in to comment.