Java 创建图形查询语言(节点/边/超边)
我正在创建一个API,它用附加属性和助手封装对象。我不希望用户访问数据库,因为我必须为API的使用者提供某些查询功能 我有以下资料: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 因此,我需要做一些简单的事情,并且
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();
}