重写 Cypher 查询以在 Neptune 上运行 openCypher - Amazon Neptune

本文属于机器翻译版本。若本译文内容与英语原文存在差异,则一律以英文原文为准。

重写 Cypher 查询以在 Neptune 上运行 openCypher

该 openCypher 语言是一种用于属性图的声明式查询语言,最初由Neo4j开发,然后在2015年开源,并在Apache 2开源许可下为该openCypher 项目做出了贡献。在 AWS,我们相信开源对每个人都有好处,我们致力于将开源的价值带给我们的客户,为开源社区带来卓越的 AWS 运营。

OpenCypher 语法记录在《密码查询语言参考版本 9》中

由于 openCypher 包含 Cypher 查询语言的语法和功能的子集,因此某些迁移方案需要以openCypher兼容的形式重写查询,或者研究其他方法以实现所需的功能。

本节包含处理常见差异的建议,但绝非详尽无遗。您应该使用这些重写对任何应用程序进行全面测试,以确保结果符合您的预期。

重写 NoneAllAny 谓词函数

这些函数不是 openCypher 规范的一部分。 openCypher 使用列表理解可以获得类似的结果。

例如,找到从节点 Start 到节点 End 的所有路径,但不允许任何旅程通过一个类属性为 D 的节点:

# Neo4J Cypher code match p=(a:Start)-[:HOP*1..]->(z:End) where none(node IN nodes(p) where node.class ='D') return p # Neptune openCypher code match p=(a:Start)-[:HOP*1..]->(z:End) where size([node IN nodes(p) where node.class = 'D']) = 0 return p

列表推导可以实现以下结果:

all => size(list_comprehension(list)) = size(list) any => size(list_comprehension(list)) >= 1 none => size(list_comprehension(list)) = 0

在中重写 Cypher 函数 reduce() openCypher

reduce()函数不是 openCypher 规范的一部分。它通常用于从列表中的元素创建数据的聚合。在许多情况下,您可以结合使用列表推导和 UNWIND 子句来获得相似的结果。

例如,以下 Cypher 查询查找安克雷奇 (ANC) 和奥斯汀 (AUS) 之间有一到三个停靠点的路径上的所有机场,并返回每条路径的总距离:

MATCH p=(a:airport {code: 'ANC'})-[r:route*1..3]->(z:airport {code: 'AUS'}) RETURN p, reduce(totalDist=0, r in relationships(p) | totalDist + r.dist) AS totalDist ORDER BY totalDist LIMIT 5

你可以在 Neptune 中写同样 openCypher 的查询,如下所示:

MATCH p=(a:airport {code: 'ANC'})-[r:route*1..3]->(z:airport {code: 'AUS'}) UNWIND [i in relationships(p) | i.dist] AS di RETURN p, sum(di) AS totalDist ORDER BY totalDist LIMIT 5

重写中的 Cypher 子句 FOREACH openCypher

该FOREACH子句不是 openCypher 规范的一部分。它通常用于在查询过程中更新数据,通常来自路径中的聚合或元素。

作为路径示例,在安克雷奇 (ANC) 和奥斯汀 (AUS) 之间不超过两个停靠点的路径上查找所有机场,并在每个机场上设置一个已访问的属性:

# Neo4J Example MATCH p=(:airport {code: 'ANC'})-[*1..2]->({code: 'AUS'}) FOREACH (n IN nodes(p) | SET n.visited = true) # Neptune openCypher MATCH p=(:airport {code: 'ANC'})-[*1..2]->({code: 'AUS'}) WITH nodes(p) as airports UNWIND airports as a SET a.visited=true

另一个示例是:

# Neo4J Example MATCH p=(start)-[*]->(finish) WHERE start.name = 'A' AND finish.name = 'D' FOREACH (n IN nodes(p) | SET n.marked = true) # Neptune openCypher MATCH p=(start)-[*]->(finish) WHERE start.name = 'A' AND finish.name = 'D' UNWIND nodes(p) AS n SET n.marked = true

在 Neptune 中重写 Neo4j 程序 APOC

以下示例用于 openCypher 取代一些最常用的APOC过程。这些示例仅供参考,旨在提供一些有关如何处理常见场景的建议。实际上,每个应用程序都是不同的,您必须制定自己的策略来提供所需的所有功能。

重写 apoc.export 过程

Neptune 使用 nept une- export 实用程序(参见)为各种输出格式(例如CSV和)的完整图表和JSON基于查询的导出提供了一系列选项。从 Neptune 数据库集群中导出数据

重写 apoc.schema 过程

Neptune 没有显式定义的架构、索引或约束,因此不再需要许多 apoc.schema 过程。示例为:

  • apoc.schema.assert

  • apoc.schema.node.constraintExists

  • apoc.schema.node.indexExists,

  • apoc.schema.relationship.constraintExists

  • apoc.schema.relationship.indexExists

  • apoc.schema.nodes

  • apoc.schema.relationships

Neptune openCypher 确实支持检索与这些过程相似的值,如下所示,但在较大的图形上可能会遇到性能问题,因为这样做需要扫描图形的很大一部分才能返回答案。

# openCypher replacement for apoc.schema.properties.distinct MATCH (n:airport) RETURN DISTINCT n.runways
# openCypher replacement for apoc.schema.properties.distinctCount MATCH (n:airport) RETURN DISTINCT n.runways, count(n.runways)

apoc.do 过程的替代方案

