<SYSTEM_TASK:>
Solve the following problem using Python, implementing the functions described below, one line at a time
<END_TASK>
<USER_TASK:>
Description:
 def query(self, properties=None, criteria=None, distinct_key=None, **kwargs):
 """ Convenience method for database access. All properties and criteria can be specified using simplified names defined in Aliases. You can use the supported_properties property to get the list of supported properties. Results are returned as an iterator of dicts to ensure memory and cpu efficiency. Note that the dict returned have keys also in the simplified names form, not in the mongo format. For example, if you query for "analysis.e_above_hull", the returned result must be accessed as r['analysis.e_above_hull'] instead of mongo's r['analysis']['e_above_hull']. This is a *feature* of the query engine to allow simple access to deeply nested docs without having to resort to some recursion to go deep into the result. However, if you query for 'analysis', the entire 'analysis' key is returned as r['analysis'] and then the subkeys can be accessed in the usual form, i.e., r['analysis']['e_above_hull'] :param properties: Properties to query for. Defaults to None which means all supported properties. :param criteria: Criteria to query for as a dict. :param distinct_key: If not None, the key for which to get distinct results :param \*\*kwargs: Other kwargs supported by pymongo.collection.find. Useful examples are limit, skip, sort, etc. :return: A QueryResults Iterable, which is somewhat like pymongo's cursor except that it performs mapping. In general, the dev does not need to concern himself with the form. It is sufficient to know that the results are in the form of an iterable of dicts. """

        if properties is not None:
            props, prop_dict = self._parse_properties(properties)
        else:
            props, prop_dict = None, None

        crit = self._parse_criteria(criteria)
        if self.query_post:
            for func in self.query_post:
                func(crit, props)
        cur = self.collection.find(filter=crit, projection=props, **kwargs)

        if distinct_key is not None:
            cur = cur.distinct(distinct_key)
            return QueryListResults(prop_dict, cur, postprocess=self.result_post)
        else:
            return QueryResults(prop_dict, cur, postprocess=self.result_post)