Cypher備忘録


# 基本

- ノードは括弧 () で表される。
- ラベルはコロンで表し、例えば (:Person) とします。
- ノード間の関係は2本のダッシュで記述します。 (:Person)--(:Movie)
- リレーションシップの方向は、大なり小なり記号 < または > を用いて示されます。 (:Person)-→(:Movie)
- リレーションシップのタイプは、2つのダッシュの間にある角括弧 [ と ] を使って記述されます。
- プロパティは、JSONのような構文で指定されます。
- ノードを取得したい場合には、コロンの前に値(下記の例では p) を置くことで、変数を代入することができます。
```
MATCH (p:Person)
```
- Neo4jのプロパティはキーと値のペアで、例えば {name: 'Tom Hanks'} のようになります。
```
// example Cypher pattern
(m:Movie {title: 'Cloud Atlas'})<-[:ACTED_IN]-(p:Person)
```
- dot記法 を使ってプロパティにアクセスできます
```
MATCH (p:Person {name: 'Tom Hanks'})
RETURN  p.born
```
- Cypherでは、ラベル、プロパティ・キー、変数は、大文字と小文字が区別されます。
- Cypherのキーワードは、大文字と小文字が区別されません

## お作法 
- **<font color="red">ラベルにはCamelCase (先頭の文字を大文字で表記)を使用して名前を付けます。</font>**
- **<font color="red">プロパティキーと変数にcamelCase (先頭は小文字、後続する単語の先頭は大文字で表記)を使用した名前を付けます。</font>**
- **<font color="red">Cypherキーワード(MATCH, WHERE 等）には、UPPERCASE(すべて大文字)を使用します。</font>**
- **<font color="red">ノードのラベルは最低でも1つ、最大でも4つまでとするのがベストプラクティスです。</font>**
- Index名は　ノード名_プロパティー

## index
### index作成
```
# 単体index
CREATE INDEX <インデックス名> IF NOT EXISTS FOR (n:ノード名) ON (n.プロパティー名);

# 複合indexの場合
CREATE INDEX <インデックス名> IF NOT EXISTS FOR (n:ノード名) ON (n.プロパティー名1, n.プロパティー名2, ・・・);
```

### index削除
```
DROP INDEX インデックス名;
```

### インデックス一覧の取得
```
show indexes
```

## プロパティー一覧の取得方法(keys）
```
MATCH (p:Person)
RETURN p.name, keys(p)
```

## グラフに存在するプロパティは何か？
**一度定義したプロパティ・キーは、そのプロパティ・キーを使用するノードやリレーションシップが現在存在しない場合でも、グラフ内に残ります。**

```
CALL db.propertyKeys()
```

## グラフに存在するラベル一覧の取得
```
CALL db.labels()
```


### リレーションの一覧
インデックスが貼れないから重い？
```
match (a)-[r]->(b) return distinct a.labels, type(r), b.labels
```


## 制約一覧の取得
```
show constraints
```




## MERGE 
Cypher の超主力キーワード
MERGE は、まずデータの存在確認を行い、存在していなければ作成し、既に存在していれば作成しない
便利だが、MERGE内で複数ノード、リレーションを作成する場合、一つでも未作成のオブジェクトがあると作成してしまう
（プライマリーキー制約があれば失敗するが、そうでなければ、同じオブジェクトが複数さくせいされてしまう。）



### MERGE のカスタマイズ
ノードが作成されたときやノードが見つかったときにプロパティを設定できるように、実行時の振る舞いを指定することができる。 
**ON CREATE SET** や **ON MATCH SET** 条件、あるいは SET キーワードを使って、任意の追加プロパティを設定することができます。

```
// 存在しなければ作成、既に存在すれば参照を返す
MERGE (p:Person {name: 'McKenna Grace'})

// ノードが作成された場合  `createdAt` プロパティーを作成
ON CREATE SET p.createdAt = datetime()

// ノードが既に存在していれば `updatedAt` プロパティーを設定
ON MATCH SET p.updatedAt = datetime()

// プロパティーの有無に関わらず `born` プロパティーを設定ト
SET p.born = 2006

RETURN p
```


## 検索

### 書き方色々

```
MATCH (p:Person)-[:ACTED_IN]->(m:Movie)
WHERE m.title='The Matrix'
RETURN p.name
```

```
MATCH (p)-[:ACTED_IN]->(m)
WHERE p:Person AND m:Movie AND m.title='The Matrix'
RETURN p.name
```

### 部分一致によるフィルタリング
```
MATCH (p:Person)-[:ACTED_IN]->()
WHERE toLower(p.name) STARTS WITH 'michael'
RETURN p.name
```

### リストによるフィルタリング
```
MATCH (p:Person)
WHERE p.born IN [1965, 1970, 1975]
RETURN p.name, p.born
```

#### INなら動くけど、=だと動かない
リレーションのプロパティーに対しては条件を指定できない？

```
MATCH (p:Person)-[r:ACTED_IN]->(m:Movie)
WHERE  'Neo' IN r.roles AND m.title='The Matrix'
RETURN p.name, r.roles
```

```
MATCH (p:Person)-[r:ACTED_IN]->(m:Movie)
WHERE  'Neo' = r.roles AND m.title='The Matrix'
RETURN p.name, r.roles

```


### 全ノード取得
```
MATCH (a) RETURN a
```

### with句
購入数が2以上のノード（User、Item）、リレーション数（購入数）を抽出
```
MATCH (a:User)-[r:BUY]->(b:Item)
WITH a,b,count(r) as cnt
WHERE cnt >= 2
RETURN *
```

リレーションの情報は取れない模様(下記だヒットしない）
```
MATCH (a:User)-[r:BUY]->(b:Item)
WITH a,b, r, count(r) as cnt
WHERE cnt >= 2
RETURN *
```

### Index一覧取得
```
show indexes
```

### 制約一覧取得
```
show constraints
```


## ソート
RETURN の後に ORDER By
```
MATCH (n)
RETURN n.name, n.age
ORDER BY n.name
```

## 複数カラムに対して重複削除
期待通りに distinct が使える
```
MATCH (n:User) RETURN distinct n.sex , n.age;
```

## ラベルの追加
```
MATCH (p:Person {name: 'Jane Doe'})
SET p:Developer
RETURN p
```


## カウント

### 全ノードの件数
```
MATCH (n) RETURN count(*);
```

### ノードの種類を特定してカウント
```
MATCH (u:User) RETURN count(*);
```

### リレーションの数
```
MATCH ()-[r]->() RETURN count(r) as count;
```

### ノード間を特定してカウント
```
MATCH (a:Company)-[r]->(b:User) RETURN count(r) as count;
```

### リレーションを特定してカウント
```
MATCH ()-[r:BUY]->() RETURN count(r) as count;
```


### ノードの種類を特定してカウント
```
MATCH (u:User) RETURN count(*);
```

## 削除
全削除
```
MATCH (n) DETACH DELETE n
```


オブジェクトの参照を取得してから**DELETE**で削除
```
MATCH (p:Person)
WHERE p.name = 'Jane Doe'
DELETE p
```

リレーションが単体で存在させる事を防ぐため、リレーションが張られているノードを削除しようとするとエラーになる

>Cannot delete node<ノードID>, because it still has relationships. To delete this node, you must first delete its relationships.

### ノードとリレーションの削除
**DETACH**を使う

```
MATCH (p:Person {name: 'Jane Doe'})
DETACH DELETE p
```

### ラベルの削除

```
MATCH (p:Person {name: 'Jane Doe'})
REMOVE p:Developer
RETURN p
```


### IDを指定して削除

#### ノードの場合
id=1のノードを削除
```
MATCH (n) where id(n)=1 DELETE n
```

#### リレーションの場合
id=2の BUY リレーション を削除
```
MATCH ()-[r:BUY]->() where id(r)=1 DELETE r
```

### リレーション削除
ユーザ（user_id=11111）と 商品（item_id: "22222"）との関係（購入）を削除
```
MATCH (a:User{user_id: "11111"})-[r:BUY]->(b:Item{item_id: "22222"}) DELETE r;
```


### プロパティの削除
ノードやリレーションシップからプロパティを削除するには、 **REMOVE** キーワードを使用するか、**プロパティを null** に設定する
<font color="red">**ノードの主キーとして使用されているプロパティも削除できるが、やってはいけない**</font>

```
MATCH (p:Person)-[r:ACTED_IN]->(m:Movie)
WHERE p.name = 'Michael Cain' AND m.title = 'The Dark Knight'
REMOVE r.roles
RETURN p, r, m
```

```
MATCH (p:Person)
WHERE p.name = 'Gene Hackman'
SET p.born = null
RETURN p
```

### indexの削除
```
DROP INDEX <index_name> IF EXISTS
```

### ノードA-B間で重複しているリレーションを一意にする

**apoc**で用意されている

```
MATCH (a:User)-[r:BUY]->(b:Item)
WITH collect(r) as cnt
WHERE size(cnt) > 1
CALL apoc.refactor.mergeRelationships(cnt) YIELD rel
RETURN COUNT(*)
```


### DBのクローン
Neo4J Desktopの場合


### DBの作成
```
CREATE DATABASE DB名;
```

### DBの削除
sysytem　データベースに切り替えてから実行する必要がある
```
DROP DATABASE DB名;
```

### DB一覧
```
SHOW DATABASES;
```

### DBを切り替える
```
:use <DB名>
```

### DB停止
```
STOP DATABASE <DB名>
```
停止確認は
> show databases;  

から 対象DBが **currentStatus＝offline** となっている事を確認

### DB開始
```
start <DB名>
```

### スキーマ確認
```
CALL db.schema.visualization()
```

### dumpファイルのロード
```shell
sudo -u neo4j /usr/bin/neo4j-admin database load --overwrite-destination=false --from-path=<dumpファイルがか格納されているディレクトリへの絶対パス> <DB名> --verbose
```

- DBはStopしておく必要あり
- neo4j-adminへのパスが通っていれば　/usr/bin/neo4j-admin　-> neo4j-admin でOK（通っているはず）
- dumpファイルは \<DB名\>.dump　になっている必要あり
- 既存の中身を差し替えたい場合は -overwrite-destination=true を指定
- DBが既にある状態で -overwrite-destination=false を指定するとエラーになる
- 4.4系で作成したDumpファイルは5.5系にエクスポートできない模様(フォーマット違うらしい）


### Cypher ShellにDBを指定してログインする
ローカルのneo4jにポート 17687 でアクセスする場合
```
/usr/bin/cypher-shell -u <ユーザ名> -p <パスワード> -d <データベース名> -a neo4j://localhost:17687
```

### cypherシェルからログアウト
```shell
:exit
```

### confの場所
```shell
/etc/neo4j/neo4j.conf
```


### 登録・削除処理の途中でメモリ不足の落ちる場合は？
バッチサイズを指定して実行。parallelはTrueにすると速いが、メモリへの負担が大きい

```shell
CALL apoc.periodic.iterate(
やりたい処理
{batchSize:5000,parallel:false}
);
```

例)
明示的にRETURN を使ってやらないとbatchSizeが効かない模様（ MATCH (n) DETACH DELETE n だけだと駄目）
```
CALL apoc.periodic.iterate(
  "MATCH (n) RETURN n",
  "DETACH DELETE n",
{batchSize:5000, parallel:False});
```


### 他で作成した dumpデータをDesktopに認識させる
windowsの場合、下記にdumpファイルを置くと認識してくれる

C:<設定による>\relate-data\projects\プロジェクトのIDっぽいフォルダ名

# 参照
[Cypherの紹介](https://graphacademy.neo4j.com/courses/jp-cypher-fundamentals/1-reading/1-intro-cypher/)