

기계 번역으로 제공되는 번역입니다. 제공된 번역과 원본 영어의 내용이 상충하는 경우에는 영어 버전이 우선합니다.

# Neptune의 OpenCypher에서 실행되도록 Cypher 쿼리를 재작성
<a name="migration-opencypher-rewrites"></a>

openCypher는 속성 그래프용 선언적 쿼리 언어로, Neo4j에서 처음 개발한 후 2015년에 오픈 소스로 제공되었으며, Apache 2 오픈 소스 라이선스에 따라 [openCypher 프로젝트](https://www.opencypher.org/)에 기여했습니다. 에서는 오픈 소스가 모두에게 유익하다고 AWS생각하며 고객에게 오픈 소스의 가치를 제공하고 오픈 소스 커뮤니티에의 운영 우수성을 제공하기 AWS 위해 최선을 다하고 있습니다.

이 구문은 [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` 노드에서 노드로 이동하는 모든 경로를 찾지만 `D`의 클래스 속성이 다음과 같은 `End` 노드를 통과하는 여정은 허용되지 않습니다. 

```
# 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\$13개의 정류장이 있는 경로의 모든 공항을 찾아 각 경로의 총 거리를 반환합니다.

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

## 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 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에서는 최소한 두 가지 방법으로 유사한 동작을 수행할 수 있습니다.
+ 한 가지 방법은 OpenCypher의 목록 이해 기능을 `UNWIND` 절과 결합하는 것입니다.
+ 또 다른 방법은 그렘린에서 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()` 함수를 ‘List Compremension’과 함께 사용하여 유사한 결과를 얻는 방법을 보여주는 재작성 예제를 사용할 수 있습니다.

```
# 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`). `CALL { WITH friend ... }`와 같이 동일한에 대해 하위 쿼리 내의 `WITH` 절을 사용하세요.

 현재 선택적 `CALL` 하위 쿼리는 지원되지 않습니다.

## Neptune openCypher와 Cypher의 기타 차이점
<a name="opencypher-compliance-other-differences"></a>
+ Neptune은 Bolt 프로토콜에 대한 TCP 연결만 지원합니다. Bolt의 WebSocket 연결은 지원되지 않습니다.
+ Neptune openCypher는 `trim()`, `ltrim()`, `rtrim()` 함수에서 유니코드로 정의된 대로 공백을 제거합니다.
+ Neptune openCypher에서 `tostring(`더블`)`은 더블의 값이 크면 E 표기법으로 자동 전환되지 않습니다.