mirror of
https://github.com/featurebasedb/featurebase.git
synced 2026-09-05 16:15:56 +00:00
This commit changes the `pql.Query` so that it can accept one or more top-level calls instead of only one. The query request format change because a query with a single call is still valid. However, the result format now returns a `results` field that has one result for each top-level call. The `profiles` field is still the same, however, it combines all profiles from all bitmap responses into one return so that there's not duplicate attributes. Fixes #59
365 lines
8.8 KiB
Go
365 lines
8.8 KiB
Go
package pql
|
|
|
|
import (
|
|
"fmt"
|
|
"sort"
|
|
"strconv"
|
|
"strings"
|
|
"time"
|
|
)
|
|
|
|
// Query represents a PQL query.
|
|
type Query struct {
|
|
Calls Calls
|
|
}
|
|
|
|
// String returns a string representation of the query.
|
|
func (q *Query) String() string {
|
|
a := make([]string, len(q.Calls))
|
|
for i, call := range q.Calls {
|
|
a[i] = call.String()
|
|
}
|
|
return strings.Join(a, "\n")
|
|
}
|
|
|
|
// Node represents any node in the AST.
|
|
type Node interface {
|
|
node()
|
|
String() string
|
|
}
|
|
|
|
func (*Bitmap) node() {}
|
|
func (*ClearBit) node() {}
|
|
func (*Count) node() {}
|
|
func (*Difference) node() {}
|
|
func (*Intersect) node() {}
|
|
func (*Profile) node() {}
|
|
func (*Range) node() {}
|
|
func (*SetBit) node() {}
|
|
func (*SetBitmapAttrs) node() {}
|
|
func (*SetProfileAttrs) node() {}
|
|
func (*TopN) node() {}
|
|
func (*Union) node() {}
|
|
|
|
// Call represents a function call in the AST.
|
|
type Call interface {
|
|
Node
|
|
call()
|
|
}
|
|
|
|
func (*Bitmap) call() {}
|
|
func (*ClearBit) call() {}
|
|
func (*Count) call() {}
|
|
func (*Difference) call() {}
|
|
func (*Intersect) call() {}
|
|
func (*Profile) call() {}
|
|
func (*Range) call() {}
|
|
func (*SetBit) call() {}
|
|
func (*SetBitmapAttrs) call() {}
|
|
func (*SetProfileAttrs) call() {}
|
|
func (*TopN) call() {}
|
|
func (*Union) call() {}
|
|
|
|
// Calls represents a list of calls.
|
|
type Calls []Call
|
|
|
|
// String returns a string representation of the calls as a comma-delimited list.
|
|
func (a Calls) String() string {
|
|
args := make([]string, len(a))
|
|
for i, c := range a {
|
|
args[i] = c.String()
|
|
}
|
|
return strings.Join(args, ", ")
|
|
}
|
|
|
|
// BitmapCall represents a function call that returns a bitmap.
|
|
type BitmapCall interface {
|
|
Call
|
|
bitmapCall()
|
|
}
|
|
|
|
// BitmapCalls represents a list of bitmap calls.
|
|
type BitmapCalls []BitmapCall
|
|
|
|
// String returns a string representation of the calls as a comma-delimited list.
|
|
func (a BitmapCalls) String() string {
|
|
args := make([]string, len(a))
|
|
for i, c := range a {
|
|
args[i] = c.String()
|
|
}
|
|
return strings.Join(args, ", ")
|
|
}
|
|
|
|
func (*Difference) bitmapCall() {}
|
|
func (*Bitmap) bitmapCall() {}
|
|
func (*Intersect) bitmapCall() {}
|
|
func (*Range) bitmapCall() {}
|
|
func (*Union) bitmapCall() {}
|
|
|
|
// Bitmap represents a Bitmap() function call.
|
|
type Bitmap struct {
|
|
ID uint64
|
|
Frame string
|
|
}
|
|
|
|
// String returns the string representation of the call.
|
|
func (c *Bitmap) String() string {
|
|
args := make([]string, 0, 2)
|
|
if c.ID != 0 {
|
|
args = append(args, fmt.Sprintf("id=%d", c.ID))
|
|
}
|
|
if c.Frame != "" {
|
|
args = append(args, fmt.Sprintf("frame=%s", c.Frame))
|
|
}
|
|
return fmt.Sprintf("Bitmap(%s)", strings.Join(args, ", "))
|
|
}
|
|
|
|
// ClearBit represents a ClearBit() function call.
|
|
type ClearBit struct {
|
|
ID uint64
|
|
Frame string
|
|
ProfileID uint64
|
|
}
|
|
|
|
// String returns the string representation of the call.
|
|
func (c *ClearBit) String() string {
|
|
args := make([]string, 0, 4)
|
|
args = append(args, fmt.Sprintf("id=%d", c.ID))
|
|
if c.Frame != "" {
|
|
args = append(args, fmt.Sprintf("frame=%s", c.Frame))
|
|
}
|
|
if c.ProfileID != 0 {
|
|
args = append(args, fmt.Sprintf("profileID=%d", c.ProfileID))
|
|
}
|
|
return fmt.Sprintf("ClearBit(%s)", strings.Join(args, ", "))
|
|
}
|
|
|
|
// Count represents a count() function call.
|
|
type Count struct {
|
|
Input BitmapCall
|
|
}
|
|
|
|
// String returns the string representation of the call.
|
|
func (c *Count) String() string {
|
|
return fmt.Sprintf("Count(%s)", c.Input.String())
|
|
}
|
|
|
|
// Difference represents an difference() function call.
|
|
type Difference struct {
|
|
Inputs BitmapCalls
|
|
}
|
|
|
|
// String returns the string representation of the call.
|
|
func (c *Difference) String() string {
|
|
return fmt.Sprintf("Difference(%s)", c.Inputs.String())
|
|
}
|
|
|
|
// Intersect represents an intersect() function call.
|
|
type Intersect struct {
|
|
Inputs BitmapCalls
|
|
}
|
|
|
|
// String returns the string representation of the call.
|
|
func (c *Intersect) String() string {
|
|
return fmt.Sprintf("Intersect(%s)", c.Inputs.String())
|
|
}
|
|
|
|
// Profile represents a Profile() function call.
|
|
type Profile struct {
|
|
ID uint64
|
|
}
|
|
|
|
// String returns the string representation of the call.
|
|
func (c *Profile) String() string {
|
|
args := make([]string, 0, 1)
|
|
args = append(args, fmt.Sprintf("id=%d", c.ID))
|
|
return fmt.Sprintf("Profile(%s)", strings.Join(args, ", "))
|
|
}
|
|
|
|
// Range represents a range() function call.
|
|
type Range struct {
|
|
ID uint64
|
|
Frame string
|
|
StartTime time.Time
|
|
EndTime time.Time
|
|
}
|
|
|
|
// String returns the string representation of the call.
|
|
func (c *Range) String() string {
|
|
args := make([]string, 0, 2)
|
|
if c.ID != 0 {
|
|
args = append(args, fmt.Sprintf("id=%d", c.ID))
|
|
}
|
|
if c.Frame != "" {
|
|
args = append(args, fmt.Sprintf("frame=%s", c.Frame))
|
|
}
|
|
if !c.StartTime.IsZero() {
|
|
args = append(args, fmt.Sprintf("start=%s", c.StartTime.Format(TimeFormat)))
|
|
}
|
|
if !c.EndTime.IsZero() {
|
|
args = append(args, fmt.Sprintf("end=%s", c.EndTime.Format(TimeFormat)))
|
|
}
|
|
return fmt.Sprintf("Range(%s)", strings.Join(args, ", "))
|
|
}
|
|
|
|
// SetBit represents a SetBit() function call.
|
|
type SetBit struct {
|
|
ID uint64
|
|
Frame string
|
|
ProfileID uint64
|
|
}
|
|
|
|
// String returns the string representation of the call.
|
|
func (c *SetBit) String() string {
|
|
args := make([]string, 0, 2)
|
|
args = append(args, fmt.Sprintf("id=%d", c.ID))
|
|
if c.Frame != "" {
|
|
args = append(args, fmt.Sprintf("frame=%s", c.Frame))
|
|
}
|
|
if c.ProfileID != 0 {
|
|
args = append(args, fmt.Sprintf("profileID=%d", c.ProfileID))
|
|
}
|
|
return fmt.Sprintf("SetBit(%s)", strings.Join(args, ", "))
|
|
}
|
|
|
|
// SetBitmapAttrs represents a SetBitmapAttrs() function call.
|
|
type SetBitmapAttrs struct {
|
|
ID uint64
|
|
Frame string
|
|
Attrs map[string]interface{}
|
|
}
|
|
|
|
// String returns the string representation of the call.
|
|
func (c *SetBitmapAttrs) String() string {
|
|
args := make([]string, 0, 2)
|
|
args = append(args, fmt.Sprintf("id=%d", c.ID))
|
|
if c.Frame != "" {
|
|
args = append(args, fmt.Sprintf("frame=%s", c.Frame))
|
|
}
|
|
|
|
// Sort keys.
|
|
keys := make([]string, 0, len(c.Attrs))
|
|
for k := range c.Attrs {
|
|
keys = append(keys, k)
|
|
}
|
|
sort.Strings(keys)
|
|
|
|
// Write key/value pairs.
|
|
for _, k := range keys {
|
|
if c.Attrs[k] == nil {
|
|
args = append(args, fmt.Sprintf("%s=null", k))
|
|
continue
|
|
}
|
|
|
|
switch v := c.Attrs[k].(type) {
|
|
case string:
|
|
args = append(args, fmt.Sprintf("%s=\"%s\"", k, v))
|
|
default:
|
|
args = append(args, fmt.Sprintf("%s=%v", k, v))
|
|
}
|
|
}
|
|
|
|
return fmt.Sprintf("SetBitmapAttrs(%s)", strings.Join(args, ", "))
|
|
}
|
|
|
|
// SetProfileAttrs represents a SetProfileAttrs() function call.
|
|
type SetProfileAttrs struct {
|
|
ID uint64
|
|
Attrs map[string]interface{}
|
|
}
|
|
|
|
// String returns the string representation of the call.
|
|
func (c *SetProfileAttrs) String() string {
|
|
args := make([]string, 0, 2)
|
|
args = append(args, fmt.Sprintf("id=%d", c.ID))
|
|
|
|
// Sort keys.
|
|
keys := make([]string, 0, len(c.Attrs))
|
|
for k := range c.Attrs {
|
|
keys = append(keys, k)
|
|
}
|
|
sort.Strings(keys)
|
|
|
|
// Write key/value pairs.
|
|
for _, k := range keys {
|
|
if c.Attrs[k] == nil {
|
|
args = append(args, fmt.Sprintf("%s=null", k))
|
|
continue
|
|
}
|
|
|
|
switch v := c.Attrs[k].(type) {
|
|
case string:
|
|
args = append(args, fmt.Sprintf("%s=\"%s\"", k, v))
|
|
default:
|
|
args = append(args, fmt.Sprintf("%s=%v", k, v))
|
|
}
|
|
}
|
|
|
|
return fmt.Sprintf("SetProfileAttrs(%s)", strings.Join(args, ", "))
|
|
}
|
|
|
|
// TopN represents a TopN() function call.
|
|
type TopN struct {
|
|
Frame string
|
|
|
|
// Maximum number of results to return.
|
|
N int
|
|
|
|
// Bitmap to use for intersection while computing top results.
|
|
// Original bitmap counts are used if no Src is provided.
|
|
Src BitmapCall
|
|
|
|
// Specific bitmaps to retrieve.
|
|
BitmapIDs []uint64
|
|
|
|
// Field name and values to filter on.
|
|
Field string
|
|
Filters []interface{}
|
|
}
|
|
|
|
// String returns the string representation of the call.
|
|
func (c *TopN) String() string {
|
|
args := make([]string, 0, 2)
|
|
if c.Src != nil {
|
|
args = append(args, c.Src.String())
|
|
}
|
|
if c.Frame != "" {
|
|
args = append(args, fmt.Sprintf("frame=%s", c.Frame))
|
|
}
|
|
if c.N > 0 {
|
|
args = append(args, fmt.Sprintf("n=%d", c.N))
|
|
}
|
|
if len(c.BitmapIDs) > 0 {
|
|
strs := make([]string, len(c.BitmapIDs))
|
|
for i := range c.BitmapIDs {
|
|
strs[i] = strconv.FormatUint(c.BitmapIDs[i], 10)
|
|
}
|
|
args = append(args, fmt.Sprintf("ids=[%s]", strings.Join(strs, ",")))
|
|
}
|
|
if c.Field != "" {
|
|
args = append(args, fmt.Sprintf("field=%q", c.Field))
|
|
}
|
|
if len(c.Filters) > 0 {
|
|
filters := make([]string, 0, len(c.Filters))
|
|
for i := range c.Filters {
|
|
switch filter := c.Filters[i].(type) {
|
|
case string:
|
|
filters = append(filters, fmt.Sprintf("%q", filter))
|
|
default:
|
|
filters = append(filters, fmt.Sprintf("%v", filter))
|
|
}
|
|
}
|
|
args = append(args, fmt.Sprintf("[%s]", strings.Join(filters, ",")))
|
|
}
|
|
return fmt.Sprintf("TopN(%s)", strings.Join(args, ", "))
|
|
}
|
|
|
|
// Union represents a union() function call.
|
|
type Union struct {
|
|
Inputs BitmapCalls
|
|
}
|
|
|
|
// String returns the string representation of the call.
|
|
func (c *Union) String() string {
|
|
return fmt.Sprintf("Union(%s)", c.Inputs.String())
|
|
}
|