func (d *datastore) Query(q dsq.Query) (dsq.Results, error) { // we can use multiple iterators concurrently. see: // https://godoc.org/github.com/syndtr/goleveldb/leveldb#DB.NewIterator // advance the iterator only if the reader reads // // run query in own sub-process tied to Results.Process(), so that // it waits for us to finish AND so that clients can signal to us // that resources should be reclaimed. qrb := dsq.NewResultBuilder(q) qrb.Process.Go(func(worker goprocess.Process) { d.runQuery(worker, qrb) }) // go wait on the worker (without signaling close) go qrb.Process.CloseAfterChildren() // Now, apply remaining things (filters, order) qr := qrb.Results() for _, f := range q.Filters { qr = dsq.NaiveFilter(qr, f) } for _, o := range q.Orders { qr = dsq.NaiveOrder(qr, o) } return qr, nil }
func (bd *boltDatastore) Query(q query.Query) (query.Results, error) { qrb := query.NewResultBuilder(q) qrb.Process.Go(func(worker goprocess.Process) { bd.db.View(func(tx *bolt.Tx) error { buck := tx.Bucket(bd.bucketName) c := buck.Cursor() var prefix []byte if qrb.Query.Prefix != "" { prefix = []byte(qrb.Query.Prefix) } cur := 0 sent := 0 for k, v := c.Seek(prefix); k != nil; k, v = c.Next() { if cur < qrb.Query.Offset { cur++ continue } if qrb.Query.Limit > 0 && sent >= qrb.Query.Limit { break } dk := ds.NewKey(string(k)).String() e := query.Entry{Key: dk} if !qrb.Query.KeysOnly { buf := make([]byte, len(v)) copy(buf, v) e.Value = buf } select { case qrb.Output <- query.Result{Entry: e}: // we sent it out sent++ case <-worker.Closing(): // client told us to end early. break } cur++ } return nil }) }) // go wait on the worker (without signaling close) go qrb.Process.CloseAfterChildren() qr := qrb.Results() for _, f := range q.Filters { qr = query.NaiveFilter(qr, f) } for _, o := range q.Orders { qr = query.NaiveOrder(qr, o) } return qr, nil }