【问题标题】:What's the better mongodb document structure?什么是更好的 mongodb 文档结构?
【发布时间】:2016-05-25 00:18:45
【问题描述】:

我正在尝试创建一个良好的文档结构,该结构具有良好的阅读性能,而不是那么慢的写入性能。 我需要存储有关 UserConnection 文档的信息,该文档表示我的数据库中两个用户之间的链接。每个链接都有一个取决于参数列表的权重。

以下是表示数据、链接和权重组件的架构:

UserConnection      Services          Components
  UserA             Name              Name
  UserB             Weight            Weight
  Weight            Components
  Services

UserConnection.Weight = sum(UserConnection.Services.Weight)
UserConnection.Services.Weight = sum(UserConnection.Services.Weight.Components)

我目前在 Django 中使用 Mongoengine。我尝试使用 EmbeddedDocument 和 EmbeddedDocumentListField 定义一些结构,但我对此并不满意。

为了简洁起见,我没有添加测试。

谁能帮我找到一个适合 MongDB 的结构?

谢谢。

【问题讨论】:

    标签: django mongodb mongoengine


    【解决方案1】:

    由于 mongoengine 在 python 类中定义了文档结构,您可以使用普通的 python 类@property 装饰方法返回计算值。

    考虑以下示例:

    import mongoengine as mdb
    
    mdb.connect("so-37396173")
    
    
    class User(mdb.Document):
        name = mdb.StringField()
    
    
    class UserConnection(mdb.Document):
        user_a = mdb.ReferenceField('User')
        user_b = mdb.ReferenceField('User')
        services = mdb.ListField(mdb.ReferenceField('Service'))
    
        @property
        def weight(self):
            return sum([s.weight for s in self.services])
    
    
    class Component(mdb.EmbeddedDocument):
        name = mdb.StringField()
        weight = mdb.FloatField()
    
    
    class Service(mdb.Document):
        name = mdb.StringField()
        components = mdb.EmbeddedDocumentListField('Component')
    
        @property
        def weight(self):
            return sum([c.weight for c in self.components])
    

    当您拥有UserConnection 对象时,您可以访问weight 属性:

    >>> uc = UserConnection.objects.first()
    >>> uc.weight
    0.8544546532
    

    这样做的缺点是weight 永远不会在UserConnection 的上下文中存储在数据库中,因此您无法在该级别对其进行聚合或排序,但aggregation framework 可能会提供一些不错的选择.如果您需要保存权重,那么您可以定义一些 signals 以在保存文档之前包含它:

    import mongoengine as mdb
    
    mdb.connect("so-37396173")
    
    class User(mdb.Document):
        name = mdb.StringField()
    
    
    class UserConnection(mdb.Document):
        user_a = mdb.ReferenceField('User')
        user_b = mdb.ReferenceField('User')
        services = mdb.ListField(mdb.ReferenceField('Service'))
        weight = mdb.FloatField()
    
    
        @classmethod
        def calc_weight(cls, sender, document, **kwargs):
            document.weight =  sum([s.weight for s in document.services])
    
    mdb.signals.pre_save.connect(UserConnection.calc_weight, sender=UserConnection)
    
    class Component(mdb.EmbeddedDocument):
        name = mdb.StringField()
        weight = mdb.FloatField()
    
    
    class Service(mdb.Document):
        name = mdb.StringField()
        components = mdb.EmbeddedDocumentListField('Component')
        weight = mdb.FloatField()
    
        @classmethod
        def calc_weight(cls, sender, document, **kwargs):
            document.weight =  sum([s.weight for s in document.components])
    
    mdb.signals.pre_save.connect(Service.calc_weight, sender=Service)
    

    这样做的一个缺点是您必须调用 save 方法,这意味着使用 upsert=True 执行 update 不会创建权重。

    您是使用嵌入式还是参考取决于what else you want to do with the data

    【讨论】:

    • 感谢史蒂夫的回答。我对此还有其他问题: 1. 为什么您使用 ReferenceField 而不是 EmbeddedDocument 来收集服务? 2. 我需要存储总和,而不是每次访问数据时即时计算。可能是服务中的字段权重可以使用 pre_save 信号填充,以便仅在添加/修改/删除组件时进行求和。你怎么看?
    • 因为 EmbeddedDocuments 不存储在自己的集合中,而是存储在父文档集合中。如果有很多项目,在集合中包含文档可以使一些查询和索引更容易一些。我同意信号是正确的选择,我将编辑答案以显示该方法。
    • 再次感谢史蒂夫!如果有一个信号也可以用于更新 upsert,那就太好了。
    猜你喜欢
    • 2015-12-05
    • 1970-01-01
    • 1970-01-01
    • 2016-07-24
    • 2016-02-29
    • 2011-11-28
    • 2016-06-28
    • 1970-01-01
    • 2018-08-23
    相关资源
    最近更新 更多