Neo4j 使用Java API时内存不足,但Cypher查询可以工作,尽管速度很慢

Neo4j 使用Java API时内存不足,但Cypher查询可以工作,尽管速度很慢,neo4j,cypher,Neo4j,Cypher,我有一个拥有1.5亿个节点和几亿个关系的图形数据库 网络中有两种类型的节点:账户节点和交易节点。每个账户节点都有一个公钥,每个交易节点都有一个数字(该交易涉及的比特币总额) 网络中还有两种类型的关系。每个关系都连接一个帐户节点和一个事务节点。一种类型的关系是“发送”,另一种类型是“接收”。每个关系也有一个数字来表示它发送或接收多少比特币 这是一个例子: (account: publickey = A)-[send: bitcoin=1.0]->(transaction :id = 1, T

我有一个拥有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
。我可以用密码来做,虽然速度很慢。大约需要20分钟。我的密码是这样的:

start src=node:keys(PublicKey="A"),dest=node:keys(PublicKey="C") 
match p=src-->(t1)-->(r1)-->(t2)-->dest 
return count(p);
然而,当我尝试使用JavaAPI进行此操作时,我得到了
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;
}
public ArrayList getPathSwithConditions节点之间(字符串indexName、字符串sfieldName、字符串sValue1、字符串sValue2、,
整数深度,最终双阈值,字符串类型){
arraylistres=null;
if(isIndexExistforNode(indexName)){
try(事务tx=graphDB.beginTx()){
IndexManager index=graphDB.index();
索引帐户=Index.forNodes(indexName);
IndexHits hits=null;
hits=accounts.get(sfieldName,sValue1);
节点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(“src或dest节点不可用”);
}
TraversalDescription td=graphDB.TraversalDescription()
.depthFirst();
if(relType.equalsIgnoreCase(“发送”)){
td=td.关系(Rels.Send、Direction.OUTGOING);
td=td.关系(相对接收、方向、输出);
}else if(relType.equalsIgnoreCase(“接收”)){
td=td.关系(相对接收、方向、传入);
td=td.关系(Rels.Send、Direction.INCOMING);
}否则{
系统输出
.println(“没有类型约束的遍历,因为函数提供了未知的关系类型。”);
}
td=td.评估器(评估器,包括深度(深度,深度))
.唯一性(唯一性.关系\路径)
.评估员(评估员返回地点(目的地));
td=td.evaluator(新evaluator(){
@凌驾
公共评估(路径){
if(path.length()==0){
返回Evaluation.EXCLUDE_并_CONTINUE;
}否则{
Node Node=path.endNode();
if(!node.hasProperty(“TBitcoin”))
返回评估。包括_和_CONTINUE;
double coin=(double)node.getProperty(“TBitcoin”);
如果(阈值!=双最小值){

如果(coinMy$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);
这至少有两个问题:

  • 中间r1可能与原始src或原始dest相同(这可能不是您想要的,您正在寻找中间人)
  • 您没有指定t1或t2是发送或接收。这意味着您正在强制cypher匹配这两种边。这意味着cypher必须查看更多内容才能给出您的答案
以下是如何收紧查询,使其性能更好:

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);
start src=node:keys(PublicKey=“A”),dest=node:keys(PublicKey=“C”)
匹配p=src-[:send]->(t1:transaction)-[:receive]->(r1)-[:send]->(t2:transaction)-[:receive]->dest
其中r1 src和
r1目的地
返回计数(p);

这应该会删掉很多你目前正在做的、你不需要做的可能的边缘和节点遍历。

如果我已经理解了你想要实现的目标,并且因为你对你的关系有一个方向,我认为你可以做一些非常简单的事情:

MATCH (src:keys{publickey:'A')-[r:SEND|RECEIVE*4]->(dest:keys{publickey:'C'})
RETURN COUNT(r)
根据您的数据集,@frobberoffits对于测试中介体的平等性提出了一个很好的观点,这是使用这种方法无法做到的,但是,对于事务源和目标相同的情况(
r1src
r1dest
),您只测试两个事务,这在您的模型中甚至可能无效。如果您正在测试3个或更多事务,那么事情会变得更有趣,因为您可能希望排除像
(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)
或遍历(警告、伪代码,从未使用过):

PathExpander=PathExapnder.forTypesAndDirections(“发送”、“传出”、“接收”、“传出”)
PathFinder=GraphAlgoFactory.AllSimplePath(扩展器,6);
Iterable path=finder.findAllPaths(src,dest);

非常感谢!我一定会尝试您的查询并让您不断更新!您能告诉我为什么不应该使用Java进行此操作吗?核心API的性能不是比Cypher更好吗?很抱歉,我是Neo4j的新手。您不是吗
PathExpander expander = PathExapnder.forTypesAndDirections("SEND", OUTGOING, "RECEIVE", OUTGOING)
PathFinder<Path> finder = GraphAlgoFactory.allSimplePaths(expander, 6);
Iterable<Path> paths = finder.findAllPaths(src, dest);