예제 #1
0
    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
예제 #2
0
    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