Neptune 上の openCypherで実行するように Cypher クエリを書き直す - Amazon Neptune

翻訳は機械翻訳により提供されています。提供された翻訳内容と英語版の間で齟齬、不一致または矛盾がある場合、英語版が優先します。

Neptune 上の openCypherで実行するように Cypher クエリを書き直す

openCypher 言語は、プロパティグラフ用の宣言型クエリ言語であり、当初は Neo4j によって開発され、その後、2015 年にオープンソース化され、Apache 2 オープンソースライセンスの下で openCypher プロジェクトに貢献しました。AWS では、オープンソースは誰にとっても良いことだと考えており、オープンソースの価値をお客様にもたらし、AWS のオペレーショナルエクセレンスをオープンソースコミュニティにもたらすことに尽力しています。

openCypher 構文は、「Cypher クエリ言語リファレンス、バージョン 9」で説明されています。

openCypher には Cypher クエリ言語の構文と機能の一部が含まれているため、移行シナリオによっては、openCypher 準拠の形式でクエリを書き直したり、必要な機能を実現するための代替方法を検討したりする必要があります。

このセクションには、一般的な相違点を処理するための推奨事項が含まれていますが、すべてを網羅しているわけではありません。これらの書き換えを使用するアプリケーションを徹底的にテストして、期待どおりの結果になることを確認する必要があります。

NoneAll、および 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 句を組み合わせて、同様の結果が得られます。

例えば、次の Cypher クエリは、アンカレッジ (ANC) とオースティン (AUS) の間の、1 ~ 3 か所の経由地があるパス上のすべての空港を検索し、各パスの合計距離を返します。

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 for 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) の間の経由地が 2 つ以下のパス上にあるすべての空港を検索し、それぞれに訪問済みのプロパティを設定します。

# 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 プロシージャを書き換える

以下の例では、最も一般的に使用されている APOC プロシージャの一部を openCypher を使用して置き換えています。これらの例は参照用であり、一般的なシナリオの処理方法に関するいくつかの提案を提供することを目的としています。実際には、アプリケーションはそれぞれ異なるため、必要な機能をすべて提供するための独自の戦略を考え出す必要があります。

apoc.export プロシージャの書き換え

Neptune には、neptune-export ユーティリティを使用して、CSV や JSON など, さまざまな出力形式で完全なグラフのエクスポートとクエリベースのエクスポートの両方を行うためのオプションが多数用意されています (「Neptune DB クラスターからデータをエクスポートする」を参照)。

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 では、同様の動作を実現する方法が少なくとも 2 つあります。

  • 1 つの方法は、openCypher のリスト内包表記機能を UNWIND 句と組み合わせることです。

  • もう 1 つの方法は、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() 関数を使用してリストプロパティを構築および分解することにより、同様の結果が得られます。

例えば、タグのリストをプロパティとして保存したい場合、rewrite という例を使用できます。これは、カンマ区切りのプロパティを取得し、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