a generator method for retrieving documents from the bucket 
            
        :param query_criteria: [optional] dictionary with valid jsonmodel query criteria 
        :param uid: [optional] string with uid of design document to update
        :param all_versions: boolean to include previous revisions in query
        :param previous_id: [optional] string with id of the last doc in a previous query
        :param purge_deleted: boolean to purge any files in results which have been deleted
        :return: dictionary with document fields

        NOTE:   to efficiently use the query architecture of couchbase, it is important
                to first create a view for any query criteria you wish to use. if a
                specific query criteria is not previously created or no query criteria
                is included, then list will scan all documents based upon the uid field
                and return the appropriate matching results

        an example of how to construct the query_criteria argument:

        query_criteria = {
            '.path.to.number': {
                'min_value': 4.5
            },
            '.path.to.string': {
                'discrete_values': [ 'pond', 'lake', 'stream', 'brook' ]
            }
        }
        
        NOTE:   only fields specified in the document schema at class initialization
                can be used as fields in query_criteria. otherwise, an error will be thrown.
                uid is automatically added to all document schemas at initialization
                
        NOTE:   the full list of all criteria are found in the reference page for the
                jsonmodel module as well as the query-rules.json file included in the
                module. 
                http://collectiveacuity.github.io/jsonModel/reference/#query-criteria

def list(self, query_criteria=None, uid='_all_users', all_versions=False, previous_id='', purge_deleted=False):
    
        '''
            a generator method for retrieving documents from the bucket 
            
        :param query_criteria: [optional] dictionary with valid jsonmodel query criteria 
        :param uid: [optional] string with uid of design document to update
        :param all_versions: boolean to include previous revisions in query
        :param previous_id: [optional] string with id of the last doc in a previous query
        :param purge_deleted: boolean to purge any files in results which have been deleted
        :return: dictionary with document fields

        NOTE:   to efficiently use the query architecture of couchbase, it is important
                to first create a view for any query criteria you wish to use. if a
                specific query criteria is not previously created or no query criteria
                is included, then list will scan all documents based upon the uid field
                and return the appropriate matching results

        an example of how to construct the query_criteria argument:

        query_criteria = {
            '.path.to.number': {
                'min_value': 4.5
            },
            '.path.to.string': {
                'discrete_values': [ 'pond', 'lake', 'stream', 'brook' ]
            }
        }
        
        NOTE:   only fields specified in the document schema at class initialization
                can be used as fields in query_criteria. otherwise, an error will be thrown.
                uid is automatically added to all document schemas at initialization
                
        NOTE:   the full list of all criteria are found in the reference page for the
                jsonmodel module as well as the query-rules.json file included in the
                module. 
                http://collectiveacuity.github.io/jsonModel/reference/#query-criteria 
        '''

        title = '%s.list' % self.__class__.__name__
    
    # validate inputs
        input_fields = {
            'uid': uid,
            'previous_id': previous_id
        }
        for key, value in input_fields.items():
            if value:
                object_title = '%s(%s=%s)' % (title, key, str(value))
                self.fields.validate(value, '.%s' % key, object_title)

    # validate inputs
        if query_criteria:
            if not self.model:
                raise ValueError('%s(query_criteria={...} requires a document_schema.' % title)
            self.model.query(query_criteria)
        else:
            query_criteria = {}
        if uid != '_all_users' and self.public:
            raise ValueError('%s(uid="%s") user ids are not applicable for a public bucket. % title')

    # define url for all documents
        hashed_criteria = ''
        ordered_criteria = None
        if not query_criteria and uid == '_all_users':
            url = self.bucket_url + '/_all_docs'
    
    # define url for sub-set of documents
        else:
                
        # define error message
            error_message = '%s(uid="%s") does  not exist. Try: %s.create_view(uid="%s")' % (title, uid, self.__class__.__name__, uid)
        
        # establish all users if no uid
            if not uid:
                raise Exception('%s(uid="") is a required field.' % title)
            
        # retrieve design document
            design_url = self.bucket_url + '/_design/%s' % uid
            response = requests.get(design_url)
            response = response.json()
            if 'error' in response.keys():
                raise Exception(error_message)
        
        # validate existence of view
            if not query_criteria:
                if not '_all_docs' in response['views'].keys():
                    raise Exception(error_message)
                url = self.bucket_url + '/_design/%s/_view/_all_docs' % uid
            else:
        
            # determine hashed key for criteria
                import hashlib
                import json
                from collections import OrderedDict
                ordered_criteria = OrderedDict(**query_criteria)
                hashed_criteria = hashlib.md5(json.dumps(query_criteria, sort_keys=True).encode('utf-8')).hexdigest()
    
            # define url based upon existence of hashed key and uid
                if hashed_criteria in response['views'].keys():
                    url = self.bucket_url + '/_design/%s/_view/%s' % (uid, hashed_criteria)
                elif uid == '_all_users':
                    hashed_criteria = ''
                    url = self.bucket_url + '/_all_docs'
                else:
                    hashed_criteria = ''
                    url = self.bucket_url + '/_design/%s/_view/_all_docs' % uid

    # determine query params
        params = {
            'limit': 101,
            'revs': all_versions
        }
        if previous_id:
            params['startkey'] = previous_id
        break_off = False

    # send request
        while True:
            response = requests.get(url, params=params)

        # report records
            response_details = response.json()

        # break off if reached end of records
            if not 'rows' in response_details.keys():
                break
            elif not response_details['rows']:
                break
        
        # evaluate each row of view results
            else:
                for i in range(len(response_details['rows'])):
    
                # skip previous key
                    if not 'startkey' in params.keys() or i:
                        row = response_details['rows'][i]
                        params['startkey'] = row['id']

                    # evaluate query criteria in view
                        if hashed_criteria and row['value']:

                            from labpack.mapping.data import reconstruct_dict
                            dot_paths = []
                            for key in ordered_criteria.keys():
                                dot_paths.append(key)
                            record_dict = reconstruct_dict(dot_paths, row['value'])
                            if self.model.query(query_criteria, record_dict):
                                doc_details = self.read(row['id'])
                                if not doc_details:
                                    if purge_deleted:
                                        self.purge(row['id']) # eliminate stranded records
                                else:
                                    yield doc_details

                    # else scan documents
                        else:
                            doc_details = self.read(row['id'])

                        # filter results with query criteria
                            if not doc_details:
                                if purge_deleted:
                                    self.purge(row['id']) # eliminate stranded records
                            else:
                                if query_criteria:
                                    if self.model.query(query_criteria, doc_details):
                                        yield doc_details
                                else:
                                    yield doc_details

                # end if no more results
                    elif len(response_details['rows']) == 1:
                        break_off = True
                        break

            if break_off:
                break