

本文為英文版的機器翻譯版本，如內容有任何歧義或不一致之處，概以英文版為準。

# 重寫 Cypher 查詢以在 Neptune 上的 OpenCpher 中執行
<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 查詢語言的語法和功能子集，因此某些遷移案例需要以 OpenCypherr 相容的格式重寫查詢，或檢查替代方法以實現所需的功能。

本節包含處理常見差異的建議，但它們絕不是全面的。您應該使用這些重寫徹底測試任何應用程式，以確保結果就是您預期的。

## 重寫 `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` 子句的組合來實現類似的結果。

例如，在安克雷奇 (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 子句
<a name="migration-opencypher-rewrites-foreach"></a>

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 程序
<a name="migration-opencypher-rewrites-apoc"></a>

下面範例會使用 openCypher 來取代一些最常用的 [APOC 程序](https://neo4j.com/blog/intro-user-defined-procedures-apoc/)。這些範例僅供參考，旨在提供一些有關如何處理常見案例的建議。實際上，每個應用程序都有所不同，您必須制定自己的策略來提供您需要的所有功能。

### 重寫 `apoc.export` 程序
<a name="migration-opencypher-rewrites-apoc-export"></a>

Neptune 會使用 [neptune-export](https://github.com/aws/neptune-export) 公用程式，為各種輸出格式 (例如 CSV 和 JSON) 的完整圖形和查詢型匯出提供一系列選項 (請參閱 [從 Neptune 資料庫叢集匯出資料](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 中，至少有兩種方法可以實現類似的行為：
+ 一種方法是將 OpenCypher 的清單理解功能與 `UNWIND` 子句結合。
+ 另一種方法是在 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()` 函數來建構和解構清單屬性，以取得類似的結果。

例如，如果想要將標籤清單儲存為屬性，則可以使用重寫範例，其中展示如何擷取逗號分隔屬性，然後使用 `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`子查詢不支援將變數匯入子查詢範圍`CALL (friend) { ... }`的語法 （在此範例中`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(`double`)` 不會自動切換到 E 表示法，表示 double 的大值。