接着看下query目录,首先是session.go文件,它定义了迭代器接口
代码语言:javascript复制type Iterator interface {
// Next advances the iterator to the next value, which will then be available through
// the Result method. It returns false if no further advancement is possible, or if an
// error was encountered during iteration. Err should be consulted to distinguish
// between the two cases.
Next(ctx context.Context) bool
// Results returns the current result. The type depends on the collation mode of the query.
Result() interface{}
// Err returns any error that was encountered by the Iterator.
Err() error
// Close the iterator and do internal cleanup.
Close() error
}
代码语言:javascript复制 type Collation int
语言可以是json格式也可以是REPL格式
代码语言:javascript复制const (
// Raw collates results as maps or arrays of graph.Refs or any other query-native or graph-native data type.
Raw = Collation(iota)
// REPL collates results as strings which will be used in CLI.
REPL = Collation(iota)
// JSON collates results as maps, arrays and values, that can be encoded to JSON.
JSON
// JSONLD collates results as maps, arrays and values compatible with JSON-LD spec.
JSONLD
)
代码语言:javascript复制type Options struct {
Limit int
Collation Collation
}
代码语言:javascript复制type Session interface {
// Execute runs the query and returns an iterator over the results.
// Type of results depends on Collation. See Options for details.
Execute(ctx context.Context, query string, opt Options) (Iterator, error)
}
代码语言:javascript复制type HTTP interface {
Session
ShapeOf(string) (interface{}, error)
}
代码语言:javascript复制type REPLSession = Session
代码语言:javascript复制type Language struct {
Name string
Session func(graph.QuadStore) Session
REPL func(graph.QuadStore) REPLSession // deprecated
HTTP func(graph.QuadStore) HTTP
// Custom HTTP handlers
HTTPQuery func(ctx context.Context, qs graph.QuadStore, w ResponseWriter, r io.Reader)
HTTPError func(w ResponseWriter, err error)
}
代码语言:javascript复制var languages = make(map[string]Language)
代码语言:javascript复制func RegisterLanguage(lang Language) {
languages[lang.Name] = lang
}
代码语言:javascript复制func NewSession(qs graph.QuadStore, lang string) Session {
if l := languages[lang]; l.Session != nil {
return l.Session(qs)
代码语言:javascript复制func GetLanguage(lang string) *Language {
l, ok := languages[lang]
代码语言:javascript复制func Execute(ctx context.Context, qs graph.QuadStore, lang, query string, opt Options) (Iterator, error) {
l := GetLanguage(lang)
if l == nil {
return nil, fmt.Errorf("unsupported language: %q", lang)
}
sess := l.Session(qs)
return sess.Execute(ctx, query, opt)
query/gizmo/environ.go定义了gizmo的翻译执行过程
代码语言:javascript复制type graphObject struct {
s *Session
}
代码语言:javascript复制func (g *graphObject) NewIRI(s string) quad.IRI {
return quad.IRI(g.s.ns.FullIRI(s))
}
代码语言:javascript复制func (g *graphObject) AddNamespace(pref, ns string) {
g.s.ns.Register(voc.Namespace{Prefix: pref ":", Full: ns})
}
代码语言:javascript复制func (g *graphObject) AddDefaultNamespaces() {
voc.CloneTo(&g.s.ns)
代码语言:javascript复制func (g *graphObject) LoadNamespaces() error {
return g.s.sch.LoadNamespaces(g.s.ctx, g.s.qs, &g.s.ns)
代码语言:javascript复制func (g *graphObject) NewV(call goja.FunctionCall) goja.Value {
return g.NewVertex(call)
代码语言:javascript复制func (g *graphObject) NewVertex(call goja.FunctionCall) goja.Value {
qv, err := toQuadValues(exportArgs(call.Arguments))
if err != nil {
return throwErr(g.s.vm, err)
}
return g.s.vm.ToValue(&pathObject{
s: g.s,
finals: true,
path: path.StartMorphism(qv...),
})
代码语言:javascript复制func (g *graphObject) NewM() *pathObject {
return g.NewMorphism()
代码语言:javascript复制func (g *graphObject) NewMorphism() *pathObject {
return &pathObject{
s: g.s,
path: path.StartMorphism(),
代码语言:javascript复制func (g *graphObject) Emit(call goja.FunctionCall) goja.Value {
value := call.Argument(0)
if !goja.IsNull(value) && !goja.IsUndefined(value) {
val := exportArgs([]goja.Value{value})[0]
if val != nil {
g.s.send(nil, &Result{Val: val})
}
}
return goja.Null()
代码语言:javascript复制func (g *graphObject) CapitalizedUri(s string) quad.IRI {
return g.NewIRI(s)
代码语言:javascript复制func (g *graphObject) CapitalizedAddNamespace(pref, ns string) {
g.AddNamespace(pref, ns)
代码语言:javascript复制func (g *graphObject) CapitalizedAddDefaultNamespaces() {
g.AddDefaultNamespaces()
代码语言:javascript复制func (g *graphObject) CapitalizedEmit(call goja.FunctionCall) goja.Value {
return g.Emit(call)
代码语言:javascript复制func cmpOpType(op iterator.Operator) func(vm *goja.Runtime, call goja.FunctionCall) goja.Value {
return func(vm *goja.Runtime, call goja.FunctionCall) goja.Value {
代码语言:javascript复制func cmpWildcard(vm *goja.Runtime, call goja.FunctionCall) goja.Value {
args := exportArgs(call.Arguments)
代码语言:javascript复制type valFilter struct {
f shape.ValueFilter
}
query/gizmo/errors.go定义了相关错误信息
代码语言:javascript复制type errArgCount2 struct {
Expected int
Got int
}
query/gizmo/finals.go
代码语言:javascript复制const TopResultTag = "id"
代码语言:javascript复制func (p *pathObject) GetLimit(limit int) error {
it := p.buildIteratorTree()
it = iterator.Tag(it, TopResultTag)
p.s.limit = limit
p.s.count = 0
return p.s.runIterator(it)
代码语言:javascript复制func (p *pathObject) toArray(call goja.FunctionCall, withTags bool) goja.Value {
代码语言:javascript复制func (p *pathObject) ForEach(call goja.FunctionCall) goja.Value {
it := p.buildIteratorTree()
query/gizmo/gizmo.go先注册了语言
代码语言:javascript复制const Name = "gizmo"
支持http和repl两种模式
代码语言:javascript复制func init() {
query.RegisterLanguage(query.Language{
Name: Name,
Session: func(qs graph.QuadStore) query.Session {
return NewSession(qs)
},
HTTP: func(qs graph.QuadStore) query.HTTP {
return NewSession(qs)
},
REPL: func(qs graph.QuadStore) query.REPLSession {
return NewSession(qs)
},
})
代码语言:javascript复制func NewSession(qs graph.QuadStore) *Session {
s := &Session{
ctx: context.Background(),
sch: schema.NewConfig(),
qs: qs, limit: -1,
}
if err := s.buildEnv(); err != nil {
代码语言:javascript复制const constructMethodPrefix = "New"
代码语言:javascript复制const backwardsCompatibilityPrefix = "Capitalized"
代码语言:javascript复制func (fieldNameMapper) MethodName(t reflect.Type, m reflect.Method) string {
if strings.HasPrefix(m.Name, backwardsCompatibilityPrefix) {
其中session定义如下:
代码语言:javascript复制type Session struct {
qs graph.QuadStore
vm *goja.Runtime
ns voc.Namespaces
sch *schema.Config
col query.Collation
last string
p *goja.Program
out chan *Result
ctx context.Context
limit int
count int
err error
shape map[string]interface{}
}
代码语言:javascript复制func (s *Session) quadValueToNative(v quad.Value) interface{} {
if v == nil {
return nil
}
if s.col == query.JSONLD {
return jsonld.FromValue(v)
代码语言:javascript复制func (s *Session) runIteratorWithCallback(it graph.Iterator, callback goja.Value, this goja.FunctionCall, limit int) error {
fnc, ok := goja.AssertFunction(callback)
代码语言:javascript复制func (s *Session) runIterator(it graph.Iterator) error {
if s.shape != nil {
iterator.OutputQueryShapeForIterator(it, s.qs, s.shape)
return nil
}
ctx, cancel := context.WithCancel(s.context())
defer cancel()
stop := false
err := graph.Iterate(ctx, it).Paths(true).TagEach(func(tags map[string]graph.Ref) {
代码语言:javascript复制type Result struct {
Meta bool
Val interface{}
Tags map[string]graph.Ref
}
中间使用到了goja解析器,它的作用是在golang环境中翻译执行javascript,因为我们的gizmo采用的是javascript语法。
代码语言:javascript复制func (s *Session) compile(qu string) error {
var p *goja.Program
if s.last == qu && s.last != "" {
p = s.p
} else {
var err error
p, err = goja.Compile("", qu, false)
代码语言:javascript复制func (s *Session) run() (goja.Value, error) {
v, err := s.vm.RunProgram(s.p)
if e, ok := err.(*goja.Exception); ok && e.Value() != nil {
if er, ok := e.Value().Export().(error); ok {
代码语言:javascript复制func (s *Session) Execute(ctx context.Context, qu string, opt query.Options) (query.Iterator, error) {
switch opt.Collation {
case query.Raw, query.JSON, query.JSONLD, query.REPL:
执行结果放在
代码语言:javascript复制type results struct {
s *Session
col query.Collation
ctx context.Context
cancel func()
running bool
errc chan error
err error
cur *Result
}
代码语言:javascript复制func (it *results) Next(ctx context.Context) bool {
if it.errc == nil {
it.s.out = make(chan *Result)
it.errc = make(chan error, 1)
it.running = true
go func() {
defer close(it.errc)
v, err := it.s.run()
代码语言:javascript复制func (s *Session) ShapeOf(qu string) (interface{}, error) {
s.shape = make(map[string]interface{})
err := s.compile(qu)
if err != nil {
return nil, err
}
_, err = s.run()
query/gizmo/traversals.go
代码语言:javascript复制type pathObject struct {
s *Session
finals bool
path *path.Path
}
代码语言:javascript复制func (p *pathObject) newVal(np *path.Path) goja.Value {
return p.s.vm.ToValue(p.new(np))
定义了路径树
代码语言:javascript复制func (p *pathObject) buildIteratorTree() graph.Iterator {
if p.path == nil {
return iterator.NewNull()
}
return p.path.BuildIteratorOn(p.s.qs)
扇入
代码语言:javascript复制func (p *pathObject) In(call goja.FunctionCall) goja.Value {
return p.inout(call, true)
扇出
代码语言:javascript复制func (p *pathObject) Out(call goja.FunctionCall) goja.Value {
return p.inout(call, false)
代码语言:javascript复制func (p *pathObject) Both(call goja.FunctionCall) goja.Value {
preds, tags, ok := toViaData(exportArgs(call.Arguments))
if !ok {
return throwErr(p.s.vm, errNoVia)
}
np := p.clonePath().BothWithTags(tags, preds...)
return p.newVal(np)
代码语言:javascript复制func (p *pathObject) Follow(path *pathObject) *pathObject {
return p.follow(path, false)
代码语言:javascript复制func (p *pathObject) FollowR(path *pathObject) *pathObject {
return p.follow(path, true)
代码语言:javascript复制func (p *pathObject) FollowRecursive(call goja.FunctionCall) goja.Value {
preds, maxDepth, tags, ok := toViaDepthData(exportArgs(call.Arguments))
if !ok || len(preds) == 0 {
return throwErr(p.s.vm, errNoVia)
} else if len(preds) != 1 {
return throwErr(p.s.vm, fmt.Errorf("expected one predicate or path for recursive follow"))
}
np := p.clonePath()
np = np.FollowRecursive(preds[0], maxDepth, tags)
return p.newVal(np)
代码语言:javascript复制 func (p *pathObject) has(call goja.FunctionCall, rev bool) goja.Value {
代码语言:javascript复制func (p *pathObject) Save(call goja.FunctionCall) goja.Value {
return p.save(call, false, false)
gizmo是图数据库专用的查询语言,可以采用比sql简单很多倍的,人类更容易理解的方式来进行图的查询和遍历。