本文為英文版的機器翻譯版本,如內容有任何歧義或不一致之處,概以英文版為準。
重寫 Cypher 查詢以在 Neptune 上的 OpenCpher 中執行
OpenCypher 語言是屬性圖的宣告式查詢語言,最初由 Neo4j 開發,然後在 2015 年成為開放原始碼,並在 Apache 2 開放原始碼授權下投入 OpenCypher 專案
OpenCypher 語法記載於 Cypher 查詢語言參考第 9 版
由於 OpenCypher 包含 Cypher 查詢語言的語法和功能子集,因此某些遷移案例需要以 OpenCypherr 相容的格式重寫查詢,或檢查替代方法以實現所需的功能。
本節包含處理常見差異的建議,但它們絕不是全面的。您應該使用這些重寫徹底測試任何應用程式,以確保結果就是您預期的。
重寫 None
、All
和 Any
述詞函數
這些函數不是 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
在 openCypher 中重寫 Cypher reduce()
函數
reduce()
函數不是 openCypher 規格的一部分。它通常用來從清單內的元素建立資料的彙總。在許多情況下,您可以使用清單理解和 UNWIND
子句的組合來實現類似的結果。
例如,在安克雷奇 (ANC) 與奧斯汀 (AUS) 之間具有一到三個停靠站的路徑上,下列 Cypher 查詢會找出所有位於這些路徑上的機場,並傳回每條路徑的總距離:
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
您可以在 OpenCypher 為 Neptune 編寫相同的查詢,如下所示:
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
在 openCypher 中重寫 Cypher FOREACH 子句
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 會使用 neptune-export
重寫 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 中,至少有兩種方法可以實現類似的行為:
一種方法是將 OpenCypher 的清單理解功能與
UNWIND
子句結合。另一種方法是在 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