def _get_many_override_none_ordering( cls, query: Q = None, parameters: dict = None, query_dict: dict = None, query_options: QueryParameterOptions = None, override_projection: Collection[str] = None, ) -> Sequence[dict]: """ Fetch all documents matching a provided query. For the first order by field the None values are sorted in the end regardless of the sorting order. This is a company-less version for internal uses. We assume the caller has either added any necessary constraints to the query or that no constraints are required. NOTE: BE VERY CAREFUL WITH THIS CALL, as it allows returning data across companies. :param query: Query object (mongoengine.Q) :param parameters: Parameters dict from which paging ordering and searching parameters are extracted. :param query_dict: If provided, passed to prepare_query() along with all of the relevant arguments to produce a query. The resulting query is AND'ed with the `query` parameter (if provided). :param query_options: query parameters options (see ParametersOptions) :param override_projection: A list of projection fields overriding any projection specified in the `param_dict` argument """ parameters = parameters or {} search_text = parameters.get("search_text") page, page_size = cls.validate_paging(parameters=parameters) query_sets = [] order_by = parameters.get(cls._ordering_key) if order_by: order_by = order_by if isinstance(order_by, list) else [order_by] order_by = [ cls._text_score if x == "@text_score" else x for x in order_by ] if not search_text and cls._text_score in order_by: raise errors.bad_request.FieldsValueError( "text score cannot be used in order_by when search text is not used" ) order_field = first(field for field in order_by if not field.startswith("$")) if (order_field and not order_field.startswith("-") and (not query_dict or order_field not in query_dict)): empty_value = None if order_field in query_options.list_fields: empty_value = [] elif order_field in query_options.pattern_fields: empty_value = "" mongo_field = order_field.replace(".", "__") non_empty = query & field_exists(mongo_field, empty_value=empty_value) empty = query & field_does_not_exist(mongo_field, empty_value=empty_value) query_sets = [cls.objects(non_empty), cls.objects(empty)] if not query_sets: query_sets = [cls.objects(query)] if search_text: query_sets = [qs.search_text(search_text) for qs in query_sets] if order_by: # add ordering query_sets = [qs.order_by(*order_by) for qs in query_sets] only = cls.get_projection(parameters, override_projection) if only: # add projection query_sets = [qs.only(*only) for qs in query_sets] else: exclude = set(cls.get_exclude_fields()) if exclude: query_sets = [qs.exclude(*exclude) for qs in query_sets] if page is None or not page_size: return [ obj.to_proper_dict(only=only) for qs in query_sets for obj in qs ] # add paging ret = [] start = page * page_size for qs in query_sets: qs_size = qs.count() if qs_size < start: start -= qs_size continue ret.extend( obj.to_proper_dict(only=only) for obj in qs.skip(start).limit(page_size)) if len(ret) >= page_size: break start = 0 page_size -= len(ret) return ret
def _get_many_override_none_ordering( cls: Union[Document, "GetMixin"], query: Q = None, parameters: dict = None, override_projection: Collection[str] = None, ) -> Sequence[dict]: """ Fetch all documents matching a provided query. For the first order by field the None values are sorted in the end regardless of the sorting order. This is a company-less version for internal uses. We assume the caller has either added any necessary constraints to the query or that no constraints are required. NOTE: BE VERY CAREFUL WITH THIS CALL, as it allows returning data across companies. :param query: Query object (mongoengine.Q) :param parameters: Parameters dict from which paging ordering and searching parameters are extracted. :param override_projection: A list of projection fields overriding any projection specified in the `param_dict` argument """ if not query: raise ValueError("query or call_data must be provided") parameters = parameters or {} search_text = parameters.get(cls._search_text_key) order_by = cls.validate_order_by(parameters=parameters, search_text=search_text) page, page_size = cls.validate_paging(parameters=parameters) only = cls.get_projection(parameters, override_projection) query_sets = [cls.objects(query)] if order_by: order_field = first(field for field in order_by if not field.startswith("$")) if (order_field and not order_field.startswith("-") and "[" not in order_field): params = {} mongo_field = order_field.replace(".", "__") if mongo_field in cls.get_field_names_for_type( of_type=ListField): params["is_list"] = True elif mongo_field in cls.get_field_names_for_type( of_type=StringField): params["empty_value"] = "" non_empty = query & field_exists(mongo_field, **params) empty = query & field_does_not_exist(mongo_field, **params) query_sets = [cls.objects(non_empty), cls.objects(empty)] query_sets = [qs.order_by(*order_by) for qs in query_sets] if search_text: query_sets = [qs.search_text(search_text) for qs in query_sets] if only: # add projection query_sets = [qs.only(*only) for qs in query_sets] else: exclude = set(cls.get_exclude_fields()) if exclude: query_sets = [qs.exclude(*exclude) for qs in query_sets] if page is None or not page_size: return [ obj.to_proper_dict(only=only) for qs in query_sets for obj in qs ] # add paging ret = [] start = page * page_size for qs in query_sets: qs_size = qs.count() if qs_size < start: start -= qs_size continue ret.extend( obj.to_proper_dict(only=only) for obj in qs.skip(start).limit(page_size)) if len(ret) >= page_size: break start = 0 page_size -= len(ret) return ret