コード例 #1
0
    def _create_index(self, parameters: dict) -> None:
        """
        Create index with given parameters
        :param parameters:
        :return:
        """
        left_index_schema = deepcopy(parameters)
        fields = left_index_schema.pop('fields')  # Key must be present
        name = left_index_schema.get('name')

        # Check if index with such name\parameters exists in db
        found_index = self._find_index(self._run_ctx['collection'], name,
                                       fields)
        if found_index:
            iname, ispec = found_index
            # Exclude index desc keys which does not get to index schema
            compare_keys = (ispec.keys()
                            | left_index_schema.keys()) - {'v', 'ns', 'key'}
            if all(
                    ispec.get(k) == left_index_schema.get(k)
                    for k in compare_keys):
                log.warning('Index %s already exists, ignore', fields)
                return
            else:
                raise MigrationError(
                    'Index {} already exists with other parameters. Please drop it before '
                    'applying the migration'.format(fields))

        try:
            self._run_ctx['collection'].create_index(fields,
                                                     **left_index_schema)
        except pymongo.errors.OperationFailure as e:
            index_id = left_index_schema.get('name', fields)
            raise MigrationError(
                'Could not create index {}'.format(index_id)) from e
コード例 #2
0
ファイル: geo.py プロジェクト: vmdhhh/mongoengine-migrate
def convert_geojson(updater: DocumentUpdater, from_type: str, to_type: str):
    """Convert GeoJSON object from one type to another"""
    from_ind, to_ind = None, None
    if updater.migration_policy.name == 'strict':
        __check_geojson_objects(updater, [from_type, to_type])
        # There could be legacy coordinate pair arrays or GeoJSON objects
        __check_legacy_point_coordinates(updater)
        __check_value_types(updater, ['object', 'array'])

    if from_type == to_type:
        return

    for ind, convertion in __CONVERTIONS:
        if from_type in convertion:
            from_ind = ind
        if to_type in convertion:
            to_ind = ind

    if from_ind is None or to_ind is None:
        raise MigrationError(
            f"Unknown geo field type. Was requested: {from_type}, {to_type}")

    depth = abs(from_ind - to_ind)
    if from_ind <= to_ind:
        __increase_geojson_nesting(updater, from_type, to_type, depth)
    else:
        __decrease_geojson_nesting(updater, from_type, to_type, depth)
コード例 #3
0
    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': lambda x: dateutil_parse(str(x)),
            'int': int,
            'long': bson.Int64,
            'decimal': float,
            'binary': bson.Binary,
            'object': dict
        }
        assert target_type in type_map

        doc = ctx.document
        field_name = updater.field_name
        if field_name in doc:
            t = type_map[target_type]
            if not isinstance(doc[field_name],
                              t) and doc[field_name] is not None:
                try:
                    doc[field_name] = type_map[target_type](doc[field_name])
                except (TypeError, ValueError) as e:
                    if updater.migration_policy.name == 'strict':
                        raise MigrationError(
                            f'Cannot convert value '
                            f'{field_name}: {doc[field_name]} to type {t}'
                        ) from e
コード例 #4
0
    def convert_type(self, updater: DocumentUpdater,
                     from_field_cls: Type[mongoengine.fields.BaseField],
                     to_field_cls: Type[mongoengine.fields.BaseField]):
        """
        Convert field type from another to a current one. This method
        is called only if such change was requested in a migration.

        We use convertion matrix here. It contains mapping between
        old and new types and appropriate converter function which
        is called to perform such convertion.

        Old field can be either actual field type which used before.
        But in case if that field was a user defined class and does
        not exist already, the BaseField will be sent.

        New field will always have target mongoengine field type
        :param updater:
        :param from_field_cls: mongoengine field class which was used
         before
        :param to_field_cls: mongoengine field class which will be used
         further
        :return:
        """

        type_converters = CONVERTION_MATRIX.get(from_field_cls) or \
            CONVERTION_MATRIX.get(get_closest_parent(from_field_cls, CONVERTION_MATRIX.keys()))

        if type_converters is None:
            raise MigrationError(f'Type converter not found for convertion '
                                 f'{from_field_cls!r} -> {to_field_cls!r}')

        type_converter = type_converters.get(to_field_cls) or \
            type_converters.get(get_closest_parent(to_field_cls, type_converters))

        if type_converter is None:
            raise MigrationError(f'Type converter not found for convertion '
                                 f'{from_field_cls!r} -> {to_field_cls!r}')

        type_converter(updater)
コード例 #5
0
    def by_doc(ctx: ByDocContext):
        doc = ctx.document
        if updater.field_name not in doc or doc[updater.field_name] is None:
            return

        f = doc[updater.field_name]
        is_dict = isinstance(f, dict)
        if is_dict and isinstance(f.get('_ref'), bson.DBRef):  # dynamic ref
            doc[updater.field_name] = str(f['_ref'].id)
        elif is_dict and isinstance(f.get('_id'), bson.ObjectId):  # manual ref
            doc[updater.field_name] = str(f['_id'])
        elif isinstance(f, bson.DBRef):
            doc[updater.field_name] = str(f.id)
        else:
            try:
                doc[updater.field_name] = str(f)
            except (TypeError, ValueError) as e:
                if updater.migration_policy.name == 'strict':
                    raise MigrationError(
                        f'Cannot convert value {updater.field_name}: '
                        f'{doc[updater.field_name]} to string') from e
コード例 #6
0
    def by_doc(ctx: ByDocContext):
        doc = ctx.document
        if updater.field_name not in doc or doc[updater.field_name] is None:
            return

        f = doc[updater.field_name]
        if isinstance(f, (list, tuple)):
            if f:
                f = f[0]
                if remove_cls_key and isinstance(f, dict) and '_cls' in f:
                    del f['_cls']
                if not isinstance(
                        f, item_type
                ) and updater.migration_policy.name == 'strict':
                    raise InconsistencyError(
                        f"Field {updater.field_name} has wrong value {f!r} "
                        f"(should be {item_type}) in record {doc}")
            else:
                f = None
            doc[updater.field_name] = f
        elif f is not None and updater.migration_policy.name == 'strict':
            raise MigrationError(
                f'Could not extract item from non-list value '
                f'{updater.field_name}: {doc[updater.field_name]}')
コード例 #7
0
def deny(updater: DocumentUpdater):
    """Convertion is denied"""
    raise MigrationError(
        f"Such type_key convertion for field "
        f"{updater.document_type}.{updater.field_name} is forbidden")