Java 创建图形查询语言(节点/边/超边)

Java 创建图形查询语言(节点/边/超边),java,graph,visitor-pattern,edges,Java,Graph,Visitor Pattern,Edges,我正在创建一个API,它用附加属性和助手封装对象。我不希望用户访问数据库,因为我必须为API的使用者提供某些查询功能 我有以下资料: Node1(w/ attributes) -- > Edge1(w/ attr.) -- > Node2(w/ attr.) 及 基本上,节点可以是特定的类型,这将决定可用属性的类型。因此,我需要能够根据不同的类型和属性查询这些“路径” 例如:从一个节点开始,找到路径typeA>typeB&attr1>typeC 因此,我需要做一些简单的事情,并且

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

我有以下资料:

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

基本上,
节点
可以是特定的
类型
,这将决定可用属性的类型。因此,我需要能够根据不同的类型和属性查询这些“路径”

例如:从一个节点开始,找到路径
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();
ConditionImpl hasMass=ConditionFactory.createHasMass(2.5);
ConditionImpl noAttributes=ConditionFactory.createNoAttributes();
列表条件=新建ArrayList();
条件。添加(hasMass);
条件。添加(无属性);
ConditionVisitor ConditionVisitor=新ConditionVisitor(条件);
接受(conditionVisitor);
List validPaths=conditionVisitor.getValidPaths();
上面的代码执行一个查询,检查起始节点的质量是否为
2.5
,链接节点(子节点)是否没有属性。访问者执行一个
条件。检查(节点)
并返回一个布尔值


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

听起来你拥有除了一些语法糖以外的所有部分

一个不可变的样式怎么样?在这个样式中,您可以创建上面的整个列表,如

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

您可以使用此样式创建任何类型的线性查询模式;如果您添加了一些额外的状态,您甚至可以通过setName(“a”)和更高版本的.node(“a”)来执行分支查询,以返回到查询的那个点。

个人而言,我喜欢访问者模式的想法,但是访问所有节点可能代价高昂

查询界面:如果用户/其他开发人员正在使用它,我将使用具有可读方法名称的生成器样式界面:

    Visitor v = QueryBuilder
                  .selectNodes(ConditionFactory.hasMass(2.5))
                  .withChildren(ConditionFactory.noAttributes())
                  .buildVisitor();
    node.accept(v);
    List<Set<Node>> validPaths = v.getValidPaths();
(我使用“freestyle”是因为现在缺少创造性,但它的意图应该很清楚)Node表示,一般来说,这可能是两个不同的接口,以便不构建奇怪的查询

    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
实现将负责在访问的节点周围“移动”,而
条件实现将检查条件是否匹配

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

节点数量的可伸缩性:您可能需要添加某种索引以加快查找“感兴趣”节点的速度。出现的一个想法是(针对每个索引)在图中添加一个层,其中每个节点为“索引变量”的一个不同属性设置建模。然后,法线边可以将这些索引节点与原始图中的节点连接起来。索引上的超边缘可以构建一个更小的搜索网络。当然,仍然有一种枯燥的方法,就是使用
attributeValue->node
mapping将索引存储在类似于map的结构中。这可能比上面的想法更有效


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

我不确定是否完全遵循您的逻辑。什么是
empty
,它指的是必须传入
节点的事实吗?我认为
empty
指的是一个“空”访问者(将匹配所有节点),该访问者随后被条件“填充”。在发布的案例中,我会将其理解为“从默认访问者开始,检查被访问节点的质量是否为2.5,在这些节点上,取每个边,取其末端的节点,检查其是否没有属性。”我明白了,因此它只处理有限的场景,并且需要为更通用的模式做一些额外的工作。不,它可以根据您的需要进行通用化。告诉我一个真正通用的例子……比如:查找一个以
TypeA
开头、有一个或多个
TypeB
并以
TypeC
结尾的路径。其中有效路径可能如下所示:
TypaA、TypeB、TypeB、TypeB、TypeC
TypeA、TypeB、TypeC
?您是否需要修改生成器符号以支持此操作?您必须支持此查询的元素:检查类型是一个条件。下面的1条或多条边可能只是
QuerySelector
中的一个新方法。因此,结果可能如下所示:
QueryBuilder.selectNodes(Condition.hasType(“A”)).recurseOnChildren(Condition.hasType(“B”)).withChildren(Condition.hasType(“C”)
。关于如何在图形上移动的逻辑将是
QuerySelector
的实现问题。我得说,我喜欢你的方法。比创建基于
字符串的查询并让消费者学习如何使用它更容易。在
示例中,您为什么要传递
条件
实例,但它标记为使用
查询选择器
实例?这是两种不同的方法。
和()
    // 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();
    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();
    }