Exemple #1
0
    def get_list_field_query(cls, field: str,
                             data: Sequence[Optional[str]]) -> Q:
        """
        Get a proper mongoengine Q object that represents an "or" query for the provided values
        with respect to the given list field, with support for "none of empty" in case a None value
        is included.

        - Exclusion can be specified by a leading "-" for each value (API versions <2.8)
            or by a preceding "__$not" value (operator)
        """
        if not isinstance(data, (list, tuple)):
            raise MakeGetAllQueryError("expected list", field)

        # TODO: backwards compatibility only for older API versions
        helper = cls.ListFieldBucketHelper(legacy=True)
        actions = bucketize(data,
                            key=helper.key,
                            value_transform=helper.value_transform)

        allow_empty = None in actions.get("in", {})
        mongoengine_field = field.replace(".", "__")

        q = RegexQ()
        for action in filter(None, actions):
            q &= RegexQ(
                **{
                    f"{mongoengine_field}__{action}":
                    list(set(filter(None, actions[action])))
                })

        if not allow_empty:
            return q

        return (q
                | Q(**{f"{mongoengine_field}__exists": False})
                | Q(**{mongoengine_field: []}))
Exemple #2
0
    def _prepare_query_no_company(cls,
                                  parameters=None,
                                  parameters_options=QueryParameterOptions()):
        """
        Prepare a query object based on the provided query dictionary and various fields.

        NOTE: BE VERY CAREFUL WITH THIS CALL, as it allows creating queries that span across companies.

        :param parameters_options: Specifies options for parsing the parameters (see ParametersOptions)
        :param parameters: Query dictionary (relevant keys are these specified by the various field names parameters).
            Supported parameters:
            - <field_name>: <value> Will query for items with this value in the field (see QueryParameterOptions for
                specific rules on handling values). Only items matching ALL of these conditions will be retrieved.
            - <any|all>: {fields: [<field1>, <field2>, ...], pattern: <pattern>} Will query for items where any or all
                provided fields match the provided pattern.
        :return: mongoengine.Q query object
        """
        parameters_options = parameters_options or cls.get_all_query_options
        dict_query = {}
        query = RegexQ()
        if parameters:
            parameters = parameters.copy()
            opts = parameters_options
            for field in opts.pattern_fields:
                pattern = parameters.pop(field, None)
                if pattern:
                    dict_query[field] = RegexWrapper(pattern)

            for field in tuple(opts.list_fields or ()):
                data = parameters.pop(field, None)
                if data:
                    if not isinstance(data, (list, tuple)):
                        raise MakeGetAllQueryError("expected list", field)
                    exclude = [t for t in data if t.startswith("-")]
                    include = list(set(data).difference(exclude))
                    mongoengine_field = field.replace(".", "__")
                    if include:
                        dict_query[f"{mongoengine_field}__in"] = include
                    if exclude:
                        dict_query[f"{mongoengine_field}__nin"] = [
                            t[1:] for t in exclude
                        ]

            for field in opts.fields or []:
                data = parameters.pop(field, None)
                if data is not None:
                    dict_query[field] = data

            for field in opts.datetime_fields or []:
                data = parameters.pop(field, None)
                if data is not None:
                    if not isinstance(data, list):
                        data = [data]
                    for d in data:  # type: str
                        m = ACCESS_REGEX.match(d)
                        if not m:
                            continue
                        try:
                            value = parse_datetime(m.group("value"))
                            prefix = m.group("prefix")
                            modifier = ACCESS_MODIFIER.get(prefix)
                            f = field if not modifier else "__".join(
                                (field, modifier))
                            dict_query[f] = value
                        except (ValueError, OverflowError):
                            pass

            for field, value in parameters.items():
                for keys, func in cls._multi_field_param_prefix.items():
                    if field not in keys:
                        continue
                    try:
                        data = cls.MultiFieldParameters(**value)
                    except Exception:
                        raise MakeGetAllQueryError("incorrect field format",
                                                   field)
                    if not data.fields:
                        break
                    regex = RegexWrapper(data.pattern, flags=re.IGNORECASE)
                    sep_fields = [f.replace(".", "__") for f in data.fields]
                    q = reduce(lambda a, x: func(a, RegexQ(**{x: regex})),
                               sep_fields, RegexQ())
                    query = query & q

        return query & RegexQ(**dict_query)