2

我正在创建一个 API,它使用额外的属性和助手来封装JPA对象。我不希望用户访问数据库,因为我必须为 API 的使用者提供某些查询功能。

我有以下内容:

Node1(w/ attributes) -- > Edge1(w/ attr.) -- > Node2(w/ attr.)

Node1(w/ attributes) -- > |
Node2(w/ attributes) -- > |  -- > HyperEdge1(w/ attr.)
Node3(w/ attributes) -- > |

节点/边缘/超边缘示例

基本上 aNode可以是某个type,这将决定可用属性的种类。所以我需要能够根据不同的类型和属性来查询这些“路径”。

例如:从一个节点开始,找到一个路径typeA > typeB & attr1 > typeC

所以我需要做一些简单的事情,并且能够将查询编写为字符串,或者可能是构建器模式样式。

到目前为止,我所拥有的是一个访问者模式,它设置为遍历节点/边缘/超边缘,这允许进行某种查询,但这并不是很简单,因为您必须为新类型的查询创建一个新的访问者。

到目前为止,这是我的实现:

    ConditionImpl hasMass = ConditionFactory.createHasMass( 2.5 );
    ConditionImpl noAttributes = ConditionFactory.createNoAttributes();

    List<ConditionImpl> conditions = new ArrayList<ConditionImpl>();
    conditions.add( hasMass );
    conditions.add( noAttributes );

    ConditionVisitor conditionVisitor = new ConditionVisitor( conditions );
    node.accept( conditionVisitor );

    List<Set<Node>> validPaths = conditionVisitor.getValidPaths();

上面的代码执行了一个查询,检查起始节点是否有质量2.5并且链接节点(子节点)没有属性。访问者执行 acondition.check( Node )并返回一个布尔值。


我从哪里开始为更简单的图创建查询语言?注意:我没有使用现有图形库的选项,我将拥有数十万个节点,加上边..

4

2 回答 2

1

就个人而言,我喜欢访问者模式的想法,但是访问所有节点可能会变得很昂贵。

查询接口:如果用户/其他开发人员正在使用它,我会使用构建器风格的接口,具有可读的方法名称:

    Visitor v = QueryBuilder
                  .selectNodes(ConditionFactory.hasMass(2.5))
                  .withChildren(ConditionFactory.noAttributes())
                  .buildVisitor();
    node.accept(v);
    List<Set<Node>> validPaths = v.getValidPaths();

正如上面所指出的,这或多或少只是你已经拥有的语法糖(但糖使一切变得不同)。我会将“在图上移动”的代码(例如“检查访问的节点是否满足条件”或“检查连接的节点是否满足条件”)与实际检查(或是)条件的代码分开。此外,在条件下使用复合材料来构建和/或:

    // Select nodes with mass 2.5, follow edges with both conditions fulfilled and check that the children on these edges have no attributes. 
    Visitor v = QueryBuilder
                  .selectNodes(ConditionFactory.hasMass(2.5))
                  .withEdges(ConditionFactory.and(ConditionFactory.freestyle("att1 > 12"), ConditionFactory.freestyle("att2 > 23")) 
                  .withChildren(ConditionFactory.noAttributes())
                  .buildVisitor();

(我现在使用“freestyle”是因为缺少创造力,但它的意图应该很清楚)节点通常这可能是两个不同的界面,以便不构建奇怪的查询。

    public interface QueryBuilder {
         QuerySelector selectNodes(Condition c);
         QuerySelector allNodes();
    }

    public interface QuerySelector {
         QuerySelector withEdges(Condition c);
         QuerySelector withChildren(Condition c);
         QuerySelector withHyperChildren(Condition c);
         // ...
         QuerySelector and(QuerySelector... selectors);
         QuerySelector or(QuerySelector... selectors);

         Visitor buildVisitor();
    }       

使用这种语法糖可以使查询从源代码中可读,而无需您实现自己的数据查询语言。然后,QuerySelector实现将负责在访问节点周围“移动”,而Conditition实现将检查条件是否匹配。

这种方法的明显缺点是,您需要预见接口中的大多数查询并且需要已经实现它们。

节点数量的可扩展性:您可能需要添加某种索引来加快查找“有趣”节点的速度。出现的一个想法是(为每个索引)向图中添加一个层,其中每个节点对“索引变量”的不同属性设置之一进行建模。然后,正常边可以将这些索引节点与原始图中的节点连接起来。然后索引上的超边缘可以构建一个更小的网络来搜索。当然,将索引存储在具有映射的类映射结构中仍然是一种无聊的方式attributeValue -> node。无论如何,这可能比上面的想法更有效。

如果您有某种索引,请确保索引也可以接收访问者,这样它就不必访问图中的所有节点。

于 2012-12-13T20:41:47.183 回答
0

听起来除了一些语法糖之外,您拥有所有的部分。

创建上面的整个列表的不可变样式怎么样

Visitor v = Visitor.empty
    .hasMass(2.5)
    .edge()
    .node()
    .hasNoAttributes();

您可以使用这种风格创建任何类型的线性查询模式;如果您添加一些额外的状态,您甚至可以通过例如 setName("A") 和稍后的 .node("A") 进行分支查询以返回查询的该点。

于 2012-12-13T20:10:15.593 回答