'Constructor, extracts (and represents) data from a resource
@param resource: the resource
@param fields: the fields to extract (selector strings)
@param start: index of the first record
@param limit: maximum number of records
@param left: additional left joins required for custom filters
@param orderby: orderby-expression for DAL
@param groupby: fields to group by (overrides fields!)
@param distinct: select distinct rows
@param virtual: include mandatory virtual fields
@param count: include the total number of matching records
@param getids: include the IDs of all matching records
@param as_rows: return the rows (don\'t extract/represent)
@param represent: render field value representations
@param raw_data: include raw data in the result
@note: as_rows / groupby prevent automatic splitting of
large multi-table joins, so use with care!
@note: with groupby, only the groupby fields will be returned
(i.e. fields will be ignored), because aggregates are
not supported (yet)'

def __init__(self, resource, fields, start=0, limit=None, left=None, orderby=None, groupby=None, distinct=False, virtual=True, count=False, getids=False, as_rows=False, represent=False, show_links=True, raw_data=False):

    self.resource = resource
    self.table = table = resource.table
    self.aqueries = aqueries = {}
    parent = resource.parent
    if (parent and (parent.accessible_query is not None)):
        method = []
        if parent._approved:
            method.append('read')
        if parent._unapproved:
            method.append('review')
        aqueries[parent.tablename] = parent.accessible_query(method, parent.table)
    tablename = table._tablename
    self.ijoins = ijoins = S3Joins(tablename)
    self.ljoins = ljoins = S3Joins(tablename)
    master_query = query = resource.get_query()
    rfilter = resource.rfilter
    filter_tables = set(ijoins.add(rfilter.get_joins(left=False)))
    filter_tables.update(ljoins.add(rfilter.get_joins(left=True)))
    master_tables = set(ljoins.add(left))
    filter_tables.update(master_tables)
    resolve = resource.resolve_selectors
    virtual_fields = rfilter.get_fields()
    (vfields, vijoins, vljoins, d) = resolve(virtual_fields, show=False)
    extra_tables = set(ijoins.extend(vijoins))
    extra_tables.update(ljoins.extend(vljoins))
    distinct |= d
    if (fields is None):
        fields = [f.name for f in resource.readable_fields()]
    (dfields, dijoins, dljoins, d) = resolve(fields, extra_fields=False)
    ijoins.extend(dijoins)
    ljoins.extend(dljoins)
    distinct |= d
    pkey = str(table._id)
    if ((not groupby) or as_rows):
        self.init_field_data(dfields)
    else:
        self.field_data = self.effort = None
    (orderby, orderby_aggr, orderby_fields, tables) = self.resolve_orderby(orderby)
    if tables:
        filter_tables.update(tables)
    filter_ijoins = ijoins.as_list(tablenames=filter_tables, aqueries=aqueries, prefer=ljoins)
    filter_ljoins = ljoins.as_list(tablenames=filter_tables, aqueries=aqueries)
    vfilter = resource.get_filter()
    efilter = rfilter.get_extra_filters()
    pagination = ((limit is not None) or start)
    if (ljoins or ijoins or efilter or (vfilter and pagination)):
        subselect = True
    else:
        subselect = False
    fq = count_only = False
    if (not groupby):
        end_count = ((vfilter or efilter) and (not pagination))
        if (count and (not end_count)):
            fq = True
            count_only = True
        if (subselect or (getids and pagination) or (extra_tables != filter_tables)):
            fq = True
            count_only = False
    ids = page = totalrows = None
    if fq:
        (totalrows, ids) = self.filter_query(query, join=filter_ijoins, left=filter_ljoins, getids=(not count_only), orderby=orderby_aggr)
    empty = False
    limitby = None
    orderby_on_limitby = True
    if (ids is not None):
        if (not ids):
            empty = True
        else:
            if (pagination and (efilter or vfilter)):
                master_ids = ids
            else:
                totalrows = len(ids)
                limitby = resource.limitby(start=start, limit=limit)
                if limitby:
                    page = ids[limitby[0]:limitby[1]]
                else:
                    page = ids
                master_ids = page
            if ((page is not None) and (not page)):
                empty = True
                master_query = None
            elif (len(master_ids) == 1):
                master_query = (table._id == master_ids[0])
            else:
                master_query = table._id.belongs(set(master_ids))
            orderby = None
            if ((not ljoins) or ijoins):
                limitby = (0, len(master_ids))
                orderby_on_limitby = False
            else:
                limitby = None
    elif (pagination and (not (efilter or vfilter or count or getids))):
        limitby = resource.limitby(start=start, limit=limit)
    if (not empty):
        if ((ids is None) and (filter_ijoins or filter_ljoins)):
            master_tables = filter_tables
        if (not groupby):
            master_tables.update(extra_tables)
        (tables, qfields, mfields, groupby) = self.master_fields(dfields, vfields, master_tables, as_rows=as_rows, groupby=groupby)
        if tables:
            master_tables.update(tables)
        if groupby:
            distinct = False
            orderby = orderby_aggr
            has_id = (pkey in qfields)
        else:
            if (distinct and orderby):
                for orderby_field in orderby_fields:
                    fn = str(orderby_field)
                    if (fn not in qfields):
                        qfields[fn] = orderby_field
            if (pkey not in qfields):
                qfields[pkey] = resource._id
            has_id = True
        master_ijoins = ijoins.as_list(tablenames=master_tables, aqueries=aqueries, prefer=ljoins)
        master_ljoins = ljoins.as_list(tablenames=master_tables, aqueries=aqueries)
        if (not virtual):
            vf = table.virtualfields
            osetattr(table, 'virtualfields', [])
        db = current.db
        rows = db(master_query).select(join=master_ijoins, left=master_ljoins, distinct=distinct, groupby=groupby, orderby=orderby, limitby=limitby, orderby_on_limitby=orderby_on_limitby, cacheable=(not as_rows), *qfields.values())
        if (not virtual):
            osetattr(table, 'virtualfields', vf)
    else:
        rows = Rows(current.db)
    if ((not len(rows)) and (not ids)):
        page = []
        ids = []
        totalrows = 0
    elif (not groupby):
        if (efilter or vfilter):
            shortcut = False
            if vfilter:
                if (pagination and (not any((getids, count, efilter)))):
                    rows = rfilter(rows, start=start, limit=limit)
                    page = self.getids(rows, pkey)
                    shortcut = True
                else:
                    rows = rfilter(rows)
            if efilter:
                if (vfilter or (not ids)):
                    ids = self.getids(rows, pkey)
                if (pagination and (not (getids or count))):
                    limit_ = (start + limit)
                else:
                    limit_ = None
                ids = rfilter.apply_extra_filters(ids, limit=limit_)
                rows = self.getrows(rows, ids, pkey)
            if pagination:
                if (not shortcut):
                    if (not efilter):
                        ids = self.getids(rows, pkey)
                    totalrows = len(ids)
                    (rows, page) = self.subset(rows, ids, start=start, limit=limit, has_id=has_id)
            else:
                if (not efilter):
                    ids = self.getids(rows, pkey)
                page = ids
                totalrows = len(ids)
        elif pagination:
            if (page is None):
                if limitby:
                    page = self.getids(rows, pkey)
                else:
                    if (not ids):
                        ids = self.getids(rows, pkey)
                    (rows, page) = self.subset(rows, ids, start=start, limit=limit, has_id=has_id)
                    totalrows = len(ids)
        elif (not ids):
            page = ids = self.getids(rows, pkey)
            totalrows = len(ids)
    self.rfields = dfields
    self.numrows = (0 if (totalrows is None) else totalrows)
    self.ids = ids
    if (groupby or as_rows):
        self.rows = rows
    elif (not rows):
        self.rows = []
    else:
        records = self.extract(rows, pkey, list(mfields), join=hasattr(rows[0], tablename), represent=represent)
        if (page is None):
            if (ids is None):
                self.ids = ids = self.getids(rows, pkey)
            page = ids
        joined_fields = self.joined_fields(dfields, qfields)
        joined_query = table._id.belongs(page)
        for (jtablename, jfields) in joined_fields.items():
            records = self.joined_query(jtablename, joined_query, jfields, records, represent=represent)
        results = {}
        field_data = self.field_data
        NONE = current.messages['NONE']
        render = self.render
        for dfield in dfields:
            if represent:
                results = render(dfield, results, none=NONE, raw_data=raw_data, show_links=show_links)
            else:
                colname = dfield.colname
                fdata = field_data[colname]
                frecords = fdata[1]
                list_type = fdata[3]
                for record_id in records:
                    if (record_id not in results):
                        result = results[record_id] = Storage()
                    else:
                        result = results[record_id]
                    data = frecords[record_id].keys()
                    if ((len(data) == 1) and (not list_type)):
                        data = data[0]
                    result[colname] = data
        self.rows = [results[record_id] for record_id in page]
