什么是更好的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=True
做update
将不会创建权重。
您是否使用嵌入式与参考取决于what else you want to do with the data。
谢谢史蒂夫的回答。我还有其他问题: 1.为什么您使用ReferenceField而不是EmbeddedDocument for Service集合? 2.我需要存储总和,并且在每次访问数据时都不会随时计算它。可能是现场权重服务可以使用pre_save信号来填充,只有当组件被添加/修改/删除时才执行总和。你怎么看待这件事? –
由于EmbeddedDocuments未存储在其自己的集合中,但存储在父级文档集合中。如果有多个项目,将文档放在集合中可以使一些查询和索引变得更容易。我同意这些信号是正确的选择,我将编辑答案以显示该方法。 –
再次感谢你史蒂夫!如果有一个信号可以在upsert更新中使用,那就太好了。 –