docs: def _series(self, name, interval, config, buckets, **kws):
      '''
      Fetch a series of buckets.
      '''
      # make a copy of the buckets because we're going to mutate it
      buckets = list(buckets)
      rval = OrderedDict()
      step = config['step']
      resolution = config.get('resolution',step)
      fetch = kws.get('fetch')
      process_row = kws.get('process_row', self._process_row)
  
      query = { 'name':name, 'interval':{'$gte':buckets[0], '$lte':buckets[-1]} }
      sort = [('interval', ASCENDING)]
      if not config['coarse']:
        sort.append( ('resolution', ASCENDING) )
  
      if fetch:
        cursor = fetch( self._client[interval], spec=query, sort=sort, method='find' )
      else:
        cursor = self._client[interval].find( spec=query, sort=sort )
      for record in cursor:
        while buckets and buckets[0] < record['interval']:
          rval[ config['i_calc'].from_bucket(buckets.pop(0)) ] = self._type_no_value()
        if buckets and buckets[0]==record['interval']:
          buckets.pop(0)
  
        i_key = config['i_calc'].from_bucket(record['interval'])
        data = process_row( record['value'] )
        if config['coarse']:
          rval[ i_key ] = data
        else:
          rval.setdefault( i_key, OrderedDict() )
          rval[ i_key ][ config['r_calc'].from_bucket(record['resolution']) ] = data
  
      # are there buckets at the end for which we received no data?
      while buckets:
        rval[ config['i_calc'].from_bucket(buckets.pop(0)) ] = self._type_no_value()
  
      return rval