【问题标题】:What's the best way to inherit properties in a tree-based structure?在基于树的结构中继承属性的最佳方式是什么?
【发布时间】:2011-03-11 06:07:29
【问题描述】:

我有一个简单的 CMS 系统,它有一个简单的树层次结构:

我们的页面 A 到 E 具有以下层次结构: A -> B -> C -> D -> E

所有页面都是同一个类,并且有父子关系。

现在,假设我有一个要在页面之间继承的属性。假设 A 是红色的: A(红色) -> B -> C -> D -> E

在这种情况下,B 到 E 将继承“红色”。

或者更复杂的场景: A(红色)-> B -> C(蓝色)-> D -> E

B 将继承红色,D/E 都将是蓝色。

解决此类问题的最佳方法是什么?我有一个包含 6,000 多片叶子的树结构,其中大约 100 片叶子具有可继承的属性。大约 100 片叶子的属性保存在数据库中。对于没有显式属性的叶子,我查找祖先并使用 memcached 来保存属性。然后有非常复杂的算法来处理这些缓存的过期问题。这非常令人费解,我想重构一个更清洁的解决方案/数据结构。

有人有什么想法吗?

谢谢!

【问题讨论】:

    标签: algorithm data-structures tree


    【解决方案1】:

    有一种数据模型可以让你完美地表达这种信息,那就是 RDF/RDFS 。 RDF 是 W3C 标准,用于基于三元组(主语、谓语、宾语)和 URI 对数据进行建模;和 RDFS 等允许您描述类层次结构和属性层次结构。好消息是有许多库可以帮助您创建和查询此类数据。

    例如,如果我想说特定文档 Lion 属于 Animal 类,而 programmer 属于 Geek 类,我可以说:

    doc:lion rdf:type class:mamal .
    doc:programmer rdf:type class:Geek .
    

    现在我可以声明类的层次结构,并说每个哺乳动物都是动物,每个动物都是生物。

    class:mamal rdfs:subClassOf class:animal .
    class:animal rdfs:subClassOf class:LivingThing .
    

    而且,每个极客都是人,每个人都是生物:

    class:geek rdfs:subClassOf class:human .
    class:human rdfs:subClassOf class:LivingThing .
    

    有一种语言,类似于SQL,叫做SPARQL 来查询这种数据,例如如果我发出查询:

    SELECT * WHERE {
           ?doc rdf:type class:LivingThing .
    }
    

    其中?doc 是一个变量,它将绑定class:LivingThing 的事物类型。我会得到这个查询的结果doc:liondoc:programmer,因为数据库技术将遵循RDFS 的语义,因此通过计算类的闭包,它会知道doc:liondoc:programmerclass:LivingThing

    以同样的方式查询:

    SELECT * WHERE {
           doc:lion rdf:type ?class .
    }
    

    会告诉我doc:lionrdf:typeclass:mamal class:animalclass:LivingThing

    就像我刚才解释的那样,使用 RDFS,您可以创建属性层次结构,然后说:

    doc:programmer doc:studies doc:computerscience .
    doc:lion doc:instint doc:hunting .
    

    我们可以说doc:skilldoc:instint 都是doc:knows 的子属性:

    doc:studies rdfs:subPropertyOf doc:knows .
    doc:instint rdfs:subPropertyOf doc:knows .
    

    使用查询:

    SELECT * WHERE {
           ?s doc:knows ?o .
    }
    

    我们会知道狮子会打猎,程序员会计算机科学。

    大多数 RDF/RDFS 数据库都可以轻松处理您在问题中提到的元素数量,并且有很多选择可以开始。如果你是 Java 人,你可以看看 Jena,还有适用于 .Net lije this one 或 Python 的框架 RDFLIB

    但最重要的是,请查看 CMS 的文档,因为可能有插件可以将元数据导出为 RDF。例如,Drupal 在这种情况下是相当先进的(参见http://drupal.org/project/rdf

    【讨论】:

      【解决方案2】:

      如果您的问题与性能有关...

      我假设您希望保存所有这些可继承属性的内存(或者您可能有很多 个),否则这可以通过虚拟属性轻松解决。

      如果您需要稀疏的可继承属性,假设您正在对 HTML DOM 属性或 CSS 属性的传播方式进行建模,您需要:

      1. 保留一个指向父节点的指针(用于向上走)
      2. 使用散列字典将属性存储在每个类(或每个实例,取决于您的需要)中,按名称作为键
      3. 如果属性不因实例而异,请使用类静态字典
      4. 如果可以逐个实例覆盖属性,请在顶部添加实例字典
      5. 访问属性时,从叶子开始查找,先查找实例字典,然后查找类静态字典,然后向上遍历树

      当然,您可以在此基础上添加更多功能。这类似于 Windows Presentation Foundation 通过 DependencyProperty 解决此问题的方式。

      如果您的问题与数据库有关...

      如果您的问题是避免读取数据库以向上遍历树(即加载父级以查找继承的属性),则需要对父级值进行某种缓存。或者,当您从数据库加载叶子时,您可以加载其所有父节点并在内存中创建主合并属性字典。

      如果你想避免多次数据库查找来找到每个父节点,一个技巧是将每个节点的路径编码到一个文本字段中,例如“1.2.1.3.4”代表第 6 层的叶子。然后,仅加载具有以子字符串开头的路径的节点。然后,您可以在一个 SQL 查询中获取整个父路径。

      【讨论】:

      • 感谢史蒂文,这非常有帮助。问题与数据库有关,所以我最终将父值缓存到内存中。我正在使用嵌套的集合数据结构,因此我能够在单个查询中获取节点的所有祖先(尽管它很慢,例如 50 毫秒)并计算正确的值并将它们存储在 memcached 中。
      • 顺便说一句,您与 CSS 属性如何继承的比较正是我的问题所在。但是我想对于浏览器来说,DOM 都在内存中,而对我来说,我的节点都在数据库中,因此我必须非常良心地知道如何调用数据库。
      • @chuboy,您可以发布您的问题查询以查看是否可以加快速度吗?我怀疑您正在使用非常复杂的逻辑来加载嵌套记录,因为 RDBMS 不太擅长在单个表中建模嵌套结构,因此需要一些技巧。
      猜你喜欢
      • 1970-01-01
      • 2020-02-10
      • 1970-01-01
      • 1970-01-01
      • 2021-06-15
      • 1970-01-01
      • 2011-05-15
      • 2019-05-10
      • 1970-01-01
      相关资源
      最近更新 更多