向neo4j数据库添加推断关系的模式

向neo4j数据库添加推断关系的模式,neo4j,Neo4j,我们正在使用批插入API将大量数据导入Neo4J数据库。该数据库将用于为只读API(嵌入式服务器)供电 我们导入的数据是现有数据库模式中保存的域概念/实体的非常接近的副本。我们正在利用这些关系来发现数据中的其他关系,并推动我们网站上的其他功能 例如,如果我们有以下内容:person-[:reads]->book-[:writenby]->person,我们可能会认为这意味着一个额外的关系person-[:isAFanOf]->person。这使我们的代码更加明显(正如我们所讨论的“IsFan o

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

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

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

在哪里做这件事最好?我们提出了一些建议:

  • 在批插入代码中,在导入所有相关实体之后
  • 在一个“爬行”网络的过程中,寻找用户添加推断的关系,添加这些关系,然后为相同的关系安排他们的邻居
  • 在API中读取时-这不是个好主意,因为这可能会让使用数据的用户花费相当长的初始加载时间
另一个复杂的问题是,数据库将每24小时用新创建的数据更新一次,因此我们需要一些东西来帮助我们进行完整导入和部分导入


非常欢迎使用示例/体验。

我可能会在导入后立即使用。下面的Cypher语句应该可以做到这一点:

START p=node(*)
MATCH p-[:reads]->book-[:writtenBy]->p2
CREATE p-[:isAFanOf]->p2

我认为建议的查询@tstorms在6000万个节点的合理时间内无法工作

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

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

我个人不会这样做,除非它真的是必要的:对于性能问题,我会在优化之前先等等看,对于查询简化,您可以使用cypher()中的命名路径或Gremlin()中的用户定义步骤。

您需要使用cypher吗?如果没有,因为您有6000万个节点,
tstorms
列出的cypher查询看起来不错,工作也很好,但它可能很难处理所有这些节点,这可能会导致大内存使用

您可以使用JavaAPI(我假设您使用的是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();
        }
    }

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

我们已经导入了大约6000万个节点(其中1800万个节点将具有这种关系)。这不是爬行吗?虽然我会测试它,因为这似乎是一种比我一直探索的更简单的方法。这肯定需要一些时间,但由于这似乎是一个单批插入,我希望性能缓慢没有那么重要。我们不介意性能在合理范围内变慢(整个现有导入我们很高兴大约10小时)-我关心的只是在一笔交易中写下数百万条关系。如果失败,等待的时间会很长。是的,也许您可以在数据的子集上尝试此操作。@t尝试如何在实时数据上插入推断关系,如[:isAFanOf];我的意思是,在编写人际关系时,person-[:reads]->book-[:writenby]->person?我不会仅仅为了性能而这样做(尽管在我的小测试中,节省的效果非常好)。我认为,对于我的团队正在进行的分析来说,图形数据库的最佳选择是能够从现有关系中推断概念。这就是“isFanOf”让我们做的事情。我们可以将这些概念保留为隐式概念——我们每次运行查询时都必须定义这些概念,或者我们可以将它们显式地放在数据模型中,让所有人都能看到。现在我倾向于后者——这就是为什么我有兴趣把它们放在数据中。而且,是的,我们有索引,其中包含所有的“person”元素。它是1800万个节点,所以我认为Lucene的阅读仍然是一个密集的操作-取决于Lucene查找的懒惰。这看起来不错-我可以考虑使用一个带有参数绑定的CypHER查询来让它更可读。GlobalGraphOperations在我身上是一个新功能,看起来它在我的大型图形中会很好地工作。遗憾的是,就我所知,同样的方法不适用于索引。但是,如果我理解您的用例,您需要扫描所有节点以找到这些关系。否则,您将为所有“reader”类型的节点编制索引,然后使用索引将它们全部取回。我查看了全局内容的底层代码,在我看来,它们似乎使用节点ID惰性地遍历所有节点。我不认为lucene会让你这么做——是的,一旦你得到lucene的结果,你可以懒洋洋地遍历节点,但你仍然必须等待node:READER(“id:*”)返回——这是我运行从那里开始的cypher查询的经验——但我很想出错!如果您的
读卡器
计数远低于您的总节点计数,这将是有益的。Lucene速度很快,所以你不必担心这会成为瓶颈。Lucene返回一个迭代器,所以您只需遍历它。这里的好处是,你将要对已知的匹配进行匹配,而不是必须检查所有内容。好的,你是对的-通过lucene的定期迭代应该足够快(我可以两者都尝试)。我对lucene的经验是,在inser过程中,我们必须进行大量的随机索引查找