1

我们正在使用批量插入 API 将大量数据导入 Neo4J 数据库。该数据库将用于为只读 API(嵌入式服务器)提供动力。

我们正在导入的数据是现有数据库模式中保存的域概念/实体的非常接近的副本。我们正在利用这些关系来查找数据中的其他关系并推动我们网站上的其他功能。

例如,如果我们有以下内容:person-[:reads]->book-[:writtenBy]->person,我们可能会认为这意味着额外的关系 person-[:isAFanOf]->person。这使我们的代码更加明显(当我们谈论“喜欢”关系时),并且我们的许多查询和遍历性能更高,因为不需要跨越两个实体。

做这件事的最佳地点在哪里?我们提出了一些建议:

  • 在批量插入代码中,在所有相关实体都已导入之后。
  • 在一个“蜘蛛”网络的过程中,寻找用户添加推断的关系,添加这些关系,然后安排他们的邻居。
  • 在 API 中读取时 - 不知道,因为这可能会使用户消耗数据的初始加载时间相当长

另一个复杂情况是,数据库将每 24 小时使用新创建的数据更新一次,因此我们需要一些可以帮助我们完成全部和部分导入案例的东西。

非常欢迎示例/经验。

4

3 回答 3

3

您需要使用 Cypher 吗?如果不是,由于您有 6000 万个节点,因此列出的密码查询tstorms看起来不错并且工作正常,但它可能会很困难,因为它会围绕所有这些进行事务,这可能会导致大量内存使用。

您可以使用 Java API(我假设您正在使用 Java)手动执行此操作。

        RelationshipType readsRelationshipType = DynamicRelationshipType.withName("reads");
        RelationshipType writtenByRelationshipType = DynamicRelationshipType.withName("writtenBy");
        RelationshipType isAFanOfRelationshipType = DynamicRelationshipType.withName("isAFanOf");
        int counter = 0;
        Transaction tx = db.beginTx();
        try {
            for (Node reader : GlobalGraphOperations.at(db).getAllNodes()) {
                for (Relationship reads : reader.getRelationships(Direction.OUTGOING, readsRelationshipType)) {
                    Node book = reads.getOtherNode(reader);
                    for (Relationship writtenBy : book.getRelationships(Direction.OUTGOING, writtenByRelationshipType)) {
                        Node author = reads.getOtherNode(book);
                        try {
                            reader.createRelationshipTo(author, isAFanOfRelationshipType);
                        } catch (Exception e) {
                            // TODO: Something for exception
                        }
                    }
                }
                counter++;
                if (counter % 100000 == 0) {
                    tx.success();
                    tx.finish();
                    tx = db.beginTx();
                }
            }
            tx.success();
        } catch (Exception e) {
            tx.failure();
        } finally {
            tx.finish();
        }
    }

此代码假定错误处理和事务数,但您可以根据需要进行调整。

于 2013-04-29T15:19:58.187 回答
2

我可能会在导入后立即执行此操作。以下 Cypher 语句应该可以解决问题:

START p=node(*)
MATCH p-[:reads]->book-[:writtenBy]->p2
CREATE p-[:isAFanOf]->p2
于 2013-04-29T13:03:20.527 回答
0

我认为@tstorms 提出的查询不会在合理的时间内对 6000 万个节点起作用。

如果您真的想这样做,您可以对@tstorms 解决方案进行一些改进:

  • 使用启动实体的索引(例如您的情况下的人)并从这些实体开始查询。
  • 您提到了必须以增量方式执行此操作的事实,因此您可能需要为最后一个批处理操作保留索引,因此您必须在已处理的节点上进行迭代。

除非确实有必要,否则我个人不会这样做:对于性能问题,我会在预先优化之前先观望,为了简化查询,您可以在密码中使用命名路径(http://docs.neo4j.org/chunked/milestone /query-match.html#match-named-path)或 Gremlin 中的用户定义步骤(https://github.com/tinkerpop/gremlin/wiki/User-Defined-Steps

于 2013-04-29T14:57:44.563 回答