什么是更好的mongodb文档结构?

问题描述:

我正在努力创造一个良好的文档结构,它具有良好的阅读性能,而不是如此缓慢的写作表现。 我需要存储有关UserConnection文档的信息,该文档表示数据库中两个用户之间的链接。每个链接的权重取决于参数列表。什么是更好的mongodb文档结构?

下面是代表数据,链接,和权重的组件架构:

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) 
我目前使用Mongoengine到Django的

。我试图用EmbeddedDocument和EmbeddedDocumentListField来定义一些结构,但我对此并不满意。

为简洁起见,我不添加我的测试。

任何人都可以请帮我找到一个好的MongDB结构?

谢谢。

因为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=Trueupdate将不会创建权重。

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

+0

谢谢史蒂夫的回答。我还有其他问题: 1.为什么您使用ReferenceField而不是EmbeddedDocument for Service集合? 2.我需要存储总和,并且在每次访问数据时都不会随时计算它。可能是现场权重服务可以使用pre_save信号来填充,只有当组件被添加/修改/删除时才执行总和。你怎么看待这件事? –

+0

由于EmbeddedDocuments未存储在其自己的集合中,但存储在父级文档集合中。如果有多个项目,将文档放在集合中可以使一些查询和索引变得更容易。我同意这些信号是正确的选择,我将编辑答案以显示该方法。 –

+0

再次感谢你史蒂夫!如果有一个信号可以在upsert更新中使用,那就太好了。 –