diff --git a/.gitignore b/.gitignore index cd99c1b06..5fe629f6a 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ default.etcd/ *.test vendor +.protoc-gen-gofast diff --git a/Makefile b/Makefile index f55fb8445..2235a4655 100644 --- a/Makefile +++ b/Makefile @@ -1,6 +1,7 @@ -.PHONY: glide vendor-update docker pilosa pilosactl crossbuild install +.PHONY: glide vendor-update docker pilosa pilosactl crossbuild install generate GLIDE := $(shell command -v glide 2>/dev/null) +PROTOC := $(shell command -v protoc 2>/dev/null) VERSION := $(shell git describe --tags) IDENTIFIER := $(VERSION)-$(GOOS)-$(GOARCH) CLONE_URL=github.com/pilosa/pilosa @@ -43,5 +44,15 @@ install: vendor go install $(LDFLAGS) $(FLAGS) $(CLONE_URL)/cmd/pilosa go install $(LDFLAGS) $(FLAGS) $(CLONE_URL)/cmd/pilosactl +.protoc-gen-gofast: vendor +ifndef PROTOC + $(error "protoc is not available please install protoc from https://github.com/google/protobuf/releases") +endif + go build -o .protoc-gen-gofast ./vendor/github.com/gogo/protobuf/protoc-gen-gofast + cp ./.protoc-gen-gofast $(GOPATH)/bin/protoc-gen-gofast + +generate: .protoc-gen-gofast + go generate github.com/pilosa/pilosa/internal + docker: docker build -t pilosa:latest . diff --git a/README.md b/README.md index f1e9a7f32..8236798c4 100644 --- a/README.md +++ b/README.md @@ -100,22 +100,55 @@ Return a list of all databases and frames in the index: $ curl "http://127.0.0.1:15000/schema" ``` +### Database and Frame Schema + +Before running a query, the corresponding database and frame must be created. Note that database and frame names can contain only lower case letters, numbers, dash (`-`), underscore (`_`) and dot (`.`). + +You can create the database `sample-db` using: + +```sh +$ curl -XPOST "http://127.0.0.1:15000/db" \ + -d '{"db": "sample-db"}' +``` + +Optionally, you can specify the column label on database creation: + +```sh +$ curl -XPOST "http://127.0.0.1:15000/db" \ + -d '{"db": "sample-db", "columnLabel": "user"}' +``` + +The frame `collaboration` may be created using the following call: + +```sh +$ curl -XPOST "http://127.0.0.1:15000/frame" \ + -d '{"db": "sample-db", "frame": "collaboration"}' +``` + +It is possible to specify the frame row label on frame creation: + +```sh +$ curl -XPOST "http://127.0.0.1:15000/frame" \ + -d '{"db": "sample-db", "frame": "collaboration"}, "options": {"rowLabel": "project"}}' +``` ### Queries Queries to Pilosa require sending a POST request where the query itself is sent as POST data. You specify the database on which to perform the query with a URL argument `db=database-name`. -A query sent to database `exampleDB` will have the following format: +In this section, we assume both the database `sample-db` with column label `user` and the frame `collaboration` with row label `project` was created. + +A query sent to database `sample-db` will have the following format: ```sh -$ curl -X POST "http://127.0.0.1:15000/query?db=exampleDB" -d 'Query()' +$ curl -X POST "http://127.0.0.1:15000/query?db=sample-db" -d 'Query()' ``` The `Query()` object referenced above should be made up of one or more of the query types listed below. So for example, a SetBit() query would look like this: ```sh -$ curl -X POST "http://127.0.0.1:15000/query?db=exampleDB" -d 'SetBit(id=10, frame="foo", profileID=1)' +$ curl -X POST "http://127.0.0.1:15000/query?db=sample-db" -d 'SetBit(project=10, frame="collaboration", user=1)' ``` Query results have the format `{"results":[]}`, where `results` is a list of results for each `Query()`. This @@ -123,26 +156,26 @@ means that you can provide multiple `Query()` objects with each HTTP request and the results of all of the queries. ```sh -$ curl -X POST "http://127.0.0.1:15000/query?db=exampleDB" -d 'Query() Query() Query()' +$ curl -X POST "http://127.0.0.1:15000/query?db=sample-db" -d 'Query() Query() Query()' ``` --- #### SetBit() ``` -SetBit(id=10, frame="foo", profileID=1) +SetBit(project=10, frame="collaboration", user=1) ``` A return value of `{"results":[true]}` indicates that the bit was toggled from 0 to 1. A return value of `{"results":[false]}` indicates that the bit was already set to 1 and therefore nothing changed. SetBit accepts an optional `timestamp` field: ``` -SetBit(id=10, frame=f, profileID=2, timestamp="2016-12-11T10:09:07") +SetBit(project=10, frame="collaboration", user=2, timestamp="2016-12-11T10:09:07") ``` --- #### ClearBit() ``` -ClearBit(id=10, frame="foo", profileID=1) +ClearBit(project=10, frame="collaboration", user=1) ``` A return value of `{"results":[true]}` indicates that the bit was toggled from 1 to 0. A return value of `{"results":[false]}` indicates that the bit was already set to 0 and therefore nothing changed. @@ -150,7 +183,7 @@ A return value of `{"results":[false]}` indicates that the bit was already set t --- #### SetBitmapAttrs() ``` -SetBitmapAttrs(id=10, frame="foo", category=123, color="blue", happy=true) +SetBitmapAttrs(project=10, frame="collaboration", stars=123, url="http://projects.pilosa.com/10", active=true) ``` Returns `{"results":[null]}` @@ -158,7 +191,7 @@ Returns `{"results":[null]}` #### SetProfileAttrs() --- ``` -SetProfileAttrs(id=10, category=123, color="blue", happy=true) +SetProfileAttrs(user=10, friends=123, username="mrpi", active=true) ``` Returns `{"results":[null]}` @@ -166,20 +199,20 @@ Returns `{"results":[null]}` --- #### Bitmap() ``` -Bitmap(id=10, frame="foo") +Bitmap(project=10, frame="collaboration") ``` -Returns `{"results":[{"attrs":{"category":123,"color":"blue","happy":true},"bits":[1,2]}]}` where `attrs` are the +Returns `{"results":[{"attrs":{"stars":123, "url":"http://projects.pilosa.com/10", "active":true},"bits":[1,2]}]}` where `attrs` are the attributes set using `SetBitmapAttrs()` and `bits` are the bits set using `SetBit()`. In order to return profile attributes attached to the profiles of a bitmap, add `&profiles=true` to the query string. Sample response: ``` -{"results":[{"attrs":{},"bits":[10]}],"profiles":[{"id":10,"attrs":{"category":123,"color":"blue","happy":true}}]} +{"results":[{"attrs":{},"bits":[10]}],"profiles":[{"user":10,"attrs":{"friends":123, "username":"mrpi", "active":true}}]} ``` --- #### Union() ``` -Union(Bitmap(id=10, frame="foo"), Bitmap(id=20, frame="foo"))) +Union(Bitmap(project=10, frame="collaboration"), Bitmap(project=20, frame="collaboration"))) ``` Returns a result set similar to that of a `Bitmap()` query, only the `attrs` dictionary will be empty: `{"results":[{"attrs":{},"bits":[1,2]}]}`. Note that a `Union()` query can be nested within other queries anywhere that you would otherwise provide a `Bitmap()`. @@ -187,7 +220,7 @@ Note that a `Union()` query can be nested within other queries anywhere that you --- #### Intersect() ``` -Intersect(Bitmap(id=10, frame="foo"), Bitmap(id=20, frame="foo"))) +Intersect(Bitmap(project=10, frame="collaboration"), Bitmap(project=20, frame="collaboration"))) ``` Returns a result set similar to that of a `Bitmap()` query, only the `attrs` dictionary will be empty: `{"results":[{"attrs":{},"bits":[1]}]}`. Note that an `Intersect()` query can be nested within other queries anywhere that you would otherwise provide a `Bitmap()`. @@ -195,7 +228,7 @@ Note that an `Intersect()` query can be nested within other queries anywhere tha --- #### Difference() ``` -Difference(Bitmap(id=10, frame="foo"), Bitmap(id=20, frame="foo"))) +Difference(Bitmap(project=10, frame="collaboration"), Bitmap(project=20, frame="collaboration"))) ``` `Difference()` represents all of the bits that are set in the first `Bitmap()` but are not set in the second `Bitmap()`. It returns a result set similar to that of a `Bitmap()` query, only the `attrs` dictionary will be empty: `{"results":[{"attrs":{},"bits":[2]}]}`. Note that a `Difference()` query can be nested within other queries anywhere that you would otherwise provide a `Bitmap()`. @@ -203,35 +236,38 @@ Note that a `Difference()` query can be nested within other queries anywhere tha --- #### Count() ``` -Count(Bitmap(id=10, frame="foo")) +Count(Bitmap(project=10, frame="collaboration")) ``` Returns the count of the number of bits set in `Bitmap()`: `{"results":[28]}` --- #### Range() ``` -Range(id=10, frame="foo", start="1970-01-01T00:00", end="2000-01-02T03:04") +Range(project=10, frame="collaboration", start="1970-01-01T00:00", end="2000-01-02T03:04") ``` --- #### TopN() ``` -TopN(frame="bar", n=20) +TopN(frame="geo") ``` -Returns the top 20 Bitmaps from frame `bar`. +Returns all Bitmaps in the cache from frame `geo` sorted by the count of bits. ``` -TopN(Bitmap(id=10, frame="foo"), frame="bar", n=20) +TopN(frame="geo", n=20) ``` -Returns the top 20 Bitmaps from `bar` sorted by the count of bits in the intersection with `Bitmap(id=10)`. - +Returns the top 20 Bitmaps from frame `geo`. ``` -TopN(Bitmap(id=10, frame="foo"), frame="bar", n=20, field="category", [81,82]) +TopN(Bitmap(project=10, frame="collaboration"), frame="geo", n=20) ``` +Returns the top 20 Bitmaps from `geo` sorted by the count of bits in the intersection with `Bitmap(project=10)`. -Returns the top 20 Bitmaps from `bar`in attribute `category` with values `81 or -82` sorted by the count of bits in the intersection with `Bitmap(id=10)`. +``` +TopN(Bitmap(project=10, frame="collaboration"), frame="geo", n=20, field="category", [81,82]) +``` +Returns the top 20 Bitmaps from `geo`in attribute `category` with values `81 or +82` sorted by the count of bits in the intersection with `Bitmap(project=10)`. ## Development diff --git a/attr.go b/attr.go index c85f77cad..804b51a34 100644 --- a/attr.go +++ b/attr.go @@ -22,6 +22,7 @@ const ( AttrTypeString = 1 AttrTypeUint = 2 AttrTypeBool = 3 + AttrTypeFloat = 4 ) // AttrStore represents a storage layer for attributes. @@ -265,11 +266,9 @@ func txUpdateAttrs(tx *bolt.Tx, id uint64, m map[string]interface{}) (map[string attr[k] = uint64(v) case uint: attr[k] = uint64(v) - case float64: - attr[k] = uint64(v) case int64: attr[k] = uint64(v) - case string, uint64, bool: + case string, uint64, bool, float64: attr[k] = v default: return nil, fmt.Errorf("invalid attr type: %T", v) @@ -318,8 +317,8 @@ func encodeAttr(key string, value interface{}) *internal.Attr { pb.Type = AttrTypeString pb.StringValue = value case float64: - pb.Type = AttrTypeUint - pb.UintValue = uint64(value) + pb.Type = AttrTypeFloat + pb.FloatValue = value case uint64: pb.Type = AttrTypeUint pb.UintValue = value @@ -342,6 +341,8 @@ func decodeAttr(attr *internal.Attr) (key string, value interface{}) { return attr.Key, attr.UintValue case AttrTypeBool: return attr.Key, attr.BoolValue + case AttrTypeFloat: + return attr.Key, attr.FloatValue default: return attr.Key, nil } diff --git a/internal/internal.pb.go b/internal/internal.pb.go index f0d8caac0..85f4102bd 100644 --- a/internal/internal.pb.go +++ b/internal/internal.pb.go @@ -122,11 +122,12 @@ func (m *Profile) GetAttrs() []*Attr { } type Attr struct { - Key string `protobuf:"bytes,1,opt,name=Key,proto3" json:"Key,omitempty"` - Type uint64 `protobuf:"varint,2,opt,name=Type,proto3" json:"Type,omitempty"` - StringValue string `protobuf:"bytes,3,opt,name=StringValue,proto3" json:"StringValue,omitempty"` - UintValue uint64 `protobuf:"varint,4,opt,name=UintValue,proto3" json:"UintValue,omitempty"` - BoolValue bool `protobuf:"varint,5,opt,name=BoolValue,proto3" json:"BoolValue,omitempty"` + Key string `protobuf:"bytes,1,opt,name=Key,proto3" json:"Key,omitempty"` + Type uint64 `protobuf:"varint,2,opt,name=Type,proto3" json:"Type,omitempty"` + StringValue string `protobuf:"bytes,3,opt,name=StringValue,proto3" json:"StringValue,omitempty"` + UintValue uint64 `protobuf:"varint,4,opt,name=UintValue,proto3" json:"UintValue,omitempty"` + BoolValue bool `protobuf:"varint,5,opt,name=BoolValue,proto3" json:"BoolValue,omitempty"` + FloatValue float64 `protobuf:"fixed64,6,opt,name=FloatValue,proto3" json:"FloatValue,omitempty"` } func (m *Attr) Reset() { *m = Attr{} } @@ -554,6 +555,11 @@ func (m *Attr) MarshalTo(dAtA []byte) (int, error) { } i++ } + if m.FloatValue != 0 { + dAtA[i] = 0x31 + i++ + i = encodeFixed64Internal(dAtA, i, uint64(math.Float64bits(float64(m.FloatValue)))) + } return i, nil } @@ -1171,6 +1177,9 @@ func (m *Attr) Size() (n int) { if m.BoolValue { n += 2 } + if m.FloatValue != 0 { + n += 9 + } return n } @@ -2188,6 +2197,24 @@ func (m *Attr) Unmarshal(dAtA []byte) error { } } m.BoolValue = bool(v != 0) + case 6: + if wireType != 1 { + return fmt.Errorf("proto: wrong wireType = %d for field FloatValue", wireType) + } + var v uint64 + if (iNdEx + 8) > l { + return io.ErrUnexpectedEOF + } + iNdEx += 8 + v = uint64(dAtA[iNdEx-8]) + v |= uint64(dAtA[iNdEx-7]) << 8 + v |= uint64(dAtA[iNdEx-6]) << 16 + v |= uint64(dAtA[iNdEx-5]) << 24 + v |= uint64(dAtA[iNdEx-4]) << 32 + v |= uint64(dAtA[iNdEx-3]) << 40 + v |= uint64(dAtA[iNdEx-2]) << 48 + v |= uint64(dAtA[iNdEx-1]) << 56 + m.FloatValue = float64(math.Float64frombits(v)) default: iNdEx = preIndex skippy, err := skipInternal(dAtA[iNdEx:]) @@ -3911,51 +3938,51 @@ var ( func init() { proto.RegisterFile("internal.proto", fileDescriptorInternal) } var fileDescriptorInternal = []byte{ - // 721 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0xac, 0x55, 0xcb, 0x6a, 0x14, 0x4d, - 0x14, 0xfe, 0x6b, 0xba, 0x7b, 0x2e, 0x67, 0x92, 0x61, 0x52, 0xcc, 0xff, 0xd3, 0x84, 0x9f, 0x61, - 0x28, 0x14, 0x06, 0xc1, 0x04, 0xe2, 0x46, 0x44, 0x10, 0x7b, 0x66, 0x24, 0x83, 0x26, 0x24, 0x95, - 0xe8, 0xce, 0x45, 0x25, 0x29, 0x93, 0x26, 0x7d, 0x19, 0xab, 0xab, 0xd5, 0x59, 0xba, 0x70, 0xe3, - 0x13, 0x88, 0x3e, 0x81, 0x6f, 0xe2, 0xd2, 0x47, 0x90, 0xf8, 0x22, 0x52, 0x97, 0xbe, 0x44, 0x31, - 0x66, 0xe1, 0xae, 0xbf, 0xef, 0xd4, 0x39, 0xf5, 0x9d, 0x5b, 0x35, 0xf4, 0xc2, 0x44, 0x72, 0x91, - 0xb0, 0x68, 0x63, 0x21, 0x52, 0x99, 0xe2, 0x76, 0x81, 0xc9, 0x36, 0x34, 0xa6, 0x01, 0x1e, 0x41, - 0xf7, 0x30, 0x8c, 0xf9, 0x7e, 0xce, 0x12, 0x99, 0xc7, 0x3e, 0x1a, 0xa1, 0x71, 0x87, 0xd6, 0x29, - 0x75, 0x62, 0x92, 0x46, 0x79, 0x9c, 0x3c, 0x61, 0x47, 0x3c, 0xf2, 0x1b, 0xe6, 0x44, 0x8d, 0x22, - 0x33, 0xf0, 0x1e, 0x09, 0x16, 0xf3, 0x6b, 0x04, 0x5b, 0x87, 0x36, 0x4d, 0x5f, 0xd7, 0x23, 0x95, - 0x98, 0x04, 0xd0, 0x0c, 0x42, 0x19, 0xb3, 0x05, 0xc6, 0xe0, 0x06, 0xa1, 0xcc, 0x7c, 0x34, 0x72, - 0xc6, 0x2e, 0xd5, 0xdf, 0xf8, 0x06, 0x78, 0x0f, 0xa5, 0x14, 0x99, 0xdf, 0x18, 0x39, 0xe3, 0xee, - 0x56, 0x6f, 0xa3, 0x4c, 0x4c, 0xd1, 0xd4, 0x18, 0xc9, 0x06, 0xb8, 0x7b, 0x2c, 0x14, 0xb8, 0x0f, - 0xce, 0x63, 0xbe, 0xd4, 0x0a, 0x5c, 0xaa, 0x3e, 0xf1, 0x00, 0xbc, 0x49, 0x9a, 0x27, 0x52, 0x5f, - 0xeb, 0x52, 0x03, 0xc8, 0x73, 0x70, 0x82, 0x50, 0x2a, 0x59, 0xe6, 0xea, 0xf9, 0xd4, 0xfa, 0x94, - 0x18, 0xff, 0x0f, 0x9d, 0x3d, 0x91, 0xbe, 0x08, 0x23, 0x3e, 0x9f, 0x5a, 0xe7, 0x8a, 0x50, 0x56, - 0x95, 0x5f, 0x26, 0x59, 0xbc, 0xf0, 0x9d, 0x11, 0x1a, 0x3b, 0xb4, 0x22, 0xc8, 0x03, 0x68, 0xd9, - 0xa3, 0xb8, 0x07, 0x8d, 0x32, 0x78, 0x63, 0x3e, 0xbd, 0x66, 0x3e, 0xef, 0x11, 0xb8, 0xea, 0xab, - 0x9e, 0x50, 0xc7, 0x24, 0x84, 0xc1, 0x3d, 0x5c, 0x2e, 0xb8, 0x95, 0xa4, 0xbf, 0x55, 0x03, 0x0e, - 0xa4, 0x08, 0x93, 0xd3, 0x67, 0x2c, 0xca, 0xb9, 0xd6, 0xd3, 0xa1, 0x75, 0x4a, 0xe9, 0x7d, 0x1a, - 0x26, 0xd2, 0xd8, 0x5d, 0x93, 0x4d, 0x49, 0x28, 0x6b, 0x90, 0xa6, 0x91, 0xb1, 0x7a, 0x23, 0x34, - 0x6e, 0xd3, 0x8a, 0x20, 0x9b, 0xd0, 0x52, 0x5a, 0x76, 0xd8, 0xa2, 0x52, 0x8f, 0xae, 0x52, 0xff, - 0x11, 0xc1, 0xca, 0x7e, 0xce, 0xc5, 0x92, 0xf2, 0x97, 0x39, 0xcf, 0xa4, 0x2a, 0xc2, 0x34, 0xb0, - 0x49, 0xa8, 0xe9, 0x1b, 0x80, 0xa7, 0xed, 0x76, 0x16, 0x0c, 0xc0, 0xff, 0x41, 0xf3, 0x20, 0x0a, - 0x8f, 0x79, 0xe6, 0x3b, 0x7a, 0x00, 0x2c, 0x52, 0x5d, 0xb2, 0xd5, 0xcc, 0xb4, 0xf4, 0x36, 0x2d, - 0x31, 0xf6, 0xa1, 0x55, 0x8c, 0x9d, 0xa7, 0x63, 0x15, 0x50, 0x45, 0xa3, 0x3c, 0x4e, 0x25, 0xf7, - 0x9b, 0xda, 0xc7, 0x22, 0xf2, 0x16, 0xc1, 0xaa, 0x15, 0x97, 0x2d, 0xd2, 0x24, 0xe3, 0xaa, 0xc6, - 0x33, 0x21, 0x8a, 0x1a, 0xcf, 0x84, 0xc0, 0x9b, 0xd0, 0xa2, 0x3c, 0xcb, 0x23, 0x59, 0xb4, 0xe9, - 0xdf, 0x2a, 0xd1, 0xc2, 0x37, 0x8f, 0x24, 0x2d, 0x4e, 0xe1, 0xdb, 0x35, 0x89, 0x8e, 0xf6, 0x58, - 0xab, 0x3c, 0xac, 0xa5, 0x52, 0x4d, 0xde, 0x21, 0xe8, 0xd6, 0xe2, 0xe0, 0x71, 0xb1, 0x02, 0x5a, - 0x44, 0x77, 0xab, 0x5f, 0x39, 0x1b, 0x9e, 0x16, 0x2b, 0xb2, 0x02, 0x68, 0xd7, 0xb6, 0x1e, 0xed, - 0xaa, 0x76, 0xa8, 0xb1, 0x2f, 0xee, 0xac, 0xb5, 0x43, 0xd1, 0xd4, 0x18, 0x55, 0x8d, 0x26, 0x67, - 0x2c, 0x39, 0xe5, 0x27, 0xb6, 0x7c, 0x05, 0x24, 0x9f, 0x11, 0xac, 0xce, 0xe3, 0x45, 0x2a, 0xe4, - 0x15, 0x9d, 0xd2, 0x3b, 0x5e, 0x74, 0xca, 0x2c, 0xfc, 0x00, 0x3c, 0xdd, 0x1b, 0x3d, 0x69, 0x2e, - 0x35, 0x40, 0x4f, 0x91, 0xdd, 0x1e, 0xd5, 0x28, 0xd5, 0xc2, 0x8a, 0xc0, 0x43, 0x80, 0x72, 0x7d, - 0x32, 0xdf, 0xd3, 0xe6, 0x1a, 0xa3, 0xec, 0xe5, 0x02, 0x65, 0x7e, 0x73, 0xe4, 0x8c, 0x1d, 0x5a, - 0x63, 0x08, 0x81, 0x5e, 0x21, 0xf5, 0x77, 0x7d, 0x23, 0x27, 0xd0, 0x0f, 0xa2, 0xf4, 0xf8, 0x7c, - 0xca, 0x24, 0xfb, 0x1b, 0x19, 0x0d, 0xc0, 0xd3, 0xf1, 0xec, 0xc6, 0x18, 0x40, 0xf6, 0x61, 0xad, - 0x76, 0x8b, 0x15, 0x73, 0x29, 0x79, 0x74, 0x75, 0xf2, 0x8d, 0x9f, 0x93, 0x27, 0x37, 0xc1, 0x9b, - 0xb0, 0xe3, 0xb3, 0x3f, 0x84, 0x21, 0x9f, 0x10, 0xac, 0xed, 0xb0, 0x37, 0x66, 0x2f, 0xca, 0xab, - 0xb7, 0xa1, 0x53, 0x92, 0x76, 0x31, 0x6f, 0x55, 0x93, 0xf0, 0xcb, 0xf9, 0x8a, 0x99, 0x25, 0x52, - 0x2c, 0x69, 0xe5, 0xbc, 0x7e, 0x1f, 0x7a, 0x97, 0x8d, 0xaa, 0xc6, 0xe7, 0xd5, 0xfb, 0x73, 0x6e, - 0x1e, 0xd4, 0x57, 0xfa, 0x9d, 0xb0, 0x0f, 0xaa, 0x06, 0xf7, 0x1a, 0x77, 0x51, 0xd0, 0xff, 0x72, - 0x31, 0x44, 0x5f, 0x2f, 0x86, 0xe8, 0xdb, 0xc5, 0x10, 0x7d, 0xf8, 0x3e, 0xfc, 0xe7, 0xa8, 0xa9, - 0x7f, 0x3e, 0x77, 0x7e, 0x04, 0x00, 0x00, 0xff, 0xff, 0xf3, 0x47, 0xed, 0xb3, 0x8e, 0x06, 0x00, - 0x00, + // 735 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0xac, 0x55, 0xcb, 0x6e, 0xd4, 0x4a, + 0x10, 0xbd, 0x3d, 0xb6, 0xe7, 0x51, 0x93, 0x8c, 0x26, 0xad, 0xb9, 0x57, 0x56, 0x74, 0x35, 0xb2, + 0x5a, 0xf7, 0x4a, 0x16, 0x12, 0x89, 0x14, 0x36, 0x08, 0x21, 0x21, 0x3c, 0x33, 0x51, 0x46, 0x90, + 0x28, 0xe9, 0x04, 0x76, 0x2c, 0x3a, 0x49, 0x93, 0x58, 0xf1, 0x63, 0xb0, 0xdb, 0xc0, 0x2c, 0x59, + 0xf0, 0x0f, 0x08, 0xbe, 0x00, 0xbe, 0x84, 0x25, 0x9f, 0x80, 0xc2, 0x8f, 0xa0, 0x7e, 0xf8, 0x11, + 0x10, 0x21, 0x0b, 0x76, 0xae, 0x53, 0x5d, 0xd5, 0x75, 0xfa, 0x54, 0x95, 0x61, 0x10, 0x26, 0x82, + 0x67, 0x09, 0x8b, 0x36, 0x16, 0x59, 0x2a, 0x52, 0xdc, 0x2d, 0x6d, 0xb2, 0x03, 0xad, 0x69, 0x80, + 0x3d, 0xe8, 0x1f, 0x85, 0x31, 0x3f, 0x28, 0x58, 0x22, 0x8a, 0xd8, 0x45, 0x1e, 0xf2, 0x7b, 0xb4, + 0x09, 0xc9, 0x13, 0x93, 0x34, 0x2a, 0xe2, 0xe4, 0x31, 0x3b, 0xe6, 0x91, 0xdb, 0xd2, 0x27, 0x1a, + 0x10, 0x99, 0x81, 0xb3, 0x9d, 0xb1, 0x98, 0xdf, 0x20, 0xd9, 0x3a, 0x74, 0x69, 0xfa, 0xaa, 0x99, + 0xa9, 0xb2, 0x49, 0x00, 0xed, 0x20, 0x14, 0x31, 0x5b, 0x60, 0x0c, 0x76, 0x10, 0x8a, 0xdc, 0x45, + 0x9e, 0xe5, 0xdb, 0x54, 0x7d, 0xe3, 0xff, 0xc0, 0x79, 0x28, 0x44, 0x96, 0xbb, 0x2d, 0xcf, 0xf2, + 0xfb, 0x5b, 0x83, 0x8d, 0x8a, 0x98, 0x84, 0xa9, 0x76, 0x92, 0x0d, 0xb0, 0xf7, 0x59, 0x98, 0xe1, + 0x21, 0x58, 0x8f, 0xf8, 0x52, 0x55, 0x60, 0x53, 0xf9, 0x89, 0x47, 0xe0, 0x4c, 0xd2, 0x22, 0x11, + 0xea, 0x5a, 0x9b, 0x6a, 0x83, 0x3c, 0x03, 0x2b, 0x08, 0x85, 0x2c, 0x4b, 0x5f, 0x3d, 0x9f, 0x9a, + 0x98, 0xca, 0xc6, 0xff, 0x42, 0x6f, 0x3f, 0x4b, 0x9f, 0x87, 0x11, 0x9f, 0x4f, 0x4d, 0x70, 0x0d, + 0x48, 0xaf, 0xe4, 0x97, 0x0b, 0x16, 0x2f, 0x5c, 0xcb, 0x43, 0xbe, 0x45, 0x6b, 0x80, 0x3c, 0x80, + 0x8e, 0x39, 0x8a, 0x07, 0xd0, 0xaa, 0x92, 0xb7, 0xe6, 0xd3, 0x1b, 0xf2, 0xf9, 0x84, 0xc0, 0x96, + 0x5f, 0x4d, 0x42, 0x3d, 0x4d, 0x08, 0x83, 0x7d, 0xb4, 0x5c, 0x70, 0x53, 0x92, 0xfa, 0x96, 0x02, + 0x1c, 0x8a, 0x2c, 0x4c, 0xce, 0x9e, 0xb2, 0xa8, 0xe0, 0xaa, 0x9e, 0x1e, 0x6d, 0x42, 0xb2, 0xde, + 0x27, 0x61, 0x22, 0xb4, 0xdf, 0xd6, 0x6c, 0x2a, 0x40, 0x7a, 0x83, 0x34, 0x8d, 0xb4, 0xd7, 0xf1, + 0x90, 0xdf, 0xa5, 0x35, 0x80, 0xc7, 0x00, 0xdb, 0x51, 0xca, 0x4c, 0x70, 0xdb, 0x43, 0x3e, 0xa2, + 0x0d, 0x84, 0x6c, 0x42, 0x47, 0xd6, 0xba, 0xcb, 0x16, 0x35, 0x3b, 0x74, 0x1d, 0xbb, 0xf7, 0x08, + 0x56, 0x0e, 0x0a, 0x9e, 0x2d, 0x29, 0x7f, 0x51, 0xf0, 0x5c, 0xc8, 0x47, 0x9a, 0x06, 0x86, 0xa4, + 0xec, 0xce, 0x11, 0x38, 0xca, 0x6f, 0x7a, 0x45, 0x1b, 0xf8, 0x1f, 0x68, 0x1f, 0x46, 0xe1, 0x09, + 0xcf, 0x5d, 0x4b, 0x35, 0x88, 0xb1, 0xa4, 0x8a, 0xe6, 0xb5, 0x73, 0x45, 0xad, 0x4b, 0x2b, 0x1b, + 0xbb, 0xd0, 0x29, 0xdb, 0xd2, 0x51, 0xb9, 0x4a, 0x53, 0x66, 0xa3, 0x3c, 0x4e, 0x85, 0x66, 0xd4, + 0xa5, 0xc6, 0x22, 0x6f, 0x10, 0xac, 0x9a, 0xe2, 0xf2, 0x45, 0x9a, 0xe4, 0x5c, 0x6a, 0x30, 0xcb, + 0xb2, 0x52, 0x83, 0x59, 0x96, 0xe1, 0x4d, 0xe8, 0x50, 0x9e, 0x17, 0x91, 0x28, 0x65, 0xfc, 0xbb, + 0x26, 0x5a, 0xc6, 0x16, 0x91, 0xa0, 0xe5, 0x29, 0x7c, 0xbb, 0x51, 0xa2, 0xa5, 0x22, 0xd6, 0xea, + 0x08, 0xe3, 0xa9, 0xab, 0x26, 0x6f, 0x11, 0xf4, 0x1b, 0x79, 0xb0, 0x5f, 0x8e, 0x88, 0x2a, 0xa2, + 0xbf, 0x35, 0xac, 0x83, 0x35, 0x4e, 0xcb, 0x11, 0x5a, 0x01, 0xb4, 0x67, 0x5a, 0x03, 0xed, 0x49, + 0x39, 0xe4, 0x58, 0x94, 0x77, 0x36, 0xe4, 0x90, 0x30, 0xd5, 0x4e, 0xf9, 0x46, 0x93, 0x73, 0x96, + 0x9c, 0xf1, 0x53, 0xf3, 0x7c, 0xa5, 0x49, 0x3e, 0x22, 0x58, 0x9d, 0xc7, 0x8b, 0x34, 0x13, 0xd7, + 0x28, 0xa5, 0x76, 0x40, 0xa9, 0x94, 0x5e, 0x08, 0x23, 0x70, 0x94, 0x36, 0xaa, 0x13, 0x6d, 0xaa, + 0x0d, 0xd5, 0x65, 0x66, 0xba, 0xa4, 0x50, 0x52, 0xc2, 0x1a, 0x90, 0x5d, 0x56, 0x8d, 0x57, 0xee, + 0x3a, 0xca, 0xdd, 0x40, 0xa4, 0xbf, 0x1a, 0xb0, 0xdc, 0x6d, 0x7b, 0x96, 0x6f, 0xd1, 0x06, 0x42, + 0x08, 0x0c, 0xca, 0x52, 0x7f, 0xa5, 0x1b, 0x39, 0x85, 0x61, 0x10, 0xa5, 0x27, 0x17, 0x53, 0x26, + 0xd8, 0x9f, 0x60, 0x34, 0x02, 0x47, 0xe5, 0x33, 0x13, 0xa5, 0x0d, 0x72, 0x00, 0x6b, 0x8d, 0x5b, + 0x4c, 0x31, 0x57, 0xc8, 0xa3, 0xeb, 0xc9, 0xb7, 0x7e, 0x24, 0x4f, 0xfe, 0x07, 0x67, 0xc2, 0x4e, + 0xce, 0x7f, 0x93, 0x86, 0x7c, 0x40, 0xb0, 0xb6, 0xcb, 0x5e, 0xeb, 0xb9, 0xa8, 0xae, 0xde, 0x81, + 0x5e, 0x05, 0x9a, 0xc1, 0xbc, 0x55, 0x77, 0xc2, 0x4f, 0xe7, 0x6b, 0x64, 0x96, 0x88, 0x6c, 0x49, + 0xeb, 0xe0, 0xf5, 0xfb, 0x30, 0xb8, 0xea, 0x94, 0x6f, 0x7c, 0x51, 0xef, 0xa7, 0x0b, 0xbd, 0x70, + 0x5f, 0xaa, 0x45, 0x61, 0x16, 0xae, 0x32, 0xee, 0xb5, 0xee, 0xa2, 0x60, 0xf8, 0xf9, 0x72, 0x8c, + 0xbe, 0x5c, 0x8e, 0xd1, 0xd7, 0xcb, 0x31, 0x7a, 0xf7, 0x6d, 0xfc, 0xd7, 0x71, 0x5b, 0xfd, 0x9c, + 0xee, 0x7c, 0x0f, 0x00, 0x00, 0xff, 0xff, 0xb6, 0x22, 0xbf, 0xb0, 0xae, 0x06, 0x00, 0x00, } diff --git a/internal/internal.proto b/internal/internal.proto index 8acbe0bab..67000243c 100644 --- a/internal/internal.proto +++ b/internal/internal.proto @@ -39,6 +39,7 @@ message Attr { string StringValue = 3; uint64 UintValue = 4; bool BoolValue = 5; + double FloatValue = 6; } message AttrMap { diff --git a/pilosa.go b/pilosa.go index e17178bf7..d2df7c5ed 100644 --- a/pilosa.go +++ b/pilosa.go @@ -2,9 +2,9 @@ package pilosa import ( "errors" + "regexp" "github.com/pilosa/pilosa/internal" - "regexp" ) // System errors. diff --git a/pql/parser.go b/pql/parser.go index 0f1ff1e87..346a47ccb 100644 --- a/pql/parser.go +++ b/pql/parser.go @@ -168,12 +168,18 @@ func (p *Parser) parseArgs() (map[string]interface{}, error) { } case STRING: value = lit - case NUMBER: + case INTEGER: v, err := strconv.ParseUint(lit, 10, 64) if err != nil { return nil, err } value = v + case FLOAT: + v, err := strconv.ParseFloat(lit, 64) + if err != nil { + return nil, err + } + value = v case LBRACK: v, err := p.parseList() if err != nil { @@ -219,7 +225,7 @@ func (p *Parser) parseList() ([]interface{}, error) { } case STRING: values = append(values, lit) - case NUMBER: + case INTEGER: v, err := strconv.ParseUint(lit, 10, 64) if err != nil { return nil, err diff --git a/pql/parser_test.go b/pql/parser_test.go index f4db973bc..e5bda9ca9 100644 --- a/pql/parser_test.go +++ b/pql/parser_test.go @@ -80,6 +80,26 @@ func TestParser_Parse(t *testing.T) { } }) + // Parse with float arguments. + t.Run("WithFloatArgs", func(t *testing.T) { + q, err := pql.ParseString(`MyCall( key=12.25, foo= 13.167, bar=2., baz=0.9)`) + if err != nil { + t.Fatal(err) + } else if !reflect.DeepEqual(q.Calls[0], + &pql.Call{ + Name: "MyCall", + Args: map[string]interface{}{ + "key": 12.25, + "foo": 13.167, + "bar": 2., + "baz": 0.9, + }, + }, + ) { + t.Fatalf("unexpected call: %#v", q.Calls[0]) + } + }) + // Parse with both child calls and arguments. t.Run("ChildrenAndArguments", func(t *testing.T) { q, err := pql.ParseString(`TopN(Bitmap(id=100, frame=other), frame=f, n=3)`) diff --git a/pql/scanner.go b/pql/scanner.go index 2b1add022..e51f0ab52 100644 --- a/pql/scanner.go +++ b/pql/scanner.go @@ -140,20 +140,26 @@ func (s *Scanner) scanIdent() (tok Token, pos Pos, lit string) { return IDENT, pos, lit } -// scanNumber consumes consecutive integer digits. +// scanNumber consumes consecutive digits and up to one '.' character. func (s *Scanner) scanNumber() (tok Token, pos Pos, lit string) { pos = s.pos + tok = INTEGER var buf bytes.Buffer + var seenDot bool for { ch := s.read() - if !isDigit(ch) { + if !isDigit(ch) && (seenDot || ch != '.') { s.unread() break } + if ch == '.' { + seenDot = true + tok = FLOAT + } buf.WriteRune(ch) } - return NUMBER, pos, buf.String() + return tok, pos, buf.String() } // scanString consumes a single-quoted or double-quoted string. diff --git a/pql/scanner_test.go b/pql/scanner_test.go index 2f2112fe6..4dc2f4d8f 100644 --- a/pql/scanner_test.go +++ b/pql/scanner_test.go @@ -29,7 +29,8 @@ func TestScanner_Scan(t *testing.T) { {s: `]`, tok: pql.RBRACK, lit: `]`}, {s: `foo`, tok: pql.IDENT, lit: `foo`}, - {s: `100`, tok: pql.NUMBER, lit: `100`}, + {s: `100`, tok: pql.INTEGER, lit: `100`}, + {s: `100.3`, tok: pql.FLOAT, lit: `100.3`}, {s: `all`, tok: pql.ALL, lit: `all`}, {s: `ALL`, tok: pql.ALL, lit: `ALL`}, // case insensitive diff --git a/pql/token.go b/pql/token.go index f794a684a..2ffc84400 100644 --- a/pql/token.go +++ b/pql/token.go @@ -15,7 +15,8 @@ const ( IDENT // main STRING // "foo" BADSTRING // bad escape or unclosed string - NUMBER // 12345 + INTEGER // 12345 + FLOAT // 100.2 literal_end keyword_beg @@ -35,8 +36,9 @@ var tokens = [...]string{ EOF: "EOF", WS: "WS", - IDENT: "IDENT", - NUMBER: "NUMBER", + IDENT: "IDENT", + INTEGER: "INTEGER", + FLOAT: "FLOAT", ALL: "ALL",