{#first-query-your-first-query} # 첫 쿼리 이 페이지는 [예제 데이터셋](example-dataset.md)을 훑습니다 — 영화 18편, 사람 28명, 그리고 그 사이의 간선들입니다. 먼저 데이터셋을 적재하고 페이지를 위에서 아래로 따라가십시오. 각 쿼리는 앞의 쿼리 위에 쌓입니다. 여기 있는 것은 모두 Cypher입니다. 이에 대응하는 GQL 표기는 [레퍼런스](../reference/clauses/index)에 적혀 있습니다. SQL이 다시 등장하는 곳은 [SQL과 Cypher 섞기](../querying/combining-sql-and-cypher.md)입니다. {#first-query-say-which-graph-you-mean} ## 어느 그래프인지 말하기 ```cypher SET graph_path = moviekb; ``` Cypher가 필요로 하는 세션 상태는 이것 하나입니다. 이것이 없으면 모든 레이블 이름을 해석할 수 없습니다. 이제 그래프에 있는 모든 것을 세어 봅니다: ```cypher MATCH (n) RETURN count(n) AS elements; ``` ``` elements ---------- 46 ``` `MATCH (n)`은 가능한 가장 비어 있는 패턴입니다 — 정점 하나, 레이블 없음, 속성 없음 — 그래서 모든 정점에 일치합니다. 46은 영화 18편에 사람 28명을 더한 것입니다. {#first-query-find-one-thing} ## 하나 찾기 패턴은 두 가지 방식으로 범위를 좁힙니다. 레이블로, 그리고 속성으로입니다. ```cypher MATCH (m:movie {title: 'The Matrix'}) RETURN m.year AS year, m.genre AS genre; ``` ``` year | genre ------+---------- 1999 | "Action" ``` `(m:movie {title: 'The Matrix'})`은 이렇게 읽습니다 — `movie` 레이블이 붙고 `title` 속성이 `'The Matrix'`인 정점, 그리고 그것을 변수 `m`에 바인딩한다. 그다음 `RETURN`이 `m`에서 값을 꺼냅니다. `"Action"`에는 따옴표가 붙고 `1999`에는 붙지 않은 것을 보십시오. `RETURN`은 속성을 `jsonb`로 돌려주므로 문자열은 JSON 따옴표가 붙은 채로 옵니다. 결과를 클라이언트 쪽 문자열과 비교하기 전에 알아 둘 만한 점입니다 — 평범한 값을 얻는 방법은 [SQL과 Cypher 섞기](#combining-sql-and-cypher-getting-a-plain-value-out)를 참고하십시오. 모든 정점에는 속성이 아닌 식별자도 있습니다: ```cypher MATCH (m:movie {title: 'The Matrix'}) RETURN m.title AS title, id(m) AS id; ``` ``` title | id --------------+----- "The Matrix" | 3.1 ``` `3.1`은 `graphid`입니다. 레이블 3, 행 1입니다. 이것이 정점의 식별자이고, `id(m)`이 그것을 묻는 방법입니다 — `m.id`는 `id`라는 이름의 *속성*을 찾는데, 그런 속성을 가진 영화는 없습니다. {#first-query-follow-an-edge} ## 간선 따라가기 그래프의 요점은 그다음에 오는 것입니다. 패턴에 간선을 더합니다: ```cypher MATCH (p:person)-[r:acted_in]->(:movie {title: 'The Matrix'}) RETURN p.name AS actor, r.role AS role ORDER BY actor; ``` ``` actor | role ----------------------+------------ "Carrie-Anne Moss" | "Trinity" "Keanu Reeves" | "Neo" "Laurence Fishburne" | "Morpheus" ``` 새로운 것이 세 가지 있습니다. `-[r:acted_in]->`는 `acted_in` 레이블이 붙은 간선이며 사람에서 영화를 향합니다. 이것이 `r`에 바인딩되므로 그 속성을 쓸 수 있습니다. `r.role`은 양 끝점이 아니라 *관계*의 속성입니다. 그리고 `(:movie {title: 'The Matrix'})`에는 변수가 없습니다. 패턴이 그 영화에 도달할 필요는 있지만 결과 투영에서는 그것을 언급하지 않기 때문입니다. {#first-query-follow-two} ## 두 번 따라가기 패턴을 반대 방향으로 늘리면, SQL이라면 자기 조인이 필요했을 질문을 한 것이 됩니다: ```cypher MATCH (:person {name: 'Keanu Reeves'})-[:acted_in]->(:movie)<-[:acted_in]-(co:person) RETURN DISTINCT co.name AS co_actor ORDER BY co_actor; ``` ``` co_actor ---------------------- "Carrie-Anne Moss" "Laurence Fishburne" ``` 패턴을 왼쪽에서 오른쪽으로 읽습니다. Keanu Reeves에서 출발해 `acted_in` 간선을 따라 영화로 나가고, 다시 다른 `acted_in` 간선을 *거꾸로* 따라 두 번째 사람에게 갑니다. 영화 세 편을 함께 한 두 사람은 행 세 개를 만들기 때문에 `DISTINCT`가 필요합니다. {#first-query-aggregate} ## 집계 집계 함수는 PostgreSQL의 것이고, 예상하는 대로 동작합니다: ```cypher MATCH (d:person)-[:directed]->(m:movie) RETURN d.name AS director, count(m) AS films ORDER BY films DESC, director LIMIT 5; ``` ``` director | films ---------------------+------- "Lana Wachowski" | 4 "James Cameron" | 3 "Lilly Wachowski" | 3 "Christopher Nolan" | 2 "Steven Spielberg" | 2 ``` `RETURN` 안에서 집계 함수 안에 들어 있지 않은 것은 모두 그룹화 키가 됩니다. 이것도 SQL과 같습니다. {#first-query-ask-for-something-that-might-not-be-there} ## 없을 수도 있는 것을 묻기 `MATCH`는 패턴이 맞지 않으면 그 행을 버립니다. `OPTIONAL MATCH`는 행을 남겨 두고 채우지 못한 변수를 null로 채웁니다 — 왼쪽 외부 조인입니다: ```cypher MATCH (d:person)-[:directed]->(:movie) WITH DISTINCT d OPTIONAL MATCH (d)-[:acted_in]->(m:movie) RETURN d.name AS director, m.title AS also_acted_in ORDER BY director LIMIT 6; ``` ``` director | also_acted_in ---------------------+--------------- "Andrew Niccol" | "Christopher Nolan" | "Denis Villeneuve" | "James Cameron" | "Joss Whedon" | "Lana Wachowski" | ``` 이 데이터셋에는 연출과 연기를 모두 한 사람이 없으므로 `also_acted_in`은 모두 null입니다 — 그것이 바로 요점입니다. 그냥 `MATCH`를 썼다면 여섯 행이 전부 사라졌을 것입니다. 가운데 있는 `WITH`는 파이프라인 구분자입니다. 쿼리의 한 부분을 끝내고 다음 부분을 시작하며, 덕분에 선택적 일치가 실행되기 전에 `DISTINCT`가 감독들에게 적용됩니다. {#first-query-walk-an-unknown-number-of-hops} ## 몇 홉인지 모르는 길을 걷기 경로를 모른 채로 두 사람이 어떻게 연결되는지 묻습니다: ```cypher MATCH p = shortestpath((a:person {name: 'Keanu Reeves'})-[*..6]-(b:person {name: 'Arnold Schwarzenegger'})) RETURN length(p) AS hops; ``` ``` hops ------ 3 ``` `[*..6]`은 가변 길이 간선입니다. 어떤 레이블이든, 어느 방향이든, 최대 여섯 개까지입니다. `p = ...`는 경로 전체를 바인딩하고, `length(p)`는 그 간선 수입니다. 이것이 SQL로는 쓸 만한 형태가 없는 쿼리입니다. `shortestpath`의 양 끝점에는 변수가 있어야 합니다. 그 변수를 쓰지 않더라도 그렇습니다. 익명으로 두면 `ERROR: node must be a reference to a specific node`가 나옵니다. 나머지는 [경로](../querying/paths.md)가 다룹니다 — 범위를 정한 구간, `allshortestpaths`, 그리고 `dijkstra`를 쓰는 가중 탐색입니다. {#first-query-write-something} ## 무언가 쓰기 쓰기는 동사만 다른 읽기처럼 보입니다. `CREATE`는 정점을 만듭니다: ```cypher CREATE (p:person {name: 'Hugo Weaving', born: 1960}) RETURN p; ``` ``` p ---------------------------------------------------- person[4.63]{"born": 1960, "name": "Hugo Weaving"} ``` 무언가에 연결하려면 먼저 양쪽 끝을 찾고 그다음 간선을 만듭니다: ```cypher MATCH (p:person {name: 'Hugo Weaving'}), (m:movie {title: 'The Matrix'}) CREATE (p)-[r:acted_in {role: 'Agent Smith'}]->(m) RETURN r; ``` ``` r ------------------------------------------------- acted_in[5.60][4.63,3.1]{"role": "Agent Smith"} ``` 간선은 `label[id][start,end]{properties}` 형태로 출력되므로 결과에서 양 끝점을 볼 수 있습니다. `SET`은 속성을 추가하거나 교체합니다: ```cypher MATCH (p:person {name: 'Hugo Weaving'}) SET p.nationality = 'Australian' RETURN p; ``` ``` p --------------------------------------------------------------------------------- person[4.63]{"born": 1960, "name": "Hugo Weaving", "nationality": "Australian"} ``` `REMOVE`는 속성을 없앱니다: ```cypher MATCH (p:person {name: 'Hugo Weaving'}) REMOVE p.nationality RETURN p; ``` ``` p ---------------------------------------------------- person[4.63]{"born": 1960, "name": "Hugo Weaving"} ``` 그리고 `DETACH DELETE`는 정점을 그에 딸린 간선과 함께 지웁니다. 아직 간선이 남아 있는 정점에 그냥 `DELETE`를 쓰면 거부됩니다. AgensGraph는 끝점이 없는 간선을 남겨 두지 않기 때문입니다: ```cypher MATCH (p:person {name: 'Hugo Weaving'}) DETACH DELETE p; ``` 이렇게 하면 데이터셋이 46개 요소로 돌아가므로 매뉴얼의 나머지 예제가 계속 맞습니다. {#first-query-mix-in-sql} ## SQL 섞어 쓰기 데이터셋과 함께 만든 `rating` 테이블은 그래프의 일부가 아닌 보통 테이블입니다. SQL이 테이블을 기대하는 자리에 Cypher를 놓아 Cypher 쿼리와 조인합니다: ```psql SELECT q.film #>> '{}' AS film, r.score FROM (MATCH (:person {name: 'Christopher Nolan'})-[:directed]->(m:movie) RETURN m.title AS film) q JOIN rating r ON r.title = q.film #>> '{}' WHERE r.source = 'critics' ORDER BY r.score DESC; ``` ``` film | score --------------+------- Inception | 8.8 Interstellar | 8.7 ``` `#>> '{}'`가 `jsonb` 제목을 평범한 `text`로 바꿔 `text` 컬럼과 비교할 수 있게 해 줍니다. 그리고 여기서는 영화 이름이 따옴표 없이 돌아오는 것을 보십시오. 이것은 Cypher가 아니라 SQL의 결과 투영이기 때문입니다. {#first-query-where-to-go-next} ## 다음에 갈 곳 * [쿼리하기](../querying/index)가 여기 나온 생각들을 하나씩 제대로 다룹니다. 읽기, 쓰기, 파이프라인, 경로, 인덱스입니다. * [레퍼런스](../reference/clauses/index)에는 절마다 전체 구문을 담은 페이지가 있습니다. * [그래프가 어떻게 저장되는가](../concepts/graph-on-postgresql.md)가 방금 만든 모든 것에 대해 서버가 실제로 무엇을 했는지 설명합니다. * [벡터 검색](../guides/search/vector.md), [전문 검색](../guides/search/full-text.md), [하이브리드 검색](../guides/search/hybrid.md)은 이 페이지가 다루지 않은 `plot`과 `embedding` 속성을 사용합니다.