Pagination 密码排序性能

Pagination 密码排序性能,pagination,neo4j,cypher,Pagination,Neo4j,Cypher,我正在努力完成一项相当普通的任务。我在Neo4J数据库中有一个大量的数据集,我想从RESTful web服务返回25个节点的数据块。我的模型很简单: (:Tenant {Hash:''})-[:owns]->(:Asset {Hash:'', Name:''}) 我对两个标签上的散列属性都有唯一的约束 如果我想获得第101个数据页,我的密码查询将如下所示: MATCH (:Tenant {Hash:'foo'})-[:owns]->(a:Asset) RETURN a ORDER

我正在努力完成一项相当普通的任务。我在Neo4J数据库中有一个大量的数据集,我想从RESTful web服务返回25个节点的数据块。我的模型很简单:

(:Tenant {Hash:''})-[:owns]->(:Asset {Hash:'', Name:''})
我对两个标签上的
散列
属性都有唯一的约束

如果我想获得第101个数据页,我的密码查询将如下所示:

MATCH (:Tenant {Hash:'foo'})-[:owns]->(a:Asset)
RETURN a
ORDER BY a.Hash
SKIP 2500
LIMIT 25
我的数据集由一个租户组成,拥有约75K资产。上述查询需要约30(!)秒才能完成。我还注意到,我在数据中前进的越远(即越高的
跳过
),查询返回所需的时间就越长

我很快发现,我的性能问题的罪魁祸首是a.Hash下的订单。当我删除它时,查询返回的结果为次秒。这实际上相当令人惊讶,因为我预计索引本身也会被排序

显然,为了实现合理的分页,我必须具有一致的排序顺序

  • 有关执行此查询的任何提示
  • 分页的其他建议?我可以看到添加了专用页面节点,但这将很难维护
  • 默认的排序顺序是什么,是否一致

虽然我实际上不太喜欢我自己的解决方案,但当您对节点的检索和分页进行编码时,即使使用相对较大的数据集,也会得到令人惊讶的好结果。如果您使用的是java,您能介绍一下以下内容吗

 try (Transaction tx = graphDb.beginTx()) {
     List<Node> nodes = IteratorUtil.asList(GlobalGraphOperations.at(graphDb)
         .getAllNodesWithLabel(DynamicLabel.label("Asset")));
     Collections.sort(nodes, new NodeComparator());
     final List<Node> result = nodes.subList(4375, 4400);
     tx.success();
 }

 static class NodeComparator implements Comparator<Node> {   
     @Override
     public int compare(final Node o1, final Node o2) {
         return o1.getProperty("hash").toString().compareTo(o2.getProperty("hash").toString());
     }            
 }
try(事务tx=graphDb.beginTx()){
列表节点=IteratorUtil.asList(GlobalGraphOperations.at(graphDb)
.getAllNodesWithLabel(DynamicLabel.label(“资产”));
排序(节点,新节点比较器());
最终列表结果=nodes.subList(43754400);
成功();
}
静态类NodeComparator实现比较器{
@凌驾
公共整数比较(最终节点o1,最终节点o2){
返回o1.getProperty(“哈希”).toString().compareTo(o2.getProperty(“哈希”).toString());
}            
}

Hey@GeoffreyBraaf本周找到了一些时间来研究您的问题,您是对的,有一些实现问题导致了不必要的缓慢

我根据Timmy的建议实现了一个Java版本,该版本在30毫秒内完成。密码版本花了100秒。致力于在Cypher中实现top-n select,将其大幅提升了600倍。所以Cypher现在需要大约150毫秒的时间来完成这个查询

见:

该工作已合并到2.0-maint中,并将作为2.0.2的一部分发布


请参阅:

索引顺序无关紧要,因为您不从索引中检索资产,而是通过关系检索资产,因此相关顺序将是节点中的关系顺序,即
[:owns]
(:Tenant)
。第二次运行时查询速度是否提高?如果您按id下单,即按id下单(a)
或按关系下单,即绑定
[o:owns]
并按id下单(o)
,该怎么办?按关系下单.id可能是一种方法。同意jjaderberg的观点,这将更好地利用数据实际索引的方式。此外,您目前正在按“哈希”排序,虽然它应该是一个一致的排序,但听起来并没有比按关系排序更有意义。id.@jjaderberg是的,第一次运行后性能稍微好一点,但并不是实质性的。我确实尝试过按
id(a)
排序,但这导致了最差的性能,更高(比如2500)的
SKIP
甚至会计算错误。@frobberoffits我完全同意,从用户的角度来看,按散列排序是无用的。无论如何,我都会这样做,因为我希望分页是一致的。鉴于我不确定默认排序顺序是否一致,我需要某种保证,对吗?给定一组稳定的节点,我希望相同的节点每次都返回相同的数据页。Geoffrey,2500页的用例是什么?没有用户看起来那么远?传入您得到的最后一个散列,并使用
而不是skip,其中a.hash>{hash}返回一个应该快得多的限制25
。那么您是否建议我加载所有节点,然后在内存中进行分页和排序?如果是这样的话,我真的不认为这会扩大规模。顺便说一句,我使用的是.NET,我通过REST端点进行连接。您可以将其作为服务器插件来实现,因此使用哪种编程语言并不重要。我知道这个解决方案看起来很奇怪,但是你会对结果感到惊讶的。我不确定我是否明白。这是可以在我的Neo4J服务器上运行的东西?理想情况下是的。有关插件的更多信息,请参阅。您可以使用RESTAPI轻松调用自定义插件。你的插件只需要接受一个页码,像上面那样进行检索并返回结果。尽管我还没能尝试一下,但我确实在Twitter上收到了Michael Hunger(非常)类似的建议。显然,在需要对大型数据集进行分页的情况下,服务器端扩展是一种解决方案。不确定具体的实施细节,但将此作为答案。太好了,很高兴(从某种意义上)为一个已经很棒的产品的改进做出了贡献!