如何在mongodb中进行级联删除文档?

23

我在Mongodb中有用户和照片文档。每张照片都属于一个用户,一张照片可能被多个用户共享。比如,用户1有p1,p2,p3三张照片,用户2有p3,p4,p5三张照片。如果我手动使用像Compass这样的工具删除用户1,那么p1和p2也应该被删除,但p3不应该被删除。如何实现这一点,我需要定义什么样的数据库结构?

当前情况是,如果我删除用户1,则不会删除任何照片,这使得从使用数据库的应用程序的角度来看,数据库已经损坏。

这是一个Spring Boot应用程序,用户和照片声明如下:

import lombok.Builder;
import lombok.Data;
import org.springframework.data.annotation.Id;
import org.springframework.data.mongodb.core.mapping.DBRef;
import org.springframework.data.mongodb.core.mapping.Document;
@Document
@Data
@Builder
public class User {

    @Id
    private String id;


    @DBRef
    private Set<Photo> photos;


    private String name;
}

@Document
@Data
@Builder
public class Photo {

    @Id
    private String id;


    private String fileName;

}

1
你必须手动完成此操作,没有支持。类似的答案在这里 - m4gic
4个回答

14

正如m4gic所提及的并在他链接的问题中(这里这里),MongoDB不支持级联删除。在您的情况下,您应该在用户对象中创建一个数组,并将完整的子文档放入该数组中,而不是将它们保留在自己的集合中。这样它们将与父项一起被删除,因为它们是其一部分。


6
请记住,最大的BSON文档大小为16兆字节。 - khocef

3
MongoDB目前不支持级联删除。由于您已经在User模型中存储了引用照片,因此可以从引用列表中获取照片ID并一起删除照片。或者,您可以将照片存储在单独的集合中,也可以将照片数组嵌入到用户对象中。
您也可以参考此链接: MongoDB中N:M关系的推荐级联删除等效方法是什么?

0

@mindcraft 是正确的,但如果您想将照片保存在单独的集合中,则可以向 photo 文档添加访问属性,例如:

{
  ref: 'https://....',
  access:[user1._id, user2._id]
}

然后你可以像这样查询 -

db.photos.find({access:{$in:[user1._id]}})

虽然专门为照片设置单独的集合不会有太大帮助。相反,尝试将照片 URL 放入数组中。


0

这里是级联删除的通用Python实现。我们假设“外键”是父对象的ObjectId。

enter方法在进入上下文时被调用,并自动调用discover_collections方法来查找具有ObjectId引用的集合。 exit方法在退出上下文时被调用,并关闭MongoDB客户端连接。

使用该类作为上下文管理器可确保正确管理MongoDB连接,并在适当的时间调用discover_collections方法。

请记得将<mongodb_connection_string>替换为您实际的MongoDB连接字符串,将“your_database_name”替换为您的数据库名称。

在上下文中调用该类:

with CascadeDelete('<mongodb_connection_string>', 'your_database_name') as cascade_delete:
    cascade_delete.delete(ObjectId('parent1'))

CascadeDelete类的实现:

from pymongo import MongoClient
from bson.objectid import ObjectId


class CascadeDelete:
    """ Usage:

        with CascadeDelete('<mongodb_connection_string>', 'your_database_name') as cascade_delete:
            cascade_delete.delete(ObjectId('parent1'))
    """
    def __init__(self, connection_string, database_name):
        self.client = MongoClient(connection_string)
        self.db = self.client[database_name]
        self.collections = []

    def __enter__(self):
        self.discover_collections()
        return self

    def __exit__(self, exc_type, exc_val, exc_tb):
        self.client.close()

    def discover_collections(self):
        for collection_name in self.db.list_collection_names():
            collection = self.db[collection_name]
            if self._has_objectid_references(collection):
                self.collections.append(collection)

    def delete(self, document_id):
        deleted_documents = set()
        self._delete_documents(document_id, deleted_documents)

        for collection in self.collections:
            collection.delete_many({'_id': {'$in': list(deleted_documents)}})

    def _delete_documents(self, document_id, deleted_documents):
        deleted_documents.add(document_id)

        for collection in self.collections:
            document = collection.find_one({'_id': document_id})
            if document:
                for key, value in document.items():
                    if isinstance(value, ObjectId) and key != '_id':
                        self._delete_documents(value, deleted_documents)

    @staticmethod
    def _has_objectid_references(collection):
        sample_document = collection.find_one()
        if not sample_document:
            return False

        for value in sample_document.values():
            if isinstance(value, ObjectId):
                return True

        return False

我希望这能帮助到某人...
此致敬礼

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接