我有一个拥有1.5亿个节点和几亿个关系的图形数据库。
网络中有两种类型的节点:帐户节点和事务节点。每个帐户节点都有一个公钥,每个事务节点都有一个数字(此事务中涉及的总比特币数量)。
网络中还有两种类型的关系。每个关系都将帐户节点与事务节点连接起来。一种关系是"发送"另一种类型是"接收"。每个关系也有一个数字来表示它发送或接收的比特币数量。
这是一个例子:
(account: publickey = A)-[send: bitcoin=1.0]->(transaction :id = 1, Tbitcoin=1.0)-[receive: bitcoin=0.5]->(account: publickey = B)
(account: publickey = A)-[send: bitcoin=1.0]->(transaction :id = 1, Tbitcoin=1.0)-[receive: bitcoin=0.5]->(account: publickey = C)
您可以想象,B
或C
也可以向其他涉及许多不同交易的帐户发送或接收比特币。
我想要做的是找到两个帐户之间深度等于4的所有路径,例如A
和C
。我可以通过Cypher做到这一点,尽管它很慢。大约需要20分钟。我的密码是这样的:
start src=node:keys(PublicKey="A"),dest=node:keys(PublicKey="C")
match p=src-->(t1)-->(r1)-->(t2)-->dest
return count(p);
但是,当我尝试使用Java API执行此操作时,我得到了OutOfMemoryError
。这是我的功能:
public ArrayList<Path> getPathsWithConditionsBetweenNodes(String indexName, String sfieldName, String sValue1, String sValue2,
int depth, final double threshold, String relType){
ArrayList<Path> res = null;
if (isIndexExistforNode(indexName)) {
try (Transaction tx = graphDB.beginTx()) {
IndexManager index = graphDB.index();
Index<Node> accounts = index.forNodes(indexName);
IndexHits<Node> hits = null;
hits = accounts.get(sfieldName, sValue1);
Node src = null, dest = null;
if(hits.iterator().hasNext())
src = hits.iterator().next();
hits = null;
hits = accounts.get(sfieldName, sValue2);
if(hits.iterator().hasNext())
dest = hits.iterator().next();
if(src==null || dest==null){
System.out.println("Either src or dest node is not avaialble.");
}
TraversalDescription td = graphDB.traversalDescription()
.depthFirst();
if (relType.equalsIgnoreCase("send")) {
td = td.relationships(Rels.Send, Direction.OUTGOING);
td = td.relationships(Rels.Receive, Direction.OUTGOING);
} else if (relType.equalsIgnoreCase("receive")) {
td= td.relationships(Rels.Receive,Direction.INCOMING);
td = td.relationships(Rels.Send,Direction.INCOMING);
} else {
System.out
.println("Traverse Without Type Constrain Because Unknown Relationship Type is Provided to The Function.");
}
td = td.evaluator(Evaluators.includingDepths(depth, depth))
.uniqueness(Uniqueness.RELATIONSHIP_PATH)
.evaluator(Evaluators.returnWhereEndNodeIs(dest));
td = td.evaluator(new Evaluator() {
@Override
public Evaluation evaluate(Path path) {
if (path.length() == 0) {
return Evaluation.EXCLUDE_AND_CONTINUE;
} else {
Node node = path.endNode();
if (!node.hasProperty("TBitcoin"))
return Evaluation.INCLUDE_AND_CONTINUE;
double coin = (double) node.getProperty("TBitcoin");
if (threshold!=Double.MIN_VALUE) {
if (coin<=threshold) {
return Evaluation.EXCLUDE_AND_PRUNE;
} else {
return Evaluation.INCLUDE_AND_CONTINUE;
}
} else {
return Evaluation.INCLUDE_AND_CONTINUE;
}
}
}
});
res = new ArrayList<Path>();
int i=0;
for(Path path : td.traverse(src)){
i++;
//System.out.println(path);
//res.add(path);
}
System.out.println();
tx.success();
} catch (Exception e) {
e.printStackTrace();
}
} else {
;
}
return res;
}
有人可以看看我的功能并给我一些想法,为什么它如此缓慢并会导致内存不足错误?我在运行此程序时设置了Xmx=15000m
。
答案 0 :(得分:0)
我的0.02美元是你不应该用java做这个,你应该用Cypher做到这一点。但是你的查询需要一些工作。这是您的基本查询:
start src=node:keys(PublicKey="A"),dest=node:keys(PublicKey="C")
match p=src-->(t1)-->(r1)-->(t2)-->dest
return count(p);
至少有两个问题:
以下是如何收紧您的查询以使其表现更好:
start src=node:keys(PublicKey="A"),dest=node:keys(PublicKey="C")
match p=src-[:send]->(t1:transaction)-[:receive]->(r1)-[:send]->(t2:transaction)-[:receive]->dest
where r1 <> src and
r1 <> dest
return count(p);
这应该删除您当前正在进行的许多可能的边缘和节点遍历,而您不需要这样做。
答案 1 :(得分:0)
如果我已经理解了你想要达到的目标,并且因为你对你们的关系有一个方向,我认为你可以通过一些非常简单的方法来解决问题:
MATCH (src:keys{publickey:'A')-[r:SEND|RECEIVE*4]->(dest:keys{publickey:'C'})
RETURN COUNT(r)
根据您的数据集@FrobberOfBits在测试使用此方法无法做到的中介的相等性时提出了一个很好的观点,但是只有两个事务正在测试事务源和目标是否相同的情况({{ 1}}和r1 <> src
),在您的模型中甚至可能无效。如果您正在测试3个或更多交易,那么事情会变得更有趣,因为您可能希望排除r1 <> dest
无耻盗窃:
(A)-->(T1)-->(B)-->(T2)-->(A)-->(T3)-->(C)
或遍历(警告,伪代码,从未使用过它):
MATCH path=(src:keys{publickey:'A')-[r:SEND|RECEIVE*6]->(dest:keys{publickey:'C'})
WHERE ALL (n IN NODES(path)
WHERE (1=LENGTH(FILTER(m IN NODES(path)
WHERE m=n))))
RETURN COUNT(path)