这些过程用于提供易于使用其他 openCypher 子句实现的条件查询执行。在 Neptune 中,至少有两种方法可以实现类似的行为:

  • 一种方法是将列表理解功能与UNWIND子句结合起来openCypher。

  • 另一种方法是使用 Gremlin 中的 choose() 和 coalesce() 步骤。

这些方法的示例如下所示。

apoc.do.when 的替代方案

# Neo4J Example MATCH (n:airport {region: 'US-AK'}) CALL apoc.do.when( n.runways>=3, 'SET n.is_large_airport=true RETURN n', 'SET n.is_large_airport=false RETURN n', {n:n} ) YIELD value WITH collect(value.n) as airports RETURN size([a in airports where a.is_large_airport]) as large_airport_count, size([a in airports where NOT a.is_large_airport]) as small_airport_count # Neptune openCypher MATCH (n:airport {region: 'US-AK'}) WITH n.region as region, collect(n) as airports WITH [a IN airports where a.runways >= 3] as large_airports, [a IN airports where a.runways < 3] as small_airports, airports UNWIND large_airports as la SET la.is_large_airport=true WITH DISTINCT small_airports, airports UNWIND small_airports as la SET la.small_airports=true WITH DISTINCT airports RETURN size([a in airports where a.is_large_airport]) as large_airport_count, size([a in airports where NOT a.is_large_airport]) as small_airport_count #Neptune Gremlin using choose() g.V(). has('airport', 'region', 'US-AK'). choose( values('runways').is(lt(3)), property(single, 'is_large_airport', false), property(single, 'is_large_airport', true)). fold(). project('large_airport_count', 'small_airport_count'). by(unfold().has('is_large_airport', true).count()). by(unfold().has('is_large_airport', false).count()) #Neptune Gremlin using coalesce() g.V(). has('airport', 'region', 'US-AK'). coalesce( where(values('runways').is(lt(3))). property(single, 'is_large_airport', false), property(single, 'is_large_airport', true)). fold(). project('large_airport_count', 'small_airport_count'). by(unfold().has('is_large_airport', true).count()). by(unfold().has('is_large_airport', false).count())

apoc.do.case 的替代方案

# Neo4J Example MATCH (n:airport {region: 'US-AK'}) CALL apoc.case([ n.runways=1, 'RETURN "Has one runway" as b', n.runways=2, 'RETURN "Has two runways" as b' ], 'RETURN "Has more than 2 runways" as b' ) YIELD value RETURN {type: value.b,airport: n} # Neptune openCypher MATCH (n:airport {region: 'US-AK'}) WITH n.region as region, collect(n) as airports WITH [a IN airports where a.runways =1] as single_runway, [a IN airports where a.runways =2] as double_runway, [a IN airports where a.runways >2] as many_runway UNWIND single_runway as sr WITH {type: "Has one runway",airport: sr} as res, double_runway, many_runway WITH DISTINCT double_runway as double_runway, collect(res) as res, many_runway UNWIND double_runway as dr WITH {type: "Has two runways",airport: dr} as two_runways, res, many_runway WITH collect(two_runways)+res as res, many_runway UNWIND many_runway as mr WITH {type: "Has more than 2 runways",airport: mr} as res2, res, many_runway WITH collect(res2)+res as res UNWIND res as r RETURN r #Neptune Gremlin using choose() g.V(). has('airport', 'region', 'US-AK'). project('type', 'airport'). by( choose(values('runways')). option(1, constant("Has one runway")). option(2, constant("Has two runways")). option(none, constant("Has more than 2 runways"))). by(elementMap()) #Neptune Gremlin using coalesce() g.V(). has('airport', 'region', 'US-AK'). project('type', 'airport'). by( coalesce( has('runways', 1).constant("Has one runway"), has('runways', 2).constant("Has two runways"), constant("Has more than 2 runways"))). by(elementMap())

基于列表的属性的替代方案

Neptune 目前不支持存储基于列表的属性。但是,通过将列表值存储为逗号分隔的字符串,然后使用 join()split() 函数来构造和解构列表属性,也可以获得类似的结果。

例如,如果我们想将标签列表另存为属性,我们可以使用示例重写,它演示如何检索逗号分隔的属性,然后使用 split()join() 函数以及列表推导来获得同等的结果:

# Neo4j Example (In this example, tags is a durable list of string. MATCH (person:person {name: "TeeMan"}) WITH person, [tag in person.tags WHERE NOT (tag IN ['test1', 'test2', 'test3'])] AS newTags SET person.tags = newTags RETURN person # Neptune openCypher MATCH (person:person {name: "TeeMan"}) WITH person, [tag in split(person.tags, ',') WHERE NOT (tag IN ['test1', 'test2', 'test3'])] AS newTags SET person.tags = join(newTags,',') RETURN person

重写CALL子查询

Neptune CALL 子查询不支持将变量导入子查询作用域的语法CALL (friend) { ... }(在本示例friend中)。请使用子查询中的子WITH句来做同样的事情,CALL { WITH friend ... }例如。

目前不支持可选CALL子查询。

Neptune 和 Cypher openCypher 之间的其他区别

  • Neptune 仅支持 Bolt 协议的TCP连接。 WebSockets不支持螺栓连接。

  • Neptune openCypher 会移除 Unicode 在、和函数中定义的空白。trim() ltrim() rtrim()

  • 在 Neptune 中openCypher,对于较大的tostring(双精度值,双精度)不会自动切换到 E 表示法。