【问题标题】:What is the simplest way to save a file-tree in a postgres database?在 postgres 数据库中保存文件树的最简单方法是什么?
【发布时间】:2013-11-07 11:10:22
【问题描述】:

我正在从 Dropbox api 中检索文件树。在 api 中,每个文件夹都是通过单独的 api 调用读取的,因此我将遍历整个文件树以获取所有文件夹。这是通过 cron-job 完成的。

从 Dropbox 中检索数据的函数如下所示:

function renderFolderTree($myobject, $path){
    $entry = $myobject->getMetadataWithChildren($path);
    foreach ($entry['contents'] as $child) {
        if ($child['is_dir']){
            $folderpath = $child['path'];
            //this will retrieve the child-folder
            renderFolderTree($myobject, $folderpath, $filetree);
            //here I need something that saves the folder
        }else{
            print_r($child);
            //here I need something that saves the file
        }
    }
}

我想将文件树保存到 postgres 数据库,以便以后可以将其作为代表它的 json 对象输出。

我是数据库设计的新手,不知道如何保存数据。我假设每个文件和文件夹都应该有自己的数据库条目。我可以让每个孩子都引用它的父母 ID,或者我可以让每个父母都包含一个孩子的列表。

作为新手,我想要一个相当简单且阅读速度比写作更重要的解决方案!

【问题讨论】:

标签: postgresql database-design


【解决方案1】:

在关系数据库中存储树有多种选择。 为了获得良好的概述,我推荐 Bill Karwin 的 slides

既然您提到读取速度是最重要的,那么闭包表将是一种合适的、强大的编码。闭包表是一个多对多关系,它为每个路径(例如,/a/b/c)存储所有父/子(传递地)。这样,对树的许多查询可以通过一个 SQL 查询(非递归)完成。

看起来像

create table nodes (
    path varchar primary key
    /* your other attributes here, can be null */
);

create table parents_children (
    parent_path varchar,
    child_path varchar,
    primary key(parent_path,child_path),
    foreign key (parent_path) references nodes (path),
    foreign key (child_path) references nodes (path)
);

要在目录 /a/b/ 下插入新文件 /a/b/c,您可以:

insert into nodes values ('/a/b/c');

insert into parents_children
select parent_path, '/a/b/c' from parents_children where child_path = '/a/b/'
union all select '/a/b/c','/a/b/c';

例如,要递归查询来自 '/a' 的所有子项,您可以这样做:

select * 
from nodes join parents_children on path = child_path
where parent_path = '/a';

一个更详尽的例子,它存储了以下文件树:

/
/a/
/a/b/
/a/b/d
/a/c
/b

插入数据:

insert into nodes values ('/');
insert into parents_children values ('/','/');

insert into nodes values ('/a/');
insert into parents_children
select parent_path, '/a/' from parents_children where child_path = '/'
union all select '/a/','/a/';

insert into nodes values ('/a/b/');
insert into parents_children
select parent_path, '/a/b/' from parents_children where child_path = '/a/'
union all select '/a/b/','/a/b/';

insert into nodes values ('/a/c');
insert into parents_children
select parent_path, '/a/c' from parents_children where child_path = '/a/'
union all select '/a/c','/a/c';

insert into nodes values ('/a/b/d');
insert into parents_children
select parent_path, '/a/b/d' from parents_children where child_path = '/a/b/'
union all select '/a/b/d','/a/b/d';

insert into nodes values ('/b');
insert into parents_children
select parent_path, '/b' from parents_children where child_path = '/'
union all select '/b','/b';

查询/a/的所有子节点

select node.*
from nodes join parents_children on path = child_path
where parent_path = '/a/';

path        
----------  
/a/         
/a/b/       
/a/b/d      
/a/c        

【讨论】:

  • 惊人的完美答案!
  • 如何将节点/a/b/ 重命名为/a-b/,即将b/a 移动到根目录下的新/a-b
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2011-09-13
  • 2017-02-11
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-09-02
  • 2010-11-02
相关资源
最近更新 更多