docs: 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)