Moved timestamp from HTTP query to PQL

This commit is contained in:
Yuce Tekol 2017-02-08 16:04:19 +03:00
parent 60f62aa559
commit 1eff86f78b
6 changed files with 447 additions and 3714 deletions

View file

@ -490,6 +490,16 @@ func (e *Executor) executeSetBit(ctx context.Context, db string, c *pql.Call, op
return false, errors.New("SetBit() profileID required")
}
var timestamp *time.Time
sTimestamp, ok := c.Args["timestamp"].(string)
if ok {
t, err := time.Parse("2006-01-02T15:04:05", sTimestamp)
if err != nil {
return false, fmt.Errorf("invalid date: %s", sTimestamp)
}
timestamp = &t
}
slice := profileID / SliceWidth
ret := false
@ -500,7 +510,7 @@ func (e *Executor) executeSetBit(ctx context.Context, db string, c *pql.Call, op
if err != nil {
return false, fmt.Errorf("db: %s", err)
}
val, err := db.SetBit(frame, id, profileID, opt.Timestamp)
val, err := db.SetBit(frame, id, profileID, timestamp)
if err != nil {
return false, err
} else if val {
@ -711,9 +721,6 @@ func (e *Executor) exec(ctx context.Context, node *Node, db string, q *pql.Query
Slices: slices,
Remote: true,
}
if opt.Timestamp != nil {
pbreq.Timestamp = opt.Timestamp.UnixNano()
}
buf, err := proto.Marshal(pbreq)
if err != nil {
return nil, err
@ -960,8 +967,7 @@ type mapResponse struct {
// ExecOptions represents an execution context for a single Execute() call.
type ExecOptions struct {
Timestamp *time.Time
Remote bool
Remote bool
}
// decodeError returns an error representation of s if s is non-blank.

View file

@ -384,6 +384,54 @@ func TestExecutor_Execute_Remote_SetBit(t *testing.T) {
}
}
// Ensure a remote query can set bits on multiple nodes.
func TestExecutor_Execute_Remote_SetBit_With_Timestamp(t *testing.T) {
c := NewCluster(2)
c.ReplicaN = 2
// Create secondary server and update second cluster node.
s := NewServer()
defer s.Close()
c.Nodes[1].Host = s.Host()
// Mock secondary server's executor to verify arguments.
var remoteCalled bool
s.Handler.Executor.ExecuteFn = func(ctx context.Context, db string, query *pql.Query, slices []uint64, opt *pilosa.ExecOptions) ([]interface{}, error) {
if db != `d` {
t.Fatalf("unexpected db: %s", db)
} else if query.String() != `SetBit(frame="f", id=10, profileID=2, timestamp="2016-12-11T10:09:07")` {
t.Fatalf("unexpected query: %s", query.String())
}
remoteCalled = true
return []interface{}{nil}, nil
}
// Create local executor data.
idx := MustOpenIndex()
defer idx.Close()
idx.CreateDBIfNotExists("d")
oldQuantum := idx.DB("d").TimeQuantum()
defer func() {
// restore db quantum
idx.DB("d").SetTimeQuantum(oldQuantum)
}()
// need to set the quantum otherwise SetBit fails silently
idx.DB("d").SetTimeQuantum("Y")
e := NewExecutor(idx.Index, c)
if _, err := e.Execute(context.Background(), "d", MustParse(`SetBit(id=10, frame=f, profileID=2, timestamp="2016-12-11T10:09:07")`), nil, nil); err != nil {
t.Fatal(err)
}
// Verify that one bit is set on both node's index.
if n := idx.MustCreateFragmentIfNotExists("d", "f_2016", 0).Bitmap(10).Count(); n != 1 {
t.Fatalf("unexpected local count: %d", n)
}
if !remoteCalled {
t.Fatalf("expected remote execution")
}
}
// Ensure a remote query can return a top-n query.
func TestExecutor_Execute_Remote_TopN(t *testing.T) {
c := NewCluster(2)

View file

@ -229,8 +229,7 @@ func (h *Handler) handlePostQuery(w http.ResponseWriter, r *http.Request) {
// Build execution options.
opt := &ExecOptions{
Timestamp: req.Timestamp,
Remote: req.Remote,
Remote: req.Remote,
}
// Parse query string.
@ -629,21 +628,6 @@ func (h *Handler) readURLQueryRequest(r *http.Request) (*QueryRequest, error) {
return nil, errors.New("invalid slice argument")
}
// Parse timestamp, if available.
var timestamp *time.Time
if v := q.Get("timestamp"); v != "" {
layout := "2006-01-02 15:04:05"
if strings.Contains(v, "T") {
layout = "2006-01-02T15:04:05"
}
t, err := time.Parse(layout, v)
if err != nil {
return nil, errors.New("invalid timestamp")
}
timestamp = &t
}
// Parse time granularity.
quantum := TimeQuantum("YMDH")
if s := q.Get("time_granularity"); s != "" {
@ -655,12 +639,11 @@ func (h *Handler) readURLQueryRequest(r *http.Request) (*QueryRequest, error) {
}
return &QueryRequest{
DB: q.Get("db"),
Query: query,
Slices: slices,
Profiles: q.Get("profiles") == "true",
Timestamp: timestamp,
Quantum: quantum,
DB: q.Get("db"),
Query: query,
Slices: slices,
Profiles: q.Get("profiles") == "true",
Quantum: quantum,
}, nil
}
@ -1072,9 +1055,6 @@ type QueryRequest struct {
// Return profile attributes, if true.
Profiles bool
// Timestamp passed into the query.
Timestamp *time.Time
// Time granularity to use with the timestamp.
Quantum TimeQuantum
@ -1093,11 +1073,6 @@ func decodeQueryRequest(pb *internal.QueryRequest) *QueryRequest {
Remote: pb.Remote,
}
if pb.Timestamp != 0 {
t := time.Unix(0, pb.Timestamp)
req.Timestamp = &t
}
return req
}

File diff suppressed because it is too large Load diff

View file

@ -48,9 +48,8 @@ message QueryRequest {
string Query = 2;
repeated uint64 Slices = 3;
bool Profiles = 4;
int64 Timestamp = 5;
string Quantum = 6;
bool Remote = 7;
string Quantum = 5;
bool Remote = 6;
}
message QueryResponse {

View file

@ -6,6 +6,7 @@ import (
"sort"
"strconv"
"strings"
"time"
)
// Query represents a PQL query.
@ -98,6 +99,9 @@ func (c *Call) String() string {
fmt.Fprintf(&buf, "%v=%s", key, joinInterfaceSlice(v))
case []uint64:
fmt.Fprintf(&buf, "%v=%s", key, joinUint64Slice(v))
case time.Time:
layout := "2006-01-02T15:04:05"
fmt.Fprintf(&buf, "%v=\"%s\"", key, v.Format(layout))
default:
fmt.Fprintf(&buf, "%v=%v", key, v)
}