func (self *LevelDbDatastore) executeQueryForSeries(database, series string, columns []string, query *parser.Query, yield func(*protocol.Series) error) error { startTimeBytes, endTimeBytes := self.byteArraysForStartAndEndTimes(common.TimeToMicroseconds(query.GetStartTime()), common.TimeToMicroseconds(query.GetEndTime())) fields, err := self.getFieldsForSeries(database, series, columns) if err != nil { return err } fieldCount := len(fields) prefixes := make([][]byte, fieldCount, fieldCount) iterators := make([]*levigo.Iterator, fieldCount, fieldCount) fieldNames := make([]string, len(fields)) // start the iterators to go through the series data for i, field := range fields { fieldNames[i] = field.Name prefixes[i] = field.Id iterators[i] = self.db.NewIterator(self.readOptions) if query.Ascending { iterators[i].Seek(append(field.Id, startTimeBytes...)) } else { iterators[i].Seek(append(append(field.Id, endTimeBytes...), MAX_SEQUENCE...)) if iterators[i].Valid() { iterators[i].Prev() } } } result := &protocol.Series{Name: &series, Fields: fieldNames, Points: make([]*protocol.Point, 0)} rawColumnValues := make([]*rawColumnValue, fieldCount, fieldCount) isValid := true limit := query.Limit if limit == 0 { limit = MAX_POINTS_TO_SCAN } resultByteCount := 0 // TODO: clean up, this is super gnarly // optimize for the case where we're pulling back only a single column or aggregate for isValid { isValid = false latestTimeRaw := make([]byte, 8, 8) latestSequenceRaw := make([]byte, 8, 8) point := &protocol.Point{Values: make([]*protocol.FieldValue, fieldCount, fieldCount)} for i, it := range iterators { if rawColumnValues[i] == nil && it.Valid() { k := it.Key() if len(k) >= 16 { t := k[8:16] if bytes.Equal(k[:8], fields[i].Id) && bytes.Compare(t, startTimeBytes) > -1 && bytes.Compare(t, endTimeBytes) < 1 { v := it.Value() s := k[16:] rawColumnValues[i] = &rawColumnValue{time: t, sequence: s, value: v} timeCompare := bytes.Compare(t, latestTimeRaw) if timeCompare == 1 { latestTimeRaw = t latestSequenceRaw = s } else if timeCompare == 0 { if bytes.Compare(s, latestSequenceRaw) == 1 { latestSequenceRaw = s } } } } } } for i, iterator := range iterators { if rawColumnValues[i] != nil && bytes.Equal(rawColumnValues[i].time, latestTimeRaw) && bytes.Equal(rawColumnValues[i].sequence, latestSequenceRaw) { isValid = true if query.Ascending { iterator.Next() } else { iterator.Prev() } fv := &protocol.FieldValue{} err := proto.Unmarshal(rawColumnValues[i].value, fv) if err != nil { return err } resultByteCount += len(rawColumnValues[i].value) point.Values[i] = fv var t uint64 binary.Read(bytes.NewBuffer(rawColumnValues[i].time), binary.BigEndian, &t) time := self.convertUintTimestampToInt64(&t) var sequence uint64 binary.Read(bytes.NewBuffer(rawColumnValues[i].sequence), binary.BigEndian, &sequence) seq32 := uint32(sequence) point.SetTimestampInMicroseconds(time) point.SequenceNumber = &seq32 rawColumnValues[i] = nil } } if isValid { limit -= 1 result.Points = append(result.Points, point) // add byte count for the timestamp and the sequence resultByteCount += 16 // check if we should send the batch along if resultByteCount > MAX_SERIES_SIZE { filteredResult, _ := Filter(query, result) if err := yield(filteredResult); err != nil { return err } resultByteCount = 0 result = &protocol.Series{Name: &series, Fields: fieldNames, Points: make([]*protocol.Point, 0)} } } if limit < 1 { break } } filteredResult, _ := Filter(query, result) if err := yield(filteredResult); err != nil { return err } emptyResult := &protocol.Series{Name: &series, Fields: fieldNames, Points: nil} return yield(emptyResult) }
func (self *LevelDbDatastore) executeQueryForSeries(database, series string, columns []string, query *parser.Query, yield func(*protocol.Series) error) error { startTimeBytes, endTimeBytes := self.byteArraysForStartAndEndTimes(common.TimeToMicroseconds(query.GetStartTime()), common.TimeToMicroseconds(query.GetEndTime())) fields, err := self.getFieldsForSeries(database, series, columns) if err != nil { return err } fieldCount := len(fields) fieldNames, iterators := self.getIterators(fields, startTimeBytes, endTimeBytes, query.Ascending) // iterators := result := &protocol.Series{Name: &series, Fields: fieldNames, Points: make([]*protocol.Point, 0)} rawColumnValues := make([]*rawColumnValue, fieldCount, fieldCount) limit := query.Limit if limit == 0 { limit = MAX_POINTS_TO_SCAN } resultByteCount := 0 // TODO: clean up, this is super gnarly // optimize for the case where we're pulling back only a single column or aggregate for { isValid := false point := &protocol.Point{Values: make([]*protocol.FieldValue, fieldCount, fieldCount)} for i, it := range iterators { if rawColumnValues[i] != nil || !it.Valid() { continue } key := it.Key() if len(key) < 16 { continue } if !isPointInRange(fields[i].Id, startTimeBytes, endTimeBytes, key) { continue } time := key[8:16] value := it.Value() sequenceNumber := key[16:] rawValue := &rawColumnValue{time: time, sequence: sequenceNumber, value: value} rawColumnValues[i] = rawValue } var pointTimeRaw []byte var pointSequenceRaw []byte // choose the highest (or lowest in case of ascending queries) timestamp // and sequence number. that will become the timestamp and sequence of // the next point. for _, value := range rawColumnValues { if value == nil { continue } pointTimeRaw, pointSequenceRaw = value.updatePointTimeAndSequence(pointTimeRaw, pointSequenceRaw, query.Ascending) } for i, iterator := range iterators { // if the value is nil, or doesn't match the point's timestamp and sequence number // then skip it if rawColumnValues[i] == nil || !bytes.Equal(rawColumnValues[i].time, pointTimeRaw) || !bytes.Equal(rawColumnValues[i].sequence, pointSequenceRaw) { continue } // if we emitted at lease one column, then we should keep // trying to get more points isValid = true // advance the iterator to read a new value in the next iteration if query.Ascending { iterator.Next() } else { iterator.Prev() } fv := &protocol.FieldValue{} err := proto.Unmarshal(rawColumnValues[i].value, fv) if err != nil { return err } resultByteCount += len(rawColumnValues[i].value) point.Values[i] = fv var t uint64 binary.Read(bytes.NewBuffer(rawColumnValues[i].time), binary.BigEndian, &t) time := self.convertUintTimestampToInt64(&t) var sequence uint64 binary.Read(bytes.NewBuffer(rawColumnValues[i].sequence), binary.BigEndian, &sequence) seq32 := uint32(sequence) point.SetTimestampInMicroseconds(time) point.SequenceNumber = &seq32 rawColumnValues[i] = nil } // stop the loop if we ran out of points if !isValid { break } limit -= 1 result.Points = append(result.Points, point) // add byte count for the timestamp and the sequence resultByteCount += 16 // check if we should send the batch along if resultByteCount > MAX_SERIES_SIZE || limit < 1 { dropped, err := self.sendBatch(query, result, yield) if err != nil { return err } limit += dropped resultByteCount = 0 result = &protocol.Series{Name: &series, Fields: fieldNames, Points: make([]*protocol.Point, 0)} } if limit < 1 { break } } if _, err := self.sendBatch(query, result, yield); err != nil { return err } emptyResult := &protocol.Series{Name: &series, Fields: fieldNames, Points: nil} _, err = self.sendBatch(query, emptyResult, yield) return err }