From 7bcbfaef09f04e243e6f338f9ce735428d166a68 Mon Sep 17 00:00:00 2001 From: Todd Gruben Date: Tue, 15 Aug 2017 11:36:49 -0500 Subject: [PATCH 01/46] fix for #770 --- roaring/roaring.go | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/roaring/roaring.go b/roaring/roaring.go index cfcda97d1..065f4e6df 100644 --- a/roaring/roaring.go +++ b/roaring/roaring.go @@ -197,6 +197,7 @@ func (b *Bitmap) CountRange(start, end uint64) (n uint64) { if len(b.keys) == 0 { return } + skey := highbits(start) ekey := highbits(end) @@ -233,7 +234,11 @@ func (b *Bitmap) CountRange(start, end uint64) (n uint64) { // Count containers in between. for x := i + 1; x < j; x++ { + if ekey < b.keys[x] { + break + } n += uint64(b.containers[x].n) + } return n From 89b72cbfc8eae946fde9c8ba873644de794134e3 Mon Sep 17 00:00:00 2001 From: Todd Gruben Date: Tue, 15 Aug 2017 12:33:19 -0500 Subject: [PATCH 02/46] provided some clarification of CountRange issue --- roaring/roaring.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/roaring/roaring.go b/roaring/roaring.go index 065f4e6df..6fa53f472 100644 --- a/roaring/roaring.go +++ b/roaring/roaring.go @@ -234,6 +234,8 @@ func (b *Bitmap) CountRange(start, end uint64) (n uint64) { // Count containers in between. for x := i + 1; x < j; x++ { + // Ensure that current container is inside the requested range since + // keys to not have to be consecutive if ekey < b.keys[x] { break } From af4350ca460040242488810612305b0296bab88d Mon Sep 17 00:00:00 2001 From: Todd Gruben Date: Tue, 15 Aug 2017 12:36:09 -0500 Subject: [PATCH 03/46] spelling correction --- roaring/roaring.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/roaring/roaring.go b/roaring/roaring.go index 6fa53f472..1a1383ed5 100644 --- a/roaring/roaring.go +++ b/roaring/roaring.go @@ -235,7 +235,7 @@ func (b *Bitmap) CountRange(start, end uint64) (n uint64) { // Count containers in between. for x := i + 1; x < j; x++ { // Ensure that current container is inside the requested range since - // keys to not have to be consecutive + // keys do not have to be consecutive if ekey < b.keys[x] { break } From f6b75e036167e48f8291606505f38329cb2231cc Mon Sep 17 00:00:00 2001 From: Matthew Jaffee Date: Tue, 15 Aug 2017 17:07:15 -0500 Subject: [PATCH 04/46] fix Bitmap.CountRange bugs, comment, test --- roaring/roaring.go | 29 ++++------ roaring/roaring_internal_test.go | 78 +++++++++++++++++++++++++ roaring/roaring_test.go | 98 ++++++++++++++++++++++++++++++++ 3 files changed, 186 insertions(+), 19 deletions(-) diff --git a/roaring/roaring.go b/roaring/roaring.go index 1a1383ed5..577c7c4c5 100644 --- a/roaring/roaring.go +++ b/roaring/roaring.go @@ -209,38 +209,29 @@ func (b *Bitmap) CountRange(start, end uint64) (n uint64) { return uint64(b.containers[i].countRange(int(lowbits(start)), int(lowbits(end)))) } - // Count first partial container. if i < 0 { - // start is before container, so we should start counting - // at first container that has value - if skey < b.keys[0] { - i = -1 - } else { - i = -i - } + // start's container did not exist + // set i to the index of the first container we have with values higher than start + i = -i - 1 } else { + // Count first partial container and advance i so we don't recount it n += uint64(b.containers[i].countRange(int(lowbits(start)), maxContainerVal+1)) + i += 1 } // Count last container. if j < 0 { - j = -j - if j > len(b.containers) { - j = len(b.containers) - } + // end's container did not exist + // set j to the index of the first container with values higher than end (or len(containers)) + j = -j - 1 } else { + // end's container exists, count it up to end n += uint64(b.containers[j].countRange(0, int(lowbits(end)))) } // Count containers in between. - for x := i + 1; x < j; x++ { - // Ensure that current container is inside the requested range since - // keys do not have to be consecutive - if ekey < b.keys[x] { - break - } + for x := i; x < j; x++ { n += uint64(b.containers[x].n) - } return n diff --git a/roaring/roaring_internal_test.go b/roaring/roaring_internal_test.go index ea4e5f0ef..9cc2f2aa7 100644 --- a/roaring/roaring_internal_test.go +++ b/roaring/roaring_internal_test.go @@ -2310,3 +2310,81 @@ func Test_BufBitmapIterator_UnreadPanic(t *testing.T) { itr.unread() itr.unread() } + +func TestSearc64(t *testing.T) { + tests := []struct { + a []uint64 + value uint64 + exp int + }{ + { + a: []uint64{1, 5, 10, 12}, + value: 5, + exp: 1, + }, + { + a: []uint64{1, 5, 10, 12}, + value: 1, + exp: 0, + }, + { + a: []uint64{1, 5, 10, 12}, + value: 0, + exp: -1, + }, + { + a: []uint64{1, 5, 10, 12}, + value: 2, + exp: -2, + }, + { + a: []uint64{1, 5, 10, 12}, + value: 7, + exp: -3, + }, + { + a: []uint64{1, 5, 10, 12}, + value: 11, + exp: -4, + }, + { + a: []uint64{1, 5, 10, 12}, + value: 13, + exp: -5, + }, + { + a: []uint64{1, 5, 10, 12}, + value: 3843534, + exp: -5, + }, + { + a: []uint64{}, + value: 3843534, + exp: -1, + }, + { + a: []uint64{}, + value: 0, + exp: -1, + }, + { + a: []uint64{0}, + value: 0, + exp: 0, + }, + { + a: []uint64{0}, + value: 1, + exp: -2, + }, + } + + for _, test := range tests { + t.Run(fmt.Sprintf("%d in %v", test.value, test.a), func(t *testing.T) { + actual := search64(test.a, test.value) + if actual != test.exp { + t.Errorf("got: %d, exp: %d", actual, test.exp) + } + }) + } +} diff --git a/roaring/roaring_test.go b/roaring/roaring_test.go index c721f3804..7e524f51f 100644 --- a/roaring/roaring_test.go +++ b/roaring/roaring_test.go @@ -54,6 +54,104 @@ func TestContainerCount(t *testing.T) { } } +func TestCountRange(t *testing.T) { + tests := []struct { + name string + bitmap []uint64 + start uint64 + end uint64 + exp uint64 + }{ + { + name: "j < 0 : 1", + bitmap: []uint64{0, 1, 2, 3 * 65536}, + start: 0, + end: 65536, + exp: 3, + }, + { + name: "i < 0 : 1", + bitmap: []uint64{0, 1, 2, 2 * 65536, 3 * 65536}, + start: 65536, + end: 3 * 65536, + exp: 1, + }, + { + name: "single-container-run", + bitmap: []uint64{0, 2, 3, 4, 5, 2 * 65536, 3 * 65536}, + start: 2, + end: 5, + exp: 3, + }, + { + name: "single-container-beg", + bitmap: []uint64{1, 2, 3, 4, 5, 2 * 65536, 3 * 65536}, + start: 1, + end: 4, + exp: 3, + }, + { + name: "partial-start", + bitmap: []uint64{1, 2, 3, 4, 5, 2 * 65536, 3 * 65536}, + start: 5, + end: 3 * 65536, + exp: 2, + }, + { + name: "partial-end", + bitmap: []uint64{1, 2 * 65536, 3 * 65536, 3*65536 + 1, 3*65536 + 2}, + start: 0, + end: (3 * 65536) + 1, + exp: 3, + }, + { + name: "partial-both", + bitmap: []uint64{65536, 65537, 65538, 2 * 65536, 2*65536 + 1, 2*65536 + 2}, + start: 65537, + end: (2 * 65536) + 1, + exp: 3, + }, + { + name: "partial-both-bookends", + bitmap: []uint64{0, 65535, 65536, 65537, 65538, 2 * 65536, 2*65536 + 1, 2*65536 + 2, 3 * 65536}, + start: 65537, + end: (2 * 65536) + 1, + exp: 3, + }, + { + name: "empty-bookends", + bitmap: []uint64{1, 65535, 5 * 65536, 5*65536 + 1}, + start: 65536, + end: 5 * 65536, + exp: 0, + }, + { + name: "i not found, j found", + bitmap: []uint64{1, 65535, 5 * 65536}, + start: 2 * 65535, + end: 5*65536 + 1, + exp: 1, + }, + { + name: "i not found, j not found", + bitmap: []uint64{1, 65535, 5 * 65536, 7 * 65536}, + start: 2 * 65535, + end: 6 * 65536, + exp: 1, + }, + } + + for _, test := range tests { + t.Run(fmt.Sprintf("%s: %d to %d in '%v'", test.name, test.start, test.end, test.bitmap), func(t *testing.T) { + b := roaring.NewBitmap(test.bitmap...) + actual := b.CountRange(test.start, test.end) + if actual != test.exp { + t.Errorf("got: %d, exp: %d", actual, test.exp) + } + }) + } +} + func TestCheckBitmap(t *testing.T) { b := roaring.NewBitmap() x := 0 From 23f0d091573602ff7db722d193499af43da9d880 Mon Sep 17 00:00:00 2001 From: Matthew Jaffee Date: Tue, 15 Aug 2017 17:47:12 -0500 Subject: [PATCH 05/46] improve description of search32 and search64 --- roaring/roaring.go | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/roaring/roaring.go b/roaring/roaring.go index 577c7c4c5..16e6159d7 100644 --- a/roaring/roaring.go +++ b/roaring/roaring.go @@ -2865,7 +2865,8 @@ func (*op) size() int { return 1 + 8 + 4 } func highbits(v uint64) uint64 { return uint64(v >> 16) } func lowbits(v uint64) uint16 { return uint16(v & 0xFFFF) } -// search32 returns the index of v in a. +// search32 returns the index of value in a. If value is not found, it works the +// same way as search64. func search32(a []uint16, value uint16) int { // Optimize for elements and the last element. n := len(a) @@ -2902,7 +2903,13 @@ func search32(a []uint16, value uint16) int { return -(lo + 1) } -// search64 returns the index of v in a. +// search64 returns the index of value in a. If value is not found, -1 * (1 + +// the index where v would be if it were inserted) is returned. This is done in +// order to both signal that value was not found (negative number), and also +// return information about where v would go if it were inserted. The +1 offset +// is necessary due to the case where v is not found, but would go at index 0. +// since negative 0 is no different from positive 0, we offset the returned +// negative indices by 1. See the test for this function for examples. func search64(a []uint64, value uint64) int { // Optimize for elements and the last element. n := len(a) From 370ab656f9096651fe8f4a6ad68bb12f5266b6dd Mon Sep 17 00:00:00 2001 From: Cody Soyland Date: Mon, 14 Aug 2017 16:05:03 -0500 Subject: [PATCH 06/46] Add metadata to doc pages (sort weight and nav headings) --- docs/administration.md | 11 +++++++++-- docs/api-reference.md | 2 ++ docs/client-libraries.md | 6 ++++++ docs/configuration.md | 7 +++++++ docs/data-model.md | 12 ++++++++++++ docs/faq.md | 2 ++ docs/getting-started.md | 7 +++++++ docs/glossary.md | 2 ++ docs/input-definition.md | 11 ++++++++--- docs/installation.md | 5 +++++ docs/introduction.md | 2 ++ docs/pdk.md | 5 +++++ docs/query-language.md | 7 +++++++ docs/tutorials.md | 5 +++++ docs/webui.md | 5 +++++ 15 files changed, 84 insertions(+), 5 deletions(-) diff --git a/docs/administration.md b/docs/administration.md index 04b1b2bf0..786f10992 100644 --- a/docs/administration.md +++ b/docs/administration.md @@ -1,5 +1,12 @@ +++ -title = "Administration Guide" +title = "Administration" +weight = 13 +nav = [ + "Installing in production", + "Imports and Exports", + "Versioning", + "Backup/restore", +] +++ ## Administration Guide @@ -164,4 +171,4 @@ We currently track the following events Goroutines: Number of running Goroutines. -OpenFiles: Number of open file handles associated with running Pilosa process ID. \ No newline at end of file +OpenFiles: Number of open file handles associated with running Pilosa process ID. diff --git a/docs/api-reference.md b/docs/api-reference.md index 0ee460562..c14ff4dcc 100644 --- a/docs/api-reference.md +++ b/docs/api-reference.md @@ -1,5 +1,7 @@ +++ title = "API Reference" +weight = 10 +nav = [] +++ diff --git a/docs/client-libraries.md b/docs/client-libraries.md index e7a439371..e896fbab4 100644 --- a/docs/client-libraries.md +++ b/docs/client-libraries.md @@ -1,5 +1,11 @@ +++ title = "Client Libraries" +weight = 12 +nav = [ + "Go", + "Python", + "Java", +] +++ ## Client Libraries diff --git a/docs/configuration.md b/docs/configuration.md index 08ff7c4a3..2ef18a532 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -1,5 +1,12 @@ +++ title = "Configuration" +weight = 7 +nav = [ + "Command line flags", + "Environment variables", + "Config file", + "All Options", +] +++ ## Configuration diff --git a/docs/data-model.md b/docs/data-model.md index f8987f87b..2e7ac03e8 100644 --- a/docs/data-model.md +++ b/docs/data-model.md @@ -1,5 +1,17 @@ +++ title = "Data Model" +weight = 5 +nav = [ + "Overview", + "Index", + "Column", + "Row", + "Frame", + "Time Quantum", + "Attribute", + "Slice", + "View", +] +++ ## Data Model diff --git a/docs/faq.md b/docs/faq.md index cea161aa3..ef9f8bfc8 100644 --- a/docs/faq.md +++ b/docs/faq.md @@ -1,5 +1,7 @@ +++ title = "FAQ" +weight = 15 +nav = [] +++ ## FAQ diff --git a/docs/getting-started.md b/docs/getting-started.md index 0c42683e9..534e733b4 100644 --- a/docs/getting-started.md +++ b/docs/getting-started.md @@ -1,5 +1,12 @@ +++ title = "Getting Started" +weight = 3 +nav = [ + "Starting Pilosa", + "Sample Project", + "Input Definition", + "What's Next?", +] +++ ## Getting Started diff --git a/docs/glossary.md b/docs/glossary.md index e4bef02e3..50906a0cf 100644 --- a/docs/glossary.md +++ b/docs/glossary.md @@ -1,5 +1,7 @@ +++ title = "Glossary" +weight = 14 +nav = [] +++ ## Glossary diff --git a/docs/input-definition.md b/docs/input-definition.md index 4b73c4991..459f1752d 100644 --- a/docs/input-definition.md +++ b/docs/input-definition.md @@ -1,12 +1,17 @@ +++ title = "Input Definition" +weight = 8 +nav = [ + "Create the Schema", + "Import Data", +] +++ ## Input Definition This document builds on the data import concepts introduced in [Getting Started](../getting-started/). Here we will demonstrate creating the index's schema and data definition. Then using this definition to import JSON data. -#### Create the Schema Using an Input Definition +### Create the Schema Input definitions allow users to define a schema based on their data and to provide data to Pilosa in a more standard format like JSON. Once an input definition is created, we can send data to Pilosa as JSON, and as long as the data adheres to the definition, Pilosa will internally perform all of the appropriate mutations. @@ -91,7 +96,7 @@ We can also set `repo_id` for multiple frames at the same time by providing fiel - mapping: The value for this field is used to lookup a `rowID` in a map. A valueMap is required for this destination type. - set-timestamp: The value for this field is used to lookup timestamp and set timestamp for the whole frame -#### Import Data Using an Input Definition +### Import Data The sample data for the "Star Trace" project is at [Pilosa Getting Started repository](https://github.com/pilosa/getting-started). @@ -125,4 +130,4 @@ curl localhost:10101/index/repository/query \ SetBit(frame="language", repo_id=91720568, language_id=5) SetBit(frame="language", repo_id=95122322, language_id=17) ' -``` \ No newline at end of file +``` diff --git a/docs/installation.md b/docs/installation.md index 9936fb95c..b6c81a2b4 100644 --- a/docs/installation.md +++ b/docs/installation.md @@ -1,5 +1,10 @@ +++ title = "Installation" +weight = 2 +nav = [ + "Installing on MacOS", + "Installing on Linux", +] +++ diff --git a/docs/introduction.md b/docs/introduction.md index 87cd94a3c..d499c8c0a 100644 --- a/docs/introduction.md +++ b/docs/introduction.md @@ -1,5 +1,7 @@ +++ title = "Introduction" +weight = 1 +nav = [] +++ diff --git a/docs/pdk.md b/docs/pdk.md index 5d50a5b98..8c02dc279 100644 --- a/docs/pdk.md +++ b/docs/pdk.md @@ -1,5 +1,10 @@ +++ title = "PDK" +weight = 11 +nav = [ + "Library", + "Examples", +] +++ ## PDK diff --git a/docs/query-language.md b/docs/query-language.md index 93ddb846a..c9d5f1e05 100644 --- a/docs/query-language.md +++ b/docs/query-language.md @@ -1,5 +1,12 @@ +++ title = "Query Language" +weight = 6 +nav = [ + "Conventions", + "Arguments and Types", + "Write Operations", + "Read Operations", +] +++ ## Query Language diff --git a/docs/tutorials.md b/docs/tutorials.md index 350aa3849..995673ca2 100644 --- a/docs/tutorials.md +++ b/docs/tutorials.md @@ -1,5 +1,10 @@ +++ title = "Tutorials" +weight = 4 +nav = [ + "Transportation", + "Chemical similarity search", +] +++ ## Tutorials diff --git a/docs/webui.md b/docs/webui.md index 8084cbb80..1e123393f 100644 --- a/docs/webui.md +++ b/docs/webui.md @@ -1,5 +1,10 @@ +++ title = "WebUI" +weight = 9 +nav = [ + "Console", + "Cluster Admin", +] +++ ## WebUI From 463c3029f2ff09fc388f7675816b91d023c778be Mon Sep 17 00:00:00 2001 From: Cody Soyland Date: Wed, 16 Aug 2017 11:39:57 -0500 Subject: [PATCH 07/46] Add sorting and navigation metadata for architecture document --- docs/architecture.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/architecture.md b/docs/architecture.md index 0ff35565c..5cc5ff6ee 100644 --- a/docs/architecture.md +++ b/docs/architecture.md @@ -1,5 +1,7 @@ +++ title = "Architecture" +weight = 6 +nav = [] +++ ## Architecture From 748b034aa8e44546eac120ca5c858aab6b8aecc5 Mon Sep 17 00:00:00 2001 From: Michael Baird Date: Fri, 18 Aug 2017 09:36:45 -0500 Subject: [PATCH 08/46] DeleteView from Frame --- frame.go | 20 ++++++++++++++++++++ frame_test.go | 31 +++++++++++++++++++++++++++++++ 2 files changed, 51 insertions(+) diff --git a/frame.go b/frame.go index de3dd6fbb..295c3abf2 100644 --- a/frame.go +++ b/frame.go @@ -509,6 +509,26 @@ func (f *Frame) newView(path, name string) *View { return view } +// DeleteView removes a view from the frame. +func (f *Frame) DeleteView(name string) error { + view := f.views[name] + if view == nil { + return ErrInvalidView + } + + // TODO capture errors lower down in this method + _ = view.Close() + + // Delete view directory. + if err := os.RemoveAll(view.Path()); err != nil { + return err + } + + delete(f.views, name) + + return nil +} + // SetBit sets a bit on a view within the frame. func (f *Frame) SetBit(name string, rowID, colID uint64, t *time.Time) (changed bool, err error) { // Validate view name. diff --git a/frame_test.go b/frame_test.go index 8cb48cca8..b560d985e 100644 --- a/frame_test.go +++ b/frame_test.go @@ -307,3 +307,34 @@ func TestFrame_RowLabelValidation(t *testing.T) { } } + +// Ensure frame can open and retrieve a view. +func TestFrame_DeleteView(t *testing.T) { + f := test.MustOpenFrame() + defer f.Close() + + // Create view. + view, err := f.CreateViewIfNotExists("v") + if err != nil { + t.Fatal(err) + } else if view == nil { + t.Fatal("expected view") + } + + err = f.DeleteView("v") + if err != nil { + t.Fatal(err) + } + + if f.View("v") != nil { + t.Fatal("view still exists in frame") + } + + // // Retrieve existing view. + view2, err := f.CreateViewIfNotExists("v") + if err != nil { + t.Fatal(err) + } else if view == view2 { + t.Fatal("failed to create new view") + } +} From 753d89d45e63b746f0543da3e63c282b4b43d699 Mon Sep 17 00:00:00 2001 From: Michael Baird Date: Fri, 18 Aug 2017 14:55:29 -0500 Subject: [PATCH 09/46] handler for delete View --- handler.go | 29 +++++++++++++++++++++++++++++ handler_test.go | 20 ++++++++++++++++++++ 2 files changed, 49 insertions(+) diff --git a/handler.go b/handler.go index f3bf5c786..89f18a20b 100644 --- a/handler.go +++ b/handler.go @@ -116,6 +116,7 @@ func NewRouter(handler *Handler) *mux.Router { router.HandleFunc("/index/{index}/frame/{frame}/restore", handler.handlePostFrameRestore).Methods("POST") router.HandleFunc("/index/{index}/frame/{frame}/time-quantum", handler.handlePatchFrameTimeQuantum).Methods("PATCH") router.HandleFunc("/index/{index}/frame/{frame}/views", handler.handleGetFrameViews).Methods("GET") + router.HandleFunc("/index/{index}/frame/{frame}/view/{view}", handler.handleDeleteView).Methods("DELETE") router.HandleFunc("/index/{index}/input/{input-definition}", handler.handlePostInput).Methods("POST") router.HandleFunc("/index/{index}/input-definition/{input-definition}", handler.handleGetInputDefinition).Methods("GET") router.HandleFunc("/index/{index}/input-definition/{input-definition}", handler.handlePostInputDefinition).Methods("POST") @@ -789,6 +790,34 @@ func (h *Handler) handleGetFrameViews(w http.ResponseWriter, r *http.Request) { } } +// handleDeleteView handles Delete /frame/view request. +func (h *Handler) handleDeleteView(w http.ResponseWriter, r *http.Request) { + indexName := mux.Vars(r)["index"] + frameName := mux.Vars(r)["frame"] + viewName := mux.Vars(r)["view"] + + // Retrieve frame. + f := h.Holder.Frame(indexName, frameName) + if f == nil { + http.Error(w, ErrFrameNotFound.Error(), http.StatusNotFound) + return + } + + if err := f.DeleteView(viewName); err != nil { + http.Error(w, err.Error(), http.StatusBadRequest) + return + } + + // TODO: Send the delete frame message to all nodes. + + // Encode response. + if err := json.NewEncoder(w).Encode(deleteViewResponse{}); err != nil { + h.logger().Printf("response encoding error: %s", err) + } +} + +type deleteViewResponse struct{} + type getFrameViewsResponse struct { Views []string `json:"views,omitempty"` } diff --git a/handler_test.go b/handler_test.go index 22a1fd4f3..2947fd1b8 100644 --- a/handler_test.go +++ b/handler_test.go @@ -1511,3 +1511,23 @@ func TestHandler_GetTimeStamp(t *testing.T) { t.Fatalf("Expected Ignore nonexistent fields") } } + +// Ensure handler can delete a view. +func TestHandler_DeleteView(t *testing.T) { + hldr := test.MustOpenHolder() + defer hldr.Close() + hldr.MustCreateFragmentIfNotExists("i0", "f0", "v0", 1).MustSetBits(30, (1*SliceWidth)+1) + + h := test.NewHandler() + h.Holder = hldr.Holder + h.Cluster = test.NewCluster(1) + w := httptest.NewRecorder() + h.ServeHTTP(w, test.MustNewHTTPRequest("DELETE", "/index/i0/frame/f0/view/v0", strings.NewReader(""))) + if w.Code != http.StatusOK { + t.Fatalf("unexpected status code: %d", w.Code) + } else if body := w.Body.String(); body != `{}`+"\n" { + t.Fatalf("unexpected body: %s", body) + } else if f := hldr.Index("i0").Frame("f0").View("v0"); f != nil { + t.Fatal("expected nil view") + } +} From 80ab4cd1937b7ccbdaf09f6ff99bc1eadffeb246 Mon Sep 17 00:00:00 2001 From: Michael Baird Date: Fri, 18 Aug 2017 17:08:13 -0500 Subject: [PATCH 10/46] delete only time based views, and handle inverse views --- handler.go | 25 +++++++++++++++++++++++-- handler_test.go | 7 ++++--- 2 files changed, 27 insertions(+), 5 deletions(-) diff --git a/handler.go b/handler.go index 89f18a20b..d355213db 100644 --- a/handler.go +++ b/handler.go @@ -803,12 +803,33 @@ func (h *Handler) handleDeleteView(w http.ResponseWriter, r *http.Request) { return } - if err := f.DeleteView(viewName); err != nil { + // Check that the view name is an integer. + if _, err := strconv.Atoi(viewName); err != nil { + http.Error(w, "only time based views can be deleted", http.StatusBadRequest) + return + } + + // Check the frame for time quantum. + if f.TimeQuantum() == "" { + http.Error(w, "frame does not contain a Time Quantum", http.StatusBadRequest) + return + } + + // Delete the standard view. + if err := f.DeleteView(ViewStandard + "_" + viewName); err != nil { http.Error(w, err.Error(), http.StatusBadRequest) return } - // TODO: Send the delete frame message to all nodes. + // Delete the inverse view. + if f.InverseEnabled() { + if err := f.DeleteView(ViewInverse + "_" + viewName); err != nil { + http.Error(w, err.Error(), http.StatusBadRequest) + return + } + } + + // TODO: Send the delete view message to all nodes. // Encode response. if err := json.NewEncoder(w).Encode(deleteViewResponse{}); err != nil { diff --git a/handler_test.go b/handler_test.go index 2947fd1b8..c8e0d949c 100644 --- a/handler_test.go +++ b/handler_test.go @@ -1516,18 +1516,19 @@ func TestHandler_GetTimeStamp(t *testing.T) { func TestHandler_DeleteView(t *testing.T) { hldr := test.MustOpenHolder() defer hldr.Close() - hldr.MustCreateFragmentIfNotExists("i0", "f0", "v0", 1).MustSetBits(30, (1*SliceWidth)+1) + hldr.MustCreateFragmentIfNotExists("i0", "f0", pilosa.ViewStandard+"_2017", 1).MustSetBits(30, (1*SliceWidth)+1) + hldr.Index("i0").Frame("f0").SetTimeQuantum("YMD") h := test.NewHandler() h.Holder = hldr.Holder h.Cluster = test.NewCluster(1) w := httptest.NewRecorder() - h.ServeHTTP(w, test.MustNewHTTPRequest("DELETE", "/index/i0/frame/f0/view/v0", strings.NewReader(""))) + h.ServeHTTP(w, test.MustNewHTTPRequest("DELETE", "/index/i0/frame/f0/view/2017", strings.NewReader(""))) if w.Code != http.StatusOK { t.Fatalf("unexpected status code: %d", w.Code) } else if body := w.Body.String(); body != `{}`+"\n" { t.Fatalf("unexpected body: %s", body) - } else if f := hldr.Index("i0").Frame("f0").View("v0"); f != nil { + } else if f := hldr.Index("i0").Frame("f0").View(pilosa.ViewStandard + "_2017"); f != nil { t.Fatal("expected nil view") } } From cf372cdd1d08158201c1e24c9857f8a6a2c2c028 Mon Sep 17 00:00:00 2001 From: Travis Date: Mon, 21 Aug 2017 14:20:37 -0500 Subject: [PATCH 11/46] Fix infinite loop in bitmap-to-array conversion. When a run ended the container (i.e. contained column 65535), then the for loop would increment the 16-bit value to 0, at which point it was still <= 65535. --- roaring/roaring.go | 4 ++++ roaring/roaring_internal_test.go | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/roaring/roaring.go b/roaring/roaring.go index 16e6159d7..06533d5c0 100644 --- a/roaring/roaring.go +++ b/roaring/roaring.go @@ -1992,6 +1992,10 @@ func intersectBitmapRun(a, b *container) *container { if a.bitmapContains(i) { output.array = append(output.array, i) } + // If the run ends the container, break to avoid an infinite loop. + if i == 65535 { + break + } } } output.n = len(output.array) diff --git a/roaring/roaring_internal_test.go b/roaring/roaring_internal_test.go index 9cc2f2aa7..21a14c3eb 100644 --- a/roaring/roaring_internal_test.go +++ b/roaring/roaring_internal_test.go @@ -623,7 +623,7 @@ func TestIntersectBitmapRunArray(t *testing.T) { }, { bitmap: []uint64{0xFFFFFFFFFFFFFFFF, 1, 1, 1, 0xA, 1, 1, 0, 1}, - runs: []interval16{{start: 63, last: 10000}}, + runs: []interval16{{start: 63, last: 10000}, {start: 65000, last: 65535}}, exp: []uint16{63, 64, 128, 192, 257, 259, 320, 384, 512}, expN: 9, }, From dbbdabef0550d877162905f7d38624d704f008db Mon Sep 17 00:00:00 2001 From: Matthew Jaffee Date: Mon, 21 Aug 2017 14:38:35 -0500 Subject: [PATCH 12/46] add failing test for XorRunRun --- roaring/roaring_internal_test.go | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/roaring/roaring_internal_test.go b/roaring/roaring_internal_test.go index 9cc2f2aa7..59db885e1 100644 --- a/roaring/roaring_internal_test.go +++ b/roaring/roaring_internal_test.go @@ -1832,6 +1832,11 @@ func TestXorRunRun(t *testing.T) { bruns: []interval16{{start: 2, last: 8}, {start: 16, last: 27}, {start: 33, last: 34}}, exp: []interval16{{start: 1, last: 1}, {start: 4, last: 4}, {start: 6, last: 6}, {start: 9, last: 9}, {start: 12, last: 15}, {start: 23, last: 27}, {start: 33, last: 34}}, }, + { + aruns: []interval16{{start: 65530, last: 65535}}, + bruns: []interval16{{start: 65531, last: 65535}}, + exp: []interval16{{start: 65530, last: 65530}}, + }, } for i, test := range tests { a.runs = test.aruns From 81d0f25ba14201b5a178c08f255b6845cfef3958 Mon Sep 17 00:00:00 2001 From: Matthew Jaffee Date: Mon, 21 Aug 2017 17:08:57 -0500 Subject: [PATCH 13/46] re-arrange GossipNodeSet.Open to hopefully avoid race --- gossip/gossip.go | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/gossip/gossip.go b/gossip/gossip.go index a967079cb..e2e43a00b 100644 --- a/gossip/gossip.go +++ b/gossip/gossip.go @@ -69,6 +69,12 @@ func (g *GossipNodeSet) Open() error { return err } g.memberlist = ml + g.broadcasts = &memberlist.TransmitLimitedQueue{ + NumNodes: func() int { + return ml.NumMembers() + }, + RetransmitMult: 3, + } // attach to gossip seed node nodes := []*pilosa.Node{&pilosa.Node{Host: g.config.gossipSeed}} //TODO: support a list of seeds @@ -76,12 +82,6 @@ func (g *GossipNodeSet) Open() error { if err != nil { return err } - g.broadcasts = &memberlist.TransmitLimitedQueue{ - NumNodes: func() int { - return ml.NumMembers() - }, - RetransmitMult: 3, - } return nil } From dec98930443323b9f962704d6448d924ad8328ea Mon Sep 17 00:00:00 2001 From: Yuce Tekol Date: Tue, 22 Aug 2017 15:18:10 +0300 Subject: [PATCH 14/46] Adds suprressing attributes and bits for Bitmap queries --- executor.go | 53 +++++++++------ executor_test.go | 19 ++++++ handler.go | 30 ++++++--- internal/public.pb.go | 151 ++++++++++++++++++++++++++++++------------ internal/public.proto | 2 + 5 files changed, 183 insertions(+), 72 deletions(-) diff --git a/executor.go b/executor.go index d58be42f1..a741dbc6e 100644 --- a/executor.go +++ b/executor.go @@ -229,36 +229,43 @@ func (e *Executor) executeBitmapCall(ctx context.Context, index string, c *pql.C // If the row label is used then return bitmap attributes. bm, _ := other.(*Bitmap) if c.Name == "Bitmap" { - - idx := e.Holder.Index(index) - if idx != nil { - columnLabel := idx.ColumnLabel() - if columnID, ok, err := c.UintArg(columnLabel); ok && err == nil { - attrs, err := idx.ColumnAttrStore().Attrs(columnID) - if err != nil { - return nil, err - } - bm.Attrs = attrs - } else if err != nil { - return nil, err - } else { - frame, _ := c.Args["frame"].(string) - if fr := idx.Frame(frame); fr != nil { - rowLabel := fr.RowLabel() - rowID, _, err := c.UintArg(rowLabel) - if err != nil { - return nil, err - } - attrs, err := fr.RowAttrStore().Attrs(rowID) + if opt.InhibitAttrs { + bm.Attrs = map[string]interface{}{} + } else { + idx := e.Holder.Index(index) + if idx != nil { + columnLabel := idx.ColumnLabel() + if columnID, ok, err := c.UintArg(columnLabel); ok && err == nil { + attrs, err := idx.ColumnAttrStore().Attrs(columnID) if err != nil { return nil, err } bm.Attrs = attrs + } else if err != nil { + return nil, err + } else { + frame, _ := c.Args["frame"].(string) + if fr := idx.Frame(frame); fr != nil { + rowLabel := fr.RowLabel() + rowID, _, err := c.UintArg(rowLabel) + if err != nil { + return nil, err + } + attrs, err := fr.RowAttrStore().Attrs(rowID) + if err != nil { + return nil, err + } + bm.Attrs = attrs + } } } } } + if opt.InhibitBits { + bm.segments = []BitmapSegment{} + } + return bm, nil } @@ -1371,7 +1378,9 @@ type mapResponse struct { // ExecOptions represents an execution context for a single Execute() call. type ExecOptions struct { - Remote bool + Remote bool + InhibitAttrs bool + InhibitBits bool } // decodeError returns an error representation of s if s is non-blank. diff --git a/executor_test.go b/executor_test.go index 3c0f7e8ad..0c535799c 100644 --- a/executor_test.go +++ b/executor_test.go @@ -59,6 +59,25 @@ func TestExecutor_Execute_Bitmap(t *testing.T) { } else if attrs := res[0].(*pilosa.Bitmap).Attrs; !reflect.DeepEqual(attrs, map[string]interface{}{"foo": "bar", "baz": int64(123)}) { t.Fatalf("unexpected attrs: %s", spew.Sdump(attrs)) } + + // Inhibit bits. + if res, err := e.Execute(context.Background(), "i", test.MustParse(`Bitmap(rowID=10, frame=f)`), nil, &pilosa.ExecOptions{InhibitBits: true}); err != nil { + t.Fatal(err) + } else if bits := res[0].(*pilosa.Bitmap).Bits(); !reflect.DeepEqual(bits, []uint64{}) { + t.Fatalf("unexpected bits: %+v", bits) + } else if attrs := res[0].(*pilosa.Bitmap).Attrs; !reflect.DeepEqual(attrs, map[string]interface{}{"foo": "bar", "baz": int64(123)}) { + t.Fatalf("unexpected attrs: %s", spew.Sdump(attrs)) + } + + // Inhibit attributes. + if res, err := e.Execute(context.Background(), "i", test.MustParse(`Bitmap(rowID=10, frame=f)`), nil, &pilosa.ExecOptions{InhibitAttrs: true}); err != nil { + t.Fatal(err) + } else if bits := res[0].(*pilosa.Bitmap).Bits(); !reflect.DeepEqual(bits, []uint64{3, SliceWidth + 1}) { + t.Fatalf("unexpected bits: %+v", bits) + } else if attrs := res[0].(*pilosa.Bitmap).Attrs; !reflect.DeepEqual(attrs, map[string]interface{}{}) { + fmt.Println("ATTRS", attrs) + t.Fatalf("unexpected attrs: %s", spew.Sdump(attrs)) + } }) t.Run("Column", func(t *testing.T) { diff --git a/handler.go b/handler.go index f3bf5c786..5d5513aff 100644 --- a/handler.go +++ b/handler.go @@ -241,7 +241,9 @@ func (h *Handler) handlePostQuery(w http.ResponseWriter, r *http.Request) { // Build execution options. opt := &ExecOptions{ - Remote: req.Remote, + Remote: req.Remote, + InhibitAttrs: req.InhibitAttrs, + InhibitBits: req.InhibitBits, } // Parse query string. @@ -257,7 +259,7 @@ func (h *Handler) handlePostQuery(w http.ResponseWriter, r *http.Request) { resp := &QueryResponse{Results: results, Err: err} // Fill column attributes if requested. - if req.ColumnAttrs { + if req.ColumnAttrs && !req.InhibitBits { // Consolidate all column ids across all calls. var columnIDs []uint64 for _, result := range results { @@ -925,9 +927,11 @@ func (h *Handler) readURLQueryRequest(r *http.Request) (*QueryRequest, error) { } return &QueryRequest{ - Query: query, - Slices: slices, - ColumnAttrs: q.Get("columnAttrs") == "true", + Query: query, + Slices: slices, + ColumnAttrs: q.Get("columnAttrs") == "true", + InhibitAttrs: q.Get("inhibitAttrs") == "true", + InhibitBits: q.Get("inhibitBits") == "true", }, nil } @@ -1396,6 +1400,12 @@ type QueryRequest struct { // Return column attributes, if true. ColumnAttrs bool + // Do not return row attributes, if true. + InhibitAttrs bool + + // Do not return bits, if true. + InhibitBits bool + // If true, indicates that query is part of a larger distributed query. // If false, this request is on the originating node. Remote bool @@ -1403,10 +1413,12 @@ type QueryRequest struct { func decodeQueryRequest(pb *internal.QueryRequest) *QueryRequest { req := &QueryRequest{ - Query: pb.Query, - Slices: pb.Slices, - ColumnAttrs: pb.ColumnAttrs, - Remote: pb.Remote, + Query: pb.Query, + Slices: pb.Slices, + ColumnAttrs: pb.ColumnAttrs, + Remote: pb.Remote, + InhibitAttrs: pb.InhibitAttrs, + InhibitBits: pb.InhibitBits, } return req diff --git a/internal/public.pb.go b/internal/public.pb.go index 43a9d7d35..d8fa51c5d 100644 --- a/internal/public.pb.go +++ b/internal/public.pb.go @@ -125,10 +125,12 @@ func (m *AttrMap) GetAttrs() []*Attr { } type QueryRequest struct { - Query string `protobuf:"bytes,1,opt,name=Query,proto3" json:"Query,omitempty"` - Slices []uint64 `protobuf:"varint,2,rep,packed,name=Slices" json:"Slices,omitempty"` - ColumnAttrs bool `protobuf:"varint,3,opt,name=ColumnAttrs,proto3" json:"ColumnAttrs,omitempty"` - Remote bool `protobuf:"varint,5,opt,name=Remote,proto3" json:"Remote,omitempty"` + Query string `protobuf:"bytes,1,opt,name=Query,proto3" json:"Query,omitempty"` + Slices []uint64 `protobuf:"varint,2,rep,packed,name=Slices" json:"Slices,omitempty"` + ColumnAttrs bool `protobuf:"varint,3,opt,name=ColumnAttrs,proto3" json:"ColumnAttrs,omitempty"` + Remote bool `protobuf:"varint,5,opt,name=Remote,proto3" json:"Remote,omitempty"` + InhibitAttrs bool `protobuf:"varint,6,opt,name=InhibitAttrs,proto3" json:"InhibitAttrs,omitempty"` + InhibitBits bool `protobuf:"varint,7,opt,name=InhibitBits,proto3" json:"InhibitBits,omitempty"` } func (m *QueryRequest) Reset() { *m = QueryRequest{} } @@ -499,6 +501,26 @@ func (m *QueryRequest) MarshalTo(dAtA []byte) (int, error) { } i++ } + if m.InhibitAttrs { + dAtA[i] = 0x30 + i++ + if m.InhibitAttrs { + dAtA[i] = 1 + } else { + dAtA[i] = 0 + } + i++ + } + if m.InhibitBits { + dAtA[i] = 0x38 + i++ + if m.InhibitBits { + dAtA[i] = 1 + } else { + dAtA[i] = 0 + } + i++ + } return i, nil } @@ -838,6 +860,12 @@ func (m *QueryRequest) Size() (n int) { if m.Remote { n += 2 } + if m.InhibitAttrs { + n += 2 + } + if m.InhibitBits { + n += 2 + } return n } @@ -1799,6 +1827,46 @@ func (m *QueryRequest) Unmarshal(dAtA []byte) error { } } m.Remote = bool(v != 0) + case 6: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field InhibitAttrs", wireType) + } + var v int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowPublic + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + v |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + m.InhibitAttrs = bool(v != 0) + case 7: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field InhibitBits", wireType) + } + var v int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowPublic + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + v |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + m.InhibitBits = bool(v != 0) default: iNdEx = preIndex skippy, err := skipPublic(dAtA[iNdEx:]) @@ -2535,41 +2603,42 @@ var ( func init() { proto.RegisterFile("public.proto", fileDescriptorPublic) } var fileDescriptorPublic = []byte{ - // 563 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x8c, 0x54, 0x4b, 0x8e, 0xd3, 0x40, - 0x10, 0xa5, 0x63, 0xe7, 0x57, 0xf9, 0x28, 0x6a, 0xf1, 0xb1, 0x10, 0x8a, 0x2c, 0x8b, 0x85, 0x57, - 0x19, 0x69, 0x38, 0x00, 0xc2, 0x49, 0x46, 0xb2, 0x10, 0x23, 0xe8, 0x0c, 0xec, 0x3d, 0x33, 0xad, - 0xc1, 0x92, 0x7f, 0x74, 0xb7, 0x81, 0x1c, 0x80, 0x13, 0xb0, 0xe1, 0x06, 0x70, 0x14, 0x96, 0x1c, - 0x01, 0x85, 0x8b, 0xa0, 0xea, 0x76, 0xc7, 0x1e, 0x16, 0x68, 0x76, 0xfd, 0x5e, 0x75, 0xb5, 0xeb, - 0xd5, 0xab, 0x32, 0x4c, 0xab, 0xfa, 0x32, 0x4b, 0xaf, 0x56, 0x95, 0x28, 0x55, 0x49, 0x47, 0x69, - 0xa1, 0xb8, 0x28, 0x92, 0x2c, 0x88, 0x60, 0x10, 0xa5, 0x2a, 0x4f, 0x2a, 0x4a, 0xc1, 0x8d, 0x52, - 0x25, 0x3d, 0xe2, 0x3b, 0xa1, 0xcb, 0xf4, 0x99, 0x3e, 0x85, 0xfe, 0x0b, 0xa5, 0x84, 0xf4, 0x7a, - 0xbe, 0x13, 0x4e, 0x4e, 0xe7, 0x2b, 0x9b, 0xb7, 0x42, 0x9a, 0x99, 0x60, 0xb0, 0x02, 0xf7, 0x75, - 0x92, 0x0a, 0xba, 0x00, 0xe7, 0x25, 0xdf, 0x7b, 0xc4, 0x27, 0xa1, 0xcb, 0xf0, 0x48, 0xef, 0x43, - 0x7f, 0x5d, 0xd6, 0x85, 0xf2, 0x7a, 0x9a, 0x33, 0x20, 0x78, 0x0b, 0x4e, 0x94, 0x2a, 0x0c, 0xb2, - 0xf2, 0x53, 0xbc, 0x69, 0x12, 0x0c, 0xa0, 0x8f, 0x61, 0xb4, 0x2e, 0xb3, 0x3a, 0x2f, 0xe2, 0x4d, - 0x93, 0x75, 0xc4, 0xf4, 0x09, 0x8c, 0x2f, 0xd2, 0x9c, 0x4b, 0x95, 0xe4, 0x95, 0xe7, 0xf8, 0x24, - 0x74, 0x58, 0x4b, 0x04, 0x5b, 0x98, 0x99, 0x9b, 0x58, 0xd5, 0x8e, 0x2b, 0x3a, 0x87, 0xde, 0xf1, - 0xf5, 0x5e, 0xbc, 0xb9, 0xa3, 0x9a, 0x1f, 0x04, 0x5c, 0x3c, 0x75, 0xe5, 0x8c, 0x8d, 0x1c, 0x0a, - 0xee, 0xc5, 0xbe, 0xe2, 0x4d, 0x5d, 0xfa, 0x4c, 0x7d, 0x98, 0xec, 0x94, 0x48, 0x8b, 0x9b, 0x77, - 0x49, 0x56, 0x73, 0x5d, 0xd5, 0x98, 0x75, 0x29, 0x54, 0x14, 0x17, 0xca, 0x84, 0x5d, 0x5d, 0xf4, - 0x11, 0xa3, 0xa2, 0xa8, 0x2c, 0x33, 0x13, 0xec, 0xfb, 0x24, 0x1c, 0xb1, 0x96, 0xa0, 0x4b, 0x80, - 0xb3, 0xac, 0x4c, 0x9a, 0xdc, 0x81, 0x4f, 0x42, 0xc2, 0x3a, 0x4c, 0x70, 0x02, 0x43, 0xac, 0xf4, - 0x55, 0x52, 0xb5, 0xda, 0xc8, 0xff, 0xb4, 0x7d, 0x84, 0xe9, 0x9b, 0x9a, 0x8b, 0x3d, 0xe3, 0x1f, - 0x6a, 0x2e, 0xb5, 0x05, 0x1a, 0x37, 0x22, 0x0d, 0xa0, 0x0f, 0x61, 0xb0, 0xcb, 0xd2, 0x2b, 0x6e, - 0x1a, 0xe5, 0xb2, 0x06, 0xa1, 0xd4, 0xb6, 0xc1, 0x52, 0x4b, 0x1d, 0xb1, 0x2e, 0x85, 0x99, 0x8c, - 0xe7, 0xa5, 0xb2, 0x5a, 0x1a, 0x14, 0x7c, 0x25, 0x30, 0x6b, 0x3e, 0x2c, 0xab, 0xb2, 0x90, 0x1c, - 0x9b, 0xbb, 0x15, 0xc2, 0x36, 0x77, 0x2b, 0x04, 0x3d, 0x81, 0x21, 0xe3, 0xb2, 0xce, 0x94, 0xf5, - 0xe7, 0x41, 0xab, 0xc1, 0xe6, 0xd6, 0x99, 0x62, 0xf6, 0x16, 0x7d, 0x0e, 0xf3, 0x5b, 0x7e, 0x63, - 0x45, 0x98, 0xf7, 0xa8, 0xcd, 0xbb, 0x15, 0x67, 0xff, 0x5c, 0x0f, 0xbe, 0x10, 0x98, 0x74, 0x5e, - 0xa6, 0xa1, 0xdd, 0x05, 0x5d, 0xd6, 0xe4, 0x74, 0xd1, 0x3e, 0x64, 0x78, 0x66, 0x77, 0x65, 0x0a, - 0xe4, 0xbc, 0x99, 0x02, 0x72, 0x8e, 0xbd, 0xc7, 0xf9, 0xb7, 0xdf, 0xef, 0xf4, 0x1e, 0x69, 0x66, - 0x82, 0xd4, 0x83, 0xe1, 0xfa, 0x7d, 0x52, 0xdc, 0xf0, 0x6b, 0x3d, 0x05, 0x23, 0x66, 0x61, 0xf0, - 0x9d, 0xc0, 0x2c, 0xce, 0xab, 0x52, 0xa8, 0x8e, 0x2f, 0x71, 0x71, 0xcd, 0x3f, 0x5b, 0x5f, 0x34, - 0x40, 0xf6, 0x4c, 0x24, 0xb9, 0x99, 0xbf, 0x31, 0x33, 0x00, 0x59, 0xed, 0x8f, 0xf6, 0xc3, 0x65, - 0x06, 0x68, 0x27, 0x70, 0x9f, 0xa4, 0xe7, 0x1a, 0x0f, 0x0d, 0xc2, 0x81, 0xb3, 0xeb, 0x24, 0xbd, - 0xbe, 0x0e, 0xb5, 0x04, 0x0e, 0xdc, 0x71, 0x9f, 0xa4, 0x37, 0xf0, 0x9d, 0xd0, 0x61, 0x1d, 0x26, - 0x5a, 0xfc, 0x3c, 0x2c, 0xc9, 0xaf, 0xc3, 0x92, 0xfc, 0x3e, 0x2c, 0xc9, 0xb7, 0x3f, 0xcb, 0x7b, - 0x97, 0x03, 0xfd, 0x43, 0x79, 0xf6, 0x37, 0x00, 0x00, 0xff, 0xff, 0xf6, 0x6d, 0x5d, 0xc9, 0x60, - 0x04, 0x00, 0x00, + // 589 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x8c, 0x54, 0x5d, 0x8e, 0xd3, 0x30, + 0x10, 0xc6, 0x4d, 0xfa, 0x37, 0xed, 0xae, 0x2a, 0x8b, 0x9f, 0x08, 0xa1, 0x2a, 0x8a, 0x78, 0xc8, + 0x53, 0x57, 0x5a, 0x0e, 0x80, 0x48, 0xdb, 0x95, 0x22, 0xc4, 0x0a, 0xdc, 0x85, 0xf7, 0x74, 0xd7, + 0xda, 0x8d, 0x94, 0xc4, 0xc1, 0x71, 0x04, 0x3d, 0x00, 0x27, 0xe0, 0x85, 0x1b, 0xc0, 0x21, 0x38, + 0x00, 0x8f, 0x1c, 0x01, 0x95, 0x8b, 0xa0, 0xb1, 0xe3, 0x26, 0xe5, 0x01, 0xf1, 0xe6, 0xef, 0x1b, + 0xcf, 0x64, 0x3e, 0x7f, 0x33, 0x81, 0x69, 0x59, 0x6f, 0xb3, 0xf4, 0x7a, 0x51, 0x4a, 0xa1, 0x04, + 0x1d, 0xa5, 0x85, 0xe2, 0xb2, 0x48, 0xb2, 0x20, 0x82, 0x41, 0x94, 0xaa, 0x3c, 0x29, 0x29, 0x05, + 0x37, 0x4a, 0x55, 0xe5, 0x11, 0xdf, 0x09, 0x5d, 0xa6, 0xcf, 0xf4, 0x29, 0xf4, 0x5f, 0x28, 0x25, + 0x2b, 0xaf, 0xe7, 0x3b, 0xe1, 0xe4, 0xfc, 0x74, 0x61, 0xf3, 0x16, 0x48, 0x33, 0x13, 0x0c, 0x16, + 0xe0, 0xbe, 0x4e, 0x52, 0x49, 0x67, 0xe0, 0xbc, 0xe4, 0x3b, 0x8f, 0xf8, 0x24, 0x74, 0x19, 0x1e, + 0xe9, 0x7d, 0xe8, 0x2f, 0x45, 0x5d, 0x28, 0xaf, 0xa7, 0x39, 0x03, 0x82, 0xb7, 0xe0, 0x44, 0xa9, + 0xc2, 0x20, 0x13, 0x1f, 0xe2, 0x55, 0x93, 0x60, 0x00, 0x7d, 0x0c, 0xa3, 0xa5, 0xc8, 0xea, 0xbc, + 0x88, 0x57, 0x4d, 0xd6, 0x01, 0xd3, 0x27, 0x30, 0xbe, 0x4a, 0x73, 0x5e, 0xa9, 0x24, 0x2f, 0x3d, + 0xc7, 0x27, 0xa1, 0xc3, 0x5a, 0x22, 0x58, 0xc3, 0x89, 0xb9, 0x89, 0x5d, 0x6d, 0xb8, 0xa2, 0xa7, + 0xd0, 0x3b, 0x54, 0xef, 0xc5, 0xab, 0xff, 0x54, 0xf3, 0x8d, 0x80, 0x8b, 0xa7, 0xae, 0x9c, 0xb1, + 0x91, 0x43, 0xc1, 0xbd, 0xda, 0x95, 0xbc, 0xe9, 0x4b, 0x9f, 0xa9, 0x0f, 0x93, 0x8d, 0x92, 0x69, + 0x71, 0xfb, 0x2e, 0xc9, 0x6a, 0xae, 0xbb, 0x1a, 0xb3, 0x2e, 0x85, 0x8a, 0xe2, 0x42, 0x99, 0xb0, + 0xab, 0x9b, 0x3e, 0x60, 0x54, 0x14, 0x09, 0x91, 0x99, 0x60, 0xdf, 0x27, 0xe1, 0x88, 0xb5, 0x04, + 0x9d, 0x03, 0x5c, 0x64, 0x22, 0x69, 0x72, 0x07, 0x3e, 0x09, 0x09, 0xeb, 0x30, 0xc1, 0x19, 0x0c, + 0xb1, 0xd3, 0x57, 0x49, 0xd9, 0x6a, 0x23, 0xff, 0xd2, 0xf6, 0x9d, 0xc0, 0xf4, 0x4d, 0xcd, 0xe5, + 0x8e, 0xf1, 0xf7, 0x35, 0xaf, 0xb4, 0x07, 0x1a, 0x37, 0x2a, 0x0d, 0xa0, 0x0f, 0x61, 0xb0, 0xc9, + 0xd2, 0x6b, 0x6e, 0x5e, 0xca, 0x65, 0x0d, 0x42, 0xad, 0xed, 0x0b, 0x57, 0x5a, 0xeb, 0x88, 0x75, + 0x29, 0xcc, 0x64, 0x3c, 0x17, 0xca, 0x8a, 0x69, 0x10, 0x0d, 0x60, 0x1a, 0x17, 0x77, 0xe9, 0x36, + 0x55, 0x26, 0x75, 0xa0, 0xa3, 0x47, 0x1c, 0x56, 0x6f, 0xb0, 0x9e, 0xc3, 0xa1, 0xa9, 0xde, 0xa1, + 0x82, 0xcf, 0x04, 0x4e, 0x9a, 0xf6, 0xab, 0x52, 0x14, 0x15, 0x47, 0x8f, 0xd6, 0x52, 0x5a, 0x8f, + 0xd6, 0x52, 0xd2, 0x33, 0x18, 0x32, 0x5e, 0xd5, 0x99, 0xb2, 0x36, 0x3f, 0x68, 0x9f, 0xc2, 0xe6, + 0xd6, 0x99, 0x62, 0xf6, 0x16, 0x7d, 0x0e, 0xa7, 0x47, 0x63, 0x83, 0xba, 0x30, 0xef, 0x51, 0x9b, + 0x77, 0x14, 0x67, 0x7f, 0x5d, 0x0f, 0x3e, 0x11, 0x98, 0x74, 0x2a, 0xd3, 0xd0, 0xae, 0x94, 0x6e, + 0x6b, 0x72, 0x3e, 0x6b, 0x0b, 0x19, 0x9e, 0xd9, 0x95, 0x9b, 0x02, 0xb9, 0x6c, 0x86, 0x89, 0x5c, + 0xa2, 0x85, 0xb8, 0x46, 0xf6, 0xfb, 0x1d, 0x0b, 0x91, 0x66, 0x26, 0x48, 0x3d, 0x18, 0x2e, 0xef, + 0x92, 0xe2, 0x96, 0xdf, 0xe8, 0x61, 0x1a, 0x31, 0x0b, 0x83, 0xaf, 0x04, 0x4e, 0xe2, 0xbc, 0x14, + 0x52, 0x75, 0xdc, 0x8d, 0x8b, 0x1b, 0xfe, 0xd1, 0xba, 0xab, 0x01, 0xb2, 0x17, 0x32, 0xc9, 0xcd, + 0x18, 0x8f, 0x99, 0x01, 0xc8, 0x6a, 0x97, 0xb5, 0xab, 0x2e, 0x33, 0x40, 0xfb, 0x89, 0x6b, 0x59, + 0x79, 0xae, 0x99, 0x04, 0x83, 0x70, 0x6e, 0xed, 0x56, 0x56, 0x5e, 0x5f, 0x87, 0x5a, 0x02, 0xe7, + 0xf6, 0xb0, 0x96, 0xe8, 0xb5, 0x13, 0x3a, 0xac, 0xc3, 0x44, 0xb3, 0x1f, 0xfb, 0x39, 0xf9, 0xb9, + 0x9f, 0x93, 0x5f, 0xfb, 0x39, 0xf9, 0xf2, 0x7b, 0x7e, 0x6f, 0x3b, 0xd0, 0xff, 0xa5, 0x67, 0x7f, + 0x02, 0x00, 0x00, 0xff, 0xff, 0xd8, 0x9d, 0xc1, 0x11, 0xa7, 0x04, 0x00, 0x00, } diff --git a/internal/public.proto b/internal/public.proto index ff161d930..50325205b 100644 --- a/internal/public.proto +++ b/internal/public.proto @@ -41,6 +41,8 @@ message QueryRequest { repeated uint64 Slices = 2; bool ColumnAttrs = 3; bool Remote = 5; + bool InhibitAttrs = 6; + bool InhibitBits = 7; } message QueryResponse { From d7bc6c3bd5e2d10eba8ff0a8e7eb3a0f8535babb Mon Sep 17 00:00:00 2001 From: Yuce Tekol Date: Tue, 22 Aug 2017 15:28:19 +0300 Subject: [PATCH 15/46] Updated docs --- docs/api-reference.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/api-reference.md b/docs/api-reference.md index c14ff4dcc..0c3990bec 100644 --- a/docs/api-reference.md +++ b/docs/api-reference.md @@ -115,6 +115,8 @@ Response: } ``` +By default, all bits and attributes (*for `Bitmap` queries only*) are returned. In order to suppress returning bits, set `inhibitBits` query argument to `true`; to suppress returning attributes, set `inhibitAttrs` query argument to `true`. + ### Change index time quantum `PATCH /index//time-quantum` From 562fba9de1e1fc6d56a97feabac0686b46d81335 Mon Sep 17 00:00:00 2001 From: Ben Johnson Date: Fri, 18 Aug 2017 09:36:58 -0600 Subject: [PATCH 16/46] Sum() and Average() field queries. --- executor.go | 142 +++++++++++++++++- executor_test.go | 122 +++++++++++++++ fragment.go | 31 ++++ fragment_test.go | 80 +++++++--- frame.go | 32 +++- handler.go | 2 + internal/public.pb.go | 341 +++++++++++++++++++++++++++++++++--------- internal/public.proto | 6 + view.go | 13 ++ 9 files changed, 671 insertions(+), 98 deletions(-) diff --git a/executor.go b/executor.go index d58be42f1..aa5b69034 100644 --- a/executor.go +++ b/executor.go @@ -161,6 +161,9 @@ func (e *Executor) executeCall(ctx context.Context, index string, c *pql.Call, s indexTag := fmt.Sprintf("index:%s", index) // Special handling for mutation and top-n calls. switch c.Name { + case "Average": + e.Holder.Stats.CountWithCustomTags(c.Name, 1, 1.0, []string{indexTag}) + return e.executeAverage(ctx, index, c, slices, opt) case "ClearBit": return e.executeClearBit(ctx, index, c, opt) case "Count": @@ -174,6 +177,9 @@ func (e *Executor) executeCall(ctx context.Context, index string, c *pql.Call, s return nil, e.executeSetRowAttrs(ctx, index, c, opt) case "SetColumnAttrs": return nil, e.executeSetColumnAttrs(ctx, index, c, opt) + case "Sum": + e.Holder.Stats.CountWithCustomTags(c.Name, 1, 1.0, []string{indexTag}) + return e.executeSum(ctx, index, c, slices, opt) case "TopN": e.Holder.Stats.CountWithCustomTags(c.Name, 1, 1.0, []string{indexTag}) return e.executeTopN(ctx, index, c, slices, opt) @@ -202,6 +208,41 @@ func (e *Executor) validateCallArgs(c *pql.Call) error { return nil } +// executeAverage executes an average() call. +func (e *Executor) executeAverage(ctx context.Context, index string, c *pql.Call, slices []uint64, opt *ExecOptions) (int64, error) { + if frame, _ := c.Args["frame"]; frame == "" { + return 0, errors.New("Average(): frame required") + } else if field, _ := c.Args["field"]; field == "" { + return 0, errors.New("Average(): field required") + } + + if len(c.Children) > 1 { + return 0, errors.New("Average() only accepts a single bitmap input") + } + + // Execute calls in bulk on each remote node and merge. + mapFn := func(slice uint64) (interface{}, error) { + return e.executeSumCountSlice(ctx, index, c, slice) + } + + // Merge returned results at coordinating node. + reduceFn := func(prev, v interface{}) interface{} { + other, _ := prev.(SumCount) + return other.Add(v.(SumCount)) + } + + result, err := e.mapReduce(ctx, index, slices, c, opt, mapFn, reduceFn) + if err != nil { + return 0, err + } + other, _ := result.(SumCount) + + if other.Count == 0 { + return 0, nil + } + return other.Sum / other.Count, nil +} + // executeBitmapCall executes a call that returns a bitmap. func (e *Executor) executeBitmapCall(ctx context.Context, index string, c *pql.Call, slices []uint64, opt *ExecOptions) (*Bitmap, error) { // Execute calls in bulk on each remote node and merge. @@ -280,6 +321,77 @@ func (e *Executor) executeBitmapCallSlice(ctx context.Context, index string, c * } } +// executeSum executes a sum() call. +func (e *Executor) executeSum(ctx context.Context, index string, c *pql.Call, slices []uint64, opt *ExecOptions) (int64, error) { + if frame, _ := c.Args["frame"]; frame == "" { + return 0, errors.New("Sum(): frame required") + } else if field, _ := c.Args["field"]; field == "" { + return 0, errors.New("Sum(): field required") + } + + if len(c.Children) > 1 { + return 0, errors.New("Sum() only accepts a single bitmap input") + } + + // Execute calls in bulk on each remote node and merge. + mapFn := func(slice uint64) (interface{}, error) { + return e.executeSumCountSlice(ctx, index, c, slice) + } + + // Merge returned results at coordinating node. + reduceFn := func(prev, v interface{}) interface{} { + other, _ := prev.(SumCount) + return other.Add(v.(SumCount)) + } + + result, err := e.mapReduce(ctx, index, slices, c, opt, mapFn, reduceFn) + if err != nil { + return 0, err + } + other, _ := result.(SumCount) + + return other.Sum, nil +} + +// executeSumCountSlice executes calculates the sum & count for fields on a slice. +func (e *Executor) executeSumCountSlice(ctx context.Context, index string, c *pql.Call, slice uint64) (SumCount, error) { + var filter *Bitmap + if len(c.Children) == 1 { + bm, err := e.executeBitmapCallSlice(ctx, index, c.Children[0], slice) + if err != nil { + return SumCount{}, err + } + filter = bm + } + + frameName, _ := c.Args["frame"].(string) + fieldName, _ := c.Args["field"].(string) + + frame := e.Holder.Frame(index, frameName) + if frame == nil { + return SumCount{}, nil + } + + field := frame.Field(fieldName) + if field == nil { + return SumCount{}, nil + } + + view := e.Holder.Fragment(index, frameName, ViewFieldPrefix+fieldName, slice) + if view == nil { + return SumCount{}, nil + } + + vsum, vcount, err := view.FieldSum(filter, field.BitDepth()) + if err != nil { + return SumCount{}, err + } + return SumCount{ + Sum: int64(vsum) + (int64(vcount) * field.Min), + Count: int64(vcount), + }, nil +} + // executeTopN executes a TopN() call. // This first performs the TopN() to determine the top results and then // requeries to retrieve the full counts for each of the top results. @@ -1179,6 +1291,8 @@ func (e *Executor) exec(ctx context.Context, node *Node, index string, q *pql.Qu var err error switch call.Name { + case "Average", "Sum": + v, err = decodeSumCount(pb.Results[i].GetSumCount()), nil case "TopN": v, err = decodePairs(pb.Results[i].GetPairs()), nil case "Count": @@ -1297,7 +1411,6 @@ func (e *Executor) mapper(ctx context.Context, ch chan mapResponse, nodes []*Nod if n.Host == e.Host { resp.result, resp.err = e.mapperLocal(ctx, nodeSlices, mapFn, reduceFn) } else if !opt.Remote { - results, err := e.exec(ctx, n, index, &pql.Query{Calls: []*pql.Call{c}}, nodeSlices, opt) if len(results) > 0 { resp.result = results[0] @@ -1414,3 +1527,30 @@ func needsSlices(calls []*pql.Call) bool { } return false } + +// SumCount represents a grouping of sum & count for Sum() and Average() calls. +type SumCount struct { + Sum int64 `json:"sum"` + Count int64 `json:"count"` +} + +func (sc *SumCount) Add(other SumCount) SumCount { + return SumCount{ + Sum: sc.Sum + other.Sum, + Count: sc.Count + other.Count, + } +} + +func encodeSumCount(sc SumCount) *internal.SumCount { + return &internal.SumCount{ + Sum: sc.Sum, + Count: sc.Count, + } +} + +func decodeSumCount(pb *internal.SumCount) SumCount { + return SumCount{ + Sum: pb.Sum, + Count: pb.Count, + } +} diff --git a/executor_test.go b/executor_test.go index 3c0f7e8ad..265293aef 100644 --- a/executor_test.go +++ b/executor_test.go @@ -422,6 +422,7 @@ func TestExecutor_Execute_TopN(t *testing.T) { } }) } + func TestExecutor_Execute_TopN_fill(t *testing.T) { hldr := test.MustOpenHolder() defer hldr.Close() @@ -559,7 +560,128 @@ func TestExecutor_Execute_TopN_Attr_Src(t *testing.T) { }}) { t.Fatalf("unexpected result: %s", spew.Sdump(result)) } +} +// Ensure a Sum() query can be executed. +func TestExecutor_Execute_Sum(t *testing.T) { + hldr := test.MustOpenHolder() + defer hldr.Close() + e := test.NewExecutor(hldr.Holder, test.NewCluster(1)) + + idx, err := hldr.CreateIndex("i", pilosa.IndexOptions{}) + if err != nil { + t.Fatal(err) + } + + if _, err := idx.CreateFrame("f", pilosa.FrameOptions{ + RangeEnabled: true, + Fields: []*pilosa.Field{ + {Name: "foo", Type: pilosa.FieldTypeInt, Min: 10, Max: 100}, + {Name: "bar", Type: pilosa.FieldTypeInt, Min: 0, Max: 100000}, + }, + }); err != nil { + t.Fatal(err) + } + + if _, err := idx.CreateFrame("other", pilosa.FrameOptions{ + RangeEnabled: true, + Fields: []*pilosa.Field{ + {Name: "foo", Type: pilosa.FieldTypeInt, Min: 0, Max: 1000}, + }, + }); err != nil { + t.Fatal(err) + } + + if _, err := e.Execute(context.Background(), "i", test.MustParse(` + SetBit(frame=f, rowID=0, columnID=0) + SetBit(frame=f, rowID=0, columnID=`+strconv.Itoa(SliceWidth+1)+`) + + SetFieldValue(frame=f, foo=20, bar=2000, columnID=0) + SetFieldValue(frame=f, foo=30, columnID=`+strconv.Itoa(SliceWidth)+`) + SetFieldValue(frame=f, foo=40, columnID=`+strconv.Itoa(SliceWidth+2)+`) + SetFieldValue(frame=f, foo=50, columnID=`+strconv.Itoa((5*SliceWidth)+100)+`) + SetFieldValue(frame=f, foo=60, columnID=`+strconv.Itoa(SliceWidth+1)+`) + SetFieldValue(frame=other, foo=1000, columnID=0) + `), nil, nil); err != nil { + t.Fatal(err) + } + + t.Run("NoFilter", func(t *testing.T) { + if result, err := e.Execute(context.Background(), "i", test.MustParse(`Sum(frame=f, field=foo)`), nil, nil); err != nil { + t.Fatal(err) + } else if result[0] != int64(200) { + t.Fatalf("unexpected result: %s", spew.Sdump(result)) + } + }) + + t.Run("WithFilter", func(t *testing.T) { + if result, err := e.Execute(context.Background(), "i", test.MustParse(`Sum(Bitmap(frame=f, rowID=0), frame=f, field=foo)`), nil, nil); err != nil { + t.Fatal(err) + } else if result[0] != int64(80) { + t.Fatalf("unexpected result: %s", spew.Sdump(result)) + } + }) +} + +// Ensure a Average() query can be executed. +func TestExecutor_Execute_Average(t *testing.T) { + hldr := test.MustOpenHolder() + defer hldr.Close() + e := test.NewExecutor(hldr.Holder, test.NewCluster(1)) + + idx, err := hldr.CreateIndex("i", pilosa.IndexOptions{}) + if err != nil { + t.Fatal(err) + } + + if _, err := idx.CreateFrame("f", pilosa.FrameOptions{ + RangeEnabled: true, + Fields: []*pilosa.Field{ + {Name: "foo", Type: pilosa.FieldTypeInt, Min: 10, Max: 100}, + {Name: "bar", Type: pilosa.FieldTypeInt, Min: 0, Max: 100000}, + }, + }); err != nil { + t.Fatal(err) + } + + if _, err := idx.CreateFrame("other", pilosa.FrameOptions{ + RangeEnabled: true, + Fields: []*pilosa.Field{ + {Name: "foo", Type: pilosa.FieldTypeInt, Min: 0, Max: 1000}, + }, + }); err != nil { + t.Fatal(err) + } + + if _, err := e.Execute(context.Background(), "i", test.MustParse(` + SetBit(frame=f, rowID=0, columnID=0) + SetBit(frame=f, rowID=0, columnID=`+strconv.Itoa(SliceWidth+2)+`) + + SetFieldValue(frame=f, foo=20, bar=2000, columnID=0) + SetFieldValue(frame=f, foo=30, columnID=`+strconv.Itoa(SliceWidth)+`) + SetFieldValue(frame=f, foo=40, columnID=`+strconv.Itoa(SliceWidth+2)+`) + SetFieldValue(frame=f, foo=50, columnID=`+strconv.Itoa((5*SliceWidth)+100)+`) + SetFieldValue(frame=f, foo=60, columnID=`+strconv.Itoa(SliceWidth+1)+`) + SetFieldValue(frame=other, foo=1000, columnID=0) + `), nil, nil); err != nil { + t.Fatal(err) + } + + t.Run("NoFilter", func(t *testing.T) { + if result, err := e.Execute(context.Background(), "i", test.MustParse(`Average(frame=f, field=foo)`), nil, nil); err != nil { + t.Fatal(err) + } else if result[0] != int64(40) { + t.Fatalf("unexpected result: %s", spew.Sdump(result)) + } + }) + + t.Run("WithFilter", func(t *testing.T) { + if result, err := e.Execute(context.Background(), "i", test.MustParse(`Average(Bitmap(frame=f, rowID=0), frame=f, field=foo)`), nil, nil); err != nil { + t.Fatal(err) + } else if result[0] != int64(30) { + t.Fatalf("unexpected result: %s", spew.Sdump(result)) + } + }) } // Ensure a range query can be executed. diff --git a/fragment.go b/fragment.go index db0d3fb13..a517cbd57 100644 --- a/fragment.go +++ b/fragment.go @@ -536,6 +536,37 @@ func (f *Fragment) SetFieldValue(columnID uint64, bitDepth uint, value uint64) ( return changed, nil } +// FieldSum returns the sum of a given field as well as the number of columns involved. +// A bitmap can be passed in to optionally filter the computed columns. +func (f *Fragment) FieldSum(filter *Bitmap, bitDepth uint) (sum, count uint64, err error) { + f.mu.Lock() + defer f.mu.Unlock() + + // Compute count based on the existance bit. + row := f.row(uint64(bitDepth), true, true) + if filter != nil { + row = row.Intersect(filter) + } + count = row.Count() + + // Compute the sum based on the bit count of each row multiplied by the + // place value of each row. For example, 10 bits in the 1's place plus + // 4 bits in the 2's place plus 3 bits in the 4's place equals a total + // sum of 30: + // + // 10*(2^0) + 4*(2^1) + 3*(2^2) = 30 + // + for i := uint(0); i < bitDepth; i++ { + row := f.row(uint64(i), true, true) + if filter != nil { + row = row.Intersect(filter) + } + sum += (1 << i) * row.Count() + } + + return sum, count, nil +} + func (f *Fragment) FieldRange(op string, bitDepth uint, predicate uint64) (*Bitmap, error) { switch op { case RangeOpEQ: diff --git a/fragment_test.go b/fragment_test.go index 702eec70a..7dfdac8ae 100644 --- a/fragment_test.go +++ b/fragment_test.go @@ -216,6 +216,45 @@ func TestFragment_SetFieldValue(t *testing.T) { }) } +// Ensure a fragment can sum field values. +func TestFragment_FieldSum(t *testing.T) { + const bitDepth = 16 + + f := test.MustOpenFragment("i", "f", pilosa.ViewStandard, 0, "") + defer f.Close() + + // Set values. + if _, err := f.SetFieldValue(1000, bitDepth, 382); err != nil { + t.Fatal(err) + } else if _, err := f.SetFieldValue(2000, bitDepth, 300); err != nil { + t.Fatal(err) + } else if _, err := f.SetFieldValue(3000, bitDepth, 2818); err != nil { + t.Fatal(err) + } else if _, err := f.SetFieldValue(4000, bitDepth, 300); err != nil { + t.Fatal(err) + } + + t.Run("NoFilter", func(t *testing.T) { + if sum, n, err := f.FieldSum(nil, bitDepth); err != nil { + t.Fatal(err) + } else if n != 4 { + t.Fatalf("unexpected count: %d", n) + } else if sum != 3800 { + t.Fatalf("unexpected sum: %d", sum) + } + }) + + t.Run("WithFilter", func(t *testing.T) { + if sum, n, err := f.FieldSum(pilosa.NewBitmap(2000, 4000, 5000), bitDepth); err != nil { + t.Fatal(err) + } else if n != 2 { + t.Fatalf("unexpected count: %d", n) + } else if sum != 600 { + t.Fatalf("unexpected sum: %d", sum) + } + }) +} + // Ensure a fragment query for matching fields. func TestFragment_FieldRange(t *testing.T) { const bitDepth = 16 @@ -940,28 +979,27 @@ func TestFragment_Zero_Tanimoto(t *testing.T) { } func TestFragment_Snapshot_Run(t *testing.T) { - f := test.MustOpenFragment("i", "f", pilosa.ViewStandard, 0, "") - defer f.Close() + f := test.MustOpenFragment("i", "f", pilosa.ViewStandard, 0, "") + defer f.Close() - // Set bits on the fragment. - for i := uint64(1); i < 3; i++ { - if _, err := f.SetBit(1000, i); err != nil { - t.Fatal(err) - } - } + // Set bits on the fragment. + for i := uint64(1); i < 3; i++ { + if _, err := f.SetBit(1000, i); err != nil { + t.Fatal(err) + } + } - // Snapshot bitmap and verify data. - if err := f.Snapshot(); err != nil { - t.Fatal(err) - } else if n := f.Row(1000).Count(); n != 2 { - t.Fatalf("unexpected count: %d", n) - } + // Snapshot bitmap and verify data. + if err := f.Snapshot(); err != nil { + t.Fatal(err) + } else if n := f.Row(1000).Count(); n != 2 { + t.Fatalf("unexpected count: %d", n) + } - // Close and reopen the fragment & verify the data. - if err := f.Reopen(); err != nil { - t.Fatal(err) - } else if n := f.Row(1000).Count(); n != 2 { - t.Fatalf("unexpected count (reopen): %d", n) - } + // Close and reopen the fragment & verify the data. + if err := f.Reopen(); err != nil { + t.Fatal(err) + } else if n := f.Row(1000).Count(); n != 2 { + t.Fatalf("unexpected count (reopen): %d", n) + } } - diff --git a/frame.go b/frame.go index de3dd6fbb..19c25eeab 100644 --- a/frame.go +++ b/frame.go @@ -124,11 +124,15 @@ func (f *Frame) MaxSlice() uint64 { f.mu.Lock() defer f.mu.Unlock() - view := f.views[ViewStandard] - if view == nil { - return 0 + var max uint64 + for _, view := range f.views { + if view.name == ViewInverse { + continue + } else if viewMaxSlice := view.MaxSlice(); viewMaxSlice > max { + max = viewMaxSlice + } } - return view.MaxSlice() + return max } // MaxInverseSlice returns the max inverse slice in the frame. @@ -639,6 +643,26 @@ func (f *Frame) SetFieldValue(columnID uint64, name string, value int64) (change return view.SetFieldValue(columnID, field.BitDepth(), baseValue) } +// FieldSum returns the sum and count for a field. +// An optional filtering bitmap can be provided. +func (f *Frame) FieldSum(filter *Bitmap, name string) (sum, count int64, err error) { + field := f.Field(name) + if field == nil { + return 0, 0, ErrFieldNotFound + } + + view := f.View(ViewFieldPrefix + name) + if view == nil { + return 0, 0, nil + } + + vsum, vcount, err := view.FieldSum(filter, field.BitDepth()) + if err != nil { + return 0, 0, err + } + return int64(vsum) + (int64(vcount) * field.Min), int64(vcount), nil +} + func (f *Frame) FieldRange(name, op string, predicate int64) (*Bitmap, error) { // Retrieve and validate field. field := f.Field(name) diff --git a/handler.go b/handler.go index f3bf5c786..1e7d47c69 100644 --- a/handler.go +++ b/handler.go @@ -1455,6 +1455,8 @@ func encodeQueryResponse(resp *QueryResponse) *internal.QueryResponse { pb.Results[i].Bitmap = encodeBitmap(result) case []Pair: pb.Results[i].Pairs = encodePairs(result) + case SumCount: + pb.Results[i].SumCount = encodeSumCount(result) case uint64: pb.Results[i].N = result case bool: diff --git a/internal/public.pb.go b/internal/public.pb.go index 43a9d7d35..66ce2dfdc 100644 --- a/internal/public.pb.go +++ b/internal/public.pb.go @@ -11,6 +11,7 @@ It has these top-level messages: Bitmap Pair + SumCount Bit ColumnAttrSet Attr @@ -66,6 +67,16 @@ func (m *Pair) String() string { return proto.CompactTextString(m) } func (*Pair) ProtoMessage() {} func (*Pair) Descriptor() ([]byte, []int) { return fileDescriptorPublic, []int{1} } +type SumCount struct { + Sum int64 `protobuf:"varint,1,opt,name=Sum,proto3" json:"Sum,omitempty"` + Count int64 `protobuf:"varint,2,opt,name=Count,proto3" json:"Count,omitempty"` +} + +func (m *SumCount) Reset() { *m = SumCount{} } +func (m *SumCount) String() string { return proto.CompactTextString(m) } +func (*SumCount) ProtoMessage() {} +func (*SumCount) Descriptor() ([]byte, []int) { return fileDescriptorPublic, []int{2} } + type Bit struct { RowID uint64 `protobuf:"varint,1,opt,name=RowID,proto3" json:"RowID,omitempty"` ColumnID uint64 `protobuf:"varint,2,opt,name=ColumnID,proto3" json:"ColumnID,omitempty"` @@ -75,7 +86,7 @@ type Bit struct { func (m *Bit) Reset() { *m = Bit{} } func (m *Bit) String() string { return proto.CompactTextString(m) } func (*Bit) ProtoMessage() {} -func (*Bit) Descriptor() ([]byte, []int) { return fileDescriptorPublic, []int{2} } +func (*Bit) Descriptor() ([]byte, []int) { return fileDescriptorPublic, []int{3} } type ColumnAttrSet struct { ID uint64 `protobuf:"varint,1,opt,name=ID,proto3" json:"ID,omitempty"` @@ -85,7 +96,7 @@ type ColumnAttrSet struct { func (m *ColumnAttrSet) Reset() { *m = ColumnAttrSet{} } func (m *ColumnAttrSet) String() string { return proto.CompactTextString(m) } func (*ColumnAttrSet) ProtoMessage() {} -func (*ColumnAttrSet) Descriptor() ([]byte, []int) { return fileDescriptorPublic, []int{3} } +func (*ColumnAttrSet) Descriptor() ([]byte, []int) { return fileDescriptorPublic, []int{4} } func (m *ColumnAttrSet) GetAttrs() []*Attr { if m != nil { @@ -106,7 +117,7 @@ type Attr struct { func (m *Attr) Reset() { *m = Attr{} } func (m *Attr) String() string { return proto.CompactTextString(m) } func (*Attr) ProtoMessage() {} -func (*Attr) Descriptor() ([]byte, []int) { return fileDescriptorPublic, []int{4} } +func (*Attr) Descriptor() ([]byte, []int) { return fileDescriptorPublic, []int{5} } type AttrMap struct { Attrs []*Attr `protobuf:"bytes,1,rep,name=Attrs" json:"Attrs,omitempty"` @@ -115,7 +126,7 @@ type AttrMap struct { func (m *AttrMap) Reset() { *m = AttrMap{} } func (m *AttrMap) String() string { return proto.CompactTextString(m) } func (*AttrMap) ProtoMessage() {} -func (*AttrMap) Descriptor() ([]byte, []int) { return fileDescriptorPublic, []int{5} } +func (*AttrMap) Descriptor() ([]byte, []int) { return fileDescriptorPublic, []int{6} } func (m *AttrMap) GetAttrs() []*Attr { if m != nil { @@ -134,7 +145,7 @@ type QueryRequest struct { func (m *QueryRequest) Reset() { *m = QueryRequest{} } func (m *QueryRequest) String() string { return proto.CompactTextString(m) } func (*QueryRequest) ProtoMessage() {} -func (*QueryRequest) Descriptor() ([]byte, []int) { return fileDescriptorPublic, []int{6} } +func (*QueryRequest) Descriptor() ([]byte, []int) { return fileDescriptorPublic, []int{7} } type QueryResponse struct { Err string `protobuf:"bytes,1,opt,name=Err,proto3" json:"Err,omitempty"` @@ -145,7 +156,7 @@ type QueryResponse struct { func (m *QueryResponse) Reset() { *m = QueryResponse{} } func (m *QueryResponse) String() string { return proto.CompactTextString(m) } func (*QueryResponse) ProtoMessage() {} -func (*QueryResponse) Descriptor() ([]byte, []int) { return fileDescriptorPublic, []int{7} } +func (*QueryResponse) Descriptor() ([]byte, []int) { return fileDescriptorPublic, []int{8} } func (m *QueryResponse) GetResults() []*QueryResult { if m != nil { @@ -162,16 +173,17 @@ func (m *QueryResponse) GetColumnAttrSets() []*ColumnAttrSet { } type QueryResult struct { - Bitmap *Bitmap `protobuf:"bytes,1,opt,name=Bitmap" json:"Bitmap,omitempty"` - N uint64 `protobuf:"varint,2,opt,name=N,proto3" json:"N,omitempty"` - Pairs []*Pair `protobuf:"bytes,3,rep,name=Pairs" json:"Pairs,omitempty"` - Changed bool `protobuf:"varint,4,opt,name=Changed,proto3" json:"Changed,omitempty"` + Bitmap *Bitmap `protobuf:"bytes,1,opt,name=Bitmap" json:"Bitmap,omitempty"` + N uint64 `protobuf:"varint,2,opt,name=N,proto3" json:"N,omitempty"` + Pairs []*Pair `protobuf:"bytes,3,rep,name=Pairs" json:"Pairs,omitempty"` + SumCount *SumCount `protobuf:"bytes,5,opt,name=SumCount" json:"SumCount,omitempty"` + Changed bool `protobuf:"varint,4,opt,name=Changed,proto3" json:"Changed,omitempty"` } func (m *QueryResult) Reset() { *m = QueryResult{} } func (m *QueryResult) String() string { return proto.CompactTextString(m) } func (*QueryResult) ProtoMessage() {} -func (*QueryResult) Descriptor() ([]byte, []int) { return fileDescriptorPublic, []int{8} } +func (*QueryResult) Descriptor() ([]byte, []int) { return fileDescriptorPublic, []int{9} } func (m *QueryResult) GetBitmap() *Bitmap { if m != nil { @@ -187,6 +199,13 @@ func (m *QueryResult) GetPairs() []*Pair { return nil } +func (m *QueryResult) GetSumCount() *SumCount { + if m != nil { + return m.SumCount + } + return nil +} + type ImportRequest struct { Index string `protobuf:"bytes,1,opt,name=Index,proto3" json:"Index,omitempty"` Frame string `protobuf:"bytes,2,opt,name=Frame,proto3" json:"Frame,omitempty"` @@ -199,11 +218,12 @@ type ImportRequest struct { func (m *ImportRequest) Reset() { *m = ImportRequest{} } func (m *ImportRequest) String() string { return proto.CompactTextString(m) } func (*ImportRequest) ProtoMessage() {} -func (*ImportRequest) Descriptor() ([]byte, []int) { return fileDescriptorPublic, []int{9} } +func (*ImportRequest) Descriptor() ([]byte, []int) { return fileDescriptorPublic, []int{10} } func init() { proto.RegisterType((*Bitmap)(nil), "internal.Bitmap") proto.RegisterType((*Pair)(nil), "internal.Pair") + proto.RegisterType((*SumCount)(nil), "internal.SumCount") proto.RegisterType((*Bit)(nil), "internal.Bit") proto.RegisterType((*ColumnAttrSet)(nil), "internal.ColumnAttrSet") proto.RegisterType((*Attr)(nil), "internal.Attr") @@ -288,6 +308,34 @@ func (m *Pair) MarshalTo(dAtA []byte) (int, error) { return i, nil } +func (m *SumCount) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalTo(dAtA) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *SumCount) MarshalTo(dAtA []byte) (int, error) { + var i int + _ = i + var l int + _ = l + if m.Sum != 0 { + dAtA[i] = 0x8 + i++ + i = encodeVarintPublic(dAtA, i, uint64(m.Sum)) + } + if m.Count != 0 { + dAtA[i] = 0x10 + i++ + i = encodeVarintPublic(dAtA, i, uint64(m.Count)) + } + return i, nil +} + func (m *Bit) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) @@ -602,6 +650,16 @@ func (m *QueryResult) MarshalTo(dAtA []byte) (int, error) { } i++ } + if m.SumCount != nil { + dAtA[i] = 0x2a + i++ + i = encodeVarintPublic(dAtA, i, uint64(m.SumCount.Size())) + n6, err := m.SumCount.MarshalTo(dAtA[i:]) + if err != nil { + return 0, err + } + i += n6 + } return i, nil } @@ -638,56 +696,56 @@ func (m *ImportRequest) MarshalTo(dAtA []byte) (int, error) { i = encodeVarintPublic(dAtA, i, uint64(m.Slice)) } if len(m.RowIDs) > 0 { - dAtA7 := make([]byte, len(m.RowIDs)*10) - var j6 int + dAtA8 := make([]byte, len(m.RowIDs)*10) + var j7 int for _, num := range m.RowIDs { for num >= 1<<7 { - dAtA7[j6] = uint8(uint64(num)&0x7f | 0x80) + dAtA8[j7] = uint8(uint64(num)&0x7f | 0x80) num >>= 7 - j6++ + j7++ } - dAtA7[j6] = uint8(num) - j6++ + dAtA8[j7] = uint8(num) + j7++ } dAtA[i] = 0x22 i++ - i = encodeVarintPublic(dAtA, i, uint64(j6)) - i += copy(dAtA[i:], dAtA7[:j6]) + i = encodeVarintPublic(dAtA, i, uint64(j7)) + i += copy(dAtA[i:], dAtA8[:j7]) } if len(m.ColumnIDs) > 0 { - dAtA9 := make([]byte, len(m.ColumnIDs)*10) - var j8 int + dAtA10 := make([]byte, len(m.ColumnIDs)*10) + var j9 int for _, num := range m.ColumnIDs { for num >= 1<<7 { - dAtA9[j8] = uint8(uint64(num)&0x7f | 0x80) + dAtA10[j9] = uint8(uint64(num)&0x7f | 0x80) num >>= 7 - j8++ + j9++ } - dAtA9[j8] = uint8(num) - j8++ + dAtA10[j9] = uint8(num) + j9++ } dAtA[i] = 0x2a i++ - i = encodeVarintPublic(dAtA, i, uint64(j8)) - i += copy(dAtA[i:], dAtA9[:j8]) + i = encodeVarintPublic(dAtA, i, uint64(j9)) + i += copy(dAtA[i:], dAtA10[:j9]) } if len(m.Timestamps) > 0 { - dAtA11 := make([]byte, len(m.Timestamps)*10) - var j10 int + dAtA12 := make([]byte, len(m.Timestamps)*10) + var j11 int for _, num1 := range m.Timestamps { num := uint64(num1) for num >= 1<<7 { - dAtA11[j10] = uint8(uint64(num)&0x7f | 0x80) + dAtA12[j11] = uint8(uint64(num)&0x7f | 0x80) num >>= 7 - j10++ + j11++ } - dAtA11[j10] = uint8(num) - j10++ + dAtA12[j11] = uint8(num) + j11++ } dAtA[i] = 0x32 i++ - i = encodeVarintPublic(dAtA, i, uint64(j10)) - i += copy(dAtA[i:], dAtA11[:j10]) + i = encodeVarintPublic(dAtA, i, uint64(j11)) + i += copy(dAtA[i:], dAtA12[:j11]) } return i, nil } @@ -750,6 +808,18 @@ func (m *Pair) Size() (n int) { return n } +func (m *SumCount) Size() (n int) { + var l int + _ = l + if m.Sum != 0 { + n += 1 + sovPublic(uint64(m.Sum)) + } + if m.Count != 0 { + n += 1 + sovPublic(uint64(m.Count)) + } + return n +} + func (m *Bit) Size() (n int) { var l int _ = l @@ -882,6 +952,10 @@ func (m *QueryResult) Size() (n int) { if m.Changed { n += 2 } + if m.SumCount != nil { + l = m.SumCount.Size() + n += 1 + l + sovPublic(uint64(l)) + } return n } @@ -1167,6 +1241,94 @@ func (m *Pair) Unmarshal(dAtA []byte) error { } return nil } +func (m *SumCount) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowPublic + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: SumCount: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: SumCount: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Sum", wireType) + } + m.Sum = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowPublic + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Sum |= (int64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + case 2: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Count", wireType) + } + m.Count = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowPublic + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Count |= (int64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + default: + iNdEx = preIndex + skippy, err := skipPublic(dAtA[iNdEx:]) + if err != nil { + return err + } + if skippy < 0 { + return ErrInvalidLengthPublic + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} func (m *Bit) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 @@ -2093,6 +2255,39 @@ func (m *QueryResult) Unmarshal(dAtA []byte) error { } } m.Changed = bool(v != 0) + case 5: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field SumCount", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowPublic + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthPublic + } + postIndex := iNdEx + msglen + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.SumCount == nil { + m.SumCount = &SumCount{} + } + if err := m.SumCount.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipPublic(dAtA[iNdEx:]) @@ -2535,41 +2730,43 @@ var ( func init() { proto.RegisterFile("public.proto", fileDescriptorPublic) } var fileDescriptorPublic = []byte{ - // 563 bytes of a gzipped FileDescriptorProto + // 596 bytes of a gzipped FileDescriptorProto 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x8c, 0x54, 0x4b, 0x8e, 0xd3, 0x40, - 0x10, 0xa5, 0x63, 0xe7, 0x57, 0xf9, 0x28, 0x6a, 0xf1, 0xb1, 0x10, 0x8a, 0x2c, 0x8b, 0x85, 0x57, - 0x19, 0x69, 0x38, 0x00, 0xc2, 0x49, 0x46, 0xb2, 0x10, 0x23, 0xe8, 0x0c, 0xec, 0x3d, 0x33, 0xad, - 0xc1, 0x92, 0x7f, 0x74, 0xb7, 0x81, 0x1c, 0x80, 0x13, 0xb0, 0xe1, 0x06, 0x70, 0x14, 0x96, 0x1c, - 0x01, 0x85, 0x8b, 0xa0, 0xea, 0x76, 0xc7, 0x1e, 0x16, 0x68, 0x76, 0xfd, 0x5e, 0x75, 0xb5, 0xeb, - 0xd5, 0xab, 0x32, 0x4c, 0xab, 0xfa, 0x32, 0x4b, 0xaf, 0x56, 0x95, 0x28, 0x55, 0x49, 0x47, 0x69, - 0xa1, 0xb8, 0x28, 0x92, 0x2c, 0x88, 0x60, 0x10, 0xa5, 0x2a, 0x4f, 0x2a, 0x4a, 0xc1, 0x8d, 0x52, - 0x25, 0x3d, 0xe2, 0x3b, 0xa1, 0xcb, 0xf4, 0x99, 0x3e, 0x85, 0xfe, 0x0b, 0xa5, 0x84, 0xf4, 0x7a, - 0xbe, 0x13, 0x4e, 0x4e, 0xe7, 0x2b, 0x9b, 0xb7, 0x42, 0x9a, 0x99, 0x60, 0xb0, 0x02, 0xf7, 0x75, - 0x92, 0x0a, 0xba, 0x00, 0xe7, 0x25, 0xdf, 0x7b, 0xc4, 0x27, 0xa1, 0xcb, 0xf0, 0x48, 0xef, 0x43, - 0x7f, 0x5d, 0xd6, 0x85, 0xf2, 0x7a, 0x9a, 0x33, 0x20, 0x78, 0x0b, 0x4e, 0x94, 0x2a, 0x0c, 0xb2, - 0xf2, 0x53, 0xbc, 0x69, 0x12, 0x0c, 0xa0, 0x8f, 0x61, 0xb4, 0x2e, 0xb3, 0x3a, 0x2f, 0xe2, 0x4d, - 0x93, 0x75, 0xc4, 0xf4, 0x09, 0x8c, 0x2f, 0xd2, 0x9c, 0x4b, 0x95, 0xe4, 0x95, 0xe7, 0xf8, 0x24, - 0x74, 0x58, 0x4b, 0x04, 0x5b, 0x98, 0x99, 0x9b, 0x58, 0xd5, 0x8e, 0x2b, 0x3a, 0x87, 0xde, 0xf1, - 0xf5, 0x5e, 0xbc, 0xb9, 0xa3, 0x9a, 0x1f, 0x04, 0x5c, 0x3c, 0x75, 0xe5, 0x8c, 0x8d, 0x1c, 0x0a, - 0xee, 0xc5, 0xbe, 0xe2, 0x4d, 0x5d, 0xfa, 0x4c, 0x7d, 0x98, 0xec, 0x94, 0x48, 0x8b, 0x9b, 0x77, - 0x49, 0x56, 0x73, 0x5d, 0xd5, 0x98, 0x75, 0x29, 0x54, 0x14, 0x17, 0xca, 0x84, 0x5d, 0x5d, 0xf4, - 0x11, 0xa3, 0xa2, 0xa8, 0x2c, 0x33, 0x13, 0xec, 0xfb, 0x24, 0x1c, 0xb1, 0x96, 0xa0, 0x4b, 0x80, - 0xb3, 0xac, 0x4c, 0x9a, 0xdc, 0x81, 0x4f, 0x42, 0xc2, 0x3a, 0x4c, 0x70, 0x02, 0x43, 0xac, 0xf4, - 0x55, 0x52, 0xb5, 0xda, 0xc8, 0xff, 0xb4, 0x7d, 0x84, 0xe9, 0x9b, 0x9a, 0x8b, 0x3d, 0xe3, 0x1f, - 0x6a, 0x2e, 0xb5, 0x05, 0x1a, 0x37, 0x22, 0x0d, 0xa0, 0x0f, 0x61, 0xb0, 0xcb, 0xd2, 0x2b, 0x6e, - 0x1a, 0xe5, 0xb2, 0x06, 0xa1, 0xd4, 0xb6, 0xc1, 0x52, 0x4b, 0x1d, 0xb1, 0x2e, 0x85, 0x99, 0x8c, - 0xe7, 0xa5, 0xb2, 0x5a, 0x1a, 0x14, 0x7c, 0x25, 0x30, 0x6b, 0x3e, 0x2c, 0xab, 0xb2, 0x90, 0x1c, - 0x9b, 0xbb, 0x15, 0xc2, 0x36, 0x77, 0x2b, 0x04, 0x3d, 0x81, 0x21, 0xe3, 0xb2, 0xce, 0x94, 0xf5, - 0xe7, 0x41, 0xab, 0xc1, 0xe6, 0xd6, 0x99, 0x62, 0xf6, 0x16, 0x7d, 0x0e, 0xf3, 0x5b, 0x7e, 0x63, - 0x45, 0x98, 0xf7, 0xa8, 0xcd, 0xbb, 0x15, 0x67, 0xff, 0x5c, 0x0f, 0xbe, 0x10, 0x98, 0x74, 0x5e, - 0xa6, 0xa1, 0xdd, 0x05, 0x5d, 0xd6, 0xe4, 0x74, 0xd1, 0x3e, 0x64, 0x78, 0x66, 0x77, 0x65, 0x0a, - 0xe4, 0xbc, 0x99, 0x02, 0x72, 0x8e, 0xbd, 0xc7, 0xf9, 0xb7, 0xdf, 0xef, 0xf4, 0x1e, 0x69, 0x66, - 0x82, 0xd4, 0x83, 0xe1, 0xfa, 0x7d, 0x52, 0xdc, 0xf0, 0x6b, 0x3d, 0x05, 0x23, 0x66, 0x61, 0xf0, - 0x9d, 0xc0, 0x2c, 0xce, 0xab, 0x52, 0xa8, 0x8e, 0x2f, 0x71, 0x71, 0xcd, 0x3f, 0x5b, 0x5f, 0x34, - 0x40, 0xf6, 0x4c, 0x24, 0xb9, 0x99, 0xbf, 0x31, 0x33, 0x00, 0x59, 0xed, 0x8f, 0xf6, 0xc3, 0x65, - 0x06, 0x68, 0x27, 0x70, 0x9f, 0xa4, 0xe7, 0x1a, 0x0f, 0x0d, 0xc2, 0x81, 0xb3, 0xeb, 0x24, 0xbd, - 0xbe, 0x0e, 0xb5, 0x04, 0x0e, 0xdc, 0x71, 0x9f, 0xa4, 0x37, 0xf0, 0x9d, 0xd0, 0x61, 0x1d, 0x26, - 0x5a, 0xfc, 0x3c, 0x2c, 0xc9, 0xaf, 0xc3, 0x92, 0xfc, 0x3e, 0x2c, 0xc9, 0xb7, 0x3f, 0xcb, 0x7b, - 0x97, 0x03, 0xfd, 0x43, 0x79, 0xf6, 0x37, 0x00, 0x00, 0xff, 0xff, 0xf6, 0x6d, 0x5d, 0xc9, 0x60, - 0x04, 0x00, 0x00, + 0x10, 0xa5, 0x63, 0x27, 0x71, 0x2a, 0x1f, 0x45, 0x2d, 0x3e, 0x16, 0x42, 0x91, 0x65, 0xb1, 0xf0, + 0x2a, 0x23, 0x85, 0x03, 0x20, 0x9c, 0x64, 0x24, 0x0b, 0x31, 0x82, 0xce, 0xc0, 0xde, 0x33, 0xd3, + 0x1a, 0x2c, 0xf9, 0x47, 0xbb, 0x0d, 0xe4, 0x1c, 0x6c, 0xb8, 0x01, 0x5c, 0x80, 0x3b, 0xb0, 0xe4, + 0x08, 0x28, 0x5c, 0x04, 0x55, 0xb7, 0x3b, 0x76, 0x58, 0xa0, 0xd9, 0xd5, 0x7b, 0xe5, 0xea, 0xae, + 0xf7, 0xaa, 0xcb, 0x30, 0x29, 0xeb, 0xab, 0x34, 0xb9, 0x5e, 0x96, 0xa2, 0x90, 0x05, 0x75, 0x92, + 0x5c, 0x72, 0x91, 0xc7, 0xa9, 0x1f, 0xc2, 0x20, 0x4c, 0x64, 0x16, 0x97, 0x94, 0x82, 0x1d, 0x26, + 0xb2, 0x72, 0x89, 0x67, 0x05, 0x36, 0x53, 0x31, 0x7d, 0x0a, 0xfd, 0x17, 0x52, 0x8a, 0xca, 0xed, + 0x79, 0x56, 0x30, 0x5e, 0xcd, 0x96, 0xa6, 0x6e, 0x89, 0x34, 0xd3, 0x49, 0x7f, 0x09, 0xf6, 0xeb, + 0x38, 0x11, 0x74, 0x0e, 0xd6, 0x4b, 0xbe, 0x77, 0x89, 0x47, 0x02, 0x9b, 0x61, 0x48, 0xef, 0x43, + 0x7f, 0x5d, 0xd4, 0xb9, 0x74, 0x7b, 0x8a, 0xd3, 0xc0, 0x5f, 0x81, 0xb3, 0xab, 0x33, 0x15, 0x63, + 0xcd, 0xae, 0xce, 0x54, 0x8d, 0xc5, 0x30, 0x3c, 0xad, 0xb1, 0x4c, 0xcd, 0x5b, 0xb0, 0xc2, 0x44, + 0x62, 0x92, 0x15, 0x9f, 0xa2, 0x4d, 0x73, 0x89, 0x06, 0xf4, 0x31, 0x38, 0xeb, 0x22, 0xad, 0xb3, + 0x3c, 0xda, 0x34, 0x37, 0x1d, 0x31, 0x7d, 0x02, 0xa3, 0xcb, 0x24, 0xe3, 0x95, 0x8c, 0xb3, 0xd2, + 0xb5, 0xd4, 0x91, 0x2d, 0xe1, 0x6f, 0x61, 0xaa, 0xbf, 0x44, 0x25, 0x3b, 0x2e, 0xe9, 0x0c, 0x7a, + 0xc7, 0xd3, 0x7b, 0xd1, 0xe6, 0x8e, 0x0e, 0x7c, 0x27, 0x60, 0x63, 0xd4, 0xb5, 0x60, 0xa4, 0x2d, + 0xa0, 0x60, 0x5f, 0xee, 0x4b, 0xde, 0xf4, 0xa5, 0x62, 0xea, 0xc1, 0x78, 0x27, 0x45, 0x92, 0xdf, + 0xbe, 0x8b, 0xd3, 0x9a, 0xab, 0xae, 0x46, 0xac, 0x4b, 0xa1, 0xa2, 0x28, 0x97, 0x3a, 0x6d, 0xab, + 0xa6, 0x8f, 0x18, 0x15, 0x85, 0x45, 0x91, 0xea, 0x64, 0xdf, 0x23, 0x81, 0xc3, 0x5a, 0x82, 0x2e, + 0x00, 0xce, 0xd3, 0x22, 0x6e, 0x6a, 0x07, 0x1e, 0x09, 0x08, 0xeb, 0x30, 0xfe, 0x19, 0x0c, 0xb1, + 0xd3, 0x57, 0x71, 0xd9, 0x6a, 0x23, 0xff, 0xd3, 0xf6, 0x11, 0x26, 0x6f, 0x6a, 0x2e, 0xf6, 0x8c, + 0x7f, 0xa8, 0x79, 0xa5, 0x46, 0xa0, 0x70, 0x23, 0x52, 0x03, 0xfa, 0x10, 0x06, 0xbb, 0x34, 0xb9, + 0xe6, 0xda, 0x28, 0x9b, 0x35, 0x08, 0xa5, 0xb6, 0x06, 0x57, 0x4a, 0xaa, 0xc3, 0xba, 0x14, 0x56, + 0x32, 0x9e, 0x15, 0xd2, 0x68, 0x69, 0x90, 0xff, 0x85, 0xc0, 0xb4, 0xb9, 0xb8, 0x2a, 0x8b, 0xbc, + 0xe2, 0x68, 0xee, 0x56, 0x08, 0x63, 0xee, 0x56, 0x08, 0x7a, 0x06, 0x43, 0xc6, 0xab, 0x3a, 0x95, + 0x66, 0x3e, 0x0f, 0x5a, 0x0d, 0xa6, 0xb6, 0x4e, 0x25, 0x33, 0x5f, 0xd1, 0xe7, 0x30, 0x3b, 0x99, + 0x37, 0x76, 0x84, 0x75, 0x8f, 0xda, 0xba, 0x93, 0x3c, 0xfb, 0xe7, 0x73, 0xff, 0x07, 0x81, 0x71, + 0xe7, 0x64, 0x1a, 0x98, 0xfd, 0x51, 0x6d, 0x8d, 0x57, 0xf3, 0xf6, 0x20, 0xcd, 0x33, 0xb3, 0x5f, + 0x13, 0x20, 0x17, 0xcd, 0x2b, 0x20, 0x17, 0xe8, 0x3d, 0xee, 0x8c, 0xb9, 0xbf, 0xe3, 0x3d, 0xd2, + 0x4c, 0x27, 0xa9, 0x0b, 0xc3, 0xf5, 0xfb, 0x38, 0xbf, 0xe5, 0x37, 0xea, 0x15, 0x38, 0xcc, 0x40, + 0xba, 0x6c, 0x77, 0x48, 0xf9, 0x36, 0x5e, 0xd1, 0xf6, 0x08, 0x93, 0x61, 0xc7, 0x6f, 0xfc, 0x6f, + 0x04, 0xa6, 0x51, 0x56, 0x16, 0x42, 0x76, 0xe6, 0x18, 0xe5, 0x37, 0xfc, 0xb3, 0x99, 0xa3, 0x02, + 0xc8, 0x9e, 0x8b, 0x38, 0xd3, 0xef, 0x75, 0xc4, 0x34, 0x40, 0x56, 0xcd, 0x53, 0xcd, 0xcf, 0x66, + 0x1a, 0xa8, 0xc9, 0xe1, 0xfe, 0x55, 0xae, 0xad, 0x67, 0xae, 0x11, 0x3e, 0x50, 0xb3, 0x7e, 0x95, + 0xdb, 0x57, 0xa9, 0x96, 0xc0, 0x07, 0x7a, 0xdc, 0xbf, 0xca, 0x1d, 0x78, 0x56, 0x60, 0xb1, 0x0e, + 0x13, 0xce, 0x7f, 0x1e, 0x16, 0xe4, 0xd7, 0x61, 0x41, 0x7e, 0x1f, 0x16, 0xe4, 0xeb, 0x9f, 0xc5, + 0xbd, 0xab, 0x81, 0xfa, 0x69, 0x3d, 0xfb, 0x1b, 0x00, 0x00, 0xff, 0xff, 0x64, 0xc1, 0x80, 0x73, + 0xc4, 0x04, 0x00, 0x00, } diff --git a/internal/public.proto b/internal/public.proto index ff161d930..c5f269928 100644 --- a/internal/public.proto +++ b/internal/public.proto @@ -12,6 +12,11 @@ message Pair { uint64 Count = 2; } +message SumCount { + int64 Sum = 1; + int64 Count = 2; +} + message Bit { uint64 RowID = 1; uint64 ColumnID = 2; @@ -53,6 +58,7 @@ message QueryResult { Bitmap Bitmap = 1; uint64 N = 2; repeated Pair Pairs = 3; + SumCount SumCount = 5; bool Changed = 4; } diff --git a/view.go b/view.go index 1aa29bbb4..31a7a53c0 100644 --- a/view.go +++ b/view.go @@ -300,6 +300,19 @@ func (v *View) SetFieldValue(columnID uint64, bitDepth uint, value uint64) (chan return frag.SetFieldValue(columnID, bitDepth, value) } +// FieldSum returns the sum & count of a field. +func (v *View) FieldSum(filter *Bitmap, bitDepth uint) (sum, count uint64, err error) { + for _, f := range v.Fragments() { + fsum, fcount, err := f.FieldSum(filter, bitDepth) + if err != nil { + return sum, count, err + } + sum += fsum + count += fcount + } + return sum, count, nil +} + // FieldRange returns bitmaps with a field value encoding matching the predicate. func (v *View) FieldRange(op string, bitDepth uint, predicate uint64) (*Bitmap, error) { bm := NewBitmap() From 941d8a757578926f7b4fdb7aaa700ed3d7d0e07c Mon Sep 17 00:00:00 2001 From: Michael Baird Date: Fri, 18 Aug 2017 09:36:45 -0500 Subject: [PATCH 17/46] DeleteView from Frame --- frame.go | 20 ++++++++++++++++++++ frame_test.go | 31 +++++++++++++++++++++++++++++++ 2 files changed, 51 insertions(+) diff --git a/frame.go b/frame.go index 19c25eeab..675736d36 100644 --- a/frame.go +++ b/frame.go @@ -513,6 +513,26 @@ func (f *Frame) newView(path, name string) *View { return view } +// DeleteView removes a view from the frame. +func (f *Frame) DeleteView(name string) error { + view := f.views[name] + if view == nil { + return ErrInvalidView + } + + // TODO capture errors lower down in this method + _ = view.Close() + + // Delete view directory. + if err := os.RemoveAll(view.Path()); err != nil { + return err + } + + delete(f.views, name) + + return nil +} + // SetBit sets a bit on a view within the frame. func (f *Frame) SetBit(name string, rowID, colID uint64, t *time.Time) (changed bool, err error) { // Validate view name. diff --git a/frame_test.go b/frame_test.go index 8cb48cca8..b560d985e 100644 --- a/frame_test.go +++ b/frame_test.go @@ -307,3 +307,34 @@ func TestFrame_RowLabelValidation(t *testing.T) { } } + +// Ensure frame can open and retrieve a view. +func TestFrame_DeleteView(t *testing.T) { + f := test.MustOpenFrame() + defer f.Close() + + // Create view. + view, err := f.CreateViewIfNotExists("v") + if err != nil { + t.Fatal(err) + } else if view == nil { + t.Fatal("expected view") + } + + err = f.DeleteView("v") + if err != nil { + t.Fatal(err) + } + + if f.View("v") != nil { + t.Fatal("view still exists in frame") + } + + // // Retrieve existing view. + view2, err := f.CreateViewIfNotExists("v") + if err != nil { + t.Fatal(err) + } else if view == view2 { + t.Fatal("failed to create new view") + } +} From b03e45e3e389aedcee3b6a9590711b631e21dc6c Mon Sep 17 00:00:00 2001 From: Michael Baird Date: Fri, 18 Aug 2017 14:55:29 -0500 Subject: [PATCH 18/46] handler for delete View --- handler.go | 29 +++++++++++++++++++++++++++++ handler_test.go | 20 ++++++++++++++++++++ 2 files changed, 49 insertions(+) diff --git a/handler.go b/handler.go index 1e7d47c69..afad04123 100644 --- a/handler.go +++ b/handler.go @@ -116,6 +116,7 @@ func NewRouter(handler *Handler) *mux.Router { router.HandleFunc("/index/{index}/frame/{frame}/restore", handler.handlePostFrameRestore).Methods("POST") router.HandleFunc("/index/{index}/frame/{frame}/time-quantum", handler.handlePatchFrameTimeQuantum).Methods("PATCH") router.HandleFunc("/index/{index}/frame/{frame}/views", handler.handleGetFrameViews).Methods("GET") + router.HandleFunc("/index/{index}/frame/{frame}/view/{view}", handler.handleDeleteView).Methods("DELETE") router.HandleFunc("/index/{index}/input/{input-definition}", handler.handlePostInput).Methods("POST") router.HandleFunc("/index/{index}/input-definition/{input-definition}", handler.handleGetInputDefinition).Methods("GET") router.HandleFunc("/index/{index}/input-definition/{input-definition}", handler.handlePostInputDefinition).Methods("POST") @@ -789,6 +790,34 @@ func (h *Handler) handleGetFrameViews(w http.ResponseWriter, r *http.Request) { } } +// handleDeleteView handles Delete /frame/view request. +func (h *Handler) handleDeleteView(w http.ResponseWriter, r *http.Request) { + indexName := mux.Vars(r)["index"] + frameName := mux.Vars(r)["frame"] + viewName := mux.Vars(r)["view"] + + // Retrieve frame. + f := h.Holder.Frame(indexName, frameName) + if f == nil { + http.Error(w, ErrFrameNotFound.Error(), http.StatusNotFound) + return + } + + if err := f.DeleteView(viewName); err != nil { + http.Error(w, err.Error(), http.StatusBadRequest) + return + } + + // TODO: Send the delete frame message to all nodes. + + // Encode response. + if err := json.NewEncoder(w).Encode(deleteViewResponse{}); err != nil { + h.logger().Printf("response encoding error: %s", err) + } +} + +type deleteViewResponse struct{} + type getFrameViewsResponse struct { Views []string `json:"views,omitempty"` } diff --git a/handler_test.go b/handler_test.go index 22a1fd4f3..2947fd1b8 100644 --- a/handler_test.go +++ b/handler_test.go @@ -1511,3 +1511,23 @@ func TestHandler_GetTimeStamp(t *testing.T) { t.Fatalf("Expected Ignore nonexistent fields") } } + +// Ensure handler can delete a view. +func TestHandler_DeleteView(t *testing.T) { + hldr := test.MustOpenHolder() + defer hldr.Close() + hldr.MustCreateFragmentIfNotExists("i0", "f0", "v0", 1).MustSetBits(30, (1*SliceWidth)+1) + + h := test.NewHandler() + h.Holder = hldr.Holder + h.Cluster = test.NewCluster(1) + w := httptest.NewRecorder() + h.ServeHTTP(w, test.MustNewHTTPRequest("DELETE", "/index/i0/frame/f0/view/v0", strings.NewReader(""))) + if w.Code != http.StatusOK { + t.Fatalf("unexpected status code: %d", w.Code) + } else if body := w.Body.String(); body != `{}`+"\n" { + t.Fatalf("unexpected body: %s", body) + } else if f := hldr.Index("i0").Frame("f0").View("v0"); f != nil { + t.Fatal("expected nil view") + } +} From 53930a530fefa052867bba49daae32805abe1d84 Mon Sep 17 00:00:00 2001 From: Michael Baird Date: Fri, 18 Aug 2017 17:08:13 -0500 Subject: [PATCH 19/46] delete only time based views, and handle inverse views --- handler.go | 25 +++++++++++++++++++++++-- handler_test.go | 7 ++++--- 2 files changed, 27 insertions(+), 5 deletions(-) diff --git a/handler.go b/handler.go index afad04123..63d76d479 100644 --- a/handler.go +++ b/handler.go @@ -803,12 +803,33 @@ func (h *Handler) handleDeleteView(w http.ResponseWriter, r *http.Request) { return } - if err := f.DeleteView(viewName); err != nil { + // Check that the view name is an integer. + if _, err := strconv.Atoi(viewName); err != nil { + http.Error(w, "only time based views can be deleted", http.StatusBadRequest) + return + } + + // Check the frame for time quantum. + if f.TimeQuantum() == "" { + http.Error(w, "frame does not contain a Time Quantum", http.StatusBadRequest) + return + } + + // Delete the standard view. + if err := f.DeleteView(ViewStandard + "_" + viewName); err != nil { http.Error(w, err.Error(), http.StatusBadRequest) return } - // TODO: Send the delete frame message to all nodes. + // Delete the inverse view. + if f.InverseEnabled() { + if err := f.DeleteView(ViewInverse + "_" + viewName); err != nil { + http.Error(w, err.Error(), http.StatusBadRequest) + return + } + } + + // TODO: Send the delete view message to all nodes. // Encode response. if err := json.NewEncoder(w).Encode(deleteViewResponse{}); err != nil { diff --git a/handler_test.go b/handler_test.go index 2947fd1b8..c8e0d949c 100644 --- a/handler_test.go +++ b/handler_test.go @@ -1516,18 +1516,19 @@ func TestHandler_GetTimeStamp(t *testing.T) { func TestHandler_DeleteView(t *testing.T) { hldr := test.MustOpenHolder() defer hldr.Close() - hldr.MustCreateFragmentIfNotExists("i0", "f0", "v0", 1).MustSetBits(30, (1*SliceWidth)+1) + hldr.MustCreateFragmentIfNotExists("i0", "f0", pilosa.ViewStandard+"_2017", 1).MustSetBits(30, (1*SliceWidth)+1) + hldr.Index("i0").Frame("f0").SetTimeQuantum("YMD") h := test.NewHandler() h.Holder = hldr.Holder h.Cluster = test.NewCluster(1) w := httptest.NewRecorder() - h.ServeHTTP(w, test.MustNewHTTPRequest("DELETE", "/index/i0/frame/f0/view/v0", strings.NewReader(""))) + h.ServeHTTP(w, test.MustNewHTTPRequest("DELETE", "/index/i0/frame/f0/view/2017", strings.NewReader(""))) if w.Code != http.StatusOK { t.Fatalf("unexpected status code: %d", w.Code) } else if body := w.Body.String(); body != `{}`+"\n" { t.Fatalf("unexpected body: %s", body) - } else if f := hldr.Index("i0").Frame("f0").View("v0"); f != nil { + } else if f := hldr.Index("i0").Frame("f0").View(pilosa.ViewStandard + "_2017"); f != nil { t.Fatal("expected nil view") } } From ff3190e0d354d906b2220fb0351126fc6edc2067 Mon Sep 17 00:00:00 2001 From: Michael Baird Date: Tue, 22 Aug 2017 11:39:00 -0500 Subject: [PATCH 20/46] Broadcast and handle the DeleteViewMessage --- broadcast.go | 5 + frame.go | 19 ++- frame_test.go | 8 +- handler.go | 27 ++-- internal/private.pb.go | 320 +++++++++++++++++++++++++++++++++-------- internal/private.proto | 6 + server.go | 9 ++ 7 files changed, 317 insertions(+), 77 deletions(-) diff --git a/broadcast.go b/broadcast.go index 866b03d02..0785a21cc 100644 --- a/broadcast.go +++ b/broadcast.go @@ -115,6 +115,7 @@ const ( MessageTypeDeleteFrame = 5 MessageTypeCreateInputDefinition = 6 MessageTypeDeleteInputDefinition = 7 + MessageTypeDeleteView = 8 ) // MarshalMessage encodes the protobuf message into a byte slice. @@ -135,6 +136,8 @@ func MarshalMessage(m proto.Message) ([]byte, error) { typ = MessageTypeCreateInputDefinition case *internal.DeleteInputDefinitionMessage: typ = MessageTypeDeleteInputDefinition + case *internal.DeleteViewMessage: + typ = MessageTypeDeleteView default: return nil, fmt.Errorf("message type not implemented for marshalling: %s", reflect.TypeOf(obj)) } @@ -165,6 +168,8 @@ func UnmarshalMessage(buf []byte) (proto.Message, error) { m = &internal.CreateInputDefinitionMessage{} case MessageTypeDeleteInputDefinition: m = &internal.DeleteInputDefinitionMessage{} + case MessageTypeDeleteView: + m = &internal.DeleteViewMessage{} default: return nil, fmt.Errorf("invalid message type: %d", typ) } diff --git a/frame.go b/frame.go index 675736d36..0b393a5c7 100644 --- a/frame.go +++ b/frame.go @@ -513,8 +513,25 @@ func (f *Frame) newView(path, name string) *View { return view } -// DeleteView removes a view from the frame. +// DeleteView removes the standard and inverse view from the frame. func (f *Frame) DeleteView(name string) error { + // Delete the standard view. + if err := f.deleteView(ViewStandard + "_" + name); err != nil { + return err + } + + // Delete the inverse view. + if f.InverseEnabled() { + if err := f.deleteView(ViewInverse + "_" + name); err != nil { + return err + } + } + + return nil +} + +// deleteView removes view from the frame. +func (f *Frame) deleteView(name string) error { view := f.views[name] if view == nil { return ErrInvalidView diff --git a/frame_test.go b/frame_test.go index b560d985e..8a8db3f27 100644 --- a/frame_test.go +++ b/frame_test.go @@ -313,8 +313,10 @@ func TestFrame_DeleteView(t *testing.T) { f := test.MustOpenFrame() defer f.Close() + viewName := pilosa.ViewStandard + "_v" + // Create view. - view, err := f.CreateViewIfNotExists("v") + view, err := f.CreateViewIfNotExists(viewName) if err != nil { t.Fatal(err) } else if view == nil { @@ -326,12 +328,12 @@ func TestFrame_DeleteView(t *testing.T) { t.Fatal(err) } - if f.View("v") != nil { + if f.View(viewName) != nil { t.Fatal("view still exists in frame") } // // Retrieve existing view. - view2, err := f.CreateViewIfNotExists("v") + view2, err := f.CreateViewIfNotExists(viewName) if err != nil { t.Fatal(err) } else if view == view2 { diff --git a/handler.go b/handler.go index 63d76d479..38254e1ae 100644 --- a/handler.go +++ b/handler.go @@ -803,34 +803,29 @@ func (h *Handler) handleDeleteView(w http.ResponseWriter, r *http.Request) { return } - // Check that the view name is an integer. - if _, err := strconv.Atoi(viewName); err != nil { - http.Error(w, "only time based views can be deleted", http.StatusBadRequest) - return - } - // Check the frame for time quantum. if f.TimeQuantum() == "" { http.Error(w, "frame does not contain a Time Quantum", http.StatusBadRequest) return } - // Delete the standard view. - if err := f.DeleteView(ViewStandard + "_" + viewName); err != nil { + // Delete the view. + if err := f.DeleteView(viewName); err != nil { http.Error(w, err.Error(), http.StatusBadRequest) return } - // Delete the inverse view. - if f.InverseEnabled() { - if err := f.DeleteView(ViewInverse + "_" + viewName); err != nil { - http.Error(w, err.Error(), http.StatusBadRequest) - return - } + // Send the delete view message to all nodes. + err := h.Broadcaster.SendSync( + &internal.DeleteViewMessage{ + Index: indexName, + Frame: frameName, + View: viewName, + }) + if err != nil { + h.logger().Printf("problem sending DeleteView message: %s", err) } - // TODO: Send the delete view message to all nodes. - // Encode response. if err := json.NewEncoder(w).Encode(deleteViewResponse{}); err != nil { h.logger().Printf("response encoding error: %s", err) diff --git a/internal/private.pb.go b/internal/private.pb.go index abde718e9..0a9956279 100644 --- a/internal/private.pb.go +++ b/internal/private.pb.go @@ -32,6 +32,7 @@ ClusterStatus FrameSchema Field + DeleteViewMessage */ package internal @@ -405,6 +406,17 @@ func (m *Field) String() string { return proto.CompactTextString(m) } func (*Field) ProtoMessage() {} func (*Field) Descriptor() ([]byte, []int) { return fileDescriptorPrivate, []int{22} } +type DeleteViewMessage struct { + Index string `protobuf:"bytes,1,opt,name=Index,proto3" json:"Index,omitempty"` + Frame string `protobuf:"bytes,2,opt,name=Frame,proto3" json:"Frame,omitempty"` + View string `protobuf:"bytes,3,opt,name=View,proto3" json:"View,omitempty"` +} + +func (m *DeleteViewMessage) Reset() { *m = DeleteViewMessage{} } +func (m *DeleteViewMessage) String() string { return proto.CompactTextString(m) } +func (*DeleteViewMessage) ProtoMessage() {} +func (*DeleteViewMessage) Descriptor() ([]byte, []int) { return fileDescriptorPrivate, []int{23} } + func init() { proto.RegisterType((*IndexMeta)(nil), "internal.IndexMeta") proto.RegisterType((*FrameMeta)(nil), "internal.FrameMeta") @@ -429,6 +441,7 @@ func init() { proto.RegisterType((*ClusterStatus)(nil), "internal.ClusterStatus") proto.RegisterType((*FrameSchema)(nil), "internal.FrameSchema") proto.RegisterType((*Field)(nil), "internal.Field") + proto.RegisterType((*DeleteViewMessage)(nil), "internal.DeleteViewMessage") } func (m *IndexMeta) Marshal() (dAtA []byte, err error) { size := m.Size() @@ -1344,6 +1357,42 @@ func (m *Field) MarshalTo(dAtA []byte) (int, error) { return i, nil } +func (m *DeleteViewMessage) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalTo(dAtA) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *DeleteViewMessage) MarshalTo(dAtA []byte) (int, error) { + var i int + _ = i + var l int + _ = l + if len(m.Index) > 0 { + dAtA[i] = 0xa + i++ + i = encodeVarintPrivate(dAtA, i, uint64(len(m.Index))) + i += copy(dAtA[i:], m.Index) + } + if len(m.Frame) > 0 { + dAtA[i] = 0x12 + i++ + i = encodeVarintPrivate(dAtA, i, uint64(len(m.Frame))) + i += copy(dAtA[i:], m.Frame) + } + if len(m.View) > 0 { + dAtA[i] = 0x1a + i++ + i = encodeVarintPrivate(dAtA, i, uint64(len(m.View))) + i += copy(dAtA[i:], m.View) + } + return i, nil +} + func encodeFixed64Private(dAtA []byte, offset int, v uint64) int { dAtA[offset] = uint8(v) dAtA[offset+1] = uint8(v >> 8) @@ -1773,6 +1822,24 @@ func (m *Field) Size() (n int) { return n } +func (m *DeleteViewMessage) Size() (n int) { + var l int + _ = l + l = len(m.Index) + if l > 0 { + n += 1 + l + sovPrivate(uint64(l)) + } + l = len(m.Frame) + if l > 0 { + n += 1 + l + sovPrivate(uint64(l)) + } + l = len(m.View) + if l > 0 { + n += 1 + l + sovPrivate(uint64(l)) + } + return n +} + func sovPrivate(x uint64) (n int) { for { n++ @@ -4882,6 +4949,143 @@ func (m *Field) Unmarshal(dAtA []byte) error { } return nil } +func (m *DeleteViewMessage) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowPrivate + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: DeleteViewMessage: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: DeleteViewMessage: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Index", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowPrivate + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthPrivate + } + postIndex := iNdEx + intStringLen + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Index = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Frame", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowPrivate + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthPrivate + } + postIndex := iNdEx + intStringLen + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Frame = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field View", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowPrivate + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthPrivate + } + postIndex := iNdEx + intStringLen + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.View = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipPrivate(dAtA[iNdEx:]) + if err != nil { + return err + } + if skippy < 0 { + return ErrInvalidLengthPrivate + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} func skipPrivate(dAtA []byte) (n int, err error) { l := len(dAtA) iNdEx := 0 @@ -4990,62 +5194,64 @@ var ( func init() { proto.RegisterFile("private.proto", fileDescriptorPrivate) } var fileDescriptorPrivate = []byte{ - // 912 bytes of a gzipped FileDescriptorProto + // 929 bytes of a gzipped FileDescriptorProto 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x9c, 0x56, 0xc1, 0x6e, 0x23, 0x45, - 0x10, 0x65, 0xec, 0xb1, 0xb1, 0x2b, 0x24, 0xf1, 0x36, 0x61, 0xe5, 0x8d, 0x22, 0x13, 0xf5, 0x81, - 0x0d, 0x91, 0xc8, 0x61, 0x91, 0x56, 0xc0, 0x72, 0x80, 0x8d, 0xb3, 0x8a, 0x05, 0x5e, 0xa0, 0xbd, - 0x5a, 0x6e, 0x48, 0x1d, 0xa7, 0xd8, 0x1d, 0x65, 0x3c, 0x63, 0xa6, 0xdb, 0x49, 0xcc, 0x81, 0x23, - 0xdf, 0x80, 0xc4, 0x91, 0x9f, 0xe1, 0x08, 0x7f, 0x80, 0xc2, 0x85, 0x3f, 0xe0, 0x8a, 0xba, 0xba, - 0x7b, 0x66, 0x3c, 0x8e, 0x13, 0x85, 0x5b, 0xd7, 0xeb, 0xd7, 0x55, 0xaf, 0x6a, 0xaa, 0xca, 0x86, - 0xf5, 0x69, 0x16, 0x9d, 0x4b, 0x8d, 0x07, 0xd3, 0x2c, 0xd5, 0x29, 0x6b, 0x45, 0x89, 0xc6, 0x2c, - 0x91, 0x31, 0xff, 0x0a, 0xda, 0x83, 0xe4, 0x14, 0x2f, 0x87, 0xa8, 0x25, 0xdb, 0x85, 0xb5, 0xc3, - 0x34, 0x9e, 0x4d, 0x92, 0x2f, 0xe5, 0x09, 0xc6, 0xdd, 0x60, 0x37, 0xd8, 0x6b, 0x8b, 0x32, 0x64, - 0x18, 0x2f, 0xa2, 0x09, 0x7e, 0x33, 0x93, 0x89, 0x9e, 0x4d, 0xba, 0x35, 0xcb, 0x28, 0x41, 0xfc, - 0xcf, 0x00, 0xda, 0xcf, 0x32, 0x39, 0x41, 0xf2, 0xb8, 0x0d, 0x2d, 0x91, 0x5e, 0x94, 0xdd, 0xe5, - 0x36, 0x7b, 0x0f, 0x36, 0x06, 0xc9, 0x39, 0x66, 0x0a, 0x8f, 0x12, 0x79, 0x12, 0xe3, 0x29, 0xb9, - 0x6b, 0x89, 0x0a, 0xca, 0x76, 0xa0, 0x7d, 0x28, 0xc7, 0xaf, 0xf1, 0xc5, 0x7c, 0x8a, 0xdd, 0x3a, - 0x39, 0x29, 0x80, 0xfc, 0x76, 0x14, 0xfd, 0x88, 0xdd, 0x70, 0x37, 0xd8, 0x5b, 0x17, 0x05, 0x50, - 0xd5, 0xdb, 0x58, 0xd2, 0xcb, 0x38, 0xbc, 0x25, 0x64, 0xf2, 0x2a, 0xd7, 0xd0, 0x24, 0x0d, 0x0b, - 0x18, 0xe7, 0xb0, 0x31, 0x98, 0x4c, 0xd3, 0x4c, 0x0b, 0x54, 0xd3, 0x34, 0x51, 0xc8, 0x3a, 0x50, - 0x3f, 0xca, 0x32, 0x97, 0x92, 0x39, 0xf2, 0x9f, 0xa0, 0xf3, 0x34, 0x4e, 0xc7, 0x67, 0x7d, 0xa9, - 0xa5, 0xc0, 0x1f, 0x66, 0xa8, 0x34, 0xdb, 0x82, 0x06, 0x15, 0xd7, 0xf1, 0xac, 0x61, 0x50, 0x2a, - 0x90, 0xab, 0x9e, 0x35, 0x0c, 0x4a, 0xef, 0x29, 0xc3, 0x50, 0x58, 0xc3, 0xa0, 0xa3, 0x38, 0x1a, - 0xdb, 0xcc, 0x42, 0x61, 0x0d, 0xc6, 0x20, 0x7c, 0x19, 0xe1, 0x85, 0x4b, 0x87, 0xce, 0x7c, 0x00, - 0xf7, 0x4a, 0xf1, 0x9d, 0xcc, 0xfb, 0xd0, 0x14, 0xe9, 0xc5, 0xa0, 0xaf, 0xba, 0xc1, 0x6e, 0x7d, - 0x2f, 0x14, 0xce, 0xa2, 0xa2, 0xd1, 0x57, 0x35, 0x57, 0x35, 0xba, 0x2a, 0x00, 0xfe, 0x00, 0x1a, - 0x54, 0x41, 0x93, 0x65, 0xf1, 0xd6, 0x1c, 0xf9, 0xaf, 0x01, 0xdc, 0x1b, 0xca, 0x4b, 0x92, 0xa1, - 0xf2, 0x30, 0xc7, 0xd0, 0xce, 0x41, 0x62, 0xaf, 0x3d, 0xda, 0x3f, 0xf0, 0x2d, 0x76, 0xb0, 0xc4, - 0x2f, 0x90, 0xa3, 0x44, 0x67, 0x73, 0x51, 0x3c, 0xde, 0xfe, 0x14, 0x36, 0x16, 0x2f, 0x8d, 0x86, - 0x33, 0x9c, 0xfb, 0x4a, 0x9f, 0xe1, 0xdc, 0xd4, 0xe4, 0x5c, 0xc6, 0x33, 0x5b, 0xbf, 0x50, 0x58, - 0xe3, 0x93, 0xda, 0x47, 0x01, 0xff, 0x0e, 0xd8, 0x61, 0x86, 0x52, 0x23, 0x39, 0x18, 0xa2, 0x52, - 0xf2, 0x15, 0xae, 0xfe, 0x0a, 0xb6, 0xb2, 0xb5, 0x72, 0x65, 0x77, 0xa0, 0x3d, 0x50, 0xae, 0xff, - 0xe8, 0x4b, 0xb4, 0x44, 0x01, 0xf0, 0x7d, 0x60, 0x7d, 0x8c, 0x51, 0xa3, 0x1b, 0x99, 0x1b, 0xfc, - 0xf3, 0x91, 0xd7, 0x72, 0x3b, 0x97, 0x3d, 0x84, 0xd0, 0x4c, 0x0b, 0x49, 0x59, 0x7b, 0xf4, 0x76, - 0x51, 0xba, 0x7c, 0x34, 0x05, 0x11, 0x78, 0xe4, 0x9d, 0xba, 0x09, 0xbb, 0x25, 0xc1, 0x6b, 0xda, - 0xcc, 0x87, 0xaa, 0x57, 0x43, 0xe5, 0x33, 0xeb, 0x42, 0x7d, 0xe6, 0x73, 0xfd, 0xbf, 0xa1, 0x78, - 0xdf, 0xa1, 0xa6, 0x5d, 0x9f, 0x9b, 0x5b, 0xfb, 0x86, 0xce, 0xab, 0x53, 0xae, 0xea, 0xf8, 0x27, - 0x70, 0x21, 0xef, 0xe6, 0xa6, 0x52, 0x39, 0xb3, 0x88, 0x7c, 0x63, 0xb9, 0x09, 0xcb, 0x6d, 0xf6, - 0x10, 0x9a, 0x14, 0x55, 0x75, 0x43, 0xea, 0xdd, 0xcd, 0x8a, 0x1a, 0xe1, 0xae, 0xcd, 0x38, 0xb9, - 0x26, 0x6f, 0xd8, 0x71, 0xb2, 0x16, 0x3b, 0x82, 0xce, 0x20, 0x99, 0xce, 0x74, 0x1f, 0xbf, 0x8f, - 0x92, 0x48, 0x47, 0x69, 0xa2, 0xba, 0x4d, 0x72, 0xf5, 0xa0, 0xac, 0x68, 0x81, 0x21, 0x96, 0x9e, - 0xf0, 0x9f, 0x03, 0xd8, 0xac, 0x80, 0x2b, 0x92, 0xf6, 0x7a, 0x6b, 0x37, 0xeb, 0x7d, 0x0c, 0xcd, - 0x67, 0x11, 0xc6, 0xa7, 0xaa, 0x5b, 0x27, 0x62, 0x6f, 0xa5, 0x1a, 0xa2, 0x09, 0xc7, 0xe6, 0xbf, - 0x05, 0xb0, 0x75, 0x1d, 0xe1, 0x5a, 0x35, 0x3d, 0x80, 0xaf, 0xb3, 0x68, 0x22, 0xb3, 0xf9, 0x17, - 0x38, 0x77, 0x2b, 0xbc, 0x84, 0xb0, 0x6f, 0xe1, 0x7e, 0xc5, 0xd7, 0xe7, 0x63, 0x5b, 0x22, 0x2b, - 0xea, 0xdd, 0x95, 0xa2, 0x2c, 0x4f, 0xac, 0x78, 0xce, 0xff, 0x0d, 0xe0, 0x9d, 0x6b, 0xaf, 0x8a, - 0x7e, 0x0c, 0xca, 0xad, 0xbf, 0x0f, 0x9d, 0x97, 0x66, 0x55, 0xf4, 0x51, 0xe9, 0x28, 0x91, 0x86, - 0xe9, 0x1a, 0x76, 0x09, 0x67, 0x03, 0x68, 0x11, 0x36, 0x94, 0x53, 0x27, 0xf3, 0x83, 0x5b, 0x64, - 0x1e, 0x78, 0xbe, 0xdd, 0x69, 0xf9, 0x73, 0x23, 0x86, 0xb6, 0xae, 0x5f, 0xe1, 0x64, 0x6c, 0x3f, - 0x81, 0xf5, 0x85, 0x07, 0x77, 0xda, 0x73, 0x29, 0xec, 0xf8, 0xdd, 0xb2, 0xa0, 0xe4, 0xe6, 0x29, - 0xfd, 0x18, 0xa0, 0xa0, 0xba, 0x05, 0x70, 0x43, 0x7f, 0x96, 0xc8, 0xfc, 0x18, 0x76, 0xfc, 0xe2, - 0xbb, 0x43, 0x40, 0xdf, 0x2d, 0xb5, 0xa2, 0x5b, 0xb8, 0x04, 0x78, 0x9e, 0x9e, 0xe2, 0x48, 0x4b, - 0x3d, 0x53, 0x86, 0x71, 0x9c, 0x2a, 0xed, 0xfb, 0xc9, 0x9c, 0x69, 0x31, 0x6b, 0xa9, 0xf3, 0x65, - 0x42, 0x06, 0x7b, 0x1f, 0xde, 0x24, 0xa7, 0xe8, 0xdb, 0x66, 0xb3, 0x32, 0xeb, 0xc2, 0xdf, 0xf3, - 0x27, 0xb0, 0x7e, 0x18, 0xcf, 0x94, 0xc6, 0xcc, 0x45, 0xd9, 0x87, 0x86, 0x89, 0xe9, 0x7f, 0x9a, - 0xb6, 0x8a, 0x97, 0x85, 0x14, 0x61, 0x29, 0xfc, 0x31, 0xac, 0x51, 0xb7, 0x8c, 0xc6, 0xaf, 0x71, - 0x22, 0x69, 0xd4, 0xec, 0x04, 0x05, 0x4b, 0xa3, 0xb6, 0x30, 0x32, 0x23, 0x68, 0xac, 0x1e, 0x11, - 0x06, 0x21, 0xfd, 0x79, 0x71, 0x85, 0xa0, 0xff, 0x2d, 0x1d, 0xa8, 0x0f, 0x23, 0xfb, 0x19, 0xea, - 0xc2, 0x1c, 0x09, 0x91, 0x97, 0xd4, 0x26, 0x06, 0x91, 0x97, 0x4f, 0x3b, 0xbf, 0x5f, 0xf5, 0x82, - 0x3f, 0xae, 0x7a, 0xc1, 0x5f, 0x57, 0xbd, 0xe0, 0x97, 0xbf, 0x7b, 0x6f, 0x9c, 0x34, 0xe9, 0xff, - 0xdb, 0x87, 0xff, 0x05, 0x00, 0x00, 0xff, 0xff, 0x29, 0x07, 0x36, 0x04, 0xd0, 0x09, 0x00, 0x00, + 0x10, 0x65, 0xec, 0xb1, 0xb1, 0x2b, 0x24, 0x71, 0x9a, 0xb0, 0xf2, 0x46, 0x91, 0x89, 0xfa, 0xc0, + 0x86, 0x48, 0xe4, 0xb0, 0x48, 0x2b, 0x60, 0x39, 0xc0, 0xc6, 0x59, 0xc5, 0x02, 0x2f, 0xd0, 0x5e, + 0x2d, 0x37, 0xa4, 0x8e, 0x53, 0xec, 0x8e, 0x32, 0x9e, 0x31, 0x33, 0x3d, 0x49, 0xcc, 0x81, 0x23, + 0xdf, 0x80, 0xc4, 0x91, 0x9f, 0xe1, 0x08, 0x7f, 0x80, 0xc2, 0x85, 0x3f, 0xe0, 0xba, 0xea, 0xea, + 0xee, 0x99, 0xf1, 0x38, 0x76, 0x94, 0xbd, 0x75, 0xbd, 0x7e, 0x5d, 0xf5, 0xba, 0xa6, 0xaa, 0xa6, + 0x61, 0x7d, 0x9a, 0x04, 0x17, 0x52, 0xe1, 0xe1, 0x34, 0x89, 0x55, 0xcc, 0x5a, 0x41, 0xa4, 0x30, + 0x89, 0x64, 0xc8, 0xbf, 0x81, 0xf6, 0x20, 0x3a, 0xc3, 0xab, 0x21, 0x2a, 0xc9, 0xf6, 0x60, 0xed, + 0x28, 0x0e, 0xb3, 0x49, 0xf4, 0xb5, 0x3c, 0xc5, 0xb0, 0xeb, 0xed, 0x79, 0xfb, 0x6d, 0x51, 0x86, + 0x34, 0xe3, 0x79, 0x30, 0xc1, 0xef, 0x32, 0x19, 0xa9, 0x6c, 0xd2, 0xad, 0x19, 0x46, 0x09, 0xe2, + 0x7f, 0x7b, 0xd0, 0x7e, 0x9a, 0xc8, 0x09, 0x92, 0xc7, 0x1d, 0x68, 0x89, 0xf8, 0xb2, 0xec, 0x2e, + 0xb7, 0xd9, 0x07, 0xb0, 0x31, 0x88, 0x2e, 0x30, 0x49, 0xf1, 0x38, 0x92, 0xa7, 0x21, 0x9e, 0x91, + 0xbb, 0x96, 0xa8, 0xa0, 0x6c, 0x17, 0xda, 0x47, 0x72, 0xfc, 0x0a, 0x9f, 0xcf, 0xa6, 0xd8, 0xad, + 0x93, 0x93, 0x02, 0xc8, 0x77, 0x47, 0xc1, 0xcf, 0xd8, 0xf5, 0xf7, 0xbc, 0xfd, 0x75, 0x51, 0x00, + 0x55, 0xbd, 0x8d, 0x05, 0xbd, 0x8c, 0xc3, 0x3b, 0x42, 0x46, 0x2f, 0x73, 0x0d, 0x4d, 0xd2, 0x30, + 0x87, 0x71, 0x0e, 0x1b, 0x83, 0xc9, 0x34, 0x4e, 0x94, 0xc0, 0x74, 0x1a, 0x47, 0x29, 0xb2, 0x0e, + 0xd4, 0x8f, 0x93, 0xc4, 0x5e, 0x49, 0x2f, 0xf9, 0x2f, 0xd0, 0x79, 0x12, 0xc6, 0xe3, 0xf3, 0xbe, + 0x54, 0x52, 0xe0, 0x4f, 0x19, 0xa6, 0x8a, 0x6d, 0x43, 0x83, 0x92, 0x6b, 0x79, 0xc6, 0xd0, 0x28, + 0x25, 0xc8, 0x66, 0xcf, 0x18, 0x1a, 0xa5, 0xf3, 0x74, 0x43, 0x5f, 0x18, 0x43, 0xa3, 0xa3, 0x30, + 0x18, 0x9b, 0x9b, 0xf9, 0xc2, 0x18, 0x8c, 0x81, 0xff, 0x22, 0xc0, 0x4b, 0x7b, 0x1d, 0x5a, 0xf3, + 0x01, 0x6c, 0x95, 0xe2, 0x5b, 0x99, 0xf7, 0xa0, 0x29, 0xe2, 0xcb, 0x41, 0x3f, 0xed, 0x7a, 0x7b, + 0xf5, 0x7d, 0x5f, 0x58, 0x8b, 0x92, 0x46, 0x5f, 0x55, 0x6f, 0xd5, 0x68, 0xab, 0x00, 0xf8, 0x7d, + 0x68, 0x50, 0x06, 0xf5, 0x2d, 0x8b, 0xb3, 0x7a, 0xc9, 0x7f, 0xf7, 0x60, 0x6b, 0x28, 0xaf, 0x48, + 0x46, 0x9a, 0x87, 0x39, 0x81, 0x76, 0x0e, 0x12, 0x7b, 0xed, 0xe1, 0xc1, 0xa1, 0x2b, 0xb1, 0xc3, + 0x05, 0x7e, 0x81, 0x1c, 0x47, 0x2a, 0x99, 0x89, 0xe2, 0xf0, 0xce, 0xe7, 0xb0, 0x31, 0xbf, 0xa9, + 0x35, 0x9c, 0xe3, 0xcc, 0x65, 0xfa, 0x1c, 0x67, 0x3a, 0x27, 0x17, 0x32, 0xcc, 0x4c, 0xfe, 0x7c, + 0x61, 0x8c, 0xcf, 0x6a, 0x9f, 0x78, 0xfc, 0x07, 0x60, 0x47, 0x09, 0x4a, 0x85, 0xe4, 0x60, 0x88, + 0x69, 0x2a, 0x5f, 0xe2, 0xf2, 0xaf, 0x60, 0x32, 0x5b, 0x2b, 0x67, 0x76, 0x17, 0xda, 0x83, 0xd4, + 0xd6, 0x1f, 0x7d, 0x89, 0x96, 0x28, 0x00, 0x7e, 0x00, 0xac, 0x8f, 0x21, 0x2a, 0xb4, 0x2d, 0xb3, + 0xc2, 0x3f, 0x1f, 0x39, 0x2d, 0xb7, 0x73, 0xd9, 0x03, 0xf0, 0x75, 0xb7, 0x90, 0x94, 0xb5, 0x87, + 0xef, 0x16, 0xa9, 0xcb, 0x5b, 0x53, 0x10, 0x81, 0x07, 0xce, 0xa9, 0xed, 0xb0, 0x5b, 0x2e, 0x78, + 0x43, 0x99, 0xb9, 0x50, 0xf5, 0x6a, 0xa8, 0xbc, 0x67, 0x6d, 0xa8, 0x2f, 0xdc, 0x5d, 0xdf, 0x34, + 0x14, 0xef, 0x5b, 0x54, 0x97, 0xeb, 0x33, 0xbd, 0x6b, 0xce, 0xd0, 0x7a, 0xf9, 0x95, 0xab, 0x3a, + 0xfe, 0xf3, 0x6c, 0xc8, 0xbb, 0xb9, 0xa9, 0x64, 0x4e, 0x0f, 0x22, 0x57, 0x58, 0xb6, 0xc3, 0x72, + 0x9b, 0x3d, 0x80, 0x26, 0x45, 0x4d, 0xbb, 0x3e, 0xd5, 0xee, 0x66, 0x45, 0x8d, 0xb0, 0xdb, 0xba, + 0x9d, 0x6c, 0x91, 0x37, 0x4c, 0x3b, 0x19, 0x8b, 0x1d, 0x43, 0x67, 0x10, 0x4d, 0x33, 0xd5, 0xc7, + 0x1f, 0x83, 0x28, 0x50, 0x41, 0x1c, 0xa5, 0xdd, 0x26, 0xb9, 0xba, 0x5f, 0x56, 0x34, 0xc7, 0x10, + 0x0b, 0x47, 0xf8, 0xaf, 0x1e, 0x6c, 0x56, 0xc0, 0x25, 0x97, 0x76, 0x7a, 0x6b, 0xab, 0xf5, 0x3e, + 0x82, 0xe6, 0xd3, 0x00, 0xc3, 0xb3, 0xb4, 0x5b, 0x27, 0x62, 0x6f, 0xa9, 0x1a, 0xa2, 0x09, 0xcb, + 0xe6, 0x7f, 0x78, 0xb0, 0x7d, 0x13, 0xe1, 0x46, 0x35, 0x3d, 0x80, 0x6f, 0x93, 0x60, 0x22, 0x93, + 0xd9, 0x57, 0x38, 0xb3, 0x23, 0xbc, 0x84, 0xb0, 0xef, 0xe1, 0x5e, 0xc5, 0xd7, 0x97, 0x63, 0x93, + 0x22, 0x23, 0xea, 0xfd, 0xa5, 0xa2, 0x0c, 0x4f, 0x2c, 0x39, 0xce, 0xff, 0xf7, 0xe0, 0xbd, 0x1b, + 0xb7, 0x8a, 0x7a, 0xf4, 0xca, 0xa5, 0x7f, 0x00, 0x9d, 0x17, 0x7a, 0x54, 0xf4, 0x31, 0x55, 0x41, + 0x24, 0x35, 0xd3, 0x16, 0xec, 0x02, 0xce, 0x06, 0xd0, 0x22, 0x6c, 0x28, 0xa7, 0x56, 0xe6, 0x47, + 0xb7, 0xc8, 0x3c, 0x74, 0x7c, 0x33, 0xd3, 0xf2, 0xe3, 0x5a, 0x0c, 0x4d, 0x5d, 0x37, 0xc2, 0xc9, + 0xd8, 0x79, 0x0c, 0xeb, 0x73, 0x07, 0xee, 0x34, 0xe7, 0x62, 0xd8, 0x75, 0xb3, 0x65, 0x4e, 0xc9, + 0xea, 0x2e, 0xfd, 0x14, 0xa0, 0xa0, 0xda, 0x01, 0xb0, 0xa2, 0x3e, 0x4b, 0x64, 0x7e, 0x02, 0xbb, + 0x6e, 0xf0, 0xdd, 0x21, 0xa0, 0xab, 0x96, 0x5a, 0x51, 0x2d, 0x5c, 0x02, 0x3c, 0x8b, 0xcf, 0x70, + 0xa4, 0xa4, 0xca, 0x52, 0xcd, 0x38, 0x89, 0x53, 0xe5, 0xea, 0x49, 0xaf, 0x69, 0x30, 0x2b, 0xa9, + 0xf2, 0x61, 0x42, 0x06, 0xfb, 0x10, 0xde, 0x26, 0xa7, 0xe8, 0xca, 0x66, 0xb3, 0xd2, 0xeb, 0xc2, + 0xed, 0xf3, 0xc7, 0xb0, 0x7e, 0x14, 0x66, 0xa9, 0xc2, 0xc4, 0x46, 0x39, 0x80, 0x86, 0x8e, 0xe9, + 0x7e, 0x4d, 0xdb, 0xc5, 0xc9, 0x42, 0x8a, 0x30, 0x14, 0xfe, 0x08, 0xd6, 0xa8, 0x5a, 0x46, 0xe3, + 0x57, 0x38, 0x91, 0xd4, 0x6a, 0xa6, 0x83, 0xbc, 0x85, 0x56, 0x9b, 0x6b, 0x99, 0x11, 0x34, 0x96, + 0xb7, 0x08, 0x03, 0x9f, 0x1e, 0x2f, 0x36, 0x11, 0xf4, 0x6e, 0xe9, 0x40, 0x7d, 0x18, 0x98, 0xcf, + 0x50, 0x17, 0x7a, 0x49, 0x88, 0xbc, 0xa2, 0x32, 0xd1, 0x88, 0xd4, 0xff, 0x90, 0x2d, 0x93, 0x76, + 0xfd, 0x87, 0x7f, 0x93, 0x69, 0xef, 0x1e, 0x0a, 0xf5, 0xe2, 0xa1, 0xf0, 0xa4, 0xf3, 0xe7, 0x75, + 0xcf, 0xfb, 0xeb, 0xba, 0xe7, 0xfd, 0x73, 0xdd, 0xf3, 0x7e, 0xfb, 0xb7, 0xf7, 0xd6, 0x69, 0x93, + 0x1e, 0x85, 0x1f, 0xbf, 0x0e, 0x00, 0x00, 0xff, 0xff, 0x9f, 0xcc, 0xe2, 0x4c, 0x25, 0x0a, 0x00, + 0x00, } diff --git a/internal/private.proto b/internal/private.proto index 3173b12a2..68e9a5e5f 100644 --- a/internal/private.proto +++ b/internal/private.proto @@ -131,3 +131,9 @@ message Field { int64 Min = 3; int64 Max = 4; } + +message DeleteViewMessage { + string Index = 1; + string Frame = 2; + string View = 3; +} \ No newline at end of file diff --git a/server.go b/server.go index 97883fae4..8194278ba 100644 --- a/server.go +++ b/server.go @@ -339,6 +339,15 @@ func (s *Server) ReceiveMessage(pb proto.Message) error { if err != nil { return err } + case *internal.DeleteViewMessage: + f := s.Holder.Frame(obj.Index, obj.Frame) + if f == nil { + return fmt.Errorf("Local Frame not found: %s", obj.Frame) + } + err := f.DeleteView(obj.View) + if err != nil { + return err + } } return nil } From 2dfa689d71d2d06b7f81a6e96bf433fea2fe276b Mon Sep 17 00:00:00 2001 From: Michael Baird Date: Tue, 22 Aug 2017 14:52:07 -0500 Subject: [PATCH 21/46] Fragment.Close now returns an error. This required changing the Holder test utility Reopen, since in those cases we needed to close the file before making permission changes that Close() was failing silently on. --- fragment.go | 2 ++ frame.go | 10 +++++++--- holder.go | 4 +++- holder_test.go | 25 ++++++++++++++++++++++++- index.go | 4 +++- test/holder.go | 6 +++--- view.go | 4 +++- 7 files changed, 45 insertions(+), 10 deletions(-) diff --git a/fragment.go b/fragment.go index a517cbd57..bd3ebdad2 100644 --- a/fragment.go +++ b/fragment.go @@ -293,11 +293,13 @@ func (f *Fragment) close() error { // Flush cache if closing gracefully. if err := f.flushCache(); err != nil { f.logger().Printf("fragment: error flushing cache on close: err=%s, path=%s", err, f.path) + return err } // Close underlying storage. if err := f.closeStorage(); err != nil { f.logger().Printf("fragment: error closing storage: err=%s, path=%s", err, f.path) + return err } // Remove checksums. diff --git a/frame.go b/frame.go index 0b393a5c7..ad44cfb56 100644 --- a/frame.go +++ b/frame.go @@ -401,7 +401,9 @@ func (f *Frame) Close() error { // Close all views. for _, view := range f.views { - _ = view.Close() + if err := view.Close(); err != nil { + return err + } } f.views = make(map[string]*View) @@ -537,8 +539,10 @@ func (f *Frame) deleteView(name string) error { return ErrInvalidView } - // TODO capture errors lower down in this method - _ = view.Close() + // Close data files before deletion + if err := view.Close(); err != nil { + return err + } // Delete view directory. if err := os.RemoveAll(view.Path()); err != nil { diff --git a/holder.go b/holder.go index f7c363524..4270c2594 100644 --- a/holder.go +++ b/holder.go @@ -133,7 +133,9 @@ func (h *Holder) Close() error { h.wg.Wait() for _, index := range h.indexes { - index.Close() + if err := index.Close(); err != nil { + return err + } } return nil } diff --git a/holder_test.go b/holder_test.go index 108349bb2..b3947abb8 100644 --- a/holder_test.go +++ b/holder_test.go @@ -34,8 +34,9 @@ func TestHolder_Open(t *testing.T) { if err := os.Mkdir(h.IndexPath("!"), 0777); err != nil { t.Fatal(err) + } else if err := h.Holder.Close(); err != nil { + t.Fatal(err) } - if err := h.Reopen(); err != nil { t.Fatal(err) } else if logOutput := h.LogOutput.String(); !strings.Contains(logOutput, `ERROR opening index: !`) { @@ -49,6 +50,8 @@ func TestHolder_Open(t *testing.T) { if _, err := h.CreateIndex("test", pilosa.IndexOptions{}); err != nil { t.Fatal(err) + } else if err := h.Holder.Close(); err != nil { + t.Fatal(err) } else if err := os.Chmod(h.IndexPath("test"), 0000); err != nil { t.Fatal(err) } @@ -64,6 +67,8 @@ func TestHolder_Open(t *testing.T) { if _, err := h.CreateIndex("test", pilosa.IndexOptions{}); err != nil { t.Fatal(err) + } else if err := h.Holder.Close(); err != nil { + t.Fatal(err) } else if err := os.Truncate(filepath.Join(h.IndexPath("test"), ".meta"), 2); err != nil { t.Fatal(err) } @@ -78,6 +83,8 @@ func TestHolder_Open(t *testing.T) { if _, err := h.CreateIndex("test", pilosa.IndexOptions{}); err != nil { t.Fatal(err) + } else if err := h.Holder.Close(); err != nil { + t.Fatal(err) } else if err := os.Truncate(filepath.Join(h.IndexPath("test"), ".data"), 2); err != nil { t.Fatal(err) } @@ -95,6 +102,8 @@ func TestHolder_Open(t *testing.T) { t.Fatal(err) } else if _, err := idx.CreateFrame("bar", pilosa.FrameOptions{}); err != nil { t.Fatal(err) + } else if err := h.Holder.Close(); err != nil { + t.Fatal(err) } else if err := os.Chmod(filepath.Join(h.Path, "foo", "bar"), 0000); err != nil { t.Fatal(err) } @@ -112,6 +121,8 @@ func TestHolder_Open(t *testing.T) { t.Fatal(err) } else if _, err := idx.CreateFrame("bar", pilosa.FrameOptions{}); err != nil { t.Fatal(err) + } else if err := h.Holder.Close(); err != nil { + t.Fatal(err) } else if err := os.Truncate(filepath.Join(h.Path, "foo", "bar", ".meta"), 2); err != nil { t.Fatal(err) } @@ -128,6 +139,8 @@ func TestHolder_Open(t *testing.T) { t.Fatal(err) } else if _, err := idx.CreateFrame("bar", pilosa.FrameOptions{}); err != nil { t.Fatal(err) + } else if err := h.Holder.Close(); err != nil { + t.Fatal(err) } else if err := os.Truncate(filepath.Join(h.Path, "foo", "bar", ".data"), 2); err != nil { t.Fatal(err) } @@ -147,6 +160,8 @@ func TestHolder_Open(t *testing.T) { t.Fatal(err) } else if _, err := frame.CreateViewIfNotExists(pilosa.ViewStandard); err != nil { t.Fatal(err) + } else if err := h.Holder.Close(); err != nil { + t.Fatal(err) } else if err := os.Chmod(filepath.Join(h.Path, "foo", "bar", "views", "standard"), 0000); err != nil { t.Fatal(err) } @@ -166,6 +181,8 @@ func TestHolder_Open(t *testing.T) { t.Fatal(err) } else if _, err := frame.CreateViewIfNotExists(pilosa.ViewStandard); err != nil { t.Fatal(err) + } else if err := h.Holder.Close(); err != nil { + t.Fatal(err) } else if err := os.Chmod(filepath.Join(h.Path, "foo", "bar", "views", "standard", "fragments"), 0000); err != nil { t.Fatal(err) } @@ -188,6 +205,8 @@ func TestHolder_Open(t *testing.T) { t.Fatal(err) } else if _, err := view.SetBit(0, 0); err != nil { t.Fatal(err) + } else if err := h.Holder.Close(); err != nil { + t.Fatal(err) } else if err := os.Chmod(filepath.Join(h.Path, "foo", "bar", "views", "standard", "fragments", "0"), 0000); err != nil { t.Fatal(err) } @@ -209,6 +228,8 @@ func TestHolder_Open(t *testing.T) { t.Fatal(err) } else if _, err := view.SetBit(0, 0); err != nil { t.Fatal(err) + } else if err := h.Holder.Close(); err != nil { + t.Fatal(err) } else if err := os.Truncate(filepath.Join(h.Path, "foo", "bar", "views", "standard", "fragments", "0"), 2); err != nil { t.Fatal(err) } @@ -232,6 +253,8 @@ func TestHolder_Open(t *testing.T) { t.Fatal(err) } else if err := view.Fragment(0).FlushCache(); err != nil { t.Fatal(err) + } else if err := h.Holder.Close(); err != nil { + t.Fatal(err) } else if err := os.Chmod(filepath.Join(h.Path, "foo", "bar", "views", "standard", "fragments", "0.cache"), 0000); err != nil { t.Fatal(err) } diff --git a/index.go b/index.go index 8f1846c5f..345215690 100644 --- a/index.go +++ b/index.go @@ -248,7 +248,9 @@ func (i *Index) Close() error { // Close all frames. for _, f := range i.frames { - f.Close() + if err := f.Close(); err != nil { + return err + } } i.frames = make(map[string]*Frame) diff --git a/test/holder.go b/test/holder.go index 6bb346ec1..099b9cb7b 100644 --- a/test/holder.go +++ b/test/holder.go @@ -45,9 +45,9 @@ func (h *Holder) Close() error { // Reopen closes the holder and instantiates and opens a new holder. func (h *Holder) Reopen() error { - if err := h.Holder.Close(); err != nil { - return err - } + // if err := h.Holder.Close(); err != nil { + // return err + // } path, logOutput := h.Path, h.Holder.LogOutput h.Holder = pilosa.NewHolder() diff --git a/view.go b/view.go index 31a7a53c0..9fa5fcc8d 100644 --- a/view.go +++ b/view.go @@ -162,7 +162,9 @@ func (v *View) Close() error { // Close all fragments. for _, frag := range v.fragments { - _ = frag.Close() + if err := frag.Close(); err != nil { + return err + } } v.fragments = make(map[uint64]*Fragment) From f5832cda02053eca9a911b96b44198e433202381 Mon Sep 17 00:00:00 2001 From: Michael Baird Date: Tue, 22 Aug 2017 15:13:21 -0500 Subject: [PATCH 22/46] Clarify that Holder Reopen requires Close to be run beforehand. --- handler.go | 2 ++ test/holder.go | 5 +---- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/handler.go b/handler.go index 38254e1ae..cfc96c397 100644 --- a/handler.go +++ b/handler.go @@ -27,6 +27,7 @@ import ( "io/ioutil" "log" "net/http" + // Imported for its side-effect of registering pprof endpoints with the server. _ "net/http/pprof" "os" "runtime/debug" @@ -43,6 +44,7 @@ import ( "unicode" + // Allow building Pilosa without the web UI. _ "github.com/pilosa/pilosa/statik" "github.com/rakyll/statik/fs" ) diff --git a/test/holder.go b/test/holder.go index 099b9cb7b..9523db407 100644 --- a/test/holder.go +++ b/test/holder.go @@ -44,11 +44,8 @@ func (h *Holder) Close() error { } // Reopen closes the holder and instantiates and opens a new holder. +// note that the hold most be Closed first func (h *Holder) Reopen() error { - // if err := h.Holder.Close(); err != nil { - // return err - // } - path, logOutput := h.Path, h.Holder.LogOutput h.Holder = pilosa.NewHolder() h.Holder.Path = path From 343ff04399ae50bd7b5d658397318de3d23d3433 Mon Sep 17 00:00:00 2001 From: Todd Gruben Date: Tue, 22 Aug 2017 16:09:03 -0500 Subject: [PATCH 23/46] fixes #780;adjusted test to avoid array conversion --- roaring/roaring.go | 34 ++++++++++++++++++++++++-------- roaring/roaring_internal_test.go | 4 ++-- 2 files changed, 28 insertions(+), 10 deletions(-) diff --git a/roaring/roaring.go b/roaring/roaring.go index 16e6159d7..266a9bfe3 100644 --- a/roaring/roaring.go +++ b/roaring/roaring.go @@ -3218,9 +3218,14 @@ func xorCompare(x *xorstm) (r1 interval16, has_data bool) { r1 = interval16{start: x.va.start, last: x.vb.start - 1} has_data = true } - x.va.start = x.vb.last + 1 - if x.va.start > x.va.last { + if x.vb.last == 65535 { x.va_valid = false + + } else { + x.va.start = x.vb.last + 1 + if x.va.start > x.va.last { + x.va_valid = false + } } } else if x.vb.start <= x.va.start && x.vb.last >= x.va.last { //va inside @@ -3230,26 +3235,39 @@ func xorCompare(x *xorstm) (r1 interval16, has_data bool) { has_data = true } - x.vb.start = x.va.last + 1 - if x.vb.start > x.vb.last { + if x.va.last == 65535 { x.vb_valid = false + } else { + x.vb.start = x.va.last + 1 + if x.vb.start > x.vb.last { + x.vb_valid = false + } } } else if x.va.start < x.vb.start && x.va.last <= x.vb.last { //va first overlap x.va_valid = false r1 = interval16{start: x.va.start, last: x.vb.start - 1} has_data = true - x.vb.start = x.va.last + 1 - if x.vb.start > x.vb.last { + if x.va.last == 65535 { x.vb_valid = false + } else { + x.vb.start = x.va.last + 1 + if x.vb.start > x.vb.last { + x.vb_valid = false + } } } else if x.vb.start < x.va.start && x.vb.last <= x.va.last { //vb first overlap x.vb_valid = false r1 = interval16{start: x.vb.start, last: x.va.start - 1} has_data = true - x.va.start = x.vb.last + 1 - if x.va.start > x.va.last { + + if x.vb.last == 65535 { x.va_valid = false + } else { + x.va.start = x.vb.last + 1 + if x.va.start > x.va.last { + x.va_valid = false + } } } return diff --git a/roaring/roaring_internal_test.go b/roaring/roaring_internal_test.go index 59db885e1..74dcc8992 100644 --- a/roaring/roaring_internal_test.go +++ b/roaring/roaring_internal_test.go @@ -1834,8 +1834,8 @@ func TestXorRunRun(t *testing.T) { }, { aruns: []interval16{{start: 65530, last: 65535}}, - bruns: []interval16{{start: 65531, last: 65535}}, - exp: []interval16{{start: 65530, last: 65530}}, + bruns: []interval16{{start: 65532, last: 65535}}, + exp: []interval16{{start: 65530, last: 65531}}, }, } for i, test := range tests { From e89ffcccbdfb61fcabd9b7bcfb8d0d925ccb77ea Mon Sep 17 00:00:00 2001 From: Yuce Tekol Date: Wed, 23 Aug 2017 04:57:22 +0300 Subject: [PATCH 24/46] Renamed inhibit* to exclude* --- docs/api-reference.md | 2 +- executor.go | 8 ++-- executor_test.go | 4 +- handler.go | 18 ++++----- internal/public.pb.go | 90 +++++++++++++++++++++---------------------- internal/public.proto | 4 +- 6 files changed, 63 insertions(+), 63 deletions(-) diff --git a/docs/api-reference.md b/docs/api-reference.md index 0c3990bec..bf599fafe 100644 --- a/docs/api-reference.md +++ b/docs/api-reference.md @@ -115,7 +115,7 @@ Response: } ``` -By default, all bits and attributes (*for `Bitmap` queries only*) are returned. In order to suppress returning bits, set `inhibitBits` query argument to `true`; to suppress returning attributes, set `inhibitAttrs` query argument to `true`. +By default, all bits and attributes (*for `Bitmap` queries only*) are returned. In order to suppress returning bits, set `excludeBits` query argument to `true`; to suppress returning attributes, set `excludeAttrs` query argument to `true`. ### Change index time quantum diff --git a/executor.go b/executor.go index a741dbc6e..7fa0e90af 100644 --- a/executor.go +++ b/executor.go @@ -229,7 +229,7 @@ func (e *Executor) executeBitmapCall(ctx context.Context, index string, c *pql.C // If the row label is used then return bitmap attributes. bm, _ := other.(*Bitmap) if c.Name == "Bitmap" { - if opt.InhibitAttrs { + if opt.ExcludeAttrs { bm.Attrs = map[string]interface{}{} } else { idx := e.Holder.Index(index) @@ -262,7 +262,7 @@ func (e *Executor) executeBitmapCall(ctx context.Context, index string, c *pql.C } } - if opt.InhibitBits { + if opt.ExcludeBits { bm.segments = []BitmapSegment{} } @@ -1379,8 +1379,8 @@ type mapResponse struct { // ExecOptions represents an execution context for a single Execute() call. type ExecOptions struct { Remote bool - InhibitAttrs bool - InhibitBits bool + ExcludeAttrs bool + ExcludeBits bool } // decodeError returns an error representation of s if s is non-blank. diff --git a/executor_test.go b/executor_test.go index 0c535799c..61bfbd053 100644 --- a/executor_test.go +++ b/executor_test.go @@ -61,7 +61,7 @@ func TestExecutor_Execute_Bitmap(t *testing.T) { } // Inhibit bits. - if res, err := e.Execute(context.Background(), "i", test.MustParse(`Bitmap(rowID=10, frame=f)`), nil, &pilosa.ExecOptions{InhibitBits: true}); err != nil { + if res, err := e.Execute(context.Background(), "i", test.MustParse(`Bitmap(rowID=10, frame=f)`), nil, &pilosa.ExecOptions{ExcludeBits: true}); err != nil { t.Fatal(err) } else if bits := res[0].(*pilosa.Bitmap).Bits(); !reflect.DeepEqual(bits, []uint64{}) { t.Fatalf("unexpected bits: %+v", bits) @@ -70,7 +70,7 @@ func TestExecutor_Execute_Bitmap(t *testing.T) { } // Inhibit attributes. - if res, err := e.Execute(context.Background(), "i", test.MustParse(`Bitmap(rowID=10, frame=f)`), nil, &pilosa.ExecOptions{InhibitAttrs: true}); err != nil { + if res, err := e.Execute(context.Background(), "i", test.MustParse(`Bitmap(rowID=10, frame=f)`), nil, &pilosa.ExecOptions{ExcludeAttrs: true}); err != nil { t.Fatal(err) } else if bits := res[0].(*pilosa.Bitmap).Bits(); !reflect.DeepEqual(bits, []uint64{3, SliceWidth + 1}) { t.Fatalf("unexpected bits: %+v", bits) diff --git a/handler.go b/handler.go index 5d5513aff..027d5f5a4 100644 --- a/handler.go +++ b/handler.go @@ -242,8 +242,8 @@ func (h *Handler) handlePostQuery(w http.ResponseWriter, r *http.Request) { // Build execution options. opt := &ExecOptions{ Remote: req.Remote, - InhibitAttrs: req.InhibitAttrs, - InhibitBits: req.InhibitBits, + ExcludeAttrs: req.ExcludeAttrs, + ExcludeBits: req.ExcludeBits, } // Parse query string. @@ -259,7 +259,7 @@ func (h *Handler) handlePostQuery(w http.ResponseWriter, r *http.Request) { resp := &QueryResponse{Results: results, Err: err} // Fill column attributes if requested. - if req.ColumnAttrs && !req.InhibitBits { + if req.ColumnAttrs && !req.ExcludeBits { // Consolidate all column ids across all calls. var columnIDs []uint64 for _, result := range results { @@ -930,8 +930,8 @@ func (h *Handler) readURLQueryRequest(r *http.Request) (*QueryRequest, error) { Query: query, Slices: slices, ColumnAttrs: q.Get("columnAttrs") == "true", - InhibitAttrs: q.Get("inhibitAttrs") == "true", - InhibitBits: q.Get("inhibitBits") == "true", + ExcludeAttrs: q.Get("excludeAttrs") == "true", + ExcludeBits: q.Get("excludeBits") == "true", }, nil } @@ -1401,10 +1401,10 @@ type QueryRequest struct { ColumnAttrs bool // Do not return row attributes, if true. - InhibitAttrs bool + ExcludeAttrs bool // Do not return bits, if true. - InhibitBits bool + ExcludeBits bool // If true, indicates that query is part of a larger distributed query. // If false, this request is on the originating node. @@ -1417,8 +1417,8 @@ func decodeQueryRequest(pb *internal.QueryRequest) *QueryRequest { Slices: pb.Slices, ColumnAttrs: pb.ColumnAttrs, Remote: pb.Remote, - InhibitAttrs: pb.InhibitAttrs, - InhibitBits: pb.InhibitBits, + ExcludeAttrs: pb.ExcludeAttrs, + ExcludeBits: pb.ExcludeBits, } return req diff --git a/internal/public.pb.go b/internal/public.pb.go index d8fa51c5d..73de1cc79 100644 --- a/internal/public.pb.go +++ b/internal/public.pb.go @@ -129,8 +129,8 @@ type QueryRequest struct { Slices []uint64 `protobuf:"varint,2,rep,packed,name=Slices" json:"Slices,omitempty"` ColumnAttrs bool `protobuf:"varint,3,opt,name=ColumnAttrs,proto3" json:"ColumnAttrs,omitempty"` Remote bool `protobuf:"varint,5,opt,name=Remote,proto3" json:"Remote,omitempty"` - InhibitAttrs bool `protobuf:"varint,6,opt,name=InhibitAttrs,proto3" json:"InhibitAttrs,omitempty"` - InhibitBits bool `protobuf:"varint,7,opt,name=InhibitBits,proto3" json:"InhibitBits,omitempty"` + ExcludeAttrs bool `protobuf:"varint,6,opt,name=ExcludeAttrs,proto3" json:"ExcludeAttrs,omitempty"` + ExcludeBits bool `protobuf:"varint,7,opt,name=ExcludeBits,proto3" json:"ExcludeBits,omitempty"` } func (m *QueryRequest) Reset() { *m = QueryRequest{} } @@ -501,20 +501,20 @@ func (m *QueryRequest) MarshalTo(dAtA []byte) (int, error) { } i++ } - if m.InhibitAttrs { + if m.ExcludeAttrs { dAtA[i] = 0x30 i++ - if m.InhibitAttrs { + if m.ExcludeAttrs { dAtA[i] = 1 } else { dAtA[i] = 0 } i++ } - if m.InhibitBits { + if m.ExcludeBits { dAtA[i] = 0x38 i++ - if m.InhibitBits { + if m.ExcludeBits { dAtA[i] = 1 } else { dAtA[i] = 0 @@ -860,10 +860,10 @@ func (m *QueryRequest) Size() (n int) { if m.Remote { n += 2 } - if m.InhibitAttrs { + if m.ExcludeAttrs { n += 2 } - if m.InhibitBits { + if m.ExcludeBits { n += 2 } return n @@ -1829,7 +1829,7 @@ func (m *QueryRequest) Unmarshal(dAtA []byte) error { m.Remote = bool(v != 0) case 6: if wireType != 0 { - return fmt.Errorf("proto: wrong wireType = %d for field InhibitAttrs", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field ExcludeAttrs", wireType) } var v int for shift := uint(0); ; shift += 7 { @@ -1846,10 +1846,10 @@ func (m *QueryRequest) Unmarshal(dAtA []byte) error { break } } - m.InhibitAttrs = bool(v != 0) + m.ExcludeAttrs = bool(v != 0) case 7: if wireType != 0 { - return fmt.Errorf("proto: wrong wireType = %d for field InhibitBits", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field ExcludeBits", wireType) } var v int for shift := uint(0); ; shift += 7 { @@ -1866,7 +1866,7 @@ func (m *QueryRequest) Unmarshal(dAtA []byte) error { break } } - m.InhibitBits = bool(v != 0) + m.ExcludeBits = bool(v != 0) default: iNdEx = preIndex skippy, err := skipPublic(dAtA[iNdEx:]) @@ -2605,40 +2605,40 @@ func init() { proto.RegisterFile("public.proto", fileDescriptorPublic) } var fileDescriptorPublic = []byte{ // 589 bytes of a gzipped FileDescriptorProto 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x8c, 0x54, 0x5d, 0x8e, 0xd3, 0x30, - 0x10, 0xc6, 0x4d, 0xfa, 0x37, 0xed, 0xae, 0x2a, 0x8b, 0x9f, 0x08, 0xa1, 0x2a, 0x8a, 0x78, 0xc8, - 0x53, 0x57, 0x5a, 0x0e, 0x80, 0x48, 0xdb, 0x95, 0x22, 0xc4, 0x0a, 0xdc, 0x85, 0xf7, 0x74, 0xd7, - 0xda, 0x8d, 0x94, 0xc4, 0xc1, 0x71, 0x04, 0x3d, 0x00, 0x27, 0xe0, 0x85, 0x1b, 0xc0, 0x21, 0x38, + 0x10, 0xc6, 0x4d, 0xfa, 0x37, 0xed, 0xae, 0x56, 0x16, 0x3f, 0x11, 0x42, 0x55, 0x14, 0xf1, 0x90, + 0xa7, 0xae, 0xb4, 0x1c, 0x00, 0x91, 0xb6, 0x2b, 0x45, 0x88, 0x15, 0xb8, 0x0b, 0xef, 0xd9, 0xd6, + 0x5a, 0x22, 0x25, 0x71, 0x70, 0x1c, 0xb1, 0x3d, 0x00, 0x27, 0xe0, 0x85, 0x1b, 0xc0, 0x21, 0x38, 0x00, 0x8f, 0x1c, 0x01, 0x95, 0x8b, 0xa0, 0xb1, 0xe3, 0x26, 0xe5, 0x01, 0xf1, 0xe6, 0xef, 0x1b, - 0xcf, 0x64, 0x3e, 0x7f, 0x33, 0x81, 0x69, 0x59, 0x6f, 0xb3, 0xf4, 0x7a, 0x51, 0x4a, 0xa1, 0x04, + 0xcf, 0x64, 0x3e, 0x7f, 0x33, 0x81, 0x69, 0x59, 0xdf, 0x64, 0xe9, 0x66, 0x5e, 0x4a, 0xa1, 0x04, 0x1d, 0xa5, 0x85, 0xe2, 0xb2, 0x48, 0xb2, 0x20, 0x82, 0x41, 0x94, 0xaa, 0x3c, 0x29, 0x29, 0x05, 0x37, 0x4a, 0x55, 0xe5, 0x11, 0xdf, 0x09, 0x5d, 0xa6, 0xcf, 0xf4, 0x29, 0xf4, 0x5f, 0x28, 0x25, - 0x2b, 0xaf, 0xe7, 0x3b, 0xe1, 0xe4, 0xfc, 0x74, 0x61, 0xf3, 0x16, 0x48, 0x33, 0x13, 0x0c, 0x16, - 0xe0, 0xbe, 0x4e, 0x52, 0x49, 0x67, 0xe0, 0xbc, 0xe4, 0x3b, 0x8f, 0xf8, 0x24, 0x74, 0x19, 0x1e, - 0xe9, 0x7d, 0xe8, 0x2f, 0x45, 0x5d, 0x28, 0xaf, 0xa7, 0x39, 0x03, 0x82, 0xb7, 0xe0, 0x44, 0xa9, - 0xc2, 0x20, 0x13, 0x1f, 0xe2, 0x55, 0x93, 0x60, 0x00, 0x7d, 0x0c, 0xa3, 0xa5, 0xc8, 0xea, 0xbc, - 0x88, 0x57, 0x4d, 0xd6, 0x01, 0xd3, 0x27, 0x30, 0xbe, 0x4a, 0x73, 0x5e, 0xa9, 0x24, 0x2f, 0x3d, - 0xc7, 0x27, 0xa1, 0xc3, 0x5a, 0x22, 0x58, 0xc3, 0x89, 0xb9, 0x89, 0x5d, 0x6d, 0xb8, 0xa2, 0xa7, - 0xd0, 0x3b, 0x54, 0xef, 0xc5, 0xab, 0xff, 0x54, 0xf3, 0x8d, 0x80, 0x8b, 0xa7, 0xae, 0x9c, 0xb1, - 0x91, 0x43, 0xc1, 0xbd, 0xda, 0x95, 0xbc, 0xe9, 0x4b, 0x9f, 0xa9, 0x0f, 0x93, 0x8d, 0x92, 0x69, - 0x71, 0xfb, 0x2e, 0xc9, 0x6a, 0xae, 0xbb, 0x1a, 0xb3, 0x2e, 0x85, 0x8a, 0xe2, 0x42, 0x99, 0xb0, - 0xab, 0x9b, 0x3e, 0x60, 0x54, 0x14, 0x09, 0x91, 0x99, 0x60, 0xdf, 0x27, 0xe1, 0x88, 0xb5, 0x04, - 0x9d, 0x03, 0x5c, 0x64, 0x22, 0x69, 0x72, 0x07, 0x3e, 0x09, 0x09, 0xeb, 0x30, 0xc1, 0x19, 0x0c, - 0xb1, 0xd3, 0x57, 0x49, 0xd9, 0x6a, 0x23, 0xff, 0xd2, 0xf6, 0x9d, 0xc0, 0xf4, 0x4d, 0xcd, 0xe5, - 0x8e, 0xf1, 0xf7, 0x35, 0xaf, 0xb4, 0x07, 0x1a, 0x37, 0x2a, 0x0d, 0xa0, 0x0f, 0x61, 0xb0, 0xc9, - 0xd2, 0x6b, 0x6e, 0x5e, 0xca, 0x65, 0x0d, 0x42, 0xad, 0xed, 0x0b, 0x57, 0x5a, 0xeb, 0x88, 0x75, - 0x29, 0xcc, 0x64, 0x3c, 0x17, 0xca, 0x8a, 0x69, 0x10, 0x0d, 0x60, 0x1a, 0x17, 0x77, 0xe9, 0x36, - 0x55, 0x26, 0x75, 0xa0, 0xa3, 0x47, 0x1c, 0x56, 0x6f, 0xb0, 0x9e, 0xc3, 0xa1, 0xa9, 0xde, 0xa1, - 0x82, 0xcf, 0x04, 0x4e, 0x9a, 0xf6, 0xab, 0x52, 0x14, 0x15, 0x47, 0x8f, 0xd6, 0x52, 0x5a, 0x8f, - 0xd6, 0x52, 0xd2, 0x33, 0x18, 0x32, 0x5e, 0xd5, 0x99, 0xb2, 0x36, 0x3f, 0x68, 0x9f, 0xc2, 0xe6, - 0xd6, 0x99, 0x62, 0xf6, 0x16, 0x7d, 0x0e, 0xa7, 0x47, 0x63, 0x83, 0xba, 0x30, 0xef, 0x51, 0x9b, - 0x77, 0x14, 0x67, 0x7f, 0x5d, 0x0f, 0x3e, 0x11, 0x98, 0x74, 0x2a, 0xd3, 0xd0, 0xae, 0x94, 0x6e, - 0x6b, 0x72, 0x3e, 0x6b, 0x0b, 0x19, 0x9e, 0xd9, 0x95, 0x9b, 0x02, 0xb9, 0x6c, 0x86, 0x89, 0x5c, - 0xa2, 0x85, 0xb8, 0x46, 0xf6, 0xfb, 0x1d, 0x0b, 0x91, 0x66, 0x26, 0x48, 0x3d, 0x18, 0x2e, 0xef, - 0x92, 0xe2, 0x96, 0xdf, 0xe8, 0x61, 0x1a, 0x31, 0x0b, 0x83, 0xaf, 0x04, 0x4e, 0xe2, 0xbc, 0x14, - 0x52, 0x75, 0xdc, 0x8d, 0x8b, 0x1b, 0xfe, 0xd1, 0xba, 0xab, 0x01, 0xb2, 0x17, 0x32, 0xc9, 0xcd, - 0x18, 0x8f, 0x99, 0x01, 0xc8, 0x6a, 0x97, 0xb5, 0xab, 0x2e, 0x33, 0x40, 0xfb, 0x89, 0x6b, 0x59, - 0x79, 0xae, 0x99, 0x04, 0x83, 0x70, 0x6e, 0xed, 0x56, 0x56, 0x5e, 0x5f, 0x87, 0x5a, 0x02, 0xe7, - 0xf6, 0xb0, 0x96, 0xe8, 0xb5, 0x13, 0x3a, 0xac, 0xc3, 0x44, 0xb3, 0x1f, 0xfb, 0x39, 0xf9, 0xb9, - 0x9f, 0x93, 0x5f, 0xfb, 0x39, 0xf9, 0xf2, 0x7b, 0x7e, 0x6f, 0x3b, 0xd0, 0xff, 0xa5, 0x67, 0x7f, - 0x02, 0x00, 0x00, 0xff, 0xff, 0xd8, 0x9d, 0xc1, 0x11, 0xa7, 0x04, 0x00, 0x00, + 0x2b, 0xaf, 0xe7, 0x3b, 0xe1, 0xe4, 0xe2, 0x74, 0x6e, 0xf3, 0xe6, 0x48, 0x33, 0x13, 0x0c, 0xe6, + 0xe0, 0xbe, 0x4e, 0x52, 0x49, 0xcf, 0xc0, 0x79, 0xc9, 0x77, 0x1e, 0xf1, 0x49, 0xe8, 0x32, 0x3c, + 0xd2, 0xfb, 0xd0, 0x5f, 0x88, 0xba, 0x50, 0x5e, 0x4f, 0x73, 0x06, 0x04, 0x6f, 0xc1, 0x89, 0x52, + 0x85, 0x41, 0x26, 0x3e, 0xc6, 0xcb, 0x26, 0xc1, 0x00, 0xfa, 0x18, 0x46, 0x0b, 0x91, 0xd5, 0x79, + 0x11, 0x2f, 0x9b, 0xac, 0x03, 0xa6, 0x4f, 0x60, 0x7c, 0x9d, 0xe6, 0xbc, 0x52, 0x49, 0x5e, 0x7a, + 0x8e, 0x4f, 0x42, 0x87, 0xb5, 0x44, 0xb0, 0x82, 0x13, 0x73, 0x13, 0xbb, 0x5a, 0x73, 0x45, 0x4f, + 0xa1, 0x77, 0xa8, 0xde, 0x8b, 0x97, 0xff, 0xa9, 0xe6, 0x1b, 0x01, 0x17, 0x4f, 0x5d, 0x39, 0x63, + 0x23, 0x87, 0x82, 0x7b, 0xbd, 0x2b, 0x79, 0xd3, 0x97, 0x3e, 0x53, 0x1f, 0x26, 0x6b, 0x25, 0xd3, + 0xe2, 0xf6, 0x5d, 0x92, 0xd5, 0x5c, 0x77, 0x35, 0x66, 0x5d, 0x0a, 0x15, 0xc5, 0x85, 0x32, 0x61, + 0x57, 0x37, 0x7d, 0xc0, 0xa8, 0x28, 0x12, 0x22, 0x33, 0xc1, 0xbe, 0x4f, 0xc2, 0x11, 0x6b, 0x09, + 0x3a, 0x03, 0xb8, 0xcc, 0x44, 0xd2, 0xe4, 0x0e, 0x7c, 0x12, 0x12, 0xd6, 0x61, 0x82, 0x73, 0x18, + 0x62, 0xa7, 0xaf, 0x92, 0xb2, 0xd5, 0x46, 0xfe, 0xa5, 0xed, 0x3b, 0x81, 0xe9, 0x9b, 0x9a, 0xcb, + 0x1d, 0xe3, 0x1f, 0x6a, 0x5e, 0x69, 0x0f, 0x34, 0x6e, 0x54, 0x1a, 0x40, 0x1f, 0xc2, 0x60, 0x9d, + 0xa5, 0x1b, 0x6e, 0x5e, 0xca, 0x65, 0x0d, 0x42, 0xad, 0xed, 0x0b, 0x57, 0x5a, 0xeb, 0x88, 0x75, + 0x29, 0xcc, 0x64, 0x3c, 0x17, 0xca, 0x8a, 0x69, 0x10, 0x0d, 0x60, 0xba, 0xba, 0xdb, 0x64, 0xf5, + 0x96, 0x9b, 0xd4, 0x81, 0x8e, 0x1e, 0x71, 0x58, 0xbd, 0xc1, 0x7a, 0x0e, 0x87, 0xa6, 0x7a, 0x87, + 0x0a, 0x3e, 0x13, 0x38, 0x69, 0xda, 0xaf, 0x4a, 0x51, 0x54, 0x1c, 0x3d, 0x5a, 0x49, 0x69, 0x3d, + 0x5a, 0x49, 0x49, 0xcf, 0x61, 0xc8, 0x78, 0x55, 0x67, 0xca, 0xda, 0xfc, 0xa0, 0x7d, 0x0a, 0x9b, + 0x5b, 0x67, 0x8a, 0xd9, 0x5b, 0xf4, 0x39, 0x9c, 0x1e, 0x8d, 0x0d, 0xea, 0xc2, 0xbc, 0x47, 0x6d, + 0xde, 0x51, 0x9c, 0xfd, 0x75, 0x3d, 0xf8, 0x44, 0x60, 0xd2, 0xa9, 0x4c, 0x43, 0xbb, 0x52, 0xba, + 0xad, 0xc9, 0xc5, 0x59, 0x5b, 0xc8, 0xf0, 0xcc, 0xae, 0xdc, 0x14, 0xc8, 0x55, 0x33, 0x4c, 0xe4, + 0x0a, 0x2d, 0xc4, 0x35, 0xb2, 0xdf, 0xef, 0x58, 0x88, 0x34, 0x33, 0x41, 0xea, 0xc1, 0x70, 0xf1, + 0x3e, 0x29, 0x6e, 0xf9, 0x56, 0x0f, 0xd3, 0x88, 0x59, 0x18, 0x7c, 0x25, 0x70, 0x12, 0xe7, 0xa5, + 0x90, 0xaa, 0xe3, 0x6e, 0x5c, 0x6c, 0xf9, 0x9d, 0x75, 0x57, 0x03, 0x64, 0x2f, 0x65, 0x92, 0x9b, + 0x31, 0x1e, 0x33, 0x03, 0x90, 0xd5, 0x2e, 0x6b, 0x57, 0x5d, 0x66, 0x80, 0xf6, 0x13, 0xd7, 0xb2, + 0xf2, 0x5c, 0x33, 0x09, 0x06, 0xe1, 0xdc, 0xda, 0xad, 0xac, 0xbc, 0xbe, 0x0e, 0xb5, 0x04, 0xce, + 0xed, 0x61, 0x2d, 0xd1, 0x6b, 0x27, 0x74, 0x58, 0x87, 0x89, 0xce, 0x7e, 0xec, 0x67, 0xe4, 0xe7, + 0x7e, 0x46, 0x7e, 0xed, 0x67, 0xe4, 0xcb, 0xef, 0xd9, 0xbd, 0x9b, 0x81, 0xfe, 0x2f, 0x3d, 0xfb, + 0x13, 0x00, 0x00, 0xff, 0xff, 0xd9, 0x81, 0x5d, 0x51, 0xa7, 0x04, 0x00, 0x00, } diff --git a/internal/public.proto b/internal/public.proto index 50325205b..dcbcb4cf6 100644 --- a/internal/public.proto +++ b/internal/public.proto @@ -41,8 +41,8 @@ message QueryRequest { repeated uint64 Slices = 2; bool ColumnAttrs = 3; bool Remote = 5; - bool InhibitAttrs = 6; - bool InhibitBits = 7; + bool ExcludeAttrs = 6; + bool ExcludeBits = 7; } message QueryResponse { From 95d89e7648239e34ed60b66e52d922a3afc07940 Mon Sep 17 00:00:00 2001 From: Todd Gruben Date: Wed, 23 Aug 2017 09:03:26 -0500 Subject: [PATCH 25/46] replaced magic number with constant; updated comments --- roaring/roaring.go | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/roaring/roaring.go b/roaring/roaring.go index 266a9bfe3..a42c09e4e 100644 --- a/roaring/roaring.go +++ b/roaring/roaring.go @@ -3218,7 +3218,8 @@ func xorCompare(x *xorstm) (r1 interval16, has_data bool) { r1 = interval16{start: x.va.start, last: x.vb.start - 1} has_data = true } - if x.vb.last == 65535 { + + if x.vb.last == maxContainerVal { // Check for overflow x.va_valid = false } else { @@ -3235,7 +3236,7 @@ func xorCompare(x *xorstm) (r1 interval16, has_data bool) { has_data = true } - if x.va.last == 65535 { + if x.va.last == maxContainerVal { //check for overflow x.vb_valid = false } else { x.vb.start = x.va.last + 1 @@ -3248,7 +3249,7 @@ func xorCompare(x *xorstm) (r1 interval16, has_data bool) { x.va_valid = false r1 = interval16{start: x.va.start, last: x.vb.start - 1} has_data = true - if x.va.last == 65535 { + if x.va.last == maxContainerVal { // check for overflow x.vb_valid = false } else { x.vb.start = x.va.last + 1 @@ -3261,7 +3262,7 @@ func xorCompare(x *xorstm) (r1 interval16, has_data bool) { r1 = interval16{start: x.vb.start, last: x.va.start - 1} has_data = true - if x.vb.last == 65535 { + if x.vb.last == maxContainerVal { // check for overflow x.va_valid = false } else { x.va.start = x.vb.last + 1 From ee3324ba8e2cbeef1956b91fd965c830151644dc Mon Sep 17 00:00:00 2001 From: Todd Gruben Date: Wed, 23 Aug 2017 11:20:33 -0500 Subject: [PATCH 26/46] overflow in arrayRun with supporting tests --- roaring/roaring.go | 15 +++++++++++---- roaring/roaring_internal_test.go | 25 +++++++++++++++++++++++++ 2 files changed, 36 insertions(+), 4 deletions(-) diff --git a/roaring/roaring.go b/roaring/roaring.go index a42c09e4e..903a04946 100644 --- a/roaring/roaring.go +++ b/roaring/roaring.go @@ -3152,8 +3152,11 @@ func xorArrayRun(a, b *container) *container { } else if va > vb.start { if va < vb.last { output.n += output.runAppendInterval(interval16{start: vb.start, last: va - 1}) - vb.start = va + 1 i++ + // candidate for overflow + // but no va must be less than max-1 + vb.start = va + 1 + if vb.start > vb.last { j++ } @@ -3162,15 +3165,19 @@ func xorArrayRun(a, b *container) *container { j++ } else { // va == vb.last vb.last-- - if vb.start < vb.last { + if vb.start <= vb.last { output.n += output.runAppendInterval(vb) } j++ i++ } - } else { - vb.start++ + } else { // we know va == vb.start + if vb.start == maxContainerVal { // protect overflow + j++ + } else { + vb.start++ + } i++ } } diff --git a/roaring/roaring_internal_test.go b/roaring/roaring_internal_test.go index 74dcc8992..6ee171fb4 100644 --- a/roaring/roaring_internal_test.go +++ b/roaring/roaring_internal_test.go @@ -1736,6 +1736,31 @@ func TestXorArrayRun(t *testing.T) { if !reflect.DeepEqual(ret.runs, expr) { t.Fatalf("test #4 expected %v, but got %v", exp, ret.array) } + + a = &container{array: []uint16{65535}, container_type: ContainerArray} + b = &container{runs: []interval16{{start: 65534, last: 65535}}, container_type: ContainerRun} + exp = []uint16{65534} + ret = xor(a, b) + if !reflect.DeepEqual(ret.array, exp) { + t.Fatalf("test #5 expected %v, but got %v", exp, ret.array) + } + + ret = xor(b, a) + if !reflect.DeepEqual(ret.array, exp) { + t.Fatalf("test #6 expected %v, but got %v", exp, ret.array) + } + + b = &container{runs: []interval16{{start: 65535, last: 65535}}, container_type: ContainerRun} + exp = []uint16{} + ret = xor(a, b) + if !reflect.DeepEqual(ret.array, exp) { + t.Fatalf("test #7 expected %v, but got %v", exp, ret.array) + } + + ret = xor(b, a) + if !reflect.DeepEqual(ret.array, exp) { + t.Fatalf("test #8 expected %v, but got %v", exp, ret.array) + } } //special case that didn't fit the xorrunrun table testing below. From 3235d83c9b6debe7266248c2ec854af3d8783a8e Mon Sep 17 00:00:00 2001 From: Todd Gruben Date: Wed, 23 Aug 2017 13:12:28 -0500 Subject: [PATCH 27/46] Xor support for PQL --- bitmap.go | 27 +++++++++++++++++++++++++++ executor.go | 21 +++++++++++++++++++++ executor_test.go | 19 +++++++++++++++++++ 3 files changed, 67 insertions(+) diff --git a/bitmap.go b/bitmap.go index ecdb4c076..eb8f7f799 100644 --- a/bitmap.go +++ b/bitmap.go @@ -97,6 +97,22 @@ func (b *Bitmap) Intersect(other *Bitmap) *Bitmap { return &Bitmap{segments: segments} } +// Xor returns the xor of b and other. +func (b *Bitmap) Xor(other *Bitmap) *Bitmap { + var segments []BitmapSegment + + itr := newMergeSegmentIterator(b.segments, other.segments) + for s0, s1 := itr.next(); s0 != nil || s1 != nil; s0, s1 = itr.next() { + // Ignore non-overlapping segments. + if s0 == nil || s1 == nil { + continue + } + segments = append(segments, *s0.Xor(s1)) + } + + return &Bitmap{segments: segments} +} + // Union returns the bitwise union of b and other. func (b *Bitmap) Union(other *Bitmap) *Bitmap { var segments []BitmapSegment @@ -342,6 +358,17 @@ func (s *BitmapSegment) Difference(other *BitmapSegment) *BitmapSegment { } } +// Xor returns the xor of s and other. +func (s *BitmapSegment) Xor(other *BitmapSegment) *BitmapSegment { + data := s.data.Xor(&other.data) + + return &BitmapSegment{ + data: *data, + slice: s.slice, + n: data.Count(), + } +} + // SetBit sets the i-th bit of the bitmap. func (s *BitmapSegment) SetBit(i uint64) (changed bool) { s.ensureWritable() diff --git a/executor.go b/executor.go index aa5b69034..24777a6f1 100644 --- a/executor.go +++ b/executor.go @@ -316,6 +316,8 @@ func (e *Executor) executeBitmapCallSlice(ctx context.Context, index string, c * return e.executeRangeSlice(ctx, index, c, slice) case "Union": return e.executeUnionSlice(ctx, index, c, slice) + case "Xor": + return e.executeXorSlice(ctx, index, c, slice) default: return nil, fmt.Errorf("unknown call: %s", c.Name) } @@ -721,6 +723,25 @@ func (e *Executor) executeUnionSlice(ctx context.Context, index string, c *pql.C return other, nil } +// executeXorSlice executes a xor() call for a local slice. +func (e *Executor) executeXorSlice(ctx context.Context, index string, c *pql.Call, slice uint64) (*Bitmap, error) { + other := NewBitmap() + for i, input := range c.Children { + bm, err := e.executeBitmapCallSlice(ctx, index, input, slice) + if err != nil { + return nil, err + } + + if i == 0 { + other = bm + } else { + other = other.Xor(bm) + } + } + other.InvalidateCount() + return other, nil +} + // executeCount executes a count() call. func (e *Executor) executeCount(ctx context.Context, index string, c *pql.Call, slices []uint64, opt *ExecOptions) (uint64, error) { if len(c.Children) == 0 { diff --git a/executor_test.go b/executor_test.go index 265293aef..269747efb 100644 --- a/executor_test.go +++ b/executor_test.go @@ -187,6 +187,25 @@ func TestExecutor_Execute_Empty_Union(t *testing.T) { } } +// Ensure a xor query can be executed. +func TestExecutor_Execute_Xor(t *testing.T) { + hldr := test.MustOpenHolder() + defer hldr.Close() + hldr.MustCreateFragmentIfNotExists("i", "general", pilosa.ViewStandard, 0).MustSetBits(10, 0) + hldr.MustCreateFragmentIfNotExists("i", "general", pilosa.ViewStandard, 1).MustSetBits(10, SliceWidth+1) + hldr.MustCreateFragmentIfNotExists("i", "general", pilosa.ViewStandard, 1).MustSetBits(10, SliceWidth+2) + + hldr.MustCreateFragmentIfNotExists("i", "general", pilosa.ViewStandard, 0).MustSetBits(11, 2) + hldr.MustCreateFragmentIfNotExists("i", "general", pilosa.ViewStandard, 1).MustSetBits(11, SliceWidth+2) + + e := test.NewExecutor(hldr.Holder, test.NewCluster(1)) + if res, err := e.Execute(context.Background(), "i", test.MustParse(`Xor(Bitmap(rowID=10), Bitmap(rowID=11))`), nil, nil); err != nil { + t.Fatal(err) + } else if bits := res[0].(*pilosa.Bitmap).Bits(); !reflect.DeepEqual(bits, []uint64{0, 2, SliceWidth + 1}) { + t.Fatalf("unexpected bits: %+v", bits) + } +} + // Ensure a count query can be executed. func TestExecutor_Execute_Count(t *testing.T) { hldr := test.MustOpenHolder() From 0f911498520eed43a1327fd52cf0ae1543e0a5f0 Mon Sep 17 00:00:00 2001 From: Todd Gruben Date: Thu, 24 Aug 2017 13:47:58 -0500 Subject: [PATCH 28/46] converted xorArrayRun test to table; xor cardinality bug fix --- roaring/roaring.go | 3 ++ roaring/roaring_internal_test.go | 79 +++++++++++++------------------- 2 files changed, 36 insertions(+), 46 deletions(-) diff --git a/roaring/roaring.go b/roaring/roaring.go index 903a04946..4d862da64 100644 --- a/roaring/roaring.go +++ b/roaring/roaring.go @@ -3177,6 +3177,9 @@ func xorArrayRun(a, b *container) *container { j++ } else { vb.start++ + if vb.start > vb.last { + j++ + } } i++ } diff --git a/roaring/roaring_internal_test.go b/roaring/roaring_internal_test.go index 6ee171fb4..4c18bc5ef 100644 --- a/roaring/roaring_internal_test.go +++ b/roaring/roaring_internal_test.go @@ -1711,56 +1711,43 @@ func TestWriteReadRun(t *testing.T) { } func TestXorArrayRun(t *testing.T) { - a := &container{array: []uint16{1, 5, 10, 11, 12}, container_type: ContainerArray} - b := &container{runs: []interval16{{start: 2, last: 10}, {start: 12, last: 13}, {start: 15, last: 16}}, container_type: ContainerRun} - exp := []uint16{1, 2, 3, 4, 6, 7, 8, 9, 11, 13, 15, 16} - - //ret := xorArrayRun(a, b) - ret := xor(a, b) - if !reflect.DeepEqual(ret.array, exp) { - t.Fatalf("test #1 expected %v, but got %v", exp, ret.array) + tests := []struct { + a *container + b *container + exp *container + }{ + { + a: &container{array: []uint16{1, 5, 10, 11, 12}, container_type: ContainerArray}, + b: &container{runs: []interval16{{start: 2, last: 10}, {start: 12, last: 13}, {start: 15, last: 16}}, container_type: ContainerRun}, + exp: &container{array: []uint16{1, 2, 3, 4, 6, 7, 8, 9, 11, 13, 15, 16}, container_type: ContainerArray, n: 12}, + }, { + a: &container{array: []uint16{1, 5, 10, 11, 12, 13, 14}, container_type: ContainerArray}, + b: &container{runs: []interval16{{start: 2, last: 10}, {start: 12, last: 13}, {start: 15, last: 16}}, container_type: ContainerRun}, + exp: &container{array: []uint16{1, 2, 3, 4, 6, 7, 8, 9, 11, 14, 15, 16}, container_type: ContainerArray, n: 12}, + }, { + a: &container{array: []uint16{65535}, container_type: ContainerArray}, + b: &container{runs: []interval16{{start: 65534, last: 65535}}, container_type: ContainerRun}, + exp: &container{array: []uint16{65534}, container_type: ContainerArray, n: 1}, + }, { + a: &container{array: []uint16{65535}, container_type: ContainerArray}, + b: &container{runs: []interval16{{start: 65535, last: 65535}}, container_type: ContainerRun}, + exp: &container{array: []uint16{}, container_type: ContainerArray, n: 0}, + }, } - ret = xor(b, a) - if !reflect.DeepEqual(ret.array, exp) { - t.Fatalf("test #2 expected %v, but got %v", exp, ret.array) - } - c := &container{array: []uint16{1, 5, 10, 11, 12, 13, 14}, container_type: ContainerArray} - // exp = []int16{1, 2, 3, 4, 6, 7, 8, 9, 11, 14, 15, 16} - expr := []interval16{{start: 1, last: 4}, {start: 6, last: 9}, {start: 11, last: 11}, {start: 14, last: 16}} - ret = xor(b, c) - if !reflect.DeepEqual(ret.runs, expr) { - t.Fatalf("test #3 expected %v, but got %v", exp, ret.runs) - } - ret = xor(c, b) - if !reflect.DeepEqual(ret.runs, expr) { - t.Fatalf("test #4 expected %v, but got %v", exp, ret.array) + for i, test := range tests { + test.a.n = test.a.count() + test.b.n = test.b.count() + ret := xor(test.a, test.b) + if !reflect.DeepEqual(ret, test.exp) { + t.Fatalf("test #%v expected %v, but got %v", i, test.exp, ret) + } + ret = xor(test.b, test.a) + if !reflect.DeepEqual(ret, test.exp) { + t.Fatalf("test #%v.1 expected %v, but got %v", i, test.exp, ret) + } } - a = &container{array: []uint16{65535}, container_type: ContainerArray} - b = &container{runs: []interval16{{start: 65534, last: 65535}}, container_type: ContainerRun} - exp = []uint16{65534} - ret = xor(a, b) - if !reflect.DeepEqual(ret.array, exp) { - t.Fatalf("test #5 expected %v, but got %v", exp, ret.array) - } - - ret = xor(b, a) - if !reflect.DeepEqual(ret.array, exp) { - t.Fatalf("test #6 expected %v, but got %v", exp, ret.array) - } - - b = &container{runs: []interval16{{start: 65535, last: 65535}}, container_type: ContainerRun} - exp = []uint16{} - ret = xor(a, b) - if !reflect.DeepEqual(ret.array, exp) { - t.Fatalf("test #7 expected %v, but got %v", exp, ret.array) - } - - ret = xor(b, a) - if !reflect.DeepEqual(ret.array, exp) { - t.Fatalf("test #8 expected %v, but got %v", exp, ret.array) - } } //special case that didn't fit the xorrunrun table testing below. From 72041eb4f22cdd01ce1183ef8f85b89f1a7e6f88 Mon Sep 17 00:00:00 2001 From: Todd Gruben Date: Thu, 24 Aug 2017 13:49:43 -0500 Subject: [PATCH 29/46] comment cleanup --- roaring/roaring.go | 2 -- 1 file changed, 2 deletions(-) diff --git a/roaring/roaring.go b/roaring/roaring.go index 4d862da64..a1b99fcbb 100644 --- a/roaring/roaring.go +++ b/roaring/roaring.go @@ -3153,8 +3153,6 @@ func xorArrayRun(a, b *container) *container { if va < vb.last { output.n += output.runAppendInterval(interval16{start: vb.start, last: va - 1}) i++ - // candidate for overflow - // but no va must be less than max-1 vb.start = va + 1 if vb.start > vb.last { From ad847f2c8ac028d40c2480f8d73fa08122807062 Mon Sep 17 00:00:00 2001 From: Ben Johnson Date: Thu, 24 Aug 2017 13:34:57 -0600 Subject: [PATCH 30/46] Add field Range() support to Executor. --- executor.go | 65 ++++++++++++++++++++++++++- executor_test.go | 113 +++++++++++++++++++++++++++++++++++++++++++++++ fragment.go | 13 +++--- fragment_test.go | 19 ++++---- frame.go | 12 +---- pql/ast.go | 10 +++++ view.go | 3 +- 7 files changed, 208 insertions(+), 27 deletions(-) diff --git a/executor.go b/executor.go index aa5b69034..fd1e921d9 100644 --- a/executor.go +++ b/executor.go @@ -620,6 +620,11 @@ func (e *Executor) executeIntersectSlice(ctx context.Context, index string, c *p // executeRangeSlice executes a range() call for a local slice. func (e *Executor) executeRangeSlice(ctx context.Context, index string, c *pql.Call, slice uint64) (*Bitmap, error) { + // Handle field ranges differently. + if c.HasConditionArg() { + return e.executeFieldRangeSlice(ctx, index, c, slice) + } + // Parse frame, use default if unset. frame, _ := c.Args["frame"].(string) if frame == "" { @@ -674,7 +679,7 @@ func (e *Executor) executeRangeSlice(ctx context.Context, index string, c *pql.C } // Parse end time. - endTimeStr, _ := c.Args["end"].(string) + endTimeStr, ok := c.Args["end"].(string) if !ok { return nil, errors.New("Range() end time required") } @@ -702,6 +707,64 @@ func (e *Executor) executeRangeSlice(ctx context.Context, index string, c *pql.C return bm, nil } +// executeFieldRangeSlice executes a range(field) call for a local slice. +func (e *Executor) executeFieldRangeSlice(ctx context.Context, index string, c *pql.Call, slice uint64) (*Bitmap, error) { + // Parse frame, use default if unset. + frame, _ := c.Args["frame"].(string) + if frame == "" { + frame = DefaultFrame + } + f := e.Holder.Frame(index, frame) + if f == nil { + return nil, ErrFrameNotFound + } + + // Remove frame field. + args := pql.CopyArgs(c.Args) + delete(args, "frame") + + // Only one conditional field should remain. + if len(args) == 0 { + return nil, errors.New("Range(): condition required") + } else if len(args) > 1 { + return nil, errors.New("Range(): too many arguments") + } + + // Extract condition field. + var fieldName string + var cond *pql.Condition + for k, v := range args { + vv, ok := v.(*pql.Condition) + if !ok { + return nil, fmt.Errorf("Range(): %q: expected condition argument, got %v", k, v) + } + fieldName, cond = k, vv + } + + // Only support integers for now. + value, ok := cond.Value.(int64) + if !ok { + return nil, errors.New("Range(): conditions only support integer values") + } + + // Find field. + field := f.Field(fieldName) + if field == nil { + return nil, ErrFieldNotFound + } else if value < field.Min || value > field.Max { + return NewBitmap(), nil + } + + // Retrieve fragment. + frag := e.Holder.Fragment(index, frame, ViewFieldPrefix+fieldName, slice) + if frag == nil { + return NewBitmap(), nil + } + + f.Stats.Count("range:field", 1, 1.0) + return frag.FieldRange(cond.Op, field.BitDepth(), uint64(value-field.Min)) +} + // executeUnionSlice executes a union() call for a local slice. func (e *Executor) executeUnionSlice(ctx context.Context, index string, c *pql.Call, slice uint64) (*Bitmap, error) { other := NewBitmap() diff --git a/executor_test.go b/executor_test.go index 265293aef..a7186a455 100644 --- a/executor_test.go +++ b/executor_test.go @@ -735,6 +735,119 @@ func TestExecutor_Execute_Range(t *testing.T) { }) } +// Ensure a Range(field) query can be executed. +func TestExecutor_Execute_FieldRange(t *testing.T) { + hldr := test.MustOpenHolder() + defer hldr.Close() + e := test.NewExecutor(hldr.Holder, test.NewCluster(1)) + + idx, err := hldr.CreateIndex("i", pilosa.IndexOptions{}) + if err != nil { + t.Fatal(err) + } + + if _, err := idx.CreateFrame("f", pilosa.FrameOptions{ + RangeEnabled: true, + Fields: []*pilosa.Field{ + {Name: "foo", Type: pilosa.FieldTypeInt, Min: 10, Max: 100}, + {Name: "bar", Type: pilosa.FieldTypeInt, Min: 0, Max: 100000}, + }, + }); err != nil { + t.Fatal(err) + } + + if _, err := idx.CreateFrame("other", pilosa.FrameOptions{ + RangeEnabled: true, + Fields: []*pilosa.Field{ + {Name: "foo", Type: pilosa.FieldTypeInt, Min: 0, Max: 1000}, + }, + }); err != nil { + t.Fatal(err) + } + + if _, err := e.Execute(context.Background(), "i", test.MustParse(` + SetBit(frame=f, rowID=0, columnID=0) + SetBit(frame=f, rowID=0, columnID=`+strconv.Itoa(SliceWidth+1)+`) + + SetFieldValue(frame=f, foo=20, bar=2000, columnID=50) + SetFieldValue(frame=f, foo=30, columnID=`+strconv.Itoa(SliceWidth)+`) + SetFieldValue(frame=f, foo=10, columnID=`+strconv.Itoa(SliceWidth+2)+`) + SetFieldValue(frame=f, foo=20, columnID=`+strconv.Itoa((5*SliceWidth)+100)+`) + SetFieldValue(frame=f, foo=60, columnID=`+strconv.Itoa(SliceWidth+1)+`) + SetFieldValue(frame=other, foo=1000, columnID=0) + `), nil, nil); err != nil { + t.Fatal(err) + } + + t.Run("EQ", func(t *testing.T) { + if result, err := e.Execute(context.Background(), "i", test.MustParse(`Range(frame=f, foo == 20)`), nil, nil); err != nil { + t.Fatal(err) + } else if !reflect.DeepEqual([]uint64{50, (5 * SliceWidth) + 100}, result[0].(*pilosa.Bitmap).Bits()) { + t.Fatalf("unexpected result: %s", spew.Sdump(result)) + } + }) + + t.Run("LT", func(t *testing.T) { + if result, err := e.Execute(context.Background(), "i", test.MustParse(`Range(frame=f, foo < 20)`), nil, nil); err != nil { + t.Fatal(err) + } else if !reflect.DeepEqual([]uint64{SliceWidth + 2}, result[0].(*pilosa.Bitmap).Bits()) { + t.Fatalf("unexpected result: %s", spew.Sdump(result)) + } + }) + + t.Run("LTE", func(t *testing.T) { + if result, err := e.Execute(context.Background(), "i", test.MustParse(`Range(frame=f, foo <= 20)`), nil, nil); err != nil { + t.Fatal(err) + } else if !reflect.DeepEqual([]uint64{50, SliceWidth + 2, (5 * SliceWidth) + 100}, result[0].(*pilosa.Bitmap).Bits()) { + t.Fatalf("unexpected result: %s", spew.Sdump(result)) + } + }) + + t.Run("GT", func(t *testing.T) { + if result, err := e.Execute(context.Background(), "i", test.MustParse(`Range(frame=f, foo > 20)`), nil, nil); err != nil { + t.Fatal(err) + } else if !reflect.DeepEqual([]uint64{SliceWidth, SliceWidth + 1}, result[0].(*pilosa.Bitmap).Bits()) { + t.Fatalf("unexpected result: %s", spew.Sdump(result)) + } + }) + + t.Run("GTE", func(t *testing.T) { + if result, err := e.Execute(context.Background(), "i", test.MustParse(`Range(frame=f, foo >= 20)`), nil, nil); err != nil { + t.Fatal(err) + } else if !reflect.DeepEqual([]uint64{50, SliceWidth, SliceWidth + 1, (5 * SliceWidth) + 100}, result[0].(*pilosa.Bitmap).Bits()) { + t.Fatalf("unexpected result: %s", spew.Sdump(result)) + } + }) + + t.Run("BelowMin", func(t *testing.T) { + if result, err := e.Execute(context.Background(), "i", test.MustParse(`Range(frame=f, foo == 0)`), nil, nil); err != nil { + t.Fatal(err) + } else if !reflect.DeepEqual([]uint64{}, result[0].(*pilosa.Bitmap).Bits()) { + t.Fatalf("unexpected result: %s", spew.Sdump(result)) + } + }) + + t.Run("AboveMax", func(t *testing.T) { + if result, err := e.Execute(context.Background(), "i", test.MustParse(`Range(frame=f, foo == 200)`), nil, nil); err != nil { + t.Fatal(err) + } else if !reflect.DeepEqual([]uint64{}, result[0].(*pilosa.Bitmap).Bits()) { + t.Fatalf("unexpected result: %s", spew.Sdump(result)) + } + }) + + t.Run("ErrFrameNotFound", func(t *testing.T) { + if _, err := e.Execute(context.Background(), "i", test.MustParse(`Range(frame=bad_frame, foo >= 20)`), nil, nil); err != pilosa.ErrFrameNotFound { + t.Fatal(err) + } + }) + + t.Run("ErrFieldNotFound", func(t *testing.T) { + if _, err := e.Execute(context.Background(), "i", test.MustParse(`Range(frame=f, bad_field >= 20)`), nil, nil); err != pilosa.ErrFieldNotFound { + t.Fatal(err) + } + }) +} + // Ensure a remote query can return a bitmap. func TestExecutor_Execute_Remote_Bitmap(t *testing.T) { c := test.NewCluster(2) diff --git a/fragment.go b/fragment.go index a517cbd57..06f451250 100644 --- a/fragment.go +++ b/fragment.go @@ -39,6 +39,7 @@ import ( "github.com/gogo/protobuf/proto" "github.com/pilosa/pilosa/internal" + "github.com/pilosa/pilosa/pql" "github.com/pilosa/pilosa/roaring" ) @@ -567,14 +568,14 @@ func (f *Fragment) FieldSum(filter *Bitmap, bitDepth uint) (sum, count uint64, e return sum, count, nil } -func (f *Fragment) FieldRange(op string, bitDepth uint, predicate uint64) (*Bitmap, error) { +func (f *Fragment) FieldRange(op pql.Token, bitDepth uint, predicate uint64) (*Bitmap, error) { switch op { - case RangeOpEQ: + case pql.EQ: return f.fieldRangeEQ(bitDepth, predicate) - case RangeOpLT, RangeOpLTE: - return f.fieldRangeLT(bitDepth, predicate, op == RangeOpLTE) - case RangeOpGT, RangeOpGTE: - return f.fieldRangeGT(bitDepth, predicate, op == RangeOpGTE) + case pql.LT, pql.LTE: + return f.fieldRangeLT(bitDepth, predicate, op == pql.LTE) + case pql.GT, pql.GTE: + return f.fieldRangeGT(bitDepth, predicate, op == pql.GTE) default: return nil, ErrInvalidRangeOperation } diff --git a/fragment_test.go b/fragment_test.go index 7dfdac8ae..941554db4 100644 --- a/fragment_test.go +++ b/fragment_test.go @@ -24,6 +24,7 @@ import ( "github.com/davecgh/go-spew/spew" "github.com/pilosa/pilosa" + "github.com/pilosa/pilosa/pql" "github.com/pilosa/pilosa/test" ) @@ -275,7 +276,7 @@ func TestFragment_FieldRange(t *testing.T) { } // Query for equality. - if b, err := f.FieldRange(pilosa.RangeOpEQ, bitDepth, 300); err != nil { + if b, err := f.FieldRange(pql.EQ, bitDepth, 300); err != nil { t.Fatal(err) } else if !reflect.DeepEqual(b.Bits(), []uint64{2000, 4000}) { t.Fatalf("unexpected bits: %+v", b.Bits()) @@ -302,28 +303,28 @@ func TestFragment_FieldRange(t *testing.T) { } // Query for fields less than (ending with set bit). - if b, err := f.FieldRange(pilosa.RangeOpLT, bitDepth, 301); err != nil { + if b, err := f.FieldRange(pql.LT, bitDepth, 301); err != nil { t.Fatal(err) } else if !reflect.DeepEqual(b.Bits(), []uint64{2000, 5000, 6000}) { t.Fatalf("unexpected bits: %+v", b.Bits()) } // Query for fields less than (ending with unset bit). - if b, err := f.FieldRange(pilosa.RangeOpLT, bitDepth, 300); err != nil { + if b, err := f.FieldRange(pql.LT, bitDepth, 300); err != nil { t.Fatal(err) } else if !reflect.DeepEqual(b.Bits(), []uint64{5000, 6000}) { t.Fatalf("unexpected bits: %+v", b.Bits()) } // Query for fields less than or equal to (ending with set bit). - if b, err := f.FieldRange(pilosa.RangeOpLTE, bitDepth, 301); err != nil { + if b, err := f.FieldRange(pql.LTE, bitDepth, 301); err != nil { t.Fatal(err) } else if !reflect.DeepEqual(b.Bits(), []uint64{2000, 4000, 5000, 6000}) { t.Fatalf("unexpected bits: %+v", b.Bits()) } // Query for fields less than or equal to (ending with unset bit). - if b, err := f.FieldRange(pilosa.RangeOpLTE, bitDepth, 300); err != nil { + if b, err := f.FieldRange(pql.LTE, bitDepth, 300); err != nil { t.Fatal(err) } else if !reflect.DeepEqual(b.Bits(), []uint64{2000, 5000, 6000}) { t.Fatalf("unexpected bits: %+v", b.Bits()) @@ -350,28 +351,28 @@ func TestFragment_FieldRange(t *testing.T) { } // Query for fields greater than (ending with unset bit). - if b, err := f.FieldRange(pilosa.RangeOpGT, bitDepth, 300); err != nil { + if b, err := f.FieldRange(pql.GT, bitDepth, 300); err != nil { t.Fatal(err) } else if !reflect.DeepEqual(b.Bits(), []uint64{1000, 3000, 4000}) { t.Fatalf("unexpected bits: %+v", b.Bits()) } // Query for fields greater than (ending with set bit). - if b, err := f.FieldRange(pilosa.RangeOpGT, bitDepth, 301); err != nil { + if b, err := f.FieldRange(pql.GT, bitDepth, 301); err != nil { t.Fatal(err) } else if !reflect.DeepEqual(b.Bits(), []uint64{1000, 3000}) { t.Fatalf("unexpected bits: %+v", b.Bits()) } // Query for fields greater than or equal to (ending with unset bit). - if b, err := f.FieldRange(pilosa.RangeOpGTE, bitDepth, 300); err != nil { + if b, err := f.FieldRange(pql.GTE, bitDepth, 300); err != nil { t.Fatal(err) } else if !reflect.DeepEqual(b.Bits(), []uint64{1000, 2000, 3000, 4000}) { t.Fatalf("unexpected bits: %+v", b.Bits()) } // Query for fields greater than or equal to (ending with set bit). - if b, err := f.FieldRange(pilosa.RangeOpGTE, bitDepth, 301); err != nil { + if b, err := f.FieldRange(pql.GTE, bitDepth, 301); err != nil { t.Fatal(err) } else if !reflect.DeepEqual(b.Bits(), []uint64{1000, 3000, 4000}) { t.Fatalf("unexpected bits: %+v", b.Bits()) diff --git a/frame.go b/frame.go index 19c25eeab..9ca2d6109 100644 --- a/frame.go +++ b/frame.go @@ -27,6 +27,7 @@ import ( "github.com/gogo/protobuf/proto" "github.com/pilosa/pilosa/internal" + "github.com/pilosa/pilosa/pql" ) // Default frame settings. @@ -40,15 +41,6 @@ const ( DefaultCacheSize = 50000 ) -// List of operators for field range queries. -const ( - RangeOpEQ = "eq" - RangeOpLT = "lt" - RangeOpLTE = "lte" - RangeOpGT = "gt" - RangeOpGTE = "gte" -) - // Frame represents a container for views. type Frame struct { mu sync.Mutex @@ -663,7 +655,7 @@ func (f *Frame) FieldSum(filter *Bitmap, name string) (sum, count int64, err err return int64(vsum) + (int64(vcount) * field.Min), int64(vcount), nil } -func (f *Frame) FieldRange(name, op string, predicate int64) (*Bitmap, error) { +func (f *Frame) FieldRange(name string, op pql.Token, predicate int64) (*Bitmap, error) { // Retrieve and validate field. field := f.Field(name) if field == nil { diff --git a/pql/ast.go b/pql/ast.go index 9555f9d90..48e5f1771 100644 --- a/pql/ast.go +++ b/pql/ast.go @@ -198,6 +198,16 @@ func (c *Call) IsInverse(rowLabel, columnLabel string) bool { return false } +// HasConditionArg returns true if any arg is a conditional. +func (c *Call) HasConditionArg() bool { + for _, v := range c.Args { + if _, ok := v.(*Condition); ok { + return true + } + } + return false +} + // Condition represents an operation & value. // When used in an argument map it represents a binary expression. type Condition struct { diff --git a/view.go b/view.go index 31a7a53c0..99ce3f7f9 100644 --- a/view.go +++ b/view.go @@ -25,6 +25,7 @@ import ( "sync" "github.com/pilosa/pilosa/internal" + "github.com/pilosa/pilosa/pql" ) // View layout modes. @@ -314,7 +315,7 @@ func (v *View) FieldSum(filter *Bitmap, bitDepth uint) (sum, count uint64, err e } // FieldRange returns bitmaps with a field value encoding matching the predicate. -func (v *View) FieldRange(op string, bitDepth uint, predicate uint64) (*Bitmap, error) { +func (v *View) FieldRange(op pql.Token, bitDepth uint, predicate uint64) (*Bitmap, error) { bm := NewBitmap() for _, frag := range v.Fragments() { other, err := frag.FieldRange(op, bitDepth, predicate) From cc6dd708f55105d65385d14ae93167577ff7139c Mon Sep 17 00:00:00 2001 From: Todd Gruben Date: Thu, 24 Aug 2017 15:32:08 -0500 Subject: [PATCH 31/46] increased test coverage for BitmapSegments;fixed bug in Xor --- bitmap.go | 8 +++-- bitmap_test.go | 92 ++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 98 insertions(+), 2 deletions(-) create mode 100644 bitmap_test.go diff --git a/bitmap.go b/bitmap.go index eb8f7f799..72296efab 100644 --- a/bitmap.go +++ b/bitmap.go @@ -103,10 +103,14 @@ func (b *Bitmap) Xor(other *Bitmap) *Bitmap { itr := newMergeSegmentIterator(b.segments, other.segments) for s0, s1 := itr.next(); s0 != nil || s1 != nil; s0, s1 = itr.next() { - // Ignore non-overlapping segments. - if s0 == nil || s1 == nil { + if s1 == nil { + segments = append(segments, *s0) + continue + } else if s0 == nil { + segments = append(segments, *s1) continue } + segments = append(segments, *s0.Xor(s1)) } diff --git a/bitmap_test.go b/bitmap_test.go new file mode 100644 index 000000000..4fcd1b115 --- /dev/null +++ b/bitmap_test.go @@ -0,0 +1,92 @@ +// Copyright 2017 Pilosa Corp. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package pilosa_test + +import ( + "reflect" + "testing" + + "github.com/pilosa/pilosa" +) + +// Ensure a bitmap can be merged +func TestBitmap_Merge(t *testing.T) { + bm1 := pilosa.NewBitmap(1, 2, 3, SliceWidth+1, 2*SliceWidth) + bm2 := pilosa.NewBitmap(3, 4, 5) + bm1.Merge(bm2) + + if bm1.Count() != 7 { + t.Fatalf("Count after merge %d != 7\n", bm1.Count()) + } + +} + +// Ensure a bitmap can Xor'ed +func TestBitmap_Xor(t *testing.T) { + bm1 := pilosa.NewBitmap(0, 1, SliceWidth) + bm2 := pilosa.NewBitmap(0, 2*SliceWidth) + exp := []uint64{1, SliceWidth, 2 * SliceWidth} + + res := bm1.Xor(bm2) + if res.Count() != 3 { + t.Fatalf("Test 1 Count after xor %d != 3\n", res.Count()) + } + + if !reflect.DeepEqual(res.Bits(), exp) { + t.Fatalf("Test 2 Results %v != expected %v\n", res.Bits(), exp) + } + res = bm2.Xor(bm1) + if res.Count() != 3 { + t.Fatalf("Test 3 Count after xor %d != 3\n", res.Count()) + } + if !reflect.DeepEqual(res.Bits(), exp) { + t.Fatalf("Test 4 Results %v != expected %v\n", res.Bits(), exp) + } + +} +func TestBitmap_Union_Segment(t *testing.T) { + bm1 := pilosa.NewBitmap(0, 1, SliceWidth) + bm2 := pilosa.NewBitmap(0, 2*SliceWidth) + exp := []uint64{0, 1, SliceWidth, 2 * SliceWidth} + res := bm1.Union(bm2) + + if res.Count() != 4 { + t.Fatalf("Test 1 Count after Union %d != 5\n", res.Count()) + } + if !reflect.DeepEqual(res.Bits(), exp) { + t.Fatalf("Test 2 Union Results %v != expected %v\n", res.Bits(), exp) + } + res = bm2.Union(bm1) + if res.Count() != 4 { + t.Fatalf("Test 3 Count after xor %d != 5\n", res.Count()) + } + if !reflect.DeepEqual(res.Bits(), exp) { + t.Fatalf("Test 2 Union Results %v != expected %v\n", res.Bits(), exp) + } +} + +func TestBitmap_Difference_Segment(t *testing.T) { + bm1 := pilosa.NewBitmap(0, 1, SliceWidth) + bm2 := pilosa.NewBitmap(0, 2*SliceWidth) + exp := []uint64{1, SliceWidth} + res := bm1.Difference(bm2) + + if res.Count() != 2 { + t.Fatalf("Test 1 Count after Difference %d != 5\n", res.Count()) + } + if !reflect.DeepEqual(res.Bits(), exp) { + t.Fatalf("Test 2 Difference Results %v != expected %v\n", res.Bits(), exp) + } +} From 60a4ad0c528a06915728daa8eb019a461416c83e Mon Sep 17 00:00:00 2001 From: Michael Baird Date: Fri, 25 Aug 2017 08:11:00 -0500 Subject: [PATCH 32/46] Use the full view name in the Delete API. Will need to append the prefix: `standard_`, `inverse_`, or `field_` --- frame.go | 21 ++------------------- frame_test.go | 4 ++-- handler.go | 6 ------ handler_test.go | 7 ++++--- 4 files changed, 8 insertions(+), 30 deletions(-) diff --git a/frame.go b/frame.go index ad44cfb56..332048cf3 100644 --- a/frame.go +++ b/frame.go @@ -515,31 +515,14 @@ func (f *Frame) newView(path, name string) *View { return view } -// DeleteView removes the standard and inverse view from the frame. +// DeleteView removes the view from the frame. func (f *Frame) DeleteView(name string) error { - // Delete the standard view. - if err := f.deleteView(ViewStandard + "_" + name); err != nil { - return err - } - - // Delete the inverse view. - if f.InverseEnabled() { - if err := f.deleteView(ViewInverse + "_" + name); err != nil { - return err - } - } - - return nil -} - -// deleteView removes view from the frame. -func (f *Frame) deleteView(name string) error { view := f.views[name] if view == nil { return ErrInvalidView } - // Close data files before deletion + // Close data files before deletion. if err := view.Close(); err != nil { return err } diff --git a/frame_test.go b/frame_test.go index 8a8db3f27..50f5fdef5 100644 --- a/frame_test.go +++ b/frame_test.go @@ -323,7 +323,7 @@ func TestFrame_DeleteView(t *testing.T) { t.Fatal("expected view") } - err = f.DeleteView("v") + err = f.DeleteView(viewName) if err != nil { t.Fatal(err) } @@ -332,7 +332,7 @@ func TestFrame_DeleteView(t *testing.T) { t.Fatal("view still exists in frame") } - // // Retrieve existing view. + // Recreate view with same name, verify that the old view was not reused. view2, err := f.CreateViewIfNotExists(viewName) if err != nil { t.Fatal(err) diff --git a/handler.go b/handler.go index cfc96c397..f876dc9b7 100644 --- a/handler.go +++ b/handler.go @@ -805,12 +805,6 @@ func (h *Handler) handleDeleteView(w http.ResponseWriter, r *http.Request) { return } - // Check the frame for time quantum. - if f.TimeQuantum() == "" { - http.Error(w, "frame does not contain a Time Quantum", http.StatusBadRequest) - return - } - // Delete the view. if err := f.DeleteView(viewName); err != nil { http.Error(w, err.Error(), http.StatusBadRequest) diff --git a/handler_test.go b/handler_test.go index c8e0d949c..dacb04b38 100644 --- a/handler_test.go +++ b/handler_test.go @@ -1516,19 +1516,20 @@ func TestHandler_GetTimeStamp(t *testing.T) { func TestHandler_DeleteView(t *testing.T) { hldr := test.MustOpenHolder() defer hldr.Close() - hldr.MustCreateFragmentIfNotExists("i0", "f0", pilosa.ViewStandard+"_2017", 1).MustSetBits(30, (1*SliceWidth)+1) + viewName := pilosa.ViewStandard + "_2017" + hldr.MustCreateFragmentIfNotExists("i0", "f0", viewName, 1).MustSetBits(30, (1*SliceWidth)+1) hldr.Index("i0").Frame("f0").SetTimeQuantum("YMD") h := test.NewHandler() h.Holder = hldr.Holder h.Cluster = test.NewCluster(1) w := httptest.NewRecorder() - h.ServeHTTP(w, test.MustNewHTTPRequest("DELETE", "/index/i0/frame/f0/view/2017", strings.NewReader(""))) + h.ServeHTTP(w, test.MustNewHTTPRequest("DELETE", "/index/i0/frame/f0/view/standard_2017", strings.NewReader(""))) if w.Code != http.StatusOK { t.Fatalf("unexpected status code: %d", w.Code) } else if body := w.Body.String(); body != `{}`+"\n" { t.Fatalf("unexpected body: %s", body) - } else if f := hldr.Index("i0").Frame("f0").View(pilosa.ViewStandard + "_2017"); f != nil { + } else if f := hldr.Index("i0").Frame("f0").View(viewName); f != nil { t.Fatal("expected nil view") } } From cef489c72629bec1343cff638215c9c7cc60f03c Mon Sep 17 00:00:00 2001 From: Travis Date: Fri, 25 Aug 2017 13:34:57 -0500 Subject: [PATCH 33/46] ensure new containers have a type --- roaring/roaring.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/roaring/roaring.go b/roaring/roaring.go index e818bf9d8..fa744e829 100644 --- a/roaring/roaring.go +++ b/roaring/roaring.go @@ -2508,7 +2508,7 @@ func differenceRunBitmap(a, b *container) *container { func differenceRunIterator(a *container, itr containerIterator) *container { - output := &container{runs: make([]interval16, 0, a.n)} + output := &container{runs: make([]interval16, 0, a.n), container_type: ContainerRun} vb, eof := itr.next() j := 0 @@ -2576,7 +2576,7 @@ func differenceRunRun(a, b *container) *container { alen := len(a.runs) blen := len(b.runs) - output := &container{runs: make([]interval16, 0, alen+blen)} // TODO allocate max then truncate? or something else + output := &container{runs: make([]interval16, 0, alen+blen), container_type: ContainerRun} // TODO allocate max then truncate? or something else // cardinality upper bound: sum of number of runs // each B-run could split an A-run in two, up to len(b.runs) times From 31f1fe0be13568eced6ae550307d16d233fb877a Mon Sep 17 00:00:00 2001 From: Travis Date: Fri, 25 Aug 2017 19:23:09 -0500 Subject: [PATCH 34/46] set container.n in differenceRunRun --- roaring/roaring.go | 1 + roaring/roaring_internal_test.go | 5 +++++ 2 files changed, 6 insertions(+) diff --git a/roaring/roaring.go b/roaring/roaring.go index fa744e829..77dfd8803 100644 --- a/roaring/roaring.go +++ b/roaring/roaring.go @@ -2621,6 +2621,7 @@ func differenceRunRun(a, b *container) *container { } } + output.n = output.count() return output } diff --git a/roaring/roaring_internal_test.go b/roaring/roaring_internal_test.go index c0caed990..5d1a985d1 100644 --- a/roaring/roaring_internal_test.go +++ b/roaring/roaring_internal_test.go @@ -1601,6 +1601,7 @@ func TestDifferenceRunRun(t *testing.T) { aruns []interval16 bruns []interval16 exp []interval16 + expn int }{ { // this tests all six overlap combinations @@ -1609,6 +1610,7 @@ func TestDifferenceRunRun(t *testing.T) { aruns: []interval16{{start: 3, last: 6}, {start: 13, last: 16}, {start: 24, last: 26}, {start: 33, last: 38}, {start: 43, last: 46}, {start: 53, last: 56}}, bruns: []interval16{{start: 1, last: 8}, {start: 11, last: 14}, {start: 21, last: 23}, {start: 35, last: 37}, {start: 44, last: 48}, {start: 57, last: 59}}, exp: []interval16{{start: 15, last: 16}, {start: 24, last: 26}, {start: 33, last: 34}, {start: 38, last: 38}, {start: 43, last: 43}, {start: 53, last: 56}}, + expn: 13, }, } for i, test := range tests { @@ -1620,6 +1622,9 @@ func TestDifferenceRunRun(t *testing.T) { if !reflect.DeepEqual(ret.runs, test.exp) { t.Fatalf("test #%v expected %v, but got %v", i, test.exp, ret.runs) } + if ret.n != test.expn { + t.Fatalf("test #%v expected n=%v, but got n=%v", i, test.expn, ret.n) + } } } From 52428ee6ac9755e40a01893e27e4a916577dee87 Mon Sep 17 00:00:00 2001 From: Travis Date: Sat, 26 Aug 2017 11:47:59 -0500 Subject: [PATCH 35/46] Correct a comment in the tests --- test/holder.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/holder.go b/test/holder.go index 9523db407..4cd0642e4 100644 --- a/test/holder.go +++ b/test/holder.go @@ -43,8 +43,8 @@ func (h *Holder) Close() error { return h.Holder.Close() } -// Reopen closes the holder and instantiates and opens a new holder. -// note that the hold most be Closed first +// Reopen instantiates and opens a new holder. +// Note that the holder must be Closed first. func (h *Holder) Reopen() error { path, logOutput := h.Path, h.Holder.LogOutput h.Holder = pilosa.NewHolder() From 5cbd31208579fc310f2d64ff4343a4690ca607d5 Mon Sep 17 00:00:00 2001 From: Michael Baird Date: Mon, 28 Aug 2017 09:23:06 -0500 Subject: [PATCH 36/46] Send delete view message to all nodes regardless if the receiving node contains that view. --- handler.go | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/handler.go b/handler.go index f876dc9b7..c7aa45ac1 100644 --- a/handler.go +++ b/handler.go @@ -807,8 +807,11 @@ func (h *Handler) handleDeleteView(w http.ResponseWriter, r *http.Request) { // Delete the view. if err := f.DeleteView(viewName); err != nil { - http.Error(w, err.Error(), http.StatusBadRequest) - return + // Ingore this error becuase views do not exist on all nodes due to slice distribution. + if err != ErrInvalidView { + http.Error(w, err.Error(), http.StatusBadRequest) + return + } } // Send the delete view message to all nodes. From 6a70f27f59602e31ebfba6ee3d9cdc820d2deb2a Mon Sep 17 00:00:00 2001 From: Todd Gruben Date: Mon, 28 Aug 2017 13:46:18 -0500 Subject: [PATCH 37/46] account for all same values in rank --- cache.go | 5 +++++ cache_test.go | 35 +++++++++++++++++++++++++++++++++++ 2 files changed, 40 insertions(+) create mode 100644 cache_test.go diff --git a/cache.go b/cache.go index 242e5a6ab..ba33fe258 100644 --- a/cache.go +++ b/cache.go @@ -273,6 +273,11 @@ func (c *RankCache) recalculate() { if cnt <= c.thresholdValue { delete(c.entries, id) } + // prevent from deleting the whole cache if all the same value + if len(c.entries) <= int(c.maxEntries) { + break + } + } } } diff --git a/cache_test.go b/cache_test.go new file mode 100644 index 000000000..21d8acf07 --- /dev/null +++ b/cache_test.go @@ -0,0 +1,35 @@ +// Copyright 2017 Pilosa Corp. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package pilosa_test + +import ( + "testing" + + "github.com/pilosa/pilosa" +) + +// Ensure a bitmap query can be executed. +func TestCache_Rank(t *testing.T) { + cacheSize := uint32(3) + cache := pilosa.NewRankCache(cacheSize) + for i := 1; i < int(2*cacheSize); i++ { + cache.Add(uint64(i), 3) + } + cache.Recalculate() + if cache.Len() != int(cacheSize) { + t.Fatalf("unexpected cache Size: %d!=%d expected\n", cache.Len(), cacheSize) + } + +} From 60ee6dcfdda3a3e112c3f9c317dd9100f9bbba64 Mon Sep 17 00:00:00 2001 From: Todd Gruben Date: Mon, 28 Aug 2017 14:12:31 -0500 Subject: [PATCH 38/46] constant cache removal --- cache.go | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/cache.go b/cache.go index ba33fe258..890a38e0b 100644 --- a/cache.go +++ b/cache.go @@ -256,8 +256,10 @@ func (c *RankCache) recalculate() { length := len(c.rankings) c.stats.Gauge("RankCache", float64(length), 1.0) + var remove_items []BitmapPair // cached, ordered list if length > int(c.maxEntries) { c.thresholdValue = rankings[c.maxEntries].Count + remove_items = c.rankings[c.maxEntries:] c.rankings = c.rankings[0:c.maxEntries] } else { c.thresholdValue = 1 @@ -269,15 +271,8 @@ func (c *RankCache) recalculate() { // If size is larger than the threshold then trim it. if len(c.entries) > c.thresholdBuffer { c.stats.Count("cache.threshold", 1, 1.0) - for id, cnt := range c.entries { - if cnt <= c.thresholdValue { - delete(c.entries, id) - } - // prevent from deleting the whole cache if all the same value - if len(c.entries) <= int(c.maxEntries) { - break - } - + for _, pair := range remove_items { + delete(c.entries, pair.ID) } } } From 773019a0f33fc8d6b80742cec81f58bb5c80f341 Mon Sep 17 00:00:00 2001 From: Todd Gruben Date: Mon, 28 Aug 2017 14:15:59 -0500 Subject: [PATCH 39/46] corrected nameing --- cache.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/cache.go b/cache.go index 890a38e0b..2e66d35b9 100644 --- a/cache.go +++ b/cache.go @@ -256,10 +256,10 @@ func (c *RankCache) recalculate() { length := len(c.rankings) c.stats.Gauge("RankCache", float64(length), 1.0) - var remove_items []BitmapPair // cached, ordered list + var removeItems []BitmapPair // cached, ordered list if length > int(c.maxEntries) { c.thresholdValue = rankings[c.maxEntries].Count - remove_items = c.rankings[c.maxEntries:] + removeItems = c.rankings[c.maxEntries:] c.rankings = c.rankings[0:c.maxEntries] } else { c.thresholdValue = 1 @@ -271,7 +271,7 @@ func (c *RankCache) recalculate() { // If size is larger than the threshold then trim it. if len(c.entries) > c.thresholdBuffer { c.stats.Count("cache.threshold", 1, 1.0) - for _, pair := range remove_items { + for _, pair := range removeItems { delete(c.entries, pair.ID) } } From fd73349cd677494535a117b63122c7b50479d905 Mon Sep 17 00:00:00 2001 From: Todd Gruben Date: Tue, 29 Aug 2017 14:45:19 -0500 Subject: [PATCH 40/46] remove go1.7 from travis --- .travis.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index fe4a2b8f1..457b991b1 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,6 +1,5 @@ language: go go: - - 1.7 - 1.8 - master addons: From a0ff973c03b77e0b30b432620bc3aeb4bb9b38bc Mon Sep 17 00:00:00 2001 From: Todd Gruben Date: Tue, 29 Aug 2017 14:49:19 -0500 Subject: [PATCH 41/46] added go1.9 to travis --- .travis.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.travis.yml b/.travis.yml index 457b991b1..beb41e392 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,6 +1,7 @@ language: go go: - 1.8 + - 1.9 - master addons: before_install: From 3d6dbcfc7bc0aed9835c8fa29101213b7de8db83 Mon Sep 17 00:00:00 2001 From: Matthew Jaffee Date: Wed, 30 Aug 2017 10:36:28 -0500 Subject: [PATCH 42/46] use context everywhere instead of golang.org version --- ctl/export_test.go | 2 +- ctl/import_test.go | 2 +- ctl/restore_test.go | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/ctl/export_test.go b/ctl/export_test.go index 433078374..4f9d9b184 100644 --- a/ctl/export_test.go +++ b/ctl/export_test.go @@ -16,13 +16,13 @@ package ctl import ( "bytes" + "context" "net/http" "strings" "testing" "github.com/pilosa/pilosa" "github.com/pilosa/pilosa/test" - "golang.org/x/net/context" ) func TestExportCommand_Validation(t *testing.T) { diff --git a/ctl/import_test.go b/ctl/import_test.go index 8c4d3793e..dd9eba675 100644 --- a/ctl/import_test.go +++ b/ctl/import_test.go @@ -16,6 +16,7 @@ package ctl import ( "bytes" + "context" "io" "io/ioutil" "net/http" @@ -24,7 +25,6 @@ import ( "github.com/pilosa/pilosa" "github.com/pilosa/pilosa/test" - "golang.org/x/net/context" ) func TestImportCommand_Validation(t *testing.T) { diff --git a/ctl/restore_test.go b/ctl/restore_test.go index 5fd5af1d8..39ac92388 100644 --- a/ctl/restore_test.go +++ b/ctl/restore_test.go @@ -17,13 +17,13 @@ package ctl import ( "bufio" "bytes" + "context" "io" "io/ioutil" "testing" "github.com/pilosa/pilosa" "github.com/pilosa/pilosa/test" - "golang.org/x/net/context" ) func TestRestoreCommand_FileRequired(t *testing.T) { From b0230f8ba99aa1aa5f4eb65cdcb6298fa9457aa0 Mon Sep 17 00:00:00 2001 From: Matthew Jaffee Date: Wed, 30 Aug 2017 10:54:08 -0500 Subject: [PATCH 43/46] dep ensure --- Gopkg.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Gopkg.lock b/Gopkg.lock index 17ba49821..3cfe7b2cf 100644 --- a/Gopkg.lock +++ b/Gopkg.lock @@ -194,6 +194,6 @@ [solve-meta] analyzer-name = "dep" analyzer-version = 1 - inputs-digest = "84ff0992f3a6023a9d4832d6aea43d6aec19878a4b9e991ba8ff8269b589e816" + inputs-digest = "e8e78a7c61547d8f4d967c8deac9334b3151e7c10c4f7909e80758956e9c8204" solver-name = "gps-cdcl" solver-version = 1 From 64335e71c5afb013d4644559a6aebcf50a8d15cc Mon Sep 17 00:00:00 2001 From: Alan Bernstein Date: Wed, 30 Aug 2017 10:56:54 -0500 Subject: [PATCH 44/46] Update repo url --- docs/tutorials.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/tutorials.md b/docs/tutorials.md index 995673ca2..d304dc80d 100644 --- a/docs/tutorials.md +++ b/docs/tutorials.md @@ -197,7 +197,7 @@ for pcount, topn in zip(pcounts, resp.json()['results']): average_amounts.append(float(wsum)/count) ``` -For more examples and details, see this [ipython notebook](https://github.com/alanbernstein/pilosa-notebooks/blob/master/taxi-use-case.ipynb). +For more examples and details, see this [ipython notebook](https://github.com/pilosa/notebooks/blob/master/taxi-use-case.ipynb). ### Chemical similarity search From 655f84a4a473f8b0f768342694ef77e04940ac6c Mon Sep 17 00:00:00 2001 From: Matthew Jaffee Date: Thu, 31 Aug 2017 16:42:23 -0500 Subject: [PATCH 45/46] add cluster config example to docs --- docs/configuration.md | 40 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/docs/configuration.md b/docs/configuration.md index 2ef18a532..0c63e635a 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -207,3 +207,43 @@ Any flag that has a value that is a comma separated list on the command line bec [metric] poll-interval = "0m15s" ``` + +### Example Cluster Configuration + +A three node cluster could be minimally configured as follows: + +#### Node 0 + + data-dir = "/home/pilosa/data" + bind = "node0.pilosa.com:10101" + gossip-port = 12000 + gossip-seed = "node0.pilosa.com:12000" + + [cluster] + replicas = 1 + type = "gossip" + hosts = ["node0.pilosa.com:10101","node1.pilosa.com:10101","node2.pilosa.com:10101"] + +#### Node 1 + + data-dir = "/home/pilosa/data" + bind = "node1.pilosa.com:10101" + gossip-port = 12000 + gossip-seed = "node0.pilosa.com:12000" + + [cluster] + replicas = 1 + type = "gossip" + hosts = ["node0.pilosa.com:10101","node1.pilosa.com:10101","node2.pilosa.com:10101"] + +#### Node 2 + + data-dir = "/home/pilosa/data" + bind = "node2.pilosa.com:10101" + gossip-port = 12000 + gossip-seed = "node0.pilosa.com:12000" + + [cluster] + replicas = 1 + type = "gossip" + hosts = ["node0.pilosa.com:10101","node1.pilosa.com:10101","node2.pilosa.com:10101"] From fbc4b6bd63c7856bed0199738739f549cdaf81e2 Mon Sep 17 00:00:00 2001 From: Matthew Jaffee Date: Fri, 1 Sep 2017 11:12:51 -0500 Subject: [PATCH 46/46] fix a number of typos in input definition docs --- docs/input-definition.md | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/docs/input-definition.md b/docs/input-definition.md index 459f1752d..c0c34ff7f 100644 --- a/docs/input-definition.md +++ b/docs/input-definition.md @@ -31,7 +31,8 @@ curl localhost:10101/index/repository/input-definition/stargazer \ "frames": [ { "name": "language", - "options": { + "options": { + "rowLabel": "language_id", "inverseEnabled": true, "timeQuantum": "YMD" } @@ -39,6 +40,7 @@ curl localhost:10101/index/repository/input-definition/stargazer \ { "name": "stargazer", "options": { + "rowLabel": "stargazer_id", "inverseEnabled": true, "timeQuantum": "YMD" } @@ -60,7 +62,7 @@ curl localhost:10101/index/repository/input-definition/stargazer \ "Go": 5, "Java": 21, "JavaScript": 13, - "Python": 17, + "Python": 17 } } ], @@ -82,7 +84,7 @@ curl localhost:10101/index/repository/input-definition/stargazer \ "valueDestination": "set-timestamp" } ], - "name": "time_value + "name": "time_value" } ] }' @@ -109,14 +111,14 @@ curl localhost:10101/index/repository/input/stargazer \ { "language_id": "Go", "repo_id": 91720568, - "stargazer_id": 513114 + "stargazer_id": 513114, "time_value": "2017-05-18T20:40" }, { "language_id": "Python", "repo_id": 95122322 - }' - ] + } + ]' ``` As defined in the input definition, field name `language_id` maps language to a corresponding id defined in `valueMap` and sets the appropriate bit in the `language` frame. The value corresponding to field name `stargazer_id` is added to the `stargazer` frame as rowID. @@ -126,7 +128,7 @@ The data input above is equivalent to the following `SetBit()` operations: curl localhost:10101/index/repository/query \ -X POST \ -d 'SetBit(frame="stargazer", repo_id=91720568, stargazer_id=513114) - 'SetBit(frame="stargazer", repo_id=91720568, stargazer_id=513114, timestamp="2017-05-18T20:40") + SetBit(frame="stargazer", repo_id=91720568, stargazer_id=513114, timestamp="2017-05-18T20:40") SetBit(frame="language", repo_id=91720568, language_id=5) SetBit(frame="language", repo_id=95122322, language_id=17) '