code: func txUpdateAttrs(tx *bolt.Tx, id uint64, m map[string]interface{}) (map[string]interface{}, error) {
  	attr, err := txAttrs(tx, id)
  	if err != nil {
  		return nil, err
  	}
  
  	// Create a new map if it is empty so we don't update emptyMap.
  	if len(attr) == 0 {
  		attr = make(map[string]interface{}, len(m))
  	}
  
  	// Merge attributes with original values.
  	// Nil values should delete keys.
  	for k, v := range m {
  		if v == nil {
  			delete(attr, k)
  			continue
  		}
  
  		switch v := v.(type) {
  		case int:
  			attr[k] = int64(v)
  		case uint:
  			attr[k] = int64(v)
  		case uint64:
  			attr[k] = int64(v)
  		case string, int64, bool, float64:
  			attr[k] = v
  		default:
  			return nil, fmt.Errorf("invalid attr type: %T", v)
  		}
  	}
  
  	// Marshal and save new values.
  	buf, err := pilosa.EncodeAttrs(attr)
  	if err != nil {
  		return nil, errors.Wrap(err, "encoding attrs")
  	}
  	if err := tx.Bucket([]byte("attrs")).Put(u64tob(id), buf); err != nil {
  		return nil, errors.Wrap(err, "saving attrs")
  	}
  	return attr, nil
  }
docs: // txUpdateAttrs updates the attributes for an id.
  // Returns the new combined set of attributes for the id.