cypher：複数のmatchと、matchをカンマで繋がる違いが分からなくて頭を机に打ち付けたのはどう考えてもお前らが悪い!

# 挨拶

初めまして！株式会社プラハCEO兼エンジニアの [dowanna](https://twitter.com/dowanna6) です！
普段は会社経営とか[会社のみんなで沖縄旅行](https://praha-inc.hatenablog.com/entry/2019/10/27/160624)をしていますが
「そういえば俺エンジニアだったな」と思い出したので、advent calendarに初参加してみました。
生暖かい目で見ていただけると喜びます。


# 本題


neo4jを使っているとcypherクエリ、特にmatchクエリを繋ぐ事がありますよね。

「cypherって、直感的でわかりやすいじゃん！」
そんな風に考えていた時期が、僕にもありました。

でも少しずつ複雑なクエリを書き始めると、意図しないmatchの挙動に悩まされて頭を机に打ち付けたくなるので、同じところで誰かが躓かないよう備忘録を残しておきます。


## 複数のmatchと、カンマつなぎのmatchの違いがわからない

特に僕が当初よく理解できなかったのが

match `hogehoge`
match `fugafuga`

と「matchクエリを」繋げる方法（パターン１）と

match `hogehoge`, `fugafuga`

と「カンマでクエリ自体」繋げる方法（パターン２）の違いです。

これ同じことじゃないの？？と思っていました。

というわけで実験してみましょう。


## データの準備
実験用の簡単なデータを作ります。

```
create (a:User{id:'1'}),(b:User{id:'2'}),(c:User{id:'3'})
create (a)-[:KNOWS]->(b)-[:KNOWS]->(c)
```

こんなグラフができます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/246648/22d33136-9267-b202-1d89-a21a684465db.png)


## 実験１：特定のnodeを取得する

### では、試しにnodeを（パターン１）で取得してみましょう

```
match (a:User{id:'1'})
match (b:User{id:'2'})
return a.id, b.id
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/246648/f9edc129-dce0-39c8-2800-065af3df1ed5.png)

### 試しにnodeを（パターン２）で取得してみましょう

```
match (a:User{id:'1'}), (b:User{id:'2'})
return a.id, b.id
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/246648/bdd2d4de-b120-1597-f13e-8440d152a14d.png)

**同じ結果が得られました。**

「なんだ、やっぱり複数のmatchも、matchのクエリつなぎも同じやんけ」

と思いきや、patternマッチになると話が変わってきます


## 実験2:patternを取得する

### まずは（パターン１=複数のmatch）で取得してみましょう。

```
match (a:User{id:'1'})-[:KNOWS]-(b:User)
match (a:User{id:'1'})-[:KNOWS*1..2]-(c:User)
return a.id, b.id, c.id
```

こんなクエリを書くことは少ないかもしれませんが、やっている事を要約すると：

①「１さんが1次的に繋がっているユーザを探す」
②「１さんが１〜２次的に繋がっているユーザを探す」
③「そいつらのidを返す」

（パターン１=複数のmatch）の結果はこちら
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/246648/bffe22df-46ff-78ae-9508-2720179e9f95.png)

割と直感的というか、意図した通りの結果が返ってくるのが分かります。


### では（パターン２=matchをカンマで繋ぐ）で取得してみましょう。

```
match (a:User{id:'1'})-[:KNOWS]-(b:User), (a:User{id:'1'})-[:KNOWS*1..2]-(c:User)
return a.id, b.id, c.id
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/246648/d3289e30-1e24-e9b0-9d6e-223e2f541ffd.png)

**あれ！？no records!?なんで！？**


## 説明：matchをクエリで繋ぐと、一つのクエリと見なされる

cypherのmatchには**「同じmatchの中で一度出てきたパターンは２度と出てこない」**という仕様が存在します。
つまりパターン２を図解すると（画質悪くて申し訳ないです）

①「１さんが1次的に繋がっているユーザを探す」
②「１さんが１〜２次的に繋がっているユーザを探す」
③「そいつらのidを返す」

まず①「１さんが1次的に繋がっているユーザを探す」ため、赤矢印のパスを通ります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/246648/7d7f0467-bffb-78e1-c03e-466d6736ff9e.png)


次に②「１さんが１〜２次的に繋がっているユーザを探す」ため、また１さんを起点にユーザを辿ろうとします。
これを青矢印②としましょう。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/246648/e906af4a-051f-23b1-f58c-7882b5aa015c.png)



しかしすでに①のパターンが出現しているため、**「同じmatchの中で一度出てきたパターンは２度と出てこない」**仕様により、このパターンが返ってくる事無く、matchクエリが終了します。

なので（パターン２=matchをカンマで繋ぐ）を実行した場合、
①「１さんが1次的に繋がっているユーザを探す」は2さんを返しますが
②「１さんが１〜２次的に繋がっているユーザを探す」はno recordsとなります。

cypherのmatchクエリをカンマでつないだ場合、**後に実行された結果が返却されるため**、②のno recordsが返されます。だから何も取得できません。


## 説明：matchを複数実行すると、別々のクエリと見なされる
では（パターン１=複数のmatch）の場合、何が起きているのか。

```
match (a:User{id:'1'})-[:KNOWS]-(b:User) // ①
match (a:User{id:'1'})-[:KNOWS*1..2]-(c:User) // ②
return a.id, b.id, c.id
```

①「１さんが1次的に繋がっているユーザを探す」
②「１さんが１〜２次的に繋がっているユーザを探す」
③「そいつらのidを返す」

処理の流れはパターン２と同じですが、カンマ区切りとは異なり、**それぞれのmatchが独立したクエリとして解釈される**点に注意が必要です。

まず①で赤矢印のパスを辿ります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/246648/7d7f0467-bffb-78e1-c03e-466d6736ff9e.png)

ここでmatchクエリが一回終了します。赤矢印が消えるようなイメージです。

```
match (a:User{id:'1'})-[:KNOWS]-(b:User) // ①　⇦このクエリは終了
match (a:User{id:'1'})-[:KNOWS*1..2]-(c:User) // ②
return a.id, b.id, c.id
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/246648/1f0b0d74-ee38-f87d-8761-3515a5853cec.png)



そして新しいクエリとしてパスを辿りはじめます。青矢印ですね。
今度は「これまで一度も出てきた事のないパターン」なので、その先に進めます。


![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/246648/62edfd92-a68f-f2d0-448b-f25c752d7577.png)

だからパターン１の結果には３が含まれているんですね。
パターン１は別々のmatchクエリだから、一つ目のmatchクエリのパターンが、二つ目のmatchクエリのパターンに影響しない。
パターン２は**同じmatchクエリ**だから、一つ目のmatchクエリのパターンが、二つ目のmatchクエリのパターンに**影響する**

これが今回伝えたかったことです。

タイトルには「お前らが悪い！」と書きましたが、matchの仕様をちゃんと読み解いていなかった僕が悪い・・・いやでもtutorialでちゃんと言及しないお前らが・・・ドキュメントにも記載しないお前らが・・・ウゥッ！！！


## まとめ
- matchを複数繋ぐクエリと、matchの中身をカンマで繋ぐクエリには明確な違いがある
- カンマで繋いだ場合、一つのmatchクエリと見なされる
- 「matchクエリ中、同じパターンは２度と返ってこない」仕様を意識しておくと、正しく使い分けられる