Data Model(数据模型)——数据模型示例和模式(二)——树结构模型



  • 1)使用父引用的树结构模型 父引用模式将每个树节点分别存储在单独的文档中。 除了树节点,文档还存储该节点的父节点的id。 假设有如下的分类层次:

                                                   图 1

    以下示例使用“父引用”创建树结构,在字段parent中存储对父类别的引用:

    db.categories.insert( { _id: "MongoDB", parent: "Databases" } )
    db.categories.insert( { _id: "dbm", parent: "Databases" } )
    db.categories.insert( { _id: "Databases", parent: "Programming" } )
    db.categories.insert( { _id: "Languages", parent: "Programming" } )
    db.categories.insert( { _id: "Programming", parent: "Books" } )
    db.categories.insert( { _id: "Books", parent: null } )
    

    用于检索节点的父节点的查询是快速直接的:

    db.categories.findOne( { _id: "MongoDB" } ).parent
    

    您可以在parent字段上创建索引以启用按父节点快速搜索:

    db.categories.createIndex( { parent: 1 } )
    

    您可以按parent字段查询来查找其直接子节点:

    db.categories.find( { parent: "Databases" } )
    

    父链接模式提供了树存储的简单解决方案,但需要多次查询来检索子树。

    2) 使用子引用的树结构模型 子引用模式将每个树节点分别存储在单独的文档中。 除了树节点,文档还在数组中存储该节点的全部子节点的id。 假设有上述图 1所示的分类层次。 以下示例使用“子引用” 创建树结构,在字段children中存储节点的全部子节点的引用:

    db.categories.insert( { _id: "MongoDB", children: [] } )
    db.categories.insert( { _id: "dbm", children: [] } )
    db.categories.insert( { _id: "Databases", children: [ "MongoDB", "dbm" ] } )
    db.categories.insert( { _id: "Languages", children: [] } )
    db.categories.insert( { _id: "Programming", children: [ "Databases", "Languages" ] } )
    db.categories.insert( { _id: "Books", children: [ "Programming" ] } )
    

    检索节点的直接子节点的查询是快速和直接的:

    db.categories.findOne( { _id: "Databases" } ).children
    

    您可以在children字段上创建索引以启用按子节点的快速搜索:

    db.categories.createIndex( { children: 1 } )
    

    您可以在children字段中查询一个节点以查找其父节点及其兄弟节点:

    db.categories.find( { children: "MongoDB" } )
    

    子引用模式为树存储提供了合适的解决方案,只要不需要对子树进行操作即可。 该模式还可以提供用于存储图的一个合适的解决方案,其中节点可以具有多个父节点。

    3)使用祖先节点数组的树结构模型 祖先节点数组模式将每个树节点分别存储在单独的文档中。除了树节点,文档还在数组中存储该节点的全部祖先节点的id或路径。 假设有上述图 1所示的分类层次。 以下示例使用“祖先节点数组”创建树结构。 除了ancestors字段之外,这些文档还在parent字段存储对直接上层类别的引用:

    db.categories.insert( { _id: "MongoDB", ancestors: [ "Books", "Programming", "Databases" ], parent: "Databases" } )
    db.categories.insert( { _id: "dbm", ancestors: [ "Books", "Programming", "Databases" ], parent: "Databases" } )
    db.categories.insert( { _id: "Databases", ancestors: [ "Books", "Programming" ], parent: "Programming" } )
    db.categories.insert( { _id: "Languages", ancestors: [ "Books", "Programming" ], parent: "Programming" } )
    db.categories.insert( { _id: "Programming", ancestors: [ "Books" ], parent: "Books" } )
    db.categories.insert( { _id: "Books", ancestors: [ ], parent: null } )
    

    用于检索节点的祖先节点或路径的查询是快速且直接的:

    db.categories.findOne( { _id: "MongoDB" } ).ancestors
    

    您可以在ancestors字段上创建索引以启用按祖先节点的快速搜索:

    db.categories.createIndex( { ancestors: 1 } )
    

    您可以通过ancestors字段查找该节点所有后代节点:

    db.categories.find( { ancestors: "Programming" } )
    

    祖先节点数组模式通过在ancestors字段的元素上创建索引,来查找节点的后代节点和祖先节点提供了一种快速有效的解决方案。 这使得祖先节点数组成为处理子树的好选择。 祖先节点数组模式比物化路径模式稍慢,但使用起来更直接。

    4)使用物化路径的树结构模型 物化路径模式将每个树节点分别存储在单独的文档中。除了树节点,文档还以字符串的形式存储该节点的全部祖先节点的id或路径。 虽然物化路径模式需要使用字符串和正则表达式的额外步骤,但该模式在使用路径时同样提供了更大的灵活性,例如通过部分路径查找节点。 假设有上述图 1所示的分类层次。 以下示例使用“物化路径” 创建树结构,将路径存储在path字段中,路径字符串使用逗号作为分隔符:

    db.categories.insert( { _id: "Books", path: null } )
    db.categories.insert( { _id: "Programming", path: ",Books," } )
    db.categories.insert( { _id: "Databases", path: ",Books,Programming," } )
    db.categories.insert( { _id: "Languages", path: ",Books,Programming," } )
    db.categories.insert( { _id: "MongoDB", path: ",Books,Programming,Databases," } )
    db.categories.insert( { _id: "dbm", path: ",Books,Programming,Databases," } )
    

    您可以查询检索整个树结构,按path字段排序:

    db.categories.find().sort( { path: 1 } )
    

    您可以在path字段上使用正则表达式来查找Programming的后代节点:

    db.categories.find( { path: /,Programming,/ } )
    

    您还可以检索Books的后代节点,其中Books也位于该层次结构的最高层:

    db.categories.find( { path: /^,Books,/ } )
    

    要在path字段上创建索引,请使用以下调用:

    db.categories.createIndex( { path: 1 } )
    

    此索引可以根据查询提高性能:

    • 对于路径从根节点Books开始的子树(例如/ ^,Books,/或/ ^,Books,Programming,/)的查询,path字段上的索引显著改善了查询性能。

    • 对于查询中没有提供从根节点开始的路径的子树的查询(例如/,Databases,/),或者关于子树的类似查询,其中节点可能在索引字符串的中间, 必须检查整个索引。 对于这些查询,如果索引显著小于整个集合,索引可以提供一些性能改善。

    5) 使用嵌套集的树结构模型 嵌套集模式将树中的每个节点标识为树的往返遍历中的停止点。 应用程序访问树中的每个节点两次, 第一次在初始行程期间,第二次在返回行程期间。 嵌套集模式将每个树节点存储为单独的文档。除了树节点之外,文档还存储该节点的父节点的id, left字段存储该节点的初始停止点,right字段存储该节点的返回停止点。 假设有如下的分类层次:

                                                           图 2

    以下示例使用嵌套集来创建树结构:

    db.categories.insert( { _id: "Books", parent: 0, left: 1, right: 12 } )
    db.categories.insert( { _id: "Programming", parent: "Books", left: 2, right: 11 } )
    db.categories.insert( { _id: "Languages", parent: "Programming", left: 3, right: 4 } )
    db.categories.insert( { _id: "Databases", parent: "Programming", left: 5, right: 10 } )
    db.categories.insert( { _id: "MongoDB", parent: "Databases", left: 6, right: 7 } )
    db.categories.insert( { _id: "dbm", parent: "Databases", left: 8, right: 9 } )
    

    您可以查询检索节点的后代:

    var databaseCategory = db.categories.findOne( { _id: "Databases" } );
    db.categories.find( { left: { $gt: databaseCategory.left }, right: { $lt: databaseCategory.right } } );
    

    嵌套集模式为查找子树提供了一种快速有效的解决方案,但是对于修改树结构则效率低下。 因此,此模式最适合不更改的静态树。

    原文地址

    本帖下載内容已隐藏,请登入以查看隐藏内容!


登录后回复
 

与 萌阔论坛 的连接断开,我们正在尝试重连,请耐心等待