

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

# Neptune 上の openCypherで実行するように Cypher クエリを書き直す
<a name="migration-opencypher-rewrites"></a>

openCypher 言語は、プロパティグラフ用の宣言型クエリ言語であり、当初は Neo4j によって開発され、その後、2015 年にオープンソース化され、Apache 2 オープンソースライセンスの下で [openCypher プロジェクト](https://www.opencypher.org/)に貢献しました。では AWS、オープンソースは誰にとっても良いことだと考えており、オープンソースの価値をお客様にもたらし、 の運用上の優秀性をオープンソースコミュニティにもたら AWS すことに尽力しています。

openCypher 構文は、「[Cypher クエリ言語リファレンス、バージョン 9](https://s3.amazonaws.com/artifacts.opencypher.org/openCypher9.pdf)」で説明されています。

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

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

## `None`、`All`、および `Any` 述語関数の書き換え
<a name="migration-opencypher-rewrites-none-all-any"></a>

これらの関数は 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()` 関数を書き換える
<a name="migration-opencypher-rewrites-reduce"></a>

`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」句の書き換え
<a name="migration-opencypher-rewrites-foreach"></a>

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 プロシージャを書き換える
<a name="migration-opencypher-rewrites-apoc"></a>

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

### `apoc.export` プロシージャの書き換え
<a name="migration-opencypher-rewrites-apoc-export"></a>

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

### `apoc.schema` プロシージャの書き換え
<a name="migration-opencypher-rewrites-apoc-schema"></a>

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` プロシージャの代替方法
<a name="migration-opencypher-rewrites-apoc-do"></a>

これらのプロシージャは、他の openCypher 句を使用して簡単に実装できる条件付きクエリを実行するために使用されます。Neptune では、同様の動作を実現する方法が少なくとも 2 つあります。
+ 1 つの方法は、openCypher のリスト内包表記機能を `UNWIND` 句と組み合わせることです。
+ もう 1 つの方法は、Gremlin の choose() ステップと coalesce() ステップを使用することです。

これらのアプローチの例を次に示します。

#### apoc.do.when の代替手段
<a name="migration-opencypher-rewrites-apoc-do-when"></a>

```
# 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 の代替手段
<a name="migration-opencypher-rewrites-apoc-do-case"></a>

```
# 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())
```

## リストベースのプロパティの代替
<a name="migration-opencypher-rewrites-lists"></a>

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
```

## CALL サブクエリの書き換え
<a name="migration-opencypher-rewrites-call-subqueries"></a>

 Neptune `CALL` サブクエリは、サブクエリスコープ (この例では `friend`) に変数をインポート `CALL (friend) { ... }` するための構文をサポートしていません。サブクエリ内で同じく `WITH` 句を使用してください (例: `CALL { WITH friend ... }`)。

 現時点では、オプションの `CALL` サブクエリはサポートされていません。

## Neptune openCypher と Cypher のその他の違い
<a name="opencypher-compliance-other-differences"></a>
+ Neptune は Bolt プロトコルの TCP 接続のみをサポートしています。Bolt の WebSockets 接続はサポートされていません。
+ Neptune openCypher は、`trim()`、`ltrim()` および `rtrim()` 関数で Unicode で定義された空白を削除します。
+ Neptune openCypher では `tostring(` ダブル `)` は、倍精度の高い値に対して E 表記に自動的に切り替わりません。