2
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)

上記に似た 2 つのコレクションがあり、多対多の関係を維持しています。

Angular のテンプレートでは、javascript 変数を次のように親のリストに設定しています。

$scope.items = {{ parents|tojson }};

chilrenこれにより、参照解除されたオブジェクトではなく、オブジェクト ID (参照) の配列である親の配列が生成されchildます。

$scope.items = [{'$oid': '123', 'name': 'foo', 'children': [{'$oid': '456'}]}];

この角度オブジェクトに、参照解除されたすべての子を含めたいと思います。これを行う効率的な方法はありますか?

これまでのところ、これが O(n^3) で機能する唯一のアプローチです。わかりやすくするために、リスト内包表記を最小化しました。を json にシリアル化できるものobj['_id'] = {'$oid': str(obj['_id']}に変換するには、倍数が必要です。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))
4

1 に答える 1

2

QuerySet.all子を参照としてのみ保存しているため、上記のメソッドを使用する場合は、常にサーバーに戻って逆参照する必要があります。mongodb の担当者は、pymongo などのドライバーを使用する場合にこれが大きなパフォーマンスの問題であることを認識しているため、サーバーで逆参照を実行できるようにする集約フレームワークを用意しています。

mongoengine でこれを使用するためのドキュメントはかなり貧弱ですが、mongoengine ソースの単体テストを見ると、空白を埋めるのに役立ちます。

この回答の助けを借りて、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 }};

ここでのパイプラインの基本的な手順は次のとおりです。

  1. children子を巻き戻す:配列内の子要素ごとに個別のドキュメントを作成します
  2. 子をルックアップ:childコレクションに移動し、に基づいてルックアップし、結果を各ドキュメントのフィールドに_id格納します。children基本的に、ObjectID を一致するドキュメントに置き換えます。
  3. 結果をグループ化します。グループ化の最初の項目に基づいて_id含め、すべての子フィールドをという名前のフィールドにプッシュします。namechildren

$lookupは mongodb 3.2 からのみ使用できます。初期バージョンの mongodb を実行する必要がある場合は、複数のクエリを作成するしかありません。また、$lookupシャードされたコレクションでは機能しません。

于 2016-02-08T14:10:09.533 に答える