8

我正在研究 Neo4j 中的树状结构数据集。在我当前的模型中,一个节点可以有n 个链接到其他节点,使其成为这些节点的子节点:

root
 |
 \-- A
     |
     \-- 1
     |
     \-- 2
  • 指向根的链接
  • 1 和 2 链接到 A
  • 根是 A 的父级
  • 1 和 2 是 A 的孩子

因为我使用的是 nodejs(使用node-neo4j),所以数据库的读取仅限于使用 Cypher。要读取节点,我使用以下查询:

START n=node(1)
    -- this is the root node

MATCH (n)<-[linkedby:links*]-x, x-[linksto?:links*1]->()
    -- get all nodes that link to the root node and all nodes linking to those nodes, etc, etc and retrieve all nodes those found nodes link to

RETURN n, x, linkedby, LAST(linkedby) AS parent, COLLECT(DISTINCT linksto) AS links      
    -- the last in the path of linkedby is the direct parent

ORDER BY length(linkedby), parent.rank?
    -- ordering so that parents are fetched before their children from the result set

我的问题:随着节点和关系数量的增加,这个查询变得非常慢(> 1 s)。

有没有更好的方法来建模节点和关系?我需要父节点和子节点之间的不同关系吗?或者我应该以任何方式更改查询?

感谢您的任何指点!


1) 现实世界问题:一种业务流程工具,其中服务与流程、组织、团队等相关联,以提供信息何时何人需要哪些服务,并提供信息谁将提供该服务或由谁负责。

例如:

服务 S 用于进程 P1 和 P2:

P1  P2
|   |
\---+-- S

服务 S 由 T 团队管理:

T
|
\-- S

团队 T 是组织 O 的一部分:

O
|
\-- T

树:

root
 |
 |
 +-- Processes
 |   |
 |   +-- P1
 |   |   |
 |   |   \-- S
 |   |
 |   \-- P2
 |   |   |
 |   |   \-- S
 |
 +-- Organisations
 |   |
 |   +-- O
 |       |
 |       \-- T
 |           |
 |           \-- S

2) 我在console.neo4j.org中的数据:

CREATE 
  (r {name:'root'}), 
  (ps {name: 'Processes'})-[:links]->r, 
    (p1 {name: 'P1'})-[:links]->ps,
    (p2 {name: 'P2'})-[:links]->ps,
      (s {name: 'Service'})-[:links]->p1,
      s-[:links]->p2,
  (org {name: 'Organisations' })-[:links]->r,
    (t {name: 'Team'})-[:links]->org,
      s-[:links]->t
4

1 回答 1

2

所以,前几天我和Michael Hunger进行了一次谈话,他承认 Cypher 不擅长处理递归查询。这应该在 2.1 中改变,但在那之前他建议我编写一个非托管扩展,然后我可以从 nodejs 调用它。

这完全解决了我的问题:使用纯 Java 检索树大约比使用 Cypher 快 10 倍。

简化代码:

public class EntitiesStream {
    public Entity load(long nodeId) {
        Node n = database.getNodeById(nodeId);
        Entity entity = Entity.from(n);
        loadChildren(n, entity);
        return entity;
    }

    private void loadChildren(Node n, Entity e) {
        for (Relationship linksFrom: n.getRelationships(Direction.INCOMING, Relationships.links)) {
            Node startNode = linksFrom.getStartNode();
            Entity childEntity = Entity.from(startNode);
            e.addChild(((Number) linksFrom.getProperty("rank")).longValue, childEntity);
            this.loadChildren(startNode, childEntity);
        }
    }
}

public class Entity {
    private final TreeMap<Long, Entity> sortedChildren;

    Entity(long dbId) {
        this.sortedChildren = new TreeMap<>();
        // ...
    }

    public static Entity from(Node node) {
        Entity e = new Entity(node.getId());
        // ...

        return e;
    }

    public void addChild(Long rank, Entity childEntity) {
        sortedChildren.put(rank, childEntity);
    }
}
于 2014-05-14T10:18:14.517 回答