From 41c900c9c475a5b48220a3362e6e2c51a3bae738 Mon Sep 17 00:00:00 2001 From: Cody Soyland Date: Fri, 21 Apr 2017 10:52:44 -0500 Subject: [PATCH 01/76] Add WIP pared-down webui/console prototype --- glide.yaml | 2 + handler.go | 17 +++++++ webui/assets/main.css | 63 +++++++++++++++++++++++ webui/assets/main.js | 114 ++++++++++++++++++++++++++++++++++++++++++ webui/index.html | 51 +++++++++++++++++++ 5 files changed, 247 insertions(+) create mode 100644 webui/assets/main.css create mode 100644 webui/assets/main.js create mode 100644 webui/index.html diff --git a/glide.yaml b/glide.yaml index d4615b0ba..b70957c75 100644 --- a/glide.yaml +++ b/glide.yaml @@ -31,3 +31,5 @@ import: - package: github.com/spf13/viper - package: github.com/gorilla/mux version: ^1.3.0 +- name: github.com/rakyll/statik + version: 274df120e9065bdd08eb1120e0375e3dc1ae8465 diff --git a/handler.go b/handler.go index 0e25c3f2f..c8de9bfe4 100644 --- a/handler.go +++ b/handler.go @@ -1,3 +1,5 @@ +//go:generate statik -src=./webui + package pilosa import ( @@ -21,6 +23,9 @@ import ( "github.com/gorilla/mux" "github.com/pilosa/pilosa/internal" "github.com/pilosa/pilosa/pql" + + _ "github.com/pilosa/pilosa/statik" + "github.com/rakyll/statik/fs" ) // Handler represents an HTTP handler. @@ -56,6 +61,8 @@ func NewHandler() *Handler { func NewRouter(handler *Handler) *mux.Router { router := mux.NewRouter() + router.HandleFunc("/", handler.handleWebUI).Methods("GET") + router.HandleFunc("/assets/{file}", handler.handleWebUI).Methods("GET") router.HandleFunc("/db", handler.handleGetDBs).Methods("GET") router.HandleFunc("/db/{db}", handler.handleGetDB).Methods("GET") router.HandleFunc("/db/{db}", handler.handlePostDB).Methods("POST") @@ -102,6 +109,16 @@ func (h *Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) { h.Router.ServeHTTP(w, r) } +func (h *Handler) handleWebUI(w http.ResponseWriter, r *http.Request) { + // If user is using curl, don't chuck HTML at them + if strings.HasPrefix(r.UserAgent(), "curl") { + http.Error(w, "Welcome. Pilosa is running. Visit https://www.pilosa.com/docs/ for more information or try the web console by visiting this URL in your browser.", http.StatusNotFound) + return + } + statikFS, _ := fs.New() + http.FileServer(statikFS).ServeHTTP(w, r) +} + // handleGetSchema handles GET /schema requests. func (h *Handler) handleGetSchema(w http.ResponseWriter, r *http.Request) { if err := json.NewEncoder(w).Encode(getSchemaResponse{ diff --git a/webui/assets/main.css b/webui/assets/main.css new file mode 100644 index 000000000..3db45d2f1 --- /dev/null +++ b/webui/assets/main.css @@ -0,0 +1,63 @@ +.output { + overflow: auto; + padding: 0 8px; +} + +.output .query { + font-family: monospace; +} + +.output .response { + font-family: monospace; +} + +.input-bar { + padding-right: 2em; + background-color: #eeeeee; +} + +.input-textarea { + font-family: monospace; + font-size: 14px; + background-color: #dddddd; + width: 100%; + border: 0; + outline: 0; + margin: 1em; +} + +body { + padding-top: 5rem; +} + +.controls { + padding: 1em; +} + +.output { + padding: 1em; +} + +.result { + background-color: #eeeeee; + padding: 1em; + margin-top: 1em; +} +.result > .tab-content > .tab-pane { + padding: 1em; +} + +.query { + background-color: #dddddd; + padding: 0.5em +} + +#exTab3 .nav-pills > li > a { + border-radius: 4px 4px 0 0 ; +} + +#exTab3 .tab-content { + color : white; + background-color: #428bca; + padding : 5px 15px; +} diff --git a/webui/assets/main.js b/webui/assets/main.js new file mode 100644 index 000000000..efd0542ed --- /dev/null +++ b/webui/assets/main.js @@ -0,0 +1,114 @@ +class REPL { + constructor(input, output) { + this.input = input + this.output = output + this.history = [] + this.history_index = 0 + this.history_buffer = '' + this.result_number = 0 + } + bind_events() { + const repl = this + const keys = { + ENTER: 13, + UP_ARROW: 38, + DOWN_ARROW: 40 + } + + this.input.addEventListener("keydown", (e) => { + if (e.keyCode == keys.UP_ARROW) { + e.preventDefault() + if (this.input.value.substring(0, this.input.selectionStart).indexOf('\n') == '-1') { + if (this.history_index == 0) { + return + } else { + if (this.history_index == this.history.length) { + this.history_buffer = this.input.value + } + this.history_index-- + this.input.value = this.history[this.history_index] + this.input.setSelectionRange(this.input.value.length, this.input.value.length) + } + } + } + if (e.keyCode == keys.DOWN_ARROW) { + e.preventDefault() + if (this.input.value.substring(this.input.selectionEnd, this.input.length).indexOf('\n') == '-1') { + if (this.history_index == this.history.length) { + return + } else { + this.history_index++ + if (this.history_index == this.history.length) { + this.input.value = this.history_buffer + } else { + this.input.value = this.history[this.history_index] + } + this.input.setSelectionRange(this.input.value.length, this.input.value.length) + } + } + } + if (e.keyCode == keys.ENTER && !e.shiftKey) { + e.preventDefault() + this.history_buffer = '' + this.history_index = this.history.length + this.history[this.history_index] = this.input.value + this.history_index++ + this.process_query(this.input.value) + this.input.value = "" + } + }) + } + + process_query(query) { + var xhr = new XMLHttpRequest(); + var dbname = 'foo'; // todo: get db name from dropdown menu + xhr.open('POST', '/db/' + dbname + '/query'); + xhr.setRequestHeader('Content-Type', 'application/text'); + + const repl = this + xhr.onload = function() { + repl.result_number++ +//const entry = document.createElement('p') + const result = ( + '
' + + '' + +'' + + '
' + + '
' + + '

' + + query + + '

' + + '

Response

' + + '

' + + xhr.responseText + + '

