diff --git a/README.md b/README.md index 4b9178f..9a3941c 100644 --- a/README.md +++ b/README.md @@ -105,27 +105,25 @@ docker> nebula-console -u -p --address= --port=9669 ``` -## Export mode for Nebula Graph Console - -When the export mode is enabled, Nebula Graph Console exports all the query results into a CSV file. When the export mode is disabled, the export stops. The syntax is as follows. +## Console side commands: > **NOTE**: The following commands are case insensitive. -* Enable nebula-console export mode: +* Export the result of the following statement to a csv file: ```nGQL -nebula> :set CSV +nebula> :csv a.csv ``` -* Disable nebula-console export mode: +* Export the execution plan in graphviz format to a dot file when profiling a statement with format "dot" or "dot:struct": ```nGQL -nebula> :unset CSV +nebula> :dot a.dot +nebula> PROFILE FORMAT="dot" GO FROM "Tony Parker" OVER like; ``` +You can paste the content in the dot file to `https://dreampuf.github.io/GraphvizOnline/` to show the execution plan. -## Load nba dataset - -To load the demonstration nba dataset, make sure that Console is connected to Nebula Graph. +* Load the demonstration nba dataset: ```ngql nebula> :play nba @@ -134,45 +132,19 @@ Start loading dataset nba... Load dataset succeeded! ``` -## Wait for heartbeat - -```nGQL -nebula> :sleep 3 -``` - -e.g. - -```nGQL -cat >> nba.ngql << EOF -CREATE SPACE nba(VID_TYPE=FIXED_STRING(32)); -:sleep 3 - -USE nba; -CREATE TAG IF NOT EXISTS player(name string, age int); -:sleep 3 - -INSERT VERTEX player(name, age) VALUES "Amar'e Stoudemire": ("Amar'e Stoudemire", 36) -EOF - -nebula-console -addr 127.0.0.1 -port 9669 -u root -p nebula -f nba.ngql - -``` - -## Export .dot file - -To export the graviz text to a `.dot` format, run the following command: +* Repeat to execute a statement n times, the average execution time will also be printed: ```ngql -nebula> :set dot +nebula> :repeat 3 ``` -For example: +* Sleep for some seconds, it's just used in `:play nba`: -```ngql -nebula> TODO +```nGQL +nebula> :sleep 3 ``` -## Disconnect Nebula Graph Console from Nebula Graph +* Exit the console You can use `:EXIT` or `:QUIT` to disconnect from Nebula Graph. For convenience, nebula-console supports using these commands in lower case without the colon (":"), such as `quit`. diff --git a/main.go b/main.go index 1ec9167..0e657af 100644 --- a/main.go +++ b/main.go @@ -25,15 +25,13 @@ import ( // Console side commands const ( - Unknown = -1 - Quit = 0 - SetCsv = 1 - UnsetCsv = 2 - PlayData = 3 - Sleep = 4 - SetDot = 5 - UnsetDot = 6 - Repeat = 7 + Unknown = -1 + Quit = 0 + PlayData = 1 + Sleep = 2 + ExportCsv = 3 + ExportDot = 4 + Repeat = 5 ) var dataSetPrinter = printer.NewDataSetPrinter() @@ -45,8 +43,6 @@ in order to get the total and avearge execution time of the statement") */ var g_repeats = 1 func welcome(interactive bool) { - defer dataSetPrinter.UnsetOutCsv() - defer planDescPrinter.UnsetOutDot() if !interactive { return } @@ -99,6 +95,8 @@ func playData(data string) (string, error) { // Console side cmd will not be sent to server func isConsoleCmd(cmd string) (isLocal bool, localCmd int, args []string) { + isLocal = false + localCmd = Unknown // Currently, command "exit" and "quit" can also exit the console if cmd == "exit" || cmd == "quit" { isLocal = true @@ -112,63 +110,56 @@ func isConsoleCmd(cmd string) (isLocal bool, localCmd int, args []string) { } isLocal = true - localCmd = Unknown if plain[len(plain)-1] == ';' { plain = plain[:len(plain)-1] } words := strings.Fields(plain[1:]) - switch len(words) { - case 1: - if words[0] == "exit" || words[0] == "quit" { - localCmd = Quit - } - case 2: - if words[0] == "unset" && words[1] == "csv" { - localCmd = UnsetCsv - } else if words[0] == "unset" && words[1] == "dot" { - localCmd = UnsetDot - } else if words[0] == "sleep" { + localCmdName := words[0] + switch localCmdName { + case "exit", "quit": + localCmd = Quit + case "sleep": + { localCmd = Sleep args = []string{words[1]} - } else if words[0] == "play" { + } + case "play": + { localCmd = PlayData args = []string{words[1]} - } else if words[0] == "repeat" { + } + case "repeat": + { localCmd = Repeat args = []string{words[1]} } - case 3: - if words[0] == "set" && words[1] == "csv" { - localCmd = SetCsv - args = []string{words[2]} - } else if words[0] == "set" && words[1] == "dot" { - localCmd = SetDot - args = []string{words[2]} + case "csv": + { + localCmd = ExportCsv + args = []string{words[1]} + } + case "dot": + { + localCmd = ExportDot + args = []string{words[1]} } - default: - localCmd = Unknown } - return } -func executeConsoleCmd(cmd int, args []string) (newSpace string) { +func executeConsoleCmd(c cli.Cli, cmd int, args []string) { switch cmd { - case SetCsv: - dataSetPrinter.SetOutCsv(args[0]) - case UnsetCsv: - dataSetPrinter.UnsetOutCsv() - case SetDot: - planDescPrinter.SetOutDot(args[0]) - case UnsetDot: - planDescPrinter.UnsetOutDot() + case ExportCsv: + dataSetPrinter.ExportCsv(args[0]) + case ExportDot: + planDescPrinter.ExportDot(args[0]) case PlayData: - var err error - newSpace, err = playData(args[0]) + newSpace, err := playData(args[0]) if err != nil { printConsoleResp("Error: load dataset failed, " + err.Error()) } else { printConsoleResp("Load dataset succeeded!") + c.SetSpace(newSpace) } case Sleep: i, err := strconv.Atoi(args[0]) @@ -187,7 +178,6 @@ func executeConsoleCmd(cmd int, args []string) (newSpace string) { default: printConsoleResp("Error: this local command not exists!") } - return newSpace } func printResultSet(res *nebula.ResultSet, startTime time.Time) (duration time.Duration) { @@ -254,14 +244,7 @@ func loop(c cli.Cli) error { if cmd == Quit { return nil } - newSpace := executeConsoleCmd(cmd, args) - if newSpace != "" { - c.SetSpace(newSpace) - session.Execute(fmt.Sprintf("USE %s", newSpace)) - if err != nil { - return err - } - } + executeConsoleCmd(c, cmd, args) continue } // Server side command diff --git a/printer/dataset_printer.go b/printer/dataset_printer.go index ce537ee..aa971c2 100644 --- a/printer/dataset_printer.go +++ b/printer/dataset_printer.go @@ -29,10 +29,7 @@ func NewDataSetPrinter() DataSetPrinter { } } -func (p *DataSetPrinter) SetOutCsv(filename string) { - if p.fd != nil { - p.UnsetOutCsv() - } +func (p *DataSetPrinter) ExportCsv(filename string) { fd, err := os.OpenFile(filename, os.O_CREATE|os.O_TRUNC|os.O_WRONLY, 0600) if err != nil { fmt.Printf("Open or Create file %s failed, %s", filename, err.Error()) @@ -42,17 +39,6 @@ func (p *DataSetPrinter) SetOutCsv(filename string) { p.filename = filename } -func (p *DataSetPrinter) UnsetOutCsv() { - if p.fd == nil { - return - } - if err := p.fd.Close(); err != nil { - fmt.Printf("Close file %s failed, %s", p.filename, err.Error()) - } - p.fd = nil - p.filename = "" -} - func (p *DataSetPrinter) PrintDataSet(res *nebula.ResultSet) { if res.GetColSize() == 0 { return @@ -86,10 +72,14 @@ func (p *DataSetPrinter) PrintDataSet(res *nebula.ResultSet) { fmt.Println(p.writer.Render()) if p.fd != nil { go func() { - p.fd.Truncate(0) - p.fd.Seek(0, 0) s := strings.Replace(p.writer.RenderCSV(), "\\\"", "", -1) fmt.Fprintln(p.fd, s) + + if err := p.fd.Close(); err != nil { + fmt.Printf("Close file %s failed, %s", p.filename, err.Error()) + } + p.fd = nil + p.filename = "" }() } } diff --git a/printer/plan_desc_printer.go b/printer/plan_desc_printer.go index 9a7f1ae..cf0a8f5 100644 --- a/printer/plan_desc_printer.go +++ b/printer/plan_desc_printer.go @@ -38,10 +38,7 @@ func NewPlanDescPrinter() PlanDescPrinter { } } -func (p *PlanDescPrinter) SetOutDot(filename string) { - if p.fd != nil { - p.UnsetOutDot() - } +func (p *PlanDescPrinter) ExportDot(filename string) { fd, err := os.OpenFile(filename, os.O_CREATE|os.O_TRUNC|os.O_WRONLY, 0600) if err != nil { fmt.Printf("Open or Create file %s failed, %s", filename, err.Error()) @@ -51,17 +48,6 @@ func (p *PlanDescPrinter) SetOutDot(filename string) { p.filename = filename } -func (p *PlanDescPrinter) UnsetOutDot() { - if p.fd == nil { - return - } - if err := p.fd.Close(); err != nil { - fmt.Printf("Close file %s failed, %s", p.filename, err.Error()) - } - p.fd = nil - p.filename = "" -} - func (p PlanDescPrinter) configWriterDotRenderStyle(renderByDot bool) { if renderByDot { p.writer.Style().Box.Left = " " @@ -132,9 +118,13 @@ func (p *PlanDescPrinter) PrintPlanDesc(res *nebula.ResultSet) { if p.fd != nil { go func() { - p.fd.Truncate(0) - p.fd.Seek(0, 0) fmt.Fprintln(p.fd, s) + + if err := p.fd.Close(); err != nil { + fmt.Printf("Close file %s failed, %s", p.filename, err.Error()) + } + p.fd = nil + p.filename = "" }() } }