예제 #1
0
def __check_value_types(updater: DocumentUpdater, allowed_types: List[str]):
    """
    Check if given field contains only given types of value.
    Raise InconsistencyError if other value types was found
    :param updater:
    :param allowed_types:
    :return:
    """
    def by_path(ctx: ByPathContext):
        # Check for data types other than objects or arrays
        fltr = {
            "$and": [
                {
                    ctx.filter_dotpath: {
                        "$ne": None
                    }
                },
                *[{
                    k: v
                } for k, v in ctx.extra_filter.items()],
                # $expr >= 3.6, $type >= 3.4
                {
                    "$expr": {
                        "$not": [{
                            "$in": [{
                                "$type": f'${ctx.filter_dotpath}'
                            }, allowed_types]
                        }]
                    }
                }
            ]
        }
        check_empty_result(ctx.collection, ctx.filter_dotpath, fltr)

    def by_doc(ctx: ByDocContext):
        # https://docs.mongodb.com/manual/reference/operator/aggregation/convert/
        type_map = {
            'double': float,
            'string': str,
            'objectId': bson.ObjectId,
            'bool': bool,
            'date': datetime,
            'int': int,
            'long': int,
            'decimal': float
        }
        assert set(allowed_types) < type_map.keys()

        doc = ctx.document
        if updater.field_name in doc:
            f = doc[updater.field_name]
            valid_types = tuple(type_map[t] for t in allowed_types)
            valid = f is None or isinstance(f, valid_types)
            if not valid:
                raise InconsistencyError(
                    f"Field {updater.field_name} has wrong type of value "
                    f"{f!r} (should be any of {valid_types}) in record {doc}")

    updater.update_combined(by_path, by_doc, False, False)
예제 #2
0
def __check_legacy_point_coordinates(updater: DocumentUpdater):
    """
    Check if all array values in field has legacy geo point
    coordinates type. Raise InconsistencyError if other arrays was found
    :param updater:
    :return:
    """
    def by_path(ctx: ByPathContext):
        fltr = {
            "$and": [
                {
                    ctx.filter_dotpath: {
                        "$ne": None
                    }
                },
                *[{
                    k: v
                } for k, v in ctx.extra_filter.items()],
                # $expr >= 3.6, $isArray >= 3.2
                {
                    "$expr": {
                        "$eq": [{
                            "$isArray": f"${ctx.filter_dotpath}"
                        }, True]
                    }
                },
                {
                    "$expr": {
                        "$ne": [{
                            "$size": f"${ctx.filter_dotpath}"
                        }, 2]
                    }
                },  # $expr >= 3.6
                # TODO: add element type check
            ]
        }
        check_empty_result(ctx.collection, ctx.filter_dotpath, fltr)

    def by_doc(ctx: ByDocContext):
        doc = ctx.document
        if updater.field_name in doc:
            f = doc[updater.field_name]
            valid = f is None or (isinstance(f, (list, tuple)) and len(f) == 2)
            if not valid:
                raise InconsistencyError(
                    f"Field {updater.field_name} has wrong value {f!r} "
                    f"(should be legacy geo point) in record {doc}")

    updater.update_combined(by_path, by_doc, False, False)
예제 #3
0
def __check_geojson_objects(updater: DocumentUpdater,
                            geojson_types: List[str]):
    """
    Check if all object values in field are GeoJSON objects of given
    types. Raise InconsistencyError if other objects found
    :param updater:
    :param geojson_types:
    :return:
    """
    def by_path(ctx: ByPathContext):
        fltr = {
            "$and": [
                {
                    ctx.filter_dotpath: {
                        "$ne": None
                    }
                },
                *[{
                    k: v
                } for k, v in ctx.extra_filter.items()],
                {
                    f'{ctx.filter_dotpath}.type': {
                        '$nin': geojson_types
                    }
                },
                # $expr >= 3.6
                {
                    "$expr": {
                        "$eq": [{
                            "$type": f'${ctx.filter_dotpath}'
                        }, 'object']
                    }
                }
            ]
        }
        check_empty_result(ctx.collection, ctx.filter_dotpath, fltr)

    def by_doc(ctx: ByDocContext):
        doc = ctx.document
        if updater.field_name in doc:
            f = doc[updater.field_name]
            valid = f is None or (isinstance(f, dict)
                                  and f.get('type') in geojson_types)
            if not valid:
                raise InconsistencyError(
                    f"Field {updater.field_name} has wrong value {f!r} "
                    f"(should be GeoJSON) in record {doc}")

    updater.update_combined(by_path, by_doc, False, False)