' + + '
' + + '
' + + '' + + '' + + '' + + '' + + '
Response code' + xhr.status + '
Response text' + xhr.responseText + '
Response URL' + xhr.responseURL + '
' + + '
' + + '
' + + '
') + // TODO: remove jquery + $(repl.output).prepend($(result)) + }; + xhr.send(query); + } +} + +function startup() { + const input = document.querySelector('.input-textarea') + const output = document.querySelector('.output') + repl = new REPL(input, output) + repl.bind_events() +} diff --git a/webui/index.html b/webui/index.html new file mode 100644 index 000000000..1fe4b9f68 --- /dev/null +++ b/webui/index.html @@ -0,0 +1,51 @@ + + + + + + + + + + +
+
+ + +
+
+
+ + + + + + + + From 2163dc38290451df486b8b02bd480bcd894d8722 Mon Sep 17 00:00:00 2001 From: Cody Soyland Date: Fri, 21 Apr 2017 10:53:31 -0500 Subject: [PATCH 02/76] .gitignore additions --- .gitignore | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.gitignore b/.gitignore index 5fe629f6a..4b571e192 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,5 @@ default.etcd/ *.test vendor .protoc-gen-gofast +statik +.DS_Store From 1cdbca1a1eea6420fc9c67d62c3060fc26ceaea2 Mon Sep 17 00:00:00 2001 From: Cody Soyland Date: Mon, 24 Apr 2017 11:22:28 -0500 Subject: [PATCH 03/76] Install statik in build process, fix glide.yaml --- Makefile | 12 +++++++++--- glide.yaml | 2 +- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/Makefile b/Makefile index 78c3e71f6..2c8fd4fa5 100644 --- a/Makefile +++ b/Makefile @@ -1,6 +1,7 @@ -.PHONY: glide vendor-update docker pilosa crossbuild install generate +.PHONY: glide vendor-update docker pilosa crossbuild install generate statik GLIDE := $(shell command -v glide 2>/dev/null) +STATIK := $(shell command -v statik 2>/dev/null) PROTOC := $(shell command -v protoc 2>/dev/null) VERSION := $(shell git describe --tags 2> /dev/null || echo unknown) IDENTIFIER := $(VERSION)-$(GOOS)-$(GOARCH) @@ -26,7 +27,7 @@ glide.lock: glide glide.yaml vendor-update: glide.lock -test: vendor +test: vendor generate go test $(shell cd $(GOPATH)/src/$(CLONE_URL); go list ./... | grep -v vendor) pilosa: vendor @@ -46,9 +47,14 @@ endif go build -o .protoc-gen-gofast ./vendor/github.com/gogo/protobuf/protoc-gen-gofast cp ./.protoc-gen-gofast $(GOPATH)/bin/protoc-gen-gofast -generate: .protoc-gen-gofast +generate: .protoc-gen-gofast statik go generate github.com/pilosa/pilosa/internal +statik: +ifndef STATIK + go install github.com/rakyll/statik +endif + docker: docker build -t "pilosa:$(VERSION)" \ --build-arg ldflags="-X main.Version=$(VERSION) -X main.BuildTime=$(BUILD_TIME)" . diff --git a/glide.yaml b/glide.yaml index b70957c75..c453a7cae 100644 --- a/glide.yaml +++ b/glide.yaml @@ -31,5 +31,5 @@ import: - package: github.com/spf13/viper - package: github.com/gorilla/mux version: ^1.3.0 -- name: github.com/rakyll/statik +- package: github.com/rakyll/statik version: 274df120e9065bdd08eb1120e0375e3dc1ae8465 From 3db8b02865385430b0efbfe1e438c94ba59084a4 Mon Sep 17 00:00:00 2001 From: Cody Soyland Date: Mon, 24 Apr 2017 11:57:26 -0500 Subject: [PATCH 04/76] Track statik directory in git so it will build without first running `go generate` --- .gitignore | 1 - statik/.gitignore | 1 + statik/doc.go | 3 +++ 3 files changed, 4 insertions(+), 1 deletion(-) create mode 100644 statik/.gitignore create mode 100644 statik/doc.go diff --git a/.gitignore b/.gitignore index 4b571e192..c924add8b 100644 --- a/.gitignore +++ b/.gitignore @@ -2,5 +2,4 @@ default.etcd/ *.test vendor .protoc-gen-gofast -statik .DS_Store diff --git a/statik/.gitignore b/statik/.gitignore new file mode 100644 index 000000000..514ee40a1 --- /dev/null +++ b/statik/.gitignore @@ -0,0 +1 @@ +statik.go diff --git a/statik/doc.go b/statik/doc.go new file mode 100644 index 000000000..85edd9e6f --- /dev/null +++ b/statik/doc.go @@ -0,0 +1,3 @@ +// Package statik contains static assets for the Web UI. `go generate` will +// produce statik.go, which is ignored by git. +package statik From 18f6d3cd152d0017ba9e218e2bf388e0d5bba8b4 Mon Sep 17 00:00:00 2001 From: Cody Soyland Date: Mon, 24 Apr 2017 12:16:44 -0500 Subject: [PATCH 05/76] Separate `go generate` make targets, remove generate from test requirements --- Makefile | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/Makefile b/Makefile index 2c8fd4fa5..cd3add926 100644 --- a/Makefile +++ b/Makefile @@ -27,7 +27,7 @@ glide.lock: glide glide.yaml vendor-update: glide.lock -test: vendor generate +test: vendor go test $(shell cd $(GOPATH)/src/$(CLONE_URL); go list ./... | grep -v vendor) pilosa: vendor @@ -42,14 +42,19 @@ install: vendor .protoc-gen-gofast: vendor ifndef PROTOC - $(error "protoc is not available please install protoc from https://github.com/google/protobuf/releases") + $(error "protoc is not available. please install protoc from https://github.com/google/protobuf/releases") endif go build -o .protoc-gen-gofast ./vendor/github.com/gogo/protobuf/protoc-gen-gofast cp ./.protoc-gen-gofast $(GOPATH)/bin/protoc-gen-gofast -generate: .protoc-gen-gofast statik +generate-protoc: .protoc-gen-gofast go generate github.com/pilosa/pilosa/internal +generate-statik: statik + go generate github.com/pilosa/pilosa/handler + +generate: generate-protoc generate-statik + statik: ifndef STATIK go install github.com/rakyll/statik From 15378c02dab77fc6a816299e8ee51b528896970b Mon Sep 17 00:00:00 2001 From: Cody Soyland Date: Mon, 24 Apr 2017 12:36:13 -0500 Subject: [PATCH 06/76] Update statik version and add subdependency --- glide.yaml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/glide.yaml b/glide.yaml index c453a7cae..54cabbdb4 100644 --- a/glide.yaml +++ b/glide.yaml @@ -32,4 +32,6 @@ import: - package: github.com/gorilla/mux version: ^1.3.0 - package: github.com/rakyll/statik - version: 274df120e9065bdd08eb1120e0375e3dc1ae8465 + version: 89fe3459b5c829c32e89bdff9c43f18aad728f2f + subpackages: + - fs From 92ec0a0cb056ca4ffcbb15cd63f4bd36ac4551b1 Mon Sep 17 00:00:00 2001 From: Cody Soyland Date: Mon, 24 Apr 2017 14:25:33 -0500 Subject: [PATCH 07/76] Update glide.lock --- glide.lock | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/glide.lock b/glide.lock index 7fffeca5e..561d9eeec 100644 --- a/glide.lock +++ b/glide.lock @@ -1,5 +1,5 @@ -hash: 743e8f978eb4ad8f80a2ab71b05caebbf50b6769b71aa457bc4f144fef8c6595 -updated: 2017-04-18T15:33:39.035615802-05:00 +hash: 5321aa381179f691f250e415abaf7776a9e701df4cf6216a1d8f2e9fa27e2b52 +updated: 2017-04-24T14:24:06.986585051-05:00 imports: - name: github.com/boltdb/bolt version: 4b1ebc1869ad66568b313d0dc410e2be72670dda @@ -52,6 +52,10 @@ imports: version: c37440a7cf42ac63b919c752ca73a85067e05992 - name: github.com/pelletier/go-toml version: 13d49d4606eb801b8f01ae542b4afc4c6ee3d84a +- name: github.com/rakyll/statik + version: 89fe3459b5c829c32e89bdff9c43f18aad728f2f + subpackages: + - fs - name: github.com/satori/go.uuid version: 879c5887cd475cd7864858769793b2ceb0d44feb - name: github.com/spf13/afero From dfd17b38a434ee18bbd8e136645dba0e1870775d Mon Sep 17 00:00:00 2001 From: Travis Date: Mon, 24 Apr 2017 15:56:07 -0500 Subject: [PATCH 08/76] Pass all Index/Frame meta data to other nodes via messages --- server.go | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/server.go b/server.go index 336d76b54..1b2f3fb87 100644 --- a/server.go +++ b/server.go @@ -247,7 +247,10 @@ func (s *Server) ReceiveMessage(pb proto.Message) error { } idx.SetRemoteMaxSlice(obj.Slice) case *internal.CreateIndexMessage: - opt := IndexOptions{ColumnLabel: obj.Meta.ColumnLabel} + opt := IndexOptions{ + ColumnLabel: obj.Meta.ColumnLabel, + TimeQuantum: TimeQuantum(obj.Meta.TimeQuantum), + } _, err := s.Holder.CreateIndex(obj.Index, opt) if err != nil { return err @@ -258,7 +261,13 @@ func (s *Server) ReceiveMessage(pb proto.Message) error { } case *internal.CreateFrameMessage: index := s.Holder.Index(obj.Index) - opt := FrameOptions{RowLabel: obj.Meta.RowLabel} + opt := FrameOptions{ + RowLabel: obj.Meta.RowLabel, + InverseEnabled: obj.Meta.InverseEnabled, + CacheType: obj.Meta.CacheType, + CacheSize: obj.Meta.CacheSize, + TimeQuantum: TimeQuantum(obj.Meta.TimeQuantum), + } _, err := index.CreateFrame(obj.Frame, opt) if err != nil { return err From 7dc06f918b82c78b5c1c0bdd08d5d319e87a0c9e Mon Sep 17 00:00:00 2001 From: Travis Date: Mon, 24 Apr 2017 15:39:20 -0500 Subject: [PATCH 09/76] Determine if a Call is inverse or not so that the map to slices is correct. --- executor.go | 45 ++++++++++++++++++++++++++++++++- pql/ast.go | 24 ++++++++++++++++++ pql/ast_test.go | 66 +++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 134 insertions(+), 1 deletion(-) diff --git a/executor.go b/executor.go index d1fd8b829..3179770e3 100644 --- a/executor.go +++ b/executor.go @@ -56,17 +56,41 @@ func (e *Executor) Execute(ctx context.Context, index string, q *pql.Query, slic opt = &ExecOptions{} } + // Don't bother calculating slices for query types that don't require it. + needsSlices := needsSlices(q.Calls) + + // MaxSlice can differ between inverse and standard views, so we need + // to send queries to different slices based on orientation. + var inverseSlices []uint64 + rowLabel := DefaultRowLabel + columnLabel := DefaultColumnLabel + // If slices aren't specified, then include all of them. if len(slices) == 0 { - if needsSlices(q.Calls) { + // Determine slices and inverseSlices for use in e.executeCall(). + if needsSlices { // Round up the number of slices. maxSlice := e.Holder.Index(index).MaxSlice() + maxInverseSlice := e.Holder.Index(index).MaxInverseSlice() // Generate a slices of all slices. slices = make([]uint64, maxSlice+1) for i := range slices { slices[i] = uint64(i) } + + // Generate a slices of all inverse slices. + inverseSlices = make([]uint64, maxInverseSlice+1) + for i := range inverseSlices { + inverseSlices[i] = uint64(i) + } + + // Fetch column label from index. + idx := e.Holder.Index(index) + if idx == nil { + return nil, ErrIndexNotFound + } + columnLabel = idx.ColumnLabel() } } @@ -78,6 +102,25 @@ func (e *Executor) Execute(ctx context.Context, index string, q *pql.Query, slic // Execute each call serially. results := make([]interface{}, 0, len(q.Calls)) for _, call := range q.Calls { + + if call.SupportsInverse() && needsSlices { + // Fetch frame & row label based on argument. + frame, _ := call.Args["frame"].(string) + if frame == "" { + frame = DefaultFrame + } + f := e.Holder.Frame(index, frame) + if f == nil { + return nil, ErrFrameNotFound + } + rowLabel = f.RowLabel() + + // If this call is to an inverse frame send to a different list of slices. + if call.IsInverse(rowLabel, columnLabel) { + slices = inverseSlices + } + } + v, err := e.executeCall(ctx, index, call, slices, opt) if err != nil { return nil, err diff --git a/pql/ast.go b/pql/ast.go index 6673bb638..57c41960a 100644 --- a/pql/ast.go +++ b/pql/ast.go @@ -156,6 +156,30 @@ func (c *Call) String() string { return buf.String() } +// SupportsInverse indicates that the call may be on an inverse frame. +func (c *Call) SupportsInverse() bool { + if c.Name == "Bitmap" { + return true + } + return false +} + +// IsInverse specifies if the call is for an inverse view. +// Return defaults to false unless absolutely sure of inversion. +func (c *Call) IsInverse(rowLabel, columnLabel string) bool { + if c.SupportsInverse() { + _, rowOK, rowErr := c.UintArg(rowLabel) + _, columnOK, columnErr := c.UintArg(columnLabel) + if rowErr != nil || columnErr != nil { + return false + } + if !rowOK && columnOK { + return true + } + } + return false +} + // CopyArgs returns a copy of m. func CopyArgs(m map[string]interface{}) map[string]interface{} { other := make(map[string]interface{}, len(m)) diff --git a/pql/ast_test.go b/pql/ast_test.go index 653bd5af5..57215938c 100644 --- a/pql/ast_test.go +++ b/pql/ast_test.go @@ -15,3 +15,69 @@ func TestCall_String(t *testing.T) { } }) } + +// Ensure call can be converted into a string. +func TestCall_SupportsInverse(t *testing.T) { + t.Run("Bitmap", func(t *testing.T) { + q, err := pql.ParseString(`Bitmap()`) + if err != nil { + t.Fatal(err) + } else if q.Calls[0].SupportsInverse() != true { + t.Fatalf("call should support inverse: %s", q.Calls[0]) + } + }) + t.Run("Count Bitmap", func(t *testing.T) { + q, err := pql.ParseString(`Count(Bitmap())`) + if err != nil { + t.Fatal(err) + } else if q.Calls[0].SupportsInverse() == true { + t.Fatalf("call should not support inverse: %s", q.Calls[0]) + } + }) + t.Run("Union Bitmaps", func(t *testing.T) { + q, err := pql.ParseString(`Union(Bitmap(), Bitmap())`) + if err != nil { + t.Fatal(err) + } else if q.Calls[0].SupportsInverse() == true { + t.Fatalf("call should not support inverse: %s", q.Calls[0]) + } + }) + +} + +// Ensure call is correctly determined to be against an inverse view. +func TestCall_IsInverse(t *testing.T) { + t.Run("Bitmap Row", func(t *testing.T) { + q, err := pql.ParseString(`Bitmap(frame="f", row=1)`) + if err != nil { + t.Fatal(err) + } else if q.Calls[0].IsInverse("row", "col") != false { + t.Fatalf("incorrect call inverse: %s", q.Calls[0]) + } + }) + t.Run("Bitmap Column", func(t *testing.T) { + q, err := pql.ParseString(`Bitmap(frame="f", col=1)`) + if err != nil { + t.Fatal(err) + } else if q.Calls[0].IsInverse("row", "col") != true { + t.Fatalf("incorrect call inverse: %s", q.Calls[0]) + } + }) + t.Run("Bitmap Column No Label", func(t *testing.T) { + q, err := pql.ParseString(`Bitmap(frame="f", col=1)`) + if err != nil { + t.Fatal(err) + } else if q.Calls[0].IsInverse("rowX", "colX") != false { + t.Fatalf("incorrect call inverse: %s", q.Calls[0]) + } + }) + t.Run("Count", func(t *testing.T) { + q, err := pql.ParseString(`Count(Bitmap(frame="f", col=1))`) + if err != nil { + t.Fatal(err) + } else if q.Calls[0].IsInverse("row", "col") != false { + t.Fatalf("incorrect call inverse: %s", q.Calls[0]) + } + }) + +} From cd6b3f9605133c916336d563447c21736993934d Mon Sep 17 00:00:00 2001 From: Travis Date: Mon, 24 Apr 2017 18:43:33 -0500 Subject: [PATCH 10/76] During import, write to standard view as well as time views --- frame.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/frame.go b/frame.go index 14514cd01..0c2683333 100644 --- a/frame.go +++ b/frame.go @@ -529,6 +529,9 @@ func (f *Frame) Import(rowIDs, columnIDs []uint64, timestamps []*time.Time) erro inverse = []string{ViewInverse} } else { standard = ViewsByTime(ViewStandard, *timestamp, q) + // In order to match the logic of `SetBit()`, we want bits + // with timestamps to write to both time and standard views. + standard = append(standard, ViewStandard) inverse = ViewsByTime(ViewInverse, *timestamp, q) } From 60c8ba238cc6f966d046317699a48e3c22e6f050 Mon Sep 17 00:00:00 2001 From: Cody Soyland Date: Tue, 25 Apr 2017 08:57:15 -0500 Subject: [PATCH 11/76] Add new webui design from Joe --- webui/assets/chevron-down.png | Bin 0 -> 3655 bytes webui/assets/main.css | 63 --------- webui/assets/main.js | 114 --------------- webui/assets/nav_cluster.svg | 1 + webui/assets/nav_console.svg | 1 + webui/assets/nav_documentation.svg | 1 + webui/assets/nav_item1.svg | 1 + webui/assets/style.css | 220 +++++++++++++++++++++++++++++ webui/index.html | 209 +++++++++++++++++++++------ 9 files changed, 389 insertions(+), 221 deletions(-) create mode 100644 webui/assets/chevron-down.png delete mode 100644 webui/assets/main.css delete mode 100644 webui/assets/main.js create mode 100644 webui/assets/nav_cluster.svg create mode 100644 webui/assets/nav_console.svg create mode 100644 webui/assets/nav_documentation.svg create mode 100644 webui/assets/nav_item1.svg create mode 100644 webui/assets/style.css diff --git a/webui/assets/chevron-down.png b/webui/assets/chevron-down.png new file mode 100644 index 0000000000000000000000000000000000000000..3312489a21a768a90cb73633e3a596c792c6941b GIT binary patch literal 3655 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 literal 0 HcmV?d00001 diff --git a/webui/assets/main.css b/webui/assets/main.css deleted file mode 100644 index 3db45d2f1..000000000 --- a/webui/assets/main.css +++ /dev/null @@ -1,63 +0,0 @@ -.output { - overflow: auto; - padding: 0 8px; -} - -.output .query { - font-family: monospace; -} - -.output .response { - font-family: monospace; -} - -.input-bar { - padding-right: 2em; - background-color: #eeeeee; -} - -.input-textarea { - font-family: monospace; - font-size: 14px; - background-color: #dddddd; - width: 100%; - border: 0; - outline: 0; - margin: 1em; -} - -body { - padding-top: 5rem; -} - -.controls { - padding: 1em; -} - -.output { - padding: 1em; -} - -.result { - background-color: #eeeeee; - padding: 1em; - margin-top: 1em; -} -.result > .tab-content > .tab-pane { - padding: 1em; -} - -.query { - background-color: #dddddd; - padding: 0.5em -} - -#exTab3 .nav-pills > li > a { - border-radius: 4px 4px 0 0 ; -} - -#exTab3 .tab-content { - color : white; - background-color: #428bca; - padding : 5px 15px; -} diff --git a/webui/assets/main.js b/webui/assets/main.js deleted file mode 100644 index efd0542ed..000000000 --- a/webui/assets/main.js +++ /dev/null @@ -1,114 +0,0 @@ -class REPL { - constructor(input, output) { - this.input = input - this.output = output - this.history = [] - this.history_index = 0 - this.history_buffer = '' - this.result_number = 0 - } - bind_events() { - const repl = this - const keys = { - ENTER: 13, - UP_ARROW: 38, - DOWN_ARROW: 40 - } - - this.input.addEventListener("keydown", (e) => { - if (e.keyCode == keys.UP_ARROW) { - e.preventDefault() - if (this.input.value.substring(0, this.input.selectionStart).indexOf('\n') == '-1') { - if (this.history_index == 0) { - return - } else { - if (this.history_index == this.history.length) { - this.history_buffer = this.input.value - } - this.history_index-- - this.input.value = this.history[this.history_index] - this.input.setSelectionRange(this.input.value.length, this.input.value.length) - } - } - } - if (e.keyCode == keys.DOWN_ARROW) { - e.preventDefault() - if (this.input.value.substring(this.input.selectionEnd, this.input.length).indexOf('\n') == '-1') { - if (this.history_index == this.history.length) { - return - } else { - this.history_index++ - if (this.history_index == this.history.length) { - this.input.value = this.history_buffer - } else { - this.input.value = this.history[this.history_index] - } - this.input.setSelectionRange(this.input.value.length, this.input.value.length) - } - } - } - if (e.keyCode == keys.ENTER && !e.shiftKey) { - e.preventDefault() - this.history_buffer = '' - this.history_index = this.history.length - this.history[this.history_index] = this.input.value - this.history_index++ - this.process_query(this.input.value) - this.input.value = "" - } - }) - } - - process_query(query) { - var xhr = new XMLHttpRequest(); - var dbname = 'foo'; // todo: get db name from dropdown menu - xhr.open('POST', '/db/' + dbname + '/query'); - xhr.setRequestHeader('Content-Type', 'application/text'); - - const repl = this - xhr.onload = function() { - repl.result_number++ -//const entry = document.createElement('p') - const result = ( - '
' + - '' + -'' + - '
' + - '
' + - '

