6

【前回の質問】

Reddit のようなコメントをアプリに追加しようとしていますが、データベースの編成にはクロージャー テーブル パターンを使用することにしました。私のアプリデータベースは次のようになります。

投稿

+----+-------+
| id | title |
+----+-------+
|  1 | Hello |
+----+-------+

コメント

+----+-----------+---------+------+
| id | parent_id | post_id | text |
+----+-----------+---------+------+
|  1 |      NULL |       1 | ...  |
|  2 |         1 |       1 | ...  |
|  3 |         2 |       1 | ...  |
|  4 |         3 |       1 | ...  |
|  5 |         3 |       1 | ...  |
|  6 |         5 |       1 | ...  |
|  7 |      NULL |       1 | ...  |
|  8 |         7 |       1 | ...  |
|  9 |         4 |       1 | ...  |
+----+-----------+---------+------+

コメントパス

+-----------+----------+-------+
| parent_id | child_id | depth |
+-----------+----------+-------+
|         1 |        1 |     0 |
|         2 |        2 |     0 |
|         1 |        2 |     1 |
|         3 |        3 |     0 |
|         2 |        3 |     1 |
|         1 |        3 |     2 |
|         4 |        4 |     0 |
|         3 |        4 |     1 |
|         2 |        4 |     2 |
|         1 |        4 |     3 |
          [...snip...]

現在、次のクエリを実行しています。

SELECT c.*, p.*
FROM comments AS c
JOIN comment_paths AS p
    ON c.id = p.child_id
WHERE p.parent_id IN
    (SELECT c2.id FROM comments AS c2 WHERE c2.parent_id IS NULL AND c2.post_id = 1)

に基づいてコメントのリストを取得しますpost_id。返されるデータは次のとおりです。

+------+-------------+-----------+--------+-------------+------------+---------+
| c.id | c.parent_id | c.post_id | c.text | p.parent_id | p.child_id | p.depth |
+------+-------------+-----------+--------+-------------+------------+---------+
|    1 |        NULL |         1 | ...    |           1 |          1 |       0 |
|    2 |           1 |         1 | ...    |           1 |          2 |       1 |
|    3 |           2 |         1 | ...    |           1 |          3 |       2 |
|    4 |           3 |         1 | ...    |           1 |          4 |       3 |
|    5 |           3 |         1 | ...    |           1 |          5 |       3 |
|    6 |           5 |         1 | ...    |           1 |          6 |       4 |
|    9 |           4 |         1 | ...    |           1 |          9 |       4 |
|    7 |        NULL |         1 | ...    |           7 |          7 |       0 |
|    8 |           7 |         1 | ...    |           7 |          8 |       1 |
+------+-------------+-----------+--------+-------------+------------+---------+

これはツリーを表します:

[1]
 |[2]
 | |[3]
 |   |[4]
 |   | |[9]
 |  [5]
 |   |[6]
[7]
 |[8]

ただし、返されたデータを Python ツリー構造に変換するのに苦労しています。基本的に、私の目標はこの質問と最終出力(HTML)に関するこの質問ですが、すでに情報を持っているので、再帰SQLステートメントに頼りたくありません。これに似た構造になりたいので、ある種の再帰が必要だと思います:

[
  {
    'id': 1,
    ...
    'children': [
                  {
                    'id': 2,
                    ...
                    'children': [ ... ]
                  }
                ]
  },
  {
    'id': 7,
    ...
    'children': [
                {
                  'id': 8,
                  ...
                }
              ]
  }
]

基本的に辞書のネストされたリストなので、Jinja の再帰 loopを使用してそれらをループできます。誰にもアイデアはありますか?

ありがとう!


編集 2013-04-17

いじって、私は「実用的な」解決策を持っていますが、それは多くの反復を行うので、この質問に対する答えとしてマークしたくありません。私が使用した解決策は次のとおりです。

comment_set = ... # previous query to grab data set

def create_tree(parent):
    parent['children'] = []
    for record in comment_set:
        if record['parent_id'] == parent['id']:
            parent['children'].append(create_tree(record))
    return parent

comment_tree = []
for record in comment_set:
    if record['parent_id'] is None: # if this is the start of a tree
        comment_tree.append(create_tree(record))

セット内のすべてのレコードが呼び出されるcomment_setたびに繰り返されるため、理想的ではありません。create_tree()しかし、それは私が今持っている最高のものです。誰にも考えはありますか?

4

2 に答える 2

5

再帰は必要ありません。参照によってノード オブジェクトを処理できる必要があるだけです。

ネストされたデータ構造を線形時間で作成するコード例を次に示します。私はこれをテストしておらず、まだ Python に精通していないため、これを疑似コードとして扱います。

2 つの for ループを使用する理由は、そうしないと、ツリーの最上位のノードが、ツリーの深いノードの前に処理されると想定しなければならないからです。以下に示す 2 つのループでは、そのような仮定は必要ありません。

for record in comment_set:
    nodes[record['id']] = record
for record in comment_set:
    if record['parent_id'] in nodes:
        nodes[record['parent_id']]['children'].append(record)
    else
        top = record;
return top

そのループの終わりまでに:

  • nodes階層内のすべてのノードの 1 次元ディクショナリになります。
  • 各ノードには、それ自体の直接の子のリストがあります。
  • top親を持たない唯一のノード (つまり、ルート ノード) にする必要があります。

これは、過去の SO 投稿で投稿した例に似ています。Turn database result into array :

于 2013-04-19T01:16:44.450 に答える
0

コードを改善するのに役立ついくつかの小さな変更を加えることができます::

comment_set = ... order by parent_id asc

def create_tree(parent, list):
    parent['children'] = []
    for record in comment_set:
        if record['parent_id'] == parent['id']:
            parent['children'].append(create_tree(record, list[1:]))
    return parent

comment_tree = []
for record in comment_set:
    if record['parent_id'] is None: # if this is the start of a tree
        comment_tree.append(create_tree(record, comment_set))
于 2014-05-27T14:08:00.160 に答える