golang 源碼分析:cayley-8-

        接着我們分析下命令行工具,這裏除了導入導出工具還有 gizmo 語法支持、graphql 支持等相關命令行工具。

        gogen.go 裏定義瞭如何生成 Gizmo 的文檔。

//go:generate go run ./cmd/docgen/docgen.go -i ./docs/GizmoAPI.md.in -o ./docs/GizmoAPI.md

imports.go 前面我們已經介紹過了

tools.go 定義了打包工具,主要用於靜態資源的打包

import _ "github.com/gobuffalo/packr/v2/packr2"

cmd/docgen/docgen.go,根據命令行參數生成最終的文檔:

packageName = flag.String("pck", "github.com/cayleygraph/cayley/query/gizmo", "")
  out         = flag.String("o", "-", "output file")
  in          = flag.String("i", "", "input file")
    func main() {
        path := filepath.Join(os.Getenv("GOPATH"), "src", *packageName)
  fset := token.NewFileSet()
  pkgs, err := parser.ParseDir(fset, path, nil, parser.ParseComments)
      p := pkgs[filepath.Base(*packageName)]
  dp := doc.New(p, *packageName, doc.AllDecls)
  var w io.Writer = os.Stdout
  if fname := *out; fname != "" && fname != "-" {
    f, err := os.Create(fname)
      var r io.Reader = strings.NewReader(placeholder)
  if fname := *in; fname != "" {
    f, err := os.Open(fname)
      sc := bufio.NewScanner(r)
  for sc.Scan() {
    line := bytes.TrimSpace(sc.Bytes())
    if bytes.Equal(line, []byte(placeholder)) {
      writeDocs(w, dp)
    } else {
      w.Write(line)
func writeDocs(w io.Writer, dp *doc.Package) {
func Signature(m *doc.Func) string {
func isJsArgs(f *ast.FieldList) bool {
func funcDocs(s string) string {

cmd/cayley/cayley.go 裏面定義了一組命令,使用了 cobra 工具來生成命令:

var (
  rootCmd = &cobra.Command{
    Use:   "cayley",
    func init() {
      rootCmd.AddCommand(
    versionCmd,
    command.NewInitDatabaseCmd(),
    command.NewLoadDatabaseCmd(),
    command.NewDumpDatabaseCmd(),
    command.NewUpgradeCmd(),
    command.NewReplCmd(),
    command.NewQueryCmd(),
    command.NewHttpCmd(),
    command.NewConvertCmd(),
    command.NewDedupCommand(),
  )
func main() {
  if err := rootCmd.Execute(); err != nil {

cmd/cayley/command 裏面是各個詳細命令的定義,比如進行數據轉換 convert.go

  func newLazyReader(open func() (quad.ReadCloser, error)) quad.ReadCloser {
  return &lazyReader{open: open}
type lazyReader struct {
  rc   quad.ReadCloser
  open func() (quad.ReadCloser, error)
}
func (r *lazyReader) ReadQuad() (quad.Quad, error) {
        rc, err := r.open()
        return r.rc.ReadQuad()
type multiReader struct {
  rc []quad.ReadCloser
  i  int
}
func (r *multiReader) ReadQuad() (quad.Quad, error) {
  for {
    if r.i >= len(r.rc) {
            rc := r.rc[r.i]
    q, err := rc.ReadQuad()
func NewConvertCmd() *cobra.Command {
  cmd := &cobra.Command{
    Use:     "convert",
        for _, path := range files {
        path := path
        multi.rc = append(multi.rc, newLazyReader(func() (quad.ReadCloser, error) {
          if dump == "-" {
            clog.Infof("reading %q", path)
          } else {
            fmt.Printf("reading %q\n", path)
          }
          return internal.QuadReaderFor(path, loadf)
        }))
      }

database.go 定義了指定數據庫需要的各種參數,命令行參數和 yaml 配置文件都可以用來啓動服務,也定義了加載數據庫和 dump 數據庫相關的命令。

const (
  KeyBackend  = "store.backend"
  KeyAddress  = "store.address"
  KeyPath     = "store.path"
  KeyReadOnly = "store.read_only"
  KeyOptions  = "store.options"
  KeyLoadBatch = "load.batch"
func registerLoadFlags(cmd *cobra.Command) {
func registerDumpFlags(cmd *cobra.Command) {
func NewInitDatabaseCmd() *cobra.Command {
  cmd := &cobra.Command{
    Use:   "init",
        if graph.IsRegistered(name) && !graph.IsPersistent(name) {
        return ErrNotPersistent
      }
      // TODO: maybe check read-only flag in config before that?
      if err := initDatabase(); err != nil {
func NewLoadDatabaseCmd() *cobra.Command {
  cmd := &cobra.Command{
    Use:   "load",
                if err = initDatabase(); err != nil {
          return err
        }
      }
      h, err := openDatabase()
func NewDumpDatabaseCmd() *cobra.Command {
  cmd := &cobra.Command{
    Use:   "dump",
func NewUpgradeCmd() *cobra.Command {
  cmd := &cobra.Command{
    Use:   "upgrade",
func printBackendInfo() {
func initDatabase() error {
        return graph.InitQuadStore(name, path, graph.Options(opts))
func openDatabase() (*graph.Handle, error) {
        qw, err := graph.NewQuadWriter("single", qs, opts)
func openForQueries(cmd *cobra.Command) (*graph.Handle, error) {

同樣支持 profile

dedup.go

type sortVals []graph.Ref
type sortProp []property
func hashProperties(h hash.Hash, m map[interface{}]property) string {
type property struct {
  Pred   graph.Ref
  Values []graph.Ref
}
func dedupProperties(ctx context.Context, h *graph.Handle, pred, typ quad.IRI) error {
func dedupValueTx(ctx context.Context, h *graph.Handle, tx *graph.Transaction, a, b graph.Ref) error {

dump.go   數據的 dump

func writerQuadsTo(path string, typ string, qr quad.Reader) error {
        f, err = os.Create(path)
func dumpDatabase(h *graph.Handle, path string, typ string) error {
          qr := graph.NewQuadStoreReader(h.QuadStore)
  defer qr.Close()
  return writerQuadsTo(path, typ, qr)

http.go 啓動 http 服務,我們就可以操作頁面訪問 cayley

func NewHttpCmd() *cobra.Command {
  cmd := &cobra.Command{
    Use:   "http",
              h, err := openForQueries(cmd)
      if err != nil {
        return err
      }
      defer h.Close()
      err = chttp.SetupRoutes(h, &chttp.Config{
        Timeout:  viper.GetDuration(keyQueryTimeout),
        ReadOnly: viper.GetBool(KeyReadOnly),
      })
        return http.ListenAndServe(host, nil)

repl.go 提供了命令解釋器,可以交互式解析命令

func getContext() (context.Context, func()) {
func registerQueryFlags(cmd *cobra.Command) {
  langs := query.Languages()
func NewReplCmd() *cobra.Command {
  cmd := &cobra.Command{
    Use:   "repl",
func NewQueryCmd() *cobra.Command {
  cmd := &cobra.Command{
    Use:     "query",
        h, err := openForQueries(cmd)
      if err != nil {
        return err
      }
      defer h.Close()
      ctx, cancel := getContext()
      defer cancel()
      timeout := viper.GetDuration("timeout")
      if timeout > 0 {
        ctx, cancel = context.WithTimeout(ctx, timeout)
        defer cancel()
      }
      lang, _ := cmd.Flags().GetString("lang")
      limit, err := cmd.Flags().GetInt("limit")
      if err != nil {
        return err
      }
      enc := json.NewEncoder(os.Stdout)
      it, err := query.Execute(ctx, h, lang, querystr, query.Options{
        Collation: query.JSON,
        Limit:     limit,
      })
本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源https://mp.weixin.qq.com/s/yoFbZ-K7bI99MeCeJop6xQ