storm
storm copied to clipboard
Support nested struct in queries
It would be quite nice to be able to do queries on nested data structs like:
db.Select(q.Eq("Interfaces.Name", "eth0"))
Or:
db.Select(q.Sub("Interfaces", q.Eq("Name", "eth0")))
Example:
package main
import (
"fmt"
"log"
"time"
"github.com/asdine/storm"
"github.com/asdine/storm/q"
"github.com/boltdb/bolt"
"github.com/pborman/uuid"
)
type Host struct {
UUID string `storm:"id"`
Hostname string
Interfaces *Interfaces
}
type Hosts []*Host
type Interface struct {
Name string
IPAddr string
}
type Interfaces []*Interface
var db *storm.DB
func main() {
host := Host{
UUID: uuid.New(),
Hostname: "foobar.example.com",
Interfaces: &Interfaces{
&Interface{
Name: "eth0",
IPAddr: "192.168.0.2",
},
&Interface{
Name: "eth1",
IPAddr: "192.168.0.3",
},
},
}
var err error
db, err = storm.Open("test.db", storm.BoltOptions(0600, &bolt.Options{Timeout: 3 * time.Second}))
if err != nil {
log.Fatalln("failed to open database:", err)
}
defer db.Close()
if err := db.Save(&host); err != nil {
log.Fatalln("save to db:", err)
}
hosts := Hosts{}
if err := db.Select(q.Eq("Interfaces.Name", "eth0")).Find(&hosts); err != nil {
log.Fatalln("query db:", err)
}
for _, h := range hosts {
fmt.Printf("%+v\n", h)
}
}
This will currently generate "unknown field" since it doesn't support nested structs.
This would be very useful indeed. For that we need to rework how the field names are stored in the matcher. Currently there are simply stored as string then compared against the field with the same name. With nested struct it will be way more difficult (but not impossible) but i think it's worth it.
I have trying use PathString (like "FieldA.FieldB#1.FieldC")
func (f *Field) PathOk(path string) (*Field, bool) {
value := &f.value
// value must be settable so we need to make sure it holds the address of the
// variable and not a copy, so we can pass the pointer to strctVal instead of a
// copy (which is not assigned to any variable, hence not settable).
// see "https://blog.golang.org/laws-of-reflection#TOC_8."
if f.value.Kind() != reflect.Ptr {
a := f.value.Addr()
value = &a
}
//v := strctVal(value.Interface())
//t := v.Type()
paths := parsePath(path)
var f1 *Field
f1 = f
f1.value = *value
for i := range paths {
p := strings.Split(paths[i], "#")
if len(p) == 0 {
return nil, false
}
var ok bool
if f1, ok = f1.FieldOk(p[0]); !ok {
return nil, false
}
if len(p) > 1 {
for k := 1; k < len(p); k++ {
if idx, er := strconv.Atoi(p[k]); er == nil {
switch f.Kind() {
case reflect.Slice, reflect.Array, reflect.String:
f1 = &Field{
field: f1.field,
value: f1.value.Index(idx),
defaultTag: f1.defaultTag,
}
case reflect.Ptr:
f1 = &Field{
field: f1.field,
value: reflect.Indirect(f1.value).Index(idx),
defaultTag: f1.defaultTag,
}
default:
return nil, false
}
}
}
}
}
return f1, true
}
this Field is from github.com/fatih/structs maybe this will help
You just need a recursive function that can traverse a nested struct using reflection. This is one example: https://github.com/mickep76/flatten/blob/master/flatten.go
Is there any progress about this issue?
No progress.
Hey @mickep76 @navono,
Sorry this issue requires a major change in the architecture in Storm and I don't have time to handle this at the moment. I am working on another database called Genji that will support nested fields and advanced SQL queries on top of them and I'm thinking of having Storm v3 using it as an engine. But it still requires time.
Let's keep this issue open for now and see how it goes