' + - query + - '

' + - '

Response

' + - '

' + - xhr.responseText + - '

' + - '
' + - '
' + - '' + - '' + - '' + - '' + - '
Response code' + xhr.status + '
Response text' + xhr.responseText + '
Response URL' + xhr.responseURL + '
' + - '
' + - '
' + - '
') - // TODO: remove jquery - $(repl.output).prepend($(result)) - }; - xhr.send(query); - } -} - -function startup() { - const input = document.querySelector('.input-textarea') - const output = document.querySelector('.output') - repl = new REPL(input, output) - repl.bind_events() -} diff --git a/webui/assets/nav_cluster.svg b/webui/assets/nav_cluster.svg new file mode 100644 index 000000000..baf6310ea --- /dev/null +++ b/webui/assets/nav_cluster.svg @@ -0,0 +1 @@ +nav_cluster_1 \ No newline at end of file diff --git a/webui/assets/nav_console.svg b/webui/assets/nav_console.svg new file mode 100644 index 000000000..2263c2a69 --- /dev/null +++ b/webui/assets/nav_console.svg @@ -0,0 +1 @@ +nav_console \ No newline at end of file diff --git a/webui/assets/nav_documentation.svg b/webui/assets/nav_documentation.svg new file mode 100644 index 000000000..12cd2108c --- /dev/null +++ b/webui/assets/nav_documentation.svg @@ -0,0 +1 @@ +documentation \ No newline at end of file diff --git a/webui/assets/nav_item1.svg b/webui/assets/nav_item1.svg new file mode 100644 index 000000000..d6d7f779e --- /dev/null +++ b/webui/assets/nav_item1.svg @@ -0,0 +1 @@ +nav_item1 \ No newline at end of file diff --git a/webui/assets/style.css b/webui/assets/style.css new file mode 100644 index 000000000..940d6657f --- /dev/null +++ b/webui/assets/style.css @@ -0,0 +1,220 @@ +*{ + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; +} +body{ + font-family: sans-serif; + background-color: #fbfcfd; + margin: 0; + color: #102445; +} +h2{ + margin-bottom: 30px; +} + +h5{ + text-transform: uppercase; + letter-spacing: 2px; + line-height: 1.21; + margin: 0; +} +a{ + line-height: 1.38; + letter-spacing: 0.2px; + text-decoration: none; + color: #102445; +} + + +a:hover{ + color: #1db598; +} + +textarea{ + width: 100%; + margin-bottom: 10px; + border-radius: 2px; + background-color: #fbfcfd; + border: solid 1.5px #e4eff4; + font-family: monospace; + font-size: 16px; + line-height: 1.5; + letter-spacing: 1.1px; + outline: none; + padding: 30px; +} + + +select{ + /*-webkit-appearance: none; + -moz-appearance: none; + appearance: none; + background: url("img/chevron-down.png") no-repeat calc(100% - 10px) !important;*/ + border-radius: 3px; + background-color: #fbfcfd; + width: 187px; + height: 50px; + border: solid 1.5px #e4eff4; + font-size: 18px; + font-weight: bold; + line-height: 1.39; + letter-spacing: 0.2px; + color: #102445; + padding: 10.5px; + +} + +button{ + width: 165px; + height: 50px; + border-radius: 3px; + background-color: #1db598; + outline: none; + border: none; + font-size: 16px; + color: white; +} + +em{ + font-style: normal; + opacity: 0.5; + font-size: 14px; + font-weight: 500; + letter-spacing: 0.2px; + color: #102445; +} + +.header{ + height: 92px; + display: flex; + align-items: center; + justify-content: space-between; + width: 90%; + margin: auto; +} + +.container{ + display: flex; + height:100%; + min-height: 100vh; +} +.nav{ + color: white; + display: flex; + flex-direction: column; + width: 150px; + background: #3c5f8d; +} + +.nav-item{ + height:150px; + display: flex; + align-items: center; + justify-content: center; + flex-direction: column; + border-bottom: 3px solid #2a4871; + cursor: pointer; +} + +.nav-active{ + background: #f2f7f9; + font-weight: bold; + color: #1db598; +} + + + +.interface{ + display: flex; + flex: 1; + flex-direction: column; + align-items: center; + background: #f2f7f9; +} + +.query{ + margin-bottom: 30px; +} +.query, +.output-container{ + width: 75%; +} + +.output{ + margin-bottom: 30px; +} + +.input-controls{ + display: flex; + justify-content: flex-end; +} + +.tabs{ + display: flex; + background: #eaf2f6; +} +.active-tab{ + background: white; + font-weight: bold; + color: #1db598; + +} + +.tab{ + height:60px; + width: 100px; + border-top-right-radius: 5px; + display: flex; + align-items: center; + justify-content: center; + visibility: visible; + cursor: pointer; + +} + +.pane{ + background: white; + padding: 30px; + display: none; +} + +.active{ + display: block; +} + + + +.result-io-header{ + display: flex; + align-items: center; + margin-bottom: 15px; +} + +.result-input, +.result-ouput{ + border-radius: 2px; + background-color: #fafafa; + border: solid 1.5px #e4eff4; + font-family: monospace; + font-size: 16px; + line-height: 1.5; + letter-spacing: 1.1px; + color: #102445; + padding: 15px; + margin-bottom: 15px; +} + + +.result-ouput{ + background-color: #edf9f7; + border-left: solid 4px #1db598; +} + + +.raw{ + height: 253px; + display: flex; + align-items: center; + justify-content: center; +} diff --git a/webui/index.html b/webui/index.html index 1fe4b9f68..a14d202bf 100644 --- a/webui/index.html +++ b/webui/index.html @@ -1,51 +1,172 @@ - - - - - - - - -
-
- - + +
-
+
- - - - - - +
+

Query

+ +
+ + +     + +
+
+ + +
+ +

Output

+
+ + +
+ +
+ + + +
+ +
+ + + From a0e496f6a0301e45f40ba9bd907c61aa0703bcf6 Mon Sep 17 00:00:00 2001 From: Cody Soyland Date: Tue, 25 Apr 2017 09:40:17 -0500 Subject: [PATCH 12/76] Update JS to hit Pilosa --- webui/assets/main.js | 129 +++++++++++++++++++++++++++++++++++++++++++ webui/index.html | 110 +----------------------------------- 2 files changed, 130 insertions(+), 109 deletions(-) create mode 100644 webui/assets/main.js diff --git a/webui/assets/main.js b/webui/assets/main.js new file mode 100644 index 000000000..b24d3ac51 --- /dev/null +++ b/webui/assets/main.js @@ -0,0 +1,129 @@ +class REPL { + constructor(input, output, button) { + this.input = input + this.output = output + this.button = button + this.history = [] + this.history_index = 0 + this.history_buffer = '' + this.result_number = 0 + } + bind_events() { + const repl = this + const keys = { + ENTER: 13, + UP_ARROW: 38, + DOWN_ARROW: 40 + } + + this.input.addEventListener("keydown", (e) => { + if (e.keyCode == keys.UP_ARROW) { + e.preventDefault() + if (this.input.value.substring(0, this.input.selectionStart).indexOf('\n') == '-1') { + if (this.history_index == 0) { + return + } else { + if (this.history_index == this.history.length) { + this.history_buffer = this.input.value + } + this.history_index-- + this.input.value = this.history[this.history_index] + this.input.setSelectionRange(this.input.value.length, this.input.value.length) + } + } + } + if (e.keyCode == keys.DOWN_ARROW) { + e.preventDefault() + if (this.input.value.substring(this.input.selectionEnd, this.input.length).indexOf('\n') == '-1') { + if (this.history_index == this.history.length) { + return + } else { + this.history_index++ + if (this.history_index == this.history.length) { + this.input.value = this.history_buffer + } else { + this.input.value = this.history[this.history_index] + } + this.input.setSelectionRange(this.input.value.length, this.input.value.length) + } + } + } + if (e.keyCode == keys.ENTER && !e.shiftKey) { + e.preventDefault() + this.submit(); + } + }) + this.button.onclick = function() { + repl.submit(); + }; + } + + submit() { + this.history_buffer = '' + this.history_index = this.history.length + this.history[this.history_index] = this.input.value + this.history_index++ + this.process_query(this.input.value) + this.input.value = "" + } + + process_query(query) { + var xhr = new XMLHttpRequest(); + var dbname = 'foo'; // todo: get db name from dropdown menu + xhr.open('POST', '/db/' + dbname + '/query'); + xhr.setRequestHeader('Content-Type', 'application/text'); + + const repl = this + xhr.onload = function() { + repl.result_number++ + repl.createSingleOutput({"input": query, "output": xhr.responseText}) + } + xhr.send(query) + } + + createSingleOutput(res) { + var node = document.createElement("div"); + node.classList.add('output'); + var markup =` +
+
+
+
+
Input
+        + Source: Index +
+
+ ${res.input} +
+
+
+
+
output
+        + .42 ms +
+
+ ${res.output} +
+
+
+
+ +
+
+ ` + node.innerHTML = markup; + this.output.insertBefore(node, this.output.firstChild) + } +} + +const input = document.getElementById('query') +const output = document.getElementById('outputs') +const button = document.getElementById('query-btn') + +repl = new REPL(input, output, button) +repl.bind_events() diff --git a/webui/index.html b/webui/index.html index a14d202bf..607c55167 100644 --- a/webui/index.html +++ b/webui/index.html @@ -59,114 +59,6 @@ - + From 7ee65e602211cd878d6872ba96258e834628427e Mon Sep 17 00:00:00 2001 From: Alan Bernstein Date: Tue, 25 Apr 2017 11:46:17 -0500 Subject: [PATCH 13/76] Populate dropdown from /schema, use selected index for queries --- webui/assets/main.js | 32 ++++++++++++++++++++++++++++---- webui/index.html | 4 ++-- 2 files changed, 30 insertions(+), 6 deletions(-) diff --git a/webui/assets/main.js b/webui/assets/main.js index b24d3ac51..6ee89ffc4 100644 --- a/webui/assets/main.js +++ b/webui/assets/main.js @@ -69,14 +69,19 @@ class REPL { process_query(query) { var xhr = new XMLHttpRequest(); - var dbname = 'foo'; // todo: get db name from dropdown menu - xhr.open('POST', '/db/' + dbname + '/query'); + var e = document.getElementById("index-dropdown"); + var indexname = e.options[e.selectedIndex].text; + xhr.open('POST', '/db/' + indexname + '/query'); // TODO db->index xhr.setRequestHeader('Content-Type', 'application/text'); const repl = this xhr.onload = function() { repl.result_number++ - repl.createSingleOutput({"input": query, "output": xhr.responseText}) + repl.createSingleOutput({ + "input": query, + "output": xhr.responseText, + "indexname": indexname, + }) } xhr.send(query) } @@ -91,7 +96,7 @@ class REPL {
Input
       - Source: Index + Source: ${res.indexname}
${res.input} @@ -119,11 +124,30 @@ class REPL { node.innerHTML = markup; this.output.insertBefore(node, this.output.firstChild) } + + populate_index_dropdown() { + var xhr = new XMLHttpRequest(); + xhr.open('GET', '/schema') + var select = document.getElementById('index-dropdown') + + xhr.onload = function() { + var schema = JSON.parse(xhr.responseText) + for(var i=0; iindex + var opt = document.createElement('option') + opt.innerHTML = schema['dbs'][i]['name'] + select.appendChild(opt) + } + } + xhr.send(null) + } + } + const input = document.getElementById('query') const output = document.getElementById('outputs') const button = document.getElementById('query-btn') repl = new REPL(input, output, button) +repl.populate_index_dropdown() repl.bind_events() diff --git a/webui/index.html b/webui/index.html index 607c55167..36030af61 100644 --- a/webui/index.html +++ b/webui/index.html @@ -34,8 +34,8 @@
-     From cd30b412f8d5113a9371cfb12dda82779758bd59 Mon Sep 17 00:00:00 2001 From: Alan Bernstein Date: Tue, 25 Apr 2017 11:59:49 -0500 Subject: [PATCH 14/76] Add real query time to output --- webui/assets/main.js | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/webui/assets/main.js b/webui/assets/main.js index 6ee89ffc4..bf16cb2df 100644 --- a/webui/assets/main.js +++ b/webui/assets/main.js @@ -75,15 +75,19 @@ class REPL { xhr.setRequestHeader('Content-Type', 'application/text'); const repl = this + var start_time = new Date().getTime(); + xhr.send(query) xhr.onload = function() { + var end_time = new Date().getTime() repl.result_number++ repl.createSingleOutput({ "input": query, "output": xhr.responseText, "indexname": indexname, + "querytime_ms": end_time - start_time, }) } - xhr.send(query) + } createSingleOutput(res) { @@ -106,7 +110,7 @@ class REPL {
output
       - .42 ms + ${res.querytime_ms} ms
${res.output} From 13b07b99b522845a63259f6f194276ee6d828d5e Mon Sep 17 00:00:00 2001 From: Travis Date: Tue, 25 Apr 2017 12:05:28 -0500 Subject: [PATCH 15/76] Change the README to be an index of links to the Docs --- README.md | 347 +++++++----------------------------------------------- 1 file changed, 41 insertions(+), 306 deletions(-) diff --git a/README.md b/README.md index acab512eb..3fc7e4c56 100644 --- a/README.md +++ b/README.md @@ -1,326 +1,61 @@ -# pilosa - -Pilosa is a bitmap index. + + + [![Build Status](https://travis-ci.com/pilosa/pilosa.svg?token=Peb4jvQ3kLbjUEhpU5aR&branch=master)](https://travis-ci.com/pilosa/pilosa) +## An open source, distributed bitmap index. +- [Docs](#docs) +- [Getting Started](#getting-started) +- [Data Model](#data-model) +- [Client Libraries](#client-drivers) +- [Get Support](#get-support) +- [Contributing](#contributing) + + +## Docs + +See our [Documentation](https://www.pilosa.com/docs/) for information about installing and working with Pilosa. + + ## Getting Started -Pilosa requires Go 1.7 or greater. +[Getting Started](https://www.pilosa.com/docs/getting-started/) -You can download the source by running `go get`: -```sh -$ go get github.com/pilosa/pilosa -``` +1. [Install Pilosa](https://www.pilosa.com/docs/installation/). -Now you can install the `pilosa` binary: +2. [Start Pilosa](https://www.pilosa.com/docs/getting-started/#starting-pilosa) with the default configuration: -```sh -$ go install github.com/pilosa/pilosa/cmd/... -``` + ```shell + pilosa server + ``` + + and verify that it's running: + + ```shell + curl localhost:10101/nodes + ``` -Now run a single pilosa node with the default configuration: +3. Follow along with the [Sample Project](https://www.pilosa.com/docs/getting-started/#sample-project) to get a better understanding of Pilosa's capabilities. -```sh -pilosa server -``` -## Configuration +## Data Model -Running just `pilosa` will show a list of available subcommands. `pilosa help -` will show usage information and the available flags for the command. +Check out how the Pilosa [Data Model](https://www.pilosa.com/docs/data-model/) works. -Any flag can be specified at the command line, in an environment variables, -and/or in a toml config file. The environment variable for any flag is that -flag, upper cased, prefixed with `PILOSA_`, and with any dashes converted to -underscores. For example, if you would specify `--cluster.poll-interval=30s` at -the command line, you would set `PILOSA_CLUSTER.POLL_INTERVAL=30s` in the -environment. For the configuration file, a dot in a flag denotes nesting with in -the config file. See the example config file below for examples of this. -You can specify a configuration by setting the `--config` flag when running -`pilosa`. +## Client Libraries +There are supported libraries for the following languages: +- [Go](https://www.pilosa.com/docs/client-libraries/#go) +- [Java](https://www.pilosa.com/docs/client-libraries/#java) +- [Python](https://www.pilosa.com/docs/client-libraries/#python) -```sh -pilosa server --config custom-config-file.cfg -``` +## Get Support -The config file uses the [TOML](https://github.com/toml-lang/toml) configuration file format, -and should look like: +There are [several channels](https://www.pilosa.com/community/#support) availble for you to reach out to us for support. -``` -data-dir = "/tmp/pil0" -bind = "127.0.0.1:10101" +## Contributing -[cluster] - poll-interval = "2m0s" - replicas = 2 - hosts = [ - "127.0.0.1:10101", - "127.0.0.1:10102", - ] - -[anti-entropy] - interval = "10m0s" - -[profile] - cpu = "/home/mycpuprofile" - cpu-time = "30s" -``` - -You can generate a template config file with default values with: - -```sh -pilosa config -``` - -The first two configuration options will be unique to each node in the cluster: - -`data-dir`: directory in which data is stored to disk - -`bind`: IP and port that pilosa will listen on - -The remaining configuration options should be the same on every node in the cluster. - -`[cluster] replicas`: the number of replicas within the cluster - -`[cluster] hosts`: specifies each node within the cluster - -`[cluster] poll-interval`: TODO - -`[anti-entropy] interval`: TODO - -There are also some profiling options for debugging and performance tuning - these don't need to be the same across the cluster and are mostly useful for doing Pilosa development. - -`[profile] cpu`: Path at which to store cpu profiling data which will be taken when pilosa starts. - -`[profile] cpu-time`: Amount of time for which to collect cpu profiling data at startup. - -## Docker - -You can create a Pilosa container using `make docker` or equivalently: -``` -docker build -t pilosa:latest . -``` - -You can run a temporary container using: -``` -docker run -it --rm --name pilosa -p 10101:10101 pilosa:latest -``` - -When you click `Ctrl+C` to stop the container, the container and the data in the container will be erased. You can leave out `--rm` flag to keep the data in the container. See [Docker documentation](https://docs.docker.com) for other options. - -## Usage - -You can interact with Pilosa via HTTP requests to the host:port on which you have Pilosa running. -The following examples illustrate how to do this using `curl` with a Pilosa cluster running on -127.0.0.1 port 10101. - -Return the version of Pilosa: -```sh -$ curl "http://127.0.0.1:10101/version" -``` - -Return a list of all indexes and frames in the index: -```sh -$ curl "http://127.0.0.1:10101/schema" -``` - -### Index and Frame Schema - -Before running a query, the corresponding index and frame must be created. Note that index and frame names can contain only lower case letters, numbers, dash (`-`), underscore (`_`) and dot (`.`). - -You can create the index `sample-idx` using: - -```sh -$ curl -XPOST "http://127.0.0.1:10101/index" \ - -d '{"index": "sample-idx"}' -``` - -Optionally, you can specify the column label on index creation: - -```sh -$ curl -XPOST "http://127.0.0.1:10101/index" \ - -d '{"index": "sample-idx", "options": {"columnLabel": "user"}}' -``` - -The frame `collaboration` may be created using the following call: - -```sh -$ curl -XPOST "http://127.0.0.1:10101/frame" \ - -d '{"index": "sample-idx", "frame": "collaboration"}' -``` - -It is possible to specify the frame row label on frame creation: - -```sh -$ curl -XPOST "http://127.0.0.1:10101/frame" \ - -d '{"index": "sample-idx", "frame": "collaboration", "options": {"rowLabel": "project"}}' -``` - -### Queries - -Queries to Pilosa require sending a POST request where the query itself is sent as POST data. -You specify the index on which to perform the query with a URL argument `index=index-name`. - -In this section, we assume both the index `sample-idx` with column label `user` and the frame `collaboration` with row label `project` was created. - -A query sent to index `sample-idx` will have the following format: - -```sh -$ curl -X POST "http://127.0.0.1:10101/query?index=sample-idx" -d 'Query()' -``` - -The `Query()` object referenced above should be made up of one or more of the query types listed below. -So for example, a SetBit() query would look like this: -```sh -$ curl -X POST "http://127.0.0.1:10101/query?index=sample-idx" -d 'SetBit(project=10, frame="collaboration", user=1)' -``` - -Query results have the format `{"results":[]}`, where `results` is a list of results for each `Query()`. This -means that you can provide multiple `Query()` objects with each HTTP request and `results` will contain -the results of all of the queries. - -```sh -$ curl -X POST "http://127.0.0.1:10101/query?index=sample-idx" -d 'Query() Query() Query()' -``` - ---- -#### SetBit() -``` -SetBit(project=10, frame="collaboration", user=1) -``` -A return value of `{"results":[true]}` indicates that the bit was toggled from 0 to 1. -A return value of `{"results":[false]}` indicates that the bit was already set to 1 and therefore nothing changed. - -SetBit accepts an optional `timestamp` field: -``` -SetBit(project=10, frame="collaboration", user=2, timestamp="2016-12-11T10:09:07") -``` - ---- -#### ClearBit() -``` -ClearBit(project=10, frame="collaboration", user=1) -``` -A return value of `{"results":[true]}` indicates that the bit was toggled from 1 to 0. -A return value of `{"results":[false]}` indicates that the bit was already set to 0 and therefore nothing changed. - ---- -#### SetRowAttrs() -``` -SetRowAttrs(project=10, frame="collaboration", stars=123, url="http://projects.pilosa.com/10", active=true) -``` -Returns `{"results":[null]}` - ---- -#### SetColumnAttrs() ---- -``` -SetColumnAttrs(user=10, friends=123, username="mrpi", active=true) -``` - -Returns `{"results":[null]}` - ---- -#### Bitmap() -``` -Bitmap(project=10, frame="collaboration") -``` -Returns `{"results":[{"attrs":{"stars":123, "url":"http://projects.pilosa.com/10", "active":true},"bits":[1,2]}]}` where `attrs` are the -attributes set using `SetRowAttrs()` and `bits` are the bits set using `SetBit()`. - -In order to return column attributes attached to the columns of a bitmap, add `&columnAttrs=true` to the query string. Sample response: -``` -{"results":[{"attrs":{},"bits":[10]}],"columnAttrs":[{"user":10,"attrs":{"friends":123, "username":"mrpi", "active":true}}]} -``` - ---- -#### Union() -``` -Union(Bitmap(project=10, frame="collaboration"), Bitmap(project=20, frame="collaboration"))) -``` -Returns a result set similar to that of a `Bitmap()` query, only the `attrs` dictionary will be empty: `{"results":[{"attrs":{},"bits":[1,2]}]}`. -Note that a `Union()` query can be nested within other queries anywhere that you would otherwise provide a `Bitmap()`. - ---- -#### Intersect() -``` -Intersect(Bitmap(project=10, frame="collaboration"), Bitmap(project=20, frame="collaboration"))) -``` -Returns a result set similar to that of a `Bitmap()` query, only the `attrs` dictionary will be empty: `{"results":[{"attrs":{},"bits":[1]}]}`. -Note that an `Intersect()` query can be nested within other queries anywhere that you would otherwise provide a `Bitmap()`. - ---- -#### Difference() -``` -Difference(Bitmap(project=10, frame="collaboration"), Bitmap(project=20, frame="collaboration"))) -``` -`Difference()` represents all of the bits that are set in the first `Bitmap()` but are not set in the second `Bitmap()`. It returns a result set similar to that of a `Bitmap()` query, only the `attrs` dictionary will be empty: `{"results":[{"attrs":{},"bits":[2]}]}`. -Note that a `Difference()` query can be nested within other queries anywhere that you would otherwise provide a `Bitmap()`. - ---- -#### Count() -``` -Count(Bitmap(project=10, frame="collaboration")) -``` -Returns the count of the number of bits set in `Bitmap()`: `{"results":[28]}` - ---- -#### Range() -``` -Range(project=10, frame="collaboration", start="1970-01-01T00:00", end="2000-01-02T03:04") -``` - ---- -#### TopN() -``` -TopN(frame="geo") -``` -Returns all Bitmaps in the cache from frame `geo` sorted by the count of bits. - -``` -TopN(frame="geo", n=20) -``` -Returns the top 20 Bitmaps from frame `geo`. - -``` -TopN(Bitmap(project=10, frame="collaboration"), frame="geo", n=20) -``` -Returns the top 20 Bitmaps from `geo` sorted by the count of bits in the intersection with `Bitmap(project=10)`. - -``` -TopN(Bitmap(project=10, frame="collaboration"), frame="geo", n=20, field="category", [81,82]) -``` -Returns the top 20 Bitmaps from `geo`in attribute `category` with values `81 or -82` sorted by the count of bits in the intersection with `Bitmap(project=10)`. - -## Development - -### Updating dependencies - -To update dependencies, you'll need to install [Glide][]. - -Then add the new dependencies in your project: - -```sh -$ glide get github.com/foo/bar -``` - -### Protobuf - -If you update protobuf (pilosa/internal/internal.proto), then you need to run `go generate` -```sh -$ go generate -``` - -### Version - -In order to set the version number, compile Pilosa with the following argument: -```sh -$ go install --ldflags="-X main.Version=1.0.0" -``` - -[Glide]: http://glide.sh/ +Pilosa is an open source project. Please see our [Contributing Guide](https://www.pilosa.com/docs/contributing/) for information about how to get involved. From fe6d2189a708317ab179eca533e6e1ed5126086c Mon Sep 17 00:00:00 2001 From: Alan Bernstein Date: Tue, 25 Apr 2017 12:10:27 -0500 Subject: [PATCH 16/76] Change output background to red on error --- webui/assets/main.js | 7 ++++++- webui/assets/style.css | 10 ++++++++-- 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/webui/assets/main.js b/webui/assets/main.js index bf16cb2df..a91391a38 100644 --- a/webui/assets/main.js +++ b/webui/assets/main.js @@ -93,6 +93,11 @@ class REPL { createSingleOutput(res) { var node = document.createElement("div"); node.classList.add('output'); + var result_class = "result-output" + var output = JSON.parse(res['output']) + if("error" in output) { + result_class = "result-error" + } var markup =`
@@ -112,7 +117,7 @@ class REPL {        ${res.querytime_ms} ms
-
+
${res.output}
diff --git a/webui/assets/style.css b/webui/assets/style.css index 940d6657f..a4ed3df1f 100644 --- a/webui/assets/style.css +++ b/webui/assets/style.css @@ -192,7 +192,8 @@ em{ } .result-input, -.result-ouput{ +.result-output, +.result-error{ border-radius: 2px; background-color: #fafafa; border: solid 1.5px #e4eff4; @@ -206,11 +207,16 @@ em{ } -.result-ouput{ +.result-output{ background-color: #edf9f7; border-left: solid 4px #1db598; } +.result-error{ + background-color: #f9edf4; + border-left: solid 4px #b51d78; +} + .raw{ height: 253px; From 56797833efd646469204fb72ea93854ddd3438a1 Mon Sep 17 00:00:00 2001 From: Alan Bernstein Date: Tue, 25 Apr 2017 12:10:41 -0500 Subject: [PATCH 17/76] Add TODO comment --- webui/assets/main.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/webui/assets/main.js b/webui/assets/main.js index a91391a38..d27978921 100644 --- a/webui/assets/main.js +++ b/webui/assets/main.js @@ -136,7 +136,7 @@ class REPL { populate_index_dropdown() { var xhr = new XMLHttpRequest(); - xhr.open('GET', '/schema') + xhr.open('GET', '/schema') // TODO schema->status (?) var select = document.getElementById('index-dropdown') xhr.onload = function() { From a899dbb2d5923e5e2bbb92ed80be46707cdf8663 Mon Sep 17 00:00:00 2001 From: Alan Bernstein Date: Tue, 25 Apr 2017 12:11:04 -0500 Subject: [PATCH 18/76] Add favicon from cloudfront --- webui/index.html | 1 + 1 file changed, 1 insertion(+) diff --git a/webui/index.html b/webui/index.html index 36030af61..eda59baa0 100644 --- a/webui/index.html +++ b/webui/index.html @@ -6,6 +6,7 @@ Pilosa Console +
From 29127362042032a0920fa038a1973acdd711ae0c Mon Sep 17 00:00:00 2001 From: Alan Bernstein Date: Tue, 25 Apr 2017 13:17:11 -0500 Subject: [PATCH 19/76] Change red error color to match style --- webui/assets/style.css | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/webui/assets/style.css b/webui/assets/style.css index a4ed3df1f..68240ad32 100644 --- a/webui/assets/style.css +++ b/webui/assets/style.css @@ -213,8 +213,9 @@ em{ } .result-error{ - background-color: #f9edf4; - border-left: solid 4px #b51d78; + background-color: #fbf1f0; + border-left: solid 4px #fa3035; + color: #fa3035; } From 79031316f7a871f9a4b7dbe91463f78fc7f7a1c4 Mon Sep 17 00:00:00 2001 From: Alan Bernstein Date: Tue, 25 Apr 2017 13:17:39 -0500 Subject: [PATCH 20/76] Remove dropdown label and add header --- webui/index.html | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/webui/index.html b/webui/index.html index eda59baa0..c8c8aa2a1 100644 --- a/webui/index.html +++ b/webui/index.html @@ -16,7 +16,7 @@