golang源码分析:cayley(9)

2023-08-09 15:11:55 浏览数 (3)

接着看下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简单很多倍的,人类更容易理解的方式来进行图的查询和遍历。

0 人点赞