class Parent(document):
name = StringField()
children = ListField(ReferenceField('Child'))
class Child(document):
name = StringField()
parents = ListField(ReferenceField(Parent))
@app.route('/home/')
def home():
parents = Parent.objects.all()
return render_template('home.html', items=parents)
我有两个类似于上面的集合,它们保持着多对多的关系。
在使用Angular的模板中,我将javascript变量设置为父类列表,如下所示:
$scope.items = {{ parents|tojson }};
这会产生一组父母,他们chilren
是一个Object Ids(引用)数组,而不是解除引用的child
个对象:
$scope.items = [{'$oid': '123', 'name': 'foo', 'children': [{'$oid': '456'}]}];
我希望这个角对象包含所有被解除引用的子对象。有没有一种有效的方法呢?
到目前为止,这是唯一适合我的方法,在O(n ^ 3)。为清晰起见,我已将列表理解最小化。将obj['_id'] = {'$oid': str(obj['_id']}
转换为可以序列化为json的东西需要多个ObjectId
。
@app.route('/home/')
def home():
parents = Parent.objects.all()
temps = []
for parent in parents:
p = parent.to_mongo()
# At this point, the children of parent and p are references only
p['_id'] = {'$oid': str(p['_id'])
temp_children = []
for child in parent.children:
# Now the child is dereferenced
c = child.to_mongo()
c['_id'] = {$oid': str(c['_id'])}
# Children have links back to Parent. Keep these as references.
c['parents'] = [{'oid': str(parent_ref)} for parent_ref in c['parents']]
temp_children.append(c)
p['children'] = temp_children
temps.append(parent.to_mongo())
return render_template('home.html', items=temps)
以下内容不起作用,但导致未解除引用的儿童:
json.loads(json.dumps(accounts))
答案 0 :(得分:2)
因为您只是将子项存储为引用,所以在使用QuerySet.all
方法时,您总是必须返回到服务器以取消引用它们。 mongodb的人都知道在使用像pymongo这样的驱动程序时这是一个很大的性能问题,所以他们有一个aggregation framework允许你在服务器上进行解除引用。
使用mongoengine的文档是pretty poor,但请查看unit tests in the mongoengine source帮助填写空白。
在this answer和的帮助下,如果您使用的是mongodb 3.2或更高版本,那么您可以按照以下方式实现您的目标:
import mongoengine as db
from bson.json_util import dumps
class Parent(db.Document):
name = db.StringField()
children = db.ListField(db.ReferenceField('Child'))
class Child(db.Document):
name = db.StringField()
parents = db.ListField(db.ReferenceField(Parent))
pipeline = [{"$unwind": "$children"},
{"$lookup":
{"from": "child",
"localField": "children",
"foreignField": "_id",
"as": "children"
}
},
{"$group": {
"_id": "$_id",
"name": {"$first": "$name"},
"children": {"$push": "$children"}
}
}
]
@app.route('/home/')
def home():
parents = []
for p in Parent.objects.aggregate(*pipeline):
parents.append(p)
items= dumps(parents)
return render_template('home.html', items=items)
然后在home.html
中,您只需要:
$scope.items = {{ items }};
这里的管道基本步骤是:
children
数组child
转到_id
集合并查找,并将结果存储在每个文档的children
字段中。基本上用匹配的文档替换ObjectID。_id
并根据分组中的第一项添加name
,并将所有子字段推送到名为children
$lookup仅适用于mongodb 3.2,如果您需要运行早期版本的mongodb,那么您将别无选择,只能进行多次查询。此外,$lookup
不适用于分片集合。