GSQLをつかってみよう

# GSQLとは
### 概要
GSQLはTigerGraphデータベースが提供するクエリ言語です。グラフスキーマの定義、データのロードと管理、データ分析をおこなうため問合せをおこなうなど様々なデータベース操作を行います。

GSQLはSQLとの類似性、チューリング完全性、組み込み済みの並列性によって高度なパフォーマンスと迅速な開発、そしてあらゆるアルゴリズムを実行する能力をもたらします。

それぞれの特徴について説明していきます。

**分析のための設計**
GSQLは複雑なグラフ分析のために開発・設計されています。GSQLの特長であるアキュムレータを利用することで超並列処理（Massively Paralelle Processing＝MPP）を従来の並列プログラミングに伴う煩わしさを感じることなく利用できます。

**チューリング完全性（Turing complete）**
GSQLはチューリング完全な言語であり、命令型および手続き型プログラミングをサポートするためアルゴリズム計算に理想的といえます。従来の制御フロー分のチューリング完全性によって、GSQLを利用した任意のアルゴリズムを記述することができます。

TigerGraphはコミュニティ検出のためのLouvainアルゴリズムやPageRankアルゴリズムなど、GSQLで実装されたアルゴリズムをオープンソースの[データサイエンスライブラリ](https://docs.tigergraph.com/graph-ml/current/intro/)として公開しています。

**高度なロードとクエリ**
MPP機能をフル活用することでデータロードとクエリを高速に実現します。

**SQLとの類似性**
GSQLはSQLライクな構文を採用しているので、リレーショナルデータベース（SQL）を習得しているプログラマー、エンジニア、アナリストにとってGSQLの学習コストは高くありません。

以下の例は製品名（ProductName）順で結果を取得するクエリです。SELECT文に類似性をみることができます。

**SQL**
```sql
SELECT p.*
FROM Product p
ORDER BY p.ProductName;
```

**GSQL**
```
CREATE QUERY products_abc() {
  products = {Product.*};

  results =
    SELECT p
    FROM products:p
    ORDER BY p.productName;

  PRINT results;
}
```

### アキュムレーターとは
アキュムレーターはGSQLにおける変数の1つです。この変数の状態（State）はクエリの実行を通じて変更できます。初期値をもち新しい値を（「＝」演算子を利用して）蓄積し続けます。アキュムレーターの各変数には型が存在します。

アキュムレーター変数は2種類に分類されます。
- **スカラーアキュムレーター変数（単一の値を保持する）**
    - SumAccum
    - MinAccum, MaxAccum
    - AvgAccum
    - AndAccum, OrAccum
    - BitwiseAndAccum, BitwiseOrAccum
- **コレクションアキュムレーター（値セットを保持する）**
    - ListAccum
    - SetAccum
    - BagAccum
    - MapAccum
    - ArrayAccum
    - HeapAccum
    - GroupByAccum

**アキュムレーター変数一覧（サマリ）**
各アキュムレーターの詳細は[マニュアルの該当ページ](https://docs.tigergraph.com/gsql-ref/current/querying/accumulators)にて説明されていますのでここでは一覧だけ記しておきます。

| アキュムレータータイプ | 初期値 |
| --- | --- |
| SumAccum<INT> | 0 |
| SumAccum<FLOAT or DOUBLE> | 0.0 |
| SumAccum<STRING> | 空の文字列 |
| MaxAccum<INT> | INT_MIN |
| MaxAccum<FLOAT or DOUBLE> | FLOAT_MIN or DOUBLE_MIN |
| MaxAccum<STRING> | 空の文字列 |
| MaxAccum<VERTEX> | バーテックスの内部ID：0 |
| MaxAccum<tupleType> | タプルの各フィールドの初期値 |
| MinAccum<INT> | INT_MAX |
| MinAccum<FLOAT or DOUBLE> | FLOAT_MAX or DOUBLE_MAX |
| MinAccum<STRING> | 空の文字列 |
| MinAccum<VERTEX> |  |
| MinAccum<tupleType> | タプルの各フィールドの初期値 |
| AvgAccum | 0.0 (倍精度浮動小数点) |
| AndAccum | TRUE |
| OrAccum | FALSE |
| BitwiseAndAccum | -1 (INT) = 64-bit sequence of 1s |
| BitwiseOrAccum | 0 (INT) = 64-bit sequence of 0s |
| ListAccum<type> | 空のリスト |
| SetAccum<type> | 空のセット |
| BagAccum<type> | 空のバッグ |
| MapAccum<type, type> | 空のマップ |
| ArrayAccum<accumType> | 空のリスト |
| HeapAccum<tuple>(heapSize, sortKey) | 空のヒープ |
| GroupByAccum< type [, type] , accumType [, accumType]* > | マップによる空のグループ |

代表的なアキュムレーター型について図を用いて解説します。

**アキュムレーターの変数例**
下図のエディタ内3〜8行目まで、6 つの異なるアキュムレーター変数（接頭辞に@@がついているもの）が宣言されています。

- SumAccum<INT>
    変数に INT 値を追加し続けることができます。
    10〜11 行目で示したように値1と2をアキュムレータに追加し、最終的に値 3を返します（右のボックスの 3 行目で示されています）。
    
- MinAccum<INT>
    取得された値のうち最小の INT 数を保持します。
    14〜15 行目で示したように、値1 と2を 変数に累積して、最終的に値1を返します（右のボックスの6行目に表示）。
    
- MaxAccum<INT>
    MaxAccum<INT> は MinAccum と対称的に取得された値のうち最大の INT 値を返します。
    18〜19行目では、値1と 2を変数に累積して、最終的に2という値を返します（右のボックスの9行目に表示）。
    
- OrAccum
    ブーリアン変数であり蓄積される新しいブーリアン値とのORをとり続けます。初期のデフォルト値はFALSEです。
    22〜23行目では、TRUEとFALSEを変数に渡し最終的にTRUEの値を返します（右のボックスの12行目に表示）。
    
- AndAccum
    OrAccum と対称的なもので、ANDの累積セマンティクスを使用します。
    26〜27行目では、TRUEとFALSEを累積して最終的にFALSEの値を返しています（右のボックスの15行目に表示）。
    
- ListAccum<INT>
    内部リスト変数に新しい整数を追加し続けます。
    30〜32 行目までは、1, 2, [3,4] をアキュムレータに追加し、最終的に [1,2,3,4] を生成しています（右のボックスの 19〜22 行目に表示）。
    ![a.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2843915/c2f991a7-f7a8-72db-68e3-d16024c975dc.png)
    

### グローバル vs. ローカル（バーテックス）アキュムレーター

上述まででアキュムレーターがGSQLにおける特殊な型をもった変数であることは理解できたと思います。
次にグローバルとローカル変数の違いを説明したいと思います。

**グローバル アキュムレーター**
クエリ全体に対して有効なアキュムレーターです。クエリ内の任意の場所でその値を更新することができます。
接頭辞：@@

**ローカル（バーテックス） アキュムレーター**
各バーテックスごとに処理されるアキュムレーターです。各バーテックスがアクセス可能な場合にのみ値を更新することができます。
接頭辞：@ ※処理内で呼び出す場合はバーテックスのエイリアス付きで呼び出すようになる

前回の投稿記事で作成したSocialグラフを利用してグローバルとローカルの各アキュムレーターの動作についてみていきましょう。
以下のSocialグラフに対して指定されたPerson ID（人物名）を元にしてその近傍への1ホップ探索を行うクエリを書きます。
このクエリの中でグローバルアキュムレーター（\@\@ global_cnt）を利用して操作したエッジの総数を累積します。
またローカルアキュムレーター（\@ vertex_cnt）には操作された各バーテックスに整数1を格納します。

**Socialグラフイメージ**
![SocialGraph](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2843915/924e0bf4-72ae-e9d3-8d4c-ce654d923a94.png)


**クエリ**
```
CREATE QUERY global_and_vertex_accumlators(VERTEX<Person> p) FOR GRAPH Social { 
  SumAccum<INT> @@global_cnt = 0;
  SumAccum<INT> @vertex_cnt = 0;
  
  persons = {Person.*};
  
  neighbors = SELECT t 
              FROM persons:s-(:e)-:t
              WHERE s == p
              ACCUM t.@vertex_cnt += 1, @@global_cnt += 1;
  
  PRINT @@global_cnt;
  PRINT neighbors[neighbors.@vertex_cnt]; 
}
```

**結果イメージ（Person ID＝Danでクエリを実行した場合）**
```json
[
  {
    "@@global_cnt": 4
  },
  {
    "neighbors": [
      {
        "attributes": {
          "neighbors.@vertex_cnt": 1
        },
        "v_id": "Tom",
        "v_type": "Person"
      },
      {
        "attributes": {
          "neighbors.@vertex_cnt": 1
        },
        "v_id": "Kevin",
        "v_type": "Person"
      },
      {
        "attributes": {
          "neighbors.@vertex_cnt": 1
        },
        "v_id": "Jenny",
        "v_type": "Person"
      },
      {
        "attributes": {
          "neighbors.@vertex_cnt": 1
        },
        "v_id": "Nancy",
        "v_type": "Person"
      }
    ]
  }
]
```

クエリ結果が示すようにDanにはTom、Kevin、Jenny、Nancyの4人の近接する友人がいてそれぞれがローカルアキュムレーター（@vertex_cnt）に値1を保持しています。そしてグローバルアキュムレーター（@@global_cnt）の値は4になっており各エッジに対して値を1ずつ累積していることがわかります。

### ACCUM句 vs. POST-ACCUM句
ACCUMとPOST-ACCUM句は段階的に計算されます。
SELECT-FROM-WHEREクエリブロックではACCUMが最初に実行され、POST-ACCUM句がその後に実行されます。

**ACCUM句**
FROM句のパターンがマッチしたエッジごとに1回ずつステートメントを実行します。
さらにACCUM句はすべてのマッチに対してそのステートメントを並列に実行します。

**POST-ACCUM句**
関係する各頂点に対して1回ずつステートメントを実行します。
POST-ACCUM句内の各ステートメントはソースバーテックスまたはターゲットバーテックスのいずれかを参照することはできますが、両方を参照することはできないことに注意してください。

各ステートメントはACCUM句で計算され集約されたアキュムレータ結果にアクセスすることができます。
説明は以上ですが、アキュムレーターについては別の機会でサンプルクエリを実行できるハンズオン環境を構築したいと思います。

# GSQLを利用するには
GSQLコマンドを実行できる環境は以下の通りです。
- TigerGraphが構築されたサーバ（TigerGraph Cloud含む）
- GSQL Clientが利用可能な端末
    - 前提条件
        - Java（openjdk） 7.0 以降
        - OpenSSL

それぞれの利用方法をみていきましょう。

以下で説明する方法はTigerGraph Cloudインスタンスがセットアップされていることを前提としていますので、環境をセットアップしていない方は「[TigerGraphをさわってみよう](https://qiita.com/masahiro-sasajima/items/92d3d7f1fe435b003e49)」から環境をセットアップし、「[GraphStudioをはじめてみよう](https://qiita.com/masahiro-sasajima/items/ef8465da5d28cc267638)」からグラフを構築してください。

### TigerGraph CloudでGSQL Shellを利用する
TigerGraph Cloud上でGSQLを利用する場合はGSQL Shellツールを 利用します。
TigerGraph Cloudの管理コンソールから左ペインメニュー「Clusters」を押下し、該当するインスタンスの「Tools」> 「GSQL Shell」を選択します。
GSQL Shell左ペインメニューのドロップダウンメニューでインスタンス内のグラフを切り替えることができます。（下スクリーンショットではSocialグラフを利用しています）
これは以下のコマンドに相当します。

```
USE GRAPH <graph_name>
```

![GSQLShell](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2843915/45b69a47-e4f8-a006-2e68-4d01e73b2efa.png)


以下のGSQLコマンドを実行（コンソール右側の再生アイコン）して無事に環境に接続できていることを確認します。

```
Social$ ls
---- Graph Social
Vertex Types:
  - VERTEX Person(PRIMARY_ID name STRING, age INT, gender STRING, state STRING) WITH STATS="OUTDEGREE_BY_EDGETYPE", PRIMARY_ID_AS_ATTRIBUTE="true"
Edge Types:
  - UNDIRECTED EDGE Friendship(FROM Person, TO Person, connect_day DATETIME)

Graphs:
  - Graph Social(Person:v, Friendship:e)
Jobs:
  - CREATE LOADING JOB file_load_job_933cd55860 FOR GRAPH Social {
      DEFINE FILENAME MyDataSource;
      LOAD MyDataSource TO VERTEX Person VALUES($0, $2, $1, $3) USING SEPARATOR=",", HEADER="true", EOL="\n";
    }

  - CREATE LOADING JOB file_load_job_3902e71de6 FOR GRAPH Social {
      DEFINE FILENAME MyDataSource;
      LOAD MyDataSource TO EDGE Friendship VALUES($0, $1, $2) USING SEPARATOR=",", HEADER="true", EOL="\n";
    }

Queries:
  - findDeepLink(vertex<Person> start_node, int depth) (installed v2)
```

### GSQL Clientをセットアップする
TigerGraph Cloudの環境に対してGSQL Clientをセットアップして接続していきます。
上記の前提条件を満たす端末で実行してください。
1. GSQL Clientメディアをダウンロードする
[TigerGraphのメディアダウンロードページ](https://dl.tigergraph.com/download.html)より、GSQL Clientをダウンロードします。
接続するサーバ（TigerGraph Cloudインスタンス）のバージョン（執筆時点は3.7.0 が最新）と同じバージョンのファイルを入手します。
注：OSがLINUX X64と記載されていますがGSQL Clientファイルはjarファイルのため上述の前提をクリアしていれば実行できます。
    
    ![Download](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2843915/2330f223-7a1e-2676-010a-f4b320765290.png)

    
2. SSL証明書を取得する
以下のコマンドを実行して、証明書を取得します。
    ```bash
    echo | openssl s_client  -connect <domain>:443 |  sed -ne '/-BEGIN CERTIFICATE-/,/-END CERTIFICATE-/p' > <path_to_certificate>
    ```
    
    **置換する部分**
    \<domain>
    インスタンスのURLを指定します。TigerGraph Cloudの管理コンソールから左ペインメニュー「Clusters」を押下し、該当するインスタンスをすると「Details」内「Network Information」 > 「Domain」で指定されている値です。
    ![domain](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2843915/3ff551af-5e69-27b1-d644-df5aaa378985.png)

    
    \<path_to_certificate>
    OpenSSLのcertsファイルを指定します。
    
3. 暗号鍵を生成する
    TigerGraph Cloudの管理コンソールから左ペインメニュー「Clusters」を押下し、該当するインスタンスの「Tools」> 「AdminPortal」を選択します。
    AdminPortal左ペインメニューから「Management」> 「Users」を選択します。
    My Profile内の「New alias」に任意（例：my_secret）の鍵名を付与し、「＋」を押下すると鍵が発行されます。

    鍵は繰り返し利用しますので、テキストパッドなどローカルに控えておいてください。
    また必ず接続するGraph（以下例では「Graph: Social」と記載されている部分）が自身の環境とあっているかを確認してください。
    ![secret.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2843915/26762e21-da19-3aab-f583-572bf1c273e2.png)

    
4. jarファイルを実行する
以下のコマンドを実行してGSQL Clientを起動します。
    
    ```java
    java -jar <path_to_client> --cacert <path_to_certificate> --ip <domain>:443 -u __GSQL__secret -p <secret>
    ```
    
    **置換する部分**
    \<path_to_client>
    GSQL Clientのjarファイルを指定します。
    
    \<path_to_certificate>
    OpenSSLのcertsファイルを指定します。（手順2と同じ）
    
    \<domain>
    インスタンスのURLを指定します。（手順2と同じ）
    
    \<secret>
    暗号鍵を指定します。（手順3で取得したもの）
    
5. 起動後の確認をする
以下のGSQLコマンドを実行して無事に環境に接続できていることを確認します。
    
    ```
    GSQL> use graph Social
    Using graph 'Social'
    
    GSQL> ls
    ---- Graph Social
    Vertex Types:
      - VERTEX Person(PRIMARY_ID name STRING, age INT, gender STRING, state STRING) WITH STATS="OUTDEGREE_BY_EDGETYPE", PRIMARY_ID_AS_ATTRIBUTE="true"
    Edge Types:
      - UNDIRECTED EDGE Friendship(FROM Person, TO Person, connect_day DATETIME)
    
    Graphs:
      - Graph Social(Person:v, Friendship:e)
    Jobs:
      - CREATE LOADING JOB file_load_job_933cd55860 FOR GRAPH Social {
          DEFINE FILENAME MyDataSource;
          LOAD MyDataSource TO VERTEX Person VALUES($0, $2, $1, $3) USING SEPARATOR=",", HEADER="true", EOL="\n";
        }
    
      - CREATE LOADING JOB file_load_job_3902e71de6 FOR GRAPH Social {
          DEFINE FILENAME MyDataSource;
          LOAD MyDataSource TO EDGE Friendship VALUES($0, $1, $2) USING SEPARATOR=",", HEADER="true", EOL="\n";
        }
    
    Queries:
      - findDeepLink(vertex<Person> start_node, int depth) (installed v2)
    
    GSQL>exit
    ```
    

# GSQLをさわってみる
### スキーマ定義とグラフの作成
ここでは前回の投稿記事「[GraphStudioをはじめてみよう](https://qiita.com/masahiro-sasajima/items/ef8465da5d28cc267638)」で作成したグラフをGSQLを用いてセットアップしてみたいと思います。
同じ名称のスキーマを定義するとエラーになるため、若干名称を変更しています。

GSQL Shellで実行する場合は、「GSQL>」をメッセージを無視してコマンドを入力してください。

1. グローバルビューにきりかえる
    グローバルビューにきりかえるためには以下のコマンドを実行します。
    ```
    GSQL> use Global
    ```
    切り替えられたことを確認するためにはlsコマンドを利用します。
    
    ```
    GSQL> ls
    ---- Global vertices, edges, and all graphs
    Vertex Types:
      - VERTEX Person(PRIMARY_ID name STRING, age INT, gender STRING, state STRING) WITH STATS="OUTDEGREE_BY_EDGETYPE", PRIMARY_ID_AS_ATTRIBUTE="true"
    Edge Types:
      - UNDIRECTED EDGE Friendship(FROM Person, TO Person, connect_day DATETIME)
    
    Graphs:
      - Graph Social(Person:v, Friendship:e)
    Jobs:
    
    JSON API version: v2
    Syntax version: v2
    ```
    定義を削除する場合のみ以下のコマンドを実行します。

    ```
    GSQL> drop all
    
    Dropping all, about 1 minute ...
    Abort all active loading jobs 
    [ABORT_SUCCESS] No active Loading Job to abort.
    
    Shutdown restpp gse gpe ...
    Graph store /usr/local/tigergraph/gstore/0/ has been cleared!
    Everything is dropped.
    ```
    
2. スキーマを定義する
    スキーマを定義していきます。GSQLコマンド実行時に複数行にまたがって定義する場合は、BEGINとEND句でコマンドを囲うことで1つのコマンドとして認識します。
    これを回避するためにコマンドを一行にまとめても構いません。
    
    **バーテックス**
    ```
    BEGIN
    CREATE VERTEX person1 (
        PRIMARY_ID name STRING,
        name STRING, age INT,
        gender STRING, state STRING
    )
    END
    ```
    
    または
    ```
    CREATE VERTEX person1 (PRIMARY_ID name STRING, name STRING, age INT, gender STRING, state STRING)
    ```
    
    **エッジ**
    ```
    CREATE UNDIRECTED EDGE friendship1 (FROM person1, TO person1, connect_day DATETIME)
    ```
    
3. グラフを定義する
    
    上記で作成したオブジェクトを含むグラフを定義します。

    ```
    CREATE GRAPH social1 (person1, friendship1)
    ```
    
    グラフを切り替えて定義内容を確認します。
    
    ```
    USE GRAPH social1
    ```
    
    ```
    LS
    ```
    
    結果イメージ
    ```
    GSQL> use graph social1
    GSQL> ls
    ---- Graph social1
    Vertex Types:
      - VERTEX person1(PRIMARY_ID name STRING, name STRING, age INT, gender STRING, state STRING) WITH STATS="OUTDEGREE_BY_EDGETYPE"
    Edge Types:
      - UNDIRECTED EDGE friendship1(FROM person1, TO person1, connect_day DATETIME)
    
    Graphs:
      - Graph social1(person1:v, friendship1:e)
    Jobs:
    Queries:
    ```
    

### データローディング

1. データローディングジョブの作成
    データローディングジョブを作成するために以下のコマンドを実行します。
    
    ```
    USE GRAPH social1
    BEGIN
    CREATE LOADING JOB load_social FOR GRAPH social1 {
       DEFINE FILENAME person_file;
       DEFINE FILENAME friendship_file;
    
       LOAD person_file TO VERTEX person1 VALUES ($0, $0, $2, $1, $3) USING header="true", separator=",", EOL="\n";
       LOAD friendship_file TO EDGE friendship1 VALUES ($0, $1, $2) USING header="true", separator=",", EOL="\n";
    }
    END
    ```
    
    **GraphStudioで設定したデータローディングジョブとの違い**
    GraphStudioでデータローディングの設定をする場合、各アップロードされたファイルごとにデータマッピングを設定したことを覚えていらっしゃると思います。
    
    ![loadingJob](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2843915/15bd9745-60f4-73fd-e050-9d06411487c5.png)
    
    この時、裏側で定義されているデータローディングジョブは以下の通りです。各マッピングごとにジョブが作成され、アップロードされたファイルはMyDataSourceとよばれるファイル名で定義されています。そのためデータ取込を開始すると各ジョブが順番に実行されます。
    
    **登録されているジョブ例**
    ```
    Jobs:
      - CREATE LOADING JOB file_load_job_933cd55860 FOR GRAPH Social {
          DEFINE FILENAME MyDataSource;
          LOAD MyDataSource TO VERTEX Person VALUES($0, $2, $1, $3) USING SEPARATOR=",", HEADER="true", EOL="\n";
        }
    
      - CREATE LOADING JOB file_load_job_3902e71de6 FOR GRAPH Social {
          DEFINE FILENAME MyDataSource;
          LOAD MyDataSource TO EDGE Friendship VALUES($0, $1, $2) USING SEPARATOR=",", HEADER="true", EOL="\n";
        }
    ```
    
2. データファイルの追加
    
    手順1で作成したデータローディングジョブで利用されるデータファイルはTigerGraphインスタンスのサーバ上に配置されている必要があります。しかしTigerGraph Cloudへファイルをアップロードするには現状GraphStudioを利用する以外に手段がありません（記事執筆時点 バージョン3.7.0）。
    
    GraphStudioの「データマッピング」を開き、データファイルの追加を押して以下のCSVファイルをアップロードします。マッピング設定は必要ありません。
    
    前回の投稿「[GraphStudioをはじめてみよう](https://qiita.com/masahiro-sasajima/items/ef8465da5d28cc267638)」を実施済みで同じファイルがインスタンス内に保存されている場合は、この手順をスキップします。
    
    アップロードするファイル
    **person.csv**
    
    ```
    name,gender,age,state
    Tom,male,40,ca
    Dan,male,34,ny
    Jenny,female,25,tx
    Kevin,male,28,az
    Amily,female,22,ca
    Nancy,female,20,ky
    Jack,male,26,fl
    ```
    
    **friendship.csv**
    
    ```
    person1,person2,date
    Tom,Dan,2017-06-03
    Tom,Jenny,2015-01-01
    Dan,Jenny,2016-08-03
    Jenny,Amily,2015-06-08
    Dan,Nancy,2016-01-03
    Nancy,Jack,2017-03-02
    Dan,Kevin,2015-12-30
    ```
    
3. データローディングジョブの実行
    手順1で作成したジョブを実行します。以下のコマンドを実行してローディング処理を行います。
    明示的に各ファイルを指定して実行します。
    
    ```
    RUN LOADING JOB load_social USING person_file="/home/tigergraph/tigergraph/data/gui/loading_data/person.csv", friendship_file="/home/tigergraph/tigergraph/data/gui/loading_data/friendship.csv"
    ```
    
    **実行結果イメージ**
    ```
    [Tip: Use "CTRL + C" to stop displaying the loading status update, then use "SHOW LOADING STATUS jobid" to track the loading progress again]
    [Tip: Manage loading jobs with "ABORT/RESUME LOADING JOB jobid"]
    Starting the following job, i.e.
      JobName: load_social, jobid: social1.load_social.file.m1.1666323897222
      Loading log: '/home/tigergraph/tigergraph/log/restpp/restpp_loader_logs/social1/social1.load_social.file.m1.1666323897222.log'
    
    Job "social1.load_social.file.m1.1666323897222" loading status
    [FINISHED] m1 ( Finished: 2 / Total: 2 )
      [LOADED]
      +------------------------------------------------------------------------------------------------------------+
      |                                                        FILENAME |   LOADED LINES |   AVG SPEED |   DURATION|
      |/home/tigergraph/tigergraph/data/gui/loading_data/friendship.csv |              8 |      70 l/s |     0.10 s|
      |    /home/tigergraph/tigergraph/data/gui/loading_data/person.csv |              8 |      70 l/s |     0.10 s|
      +------------------------------------------------------------------------------------------------------------+
    ```
    

### 基本的なクエリを実行する

ここからはいくつか基本的なクエリを実行していきたいと思います。
バーテックスの参照はSQLにおけるテーブルの参照と非常に近しい構文をしています。

**バーテックスのデータを参照する**

```
SELECT FROM person1 WHERE primary_id=="Tom"
SELECT name FROM person1 WHERE state=="ca"
SELECT name, age FROM person1 WHERE age > 30
```

**実行結果**

```
GSQL > SELECT * FROM person1 WHERE primary_id=="Tom"
[{
  "v_id": "Tom",
  "attributes": {
    "gender": "male",
    "name": "Tom",
    "state": "ca",
    "age": 40
  },
  "v_type": "person1"
}]
GSQL > SELECT name FROM person1 WHERE state=="ca"
[
  {
    "v_id": "Amily",
    "attributes": {"name": "Amily"},
    "v_type": "person1"
  },
  {
    "v_id": "Tom",
    "attributes": {"name": "Tom"},
    "v_type": "person1"
  }
]
GSQL > SELECT name, age FROM person1 WHERE age > 30
[
  {
    "v_id": "Tom",
    "attributes": {
      "name": "Tom",
      "age": 40
    },
    "v_type": "person1"
  },
  {
    "v_id": "Dan",
    "attributes": {
      "name": "Dan",
      "age": 34
    },
    "v_type": "person1"
  }
]
```

**バーテックスの件数を参照する**

```
SELECT count(*) FROM person1
```

**実行結果**

```
GSQL > SELECT count(*) FROM person1
[{
  "count": 7,
  "v_type": "person1"
}]
```

エッジを参照していきます。
エッジを参照する場合は参照するために必要なエッジをふくむ構文が必要です。

```
source_type -(edge_type)- target_type
```

**エッジデータを参照する**
次のクエリは同じ結果を返します。

```
SELECT * FROM person1-(friendship1)-person1 WHERE from_id =="Tom"
SELECT * FROM person1-(ANY)-ANY WHERE from_id =="Tom"
```

**実行結果**
```
GSQL > SELECT * FROM person1-(friendship1)-person1 WHERE from_id =="Tom"
[
  {
    "from_type": "person1",
    "to_type": "person1",
    "directed": false,
    "from_id": "Tom",
    "to_id": "Dan",
    "attributes": {"connect_day": "2017-06-03 00:00:00"},
    "e_type": "friendship1"
  },
  {
    "from_type": "person1",
    "to_type": "person1",
    "directed": false,
    "from_id": "Tom",
    "to_id": "Jenny",
    "attributes": {"connect_day": "2015-01-01 00:00:00"},
    "e_type": "friendship1"
  }
]

GSQL > SELECT * FROM person1-(ANY)->ANY WHERE from_id =="Tom"
[
  {
    "from_type": "person1",
    "to_type": "person1",
    "directed": false,
    "from_id": "Tom",
    "to_id": "Dan",
    "attributes": {"connect_day": "2017-06-03 00:00:00"},
    "e_type": "friendship1"
  },
  {
    "from_type": "person1",
    "to_type": "person1",
    "directed": false,
    "from_id": "Tom",
    "to_id": "Jenny",
    "attributes": {"connect_day": "2015-01-01 00:00:00"},
    "e_type": "friendship1"
  }
]
```

**エッジの件数を参照する**
```
SELECT count(*) FROM person1-(friendship1)-person1
```

**実行結果**
```
GSQL > SELECT count(*) FROM person1-(friendship1)-person1
[{
  "count": 7,
  "e_type": "friendship1"
}]
```

### パラメータつきクエリを実行する
GSQL はパラメータ化されたバーテックスセットに対して最大限の力を発揮します。
パラメータ化されたクエリによりあるバーテックスセットから隣接するバーテックスセットまで、グラフを何度も走査し、その過程で計算を実行することができます。
組み込みの並列実行と便利な集計操作（アキュムレーター）によりこれを実現可能です。
またあるクエリから別のクエリを呼び出すこともできます。 
ここでは簡単なパラメータ化されたクエリを定義して実行していきましょう。

GSQLのパラメータつきクエリは3つのステップで構成されています。
1. GSQLでクエリを定義する。このクエリはGSQLカタログに追加される。
2. カタログに1つまたは複数のクエリをインストールし各クエリのRESTエンドポイントを生成する。
3. インストールされたクエリに適切なパラメータを与え、GSQLコマンド、または、RESTエンドポイントにHTTPリクエストを送信して実行する。

インストールをおこなわないインタープレットなクエリを実行する事もできますが、パラメータつきクエリはインストールが必要です。

**1ホップクエリ**
1. クエリを定義する
    以下のクエリを定義します。
    このクエリの特徴は1つのSELECT文です。
    
    クエリ呼び出しから渡されたパラメータ p で識別される人物のバーテックスを含むバーテックスセット start を生成することから始まります。
    中括弧は、囲んだ項目を含むセットを構築するようにGSQLに指示しています。

    次に、SELECT文はFROM句に記述されたパターンに従って1ホップの探索を記述します。
    
    ```
    start:s -(friendship1:e)- person1:tgt
    ```
    
    このパターンは与えられたソースセット（start）から始まり、与えられた無向エッジ・タイプ（friendship1）を持ち、与えられた頂点タイプ（person1）で終わるエッジをすべて選択することを意味します。
    FROM 句では、バーテックスおよびエッジセットのエイリアスを :\<alias> を使用して定義しています。
    - s ：ソースバーテックスのエイリアス
    - e ：エッジのエイリアス
    - tgt：ターゲットバーテックスのエイリアス
    
    変数への代入（result = SELECT tgt）は、SELECT文が（SELECTクエリブロックでフィルタリングされ処理された）ターゲットバーテックスセットを返し、その出力セットをresultという変数に代入していることを意味します。
    
    最後に結果のバーテックスセットをJSONフォーマットで出力します。
    
    ```
    USE GRAPH social1
    BEGIN
    CREATE QUERY hello(VERTEX<person1> p) FOR GRAPH social1{
      start = {p};
      result = SELECT tgt
               FROM start:s-(friendship1:e)-person1:tgt;
      PRINT result;
    }
    END
    ```
    
    **実行結果**
    
    ```
    GSQL > USE GRAPH social1
    Using graph 'social1'
    GSQL > BEGIN
    GSQL > CREATE QUERY hello(VERTEX<person1> p) FOR GRAPH social1{
    GSQL >   start = {p};
    GSQL >   result = SELECT tgt
    GSQL >            FROM start:s-(friendship1:e)-person1:tgt;
    GSQL >   PRINT result;
    GSQL > }
    GSQL > END
    Successfully created queries: [hello].
    ```
    
    **確認**
    
    ```
    GSQL > ls
    ---- Graph social1
    Vertex Types:
      - VERTEX person1(PRIMARY_ID name STRING, name STRING, age INT, gender STRING, state STRING) WITH STATS="OUTDEGREE_BY_EDGETYPE"
    Edge Types:
      - UNDIRECTED EDGE friendship1(FROM person1, TO person1, connect_day DATETIME)
    
    Graphs:
      - Graph social1(person1:v, friendship1:e)
    Jobs:
      - CREATE LOADING JOB load_social FOR GRAPH social1 {
          DEFINE FILENAME friendship_file;
          DEFINE FILENAME person_file;
          LOAD person_file TO VERTEX person1 VALUES($0, $0, $2, $1, $3) USING SEPARATOR=",", HEADER="true", EOL="\n";
          LOAD friendship_file TO EDGE friendship1 VALUES($0, $1, $2) USING SEPARATOR=",", HEADER="true", EOL="\n";
        }
    
    Queries:
      - hello(vertex<person1> p)
    ```
    
2. クエリをインストールする
    このクエリは保存された状態でありインストールされていません。次のコマンドを利用してクエリをインストールします。
    インストールが完了するとGSQL上でクエリを実行する、またはREST APIのエンドポイントが生成されます。
    
    ```
    INSTALL QUERY hello
    ```
    
    **実行結果**
    ```
    GSQL > INSTALL QUERY hello
    Start installing queries, about 1 minute ...
    hello query: curl -X GET 'https://127.0.0.1:9000/query/social1/hello?p=VALUE'. Add -H "Authorization: Bearer TOKEN" if authentication is enabled.
    Select 'm1' as compile server, now connecting ...
    Node 'm1' is prepared as compile server.
    
    [========================================================================================================] 100% (1/1)
    Query installation finished.
    ```
    
3. クエリを実行する
    クエリに引数を渡して（パラメータを付与して）実行してみたいと思います。
    
    ```
    RUN QUERY hello("Tom")
    ```
    
    **実行結果**
    
    ```
    GSQL > RUN QUERY hello("Tom")
    {
      "error": false,
      "message": "",
      "version": {
        "schema": 7,
        "edition": "enterprise",
        "api": "v2"
      },
      "results": [{"result": [
        {
          "v_id": "Dan",
          "attributes": {
            "gender": "male",
            "name": "Dan",
            "state": "ny",
            "age": 34
          },
          "v_type": "person1"
        },
        {
          "v_id": "Jenny",
          "attributes": {
            "gender": "female",
            "name": "Jenny",
            "state": "tx",
            "age": 25
          },
          "v_type": "person1"
        }
      ]}]
    }
    ```
    

**番外編（INTERPRETEDクエリ）**

上記クエリのようにパラメータは付与できませんが、インストールを実施せずにクエリを実行する場合は以下のようにコマンドを定義します。

```
USE GRAPH social1
BEGIN
INTERPRET QUERY () {
  result = SELECT tgt
           FROM person1:s-(friendship1:e) -person1:tgt
           WHERE s.name == "Tom";
  PRINT result;
}
END
```

**実行結果**

```
GSQL > USE GRAPH social1
Using graph 'social1'
GSQL > BEGIN
GSQL > INTERPRET QUERY () {
GSQL >   result = SELECT tgt
GSQL >            FROM person1:s-(friendship1:e) -person1:tgt
GSQL >            WHERE s.name == "Tom";
GSQL >   PRINT result;
GSQL > }
GSQL > END
{
  "error": false,
  "message": "",
  "version": {
    "schema": 7,
    "edition": "enterprise",
    "api": "v2"
  },
  "results": [{"result": [
    {
      "v_id": "Dan",
      "attributes": {
        "gender": "male",
        "name": "Dan",
        "state": "ny",
        "age": 34
      },
      "v_type": "person1"
    },
    {
      "v_id": "Jenny",
      "attributes": {
        "gender": "female",
        "name": "Jenny",
        "state": "tx",
        "age": 25
      },
      "v_type": "person1"
    }
  ]}]
}
```

**2ホップクエリ（アキュムレーター利用）**

このクエリではパラメータ化された人物から2ホップ離れた人物を見つけます。また2ホップはなれた人物の平均年齢も計算していきたいと思います。

この種のグラフ探索のアルゴリズムを実行する場合の標準的なアプローチとしてアルゴリズムが初めてバーテックスを訪問したときにその「訪問」したことをブーリアン変数を利用することで再度認識させないことです。これを実現するためにOrAccum型のローカル アキュムレーターを定義します。このブーリアン アキュムレーター変数の初期値はfalseです。

また平均年齢を計算させるためにAvgAccum型のグローバル アキュムレーターを定義します。

開始セットを定義したのちに、1ホップ目の探索をおこないます。SELECT句、FROM句は上記の1ホップクエリをおなじですがACCUM句が追加されました。このACCUM句内の「+=」演算子はFROM句にマッチする各エッジについて右辺の値（true）を左辺のアキュムレーターに累積することを意味しています。なおソースバーテックスやターゲットバーテックスは複数回訪問される可能性があります。結果はFirstNeighborsという変数に代入されます。

2つめのSELECTブロックは1ホップ先へすすみバーテックスセットを格納するFirstNeighborsから開始して2ホップ目の隣接するバーテックスを探索します。FROM句からエッジタイプのfriendship1とターゲット頂点タイプのperson1を省略しましたが、エイリアスが定義されています。
エイリアスが書かれていない場合はALL型と解釈されます。
このグラフには1つのバーテックスタイプと1つのエッジタイプしかないので、論理的には明示的にタイプを指定した場合と同じになります。 
WHERE句は訪問済みとマークされたバーテックス（1ホップおよび開始点となるバーテックス p）をフィルタリングして除外します。
このSELECTブロックではACCUMの代わりにPOST-ACCUM句を使用しています。 
これはPOST-ACCUMがエッジセットではなく、バーテックスセットをトラバースするため、バーテックスをダブルカウントしないことを保証しているからです。 
ここでは2ホップ目の人物の年齢を累積し、その平均を求めます。

最後にp の SecondNeighbors が表示されます。


```
USE GRAPH social1
BEGIN
CREATE QUERY hello2 (VERTEX<person1> p) FOR GRAPH social1{
  OrAccum  @visited = false;
  AvgAccum @@avgAge;
  Start = {p};

  FirstNeighbors = SELECT tgt
                   FROM Start:s -(friendship1:e)- person1:tgt
                   ACCUM tgt.@visited += true, s.@visited += true;

  SecondNeighbors = SELECT tgt
                    FROM FirstNeighbors -(:e)- :tgt
                    WHERE tgt.@visited == false
                    POST_ACCUM @@avgAge += tgt.age;

  PRINT SecondNeighbors;
  PRINT @@avgAge;
}
END
INSTALL QUERY hello2
```

**実行結果**

```
GSQL > USE GRAPH social1
Using graph 'social1'
GSQL > BEGIN
GSQL > CREATE QUERY hello2 (VERTEX<person1> p) FOR GRAPH social1{
GSQL >   OrAccum  @visited = false;
GSQL >   AvgAccum @@avgAge;
GSQL >   Start = {p};
GSQL >
GSQL >   FirstNeighbors = SELECT tgt
GSQL >                    FROM Start:s -(friendship1:e)- person1:tgt
GSQL >                    ACCUM tgt.@visited += true, s.@visited += true;
GSQL >
GSQL >   SecondNeighbors = SELECT tgt
GSQL >                     FROM FirstNeighbors -(:e)- :tgt
GSQL >                     WHERE tgt.@visited == false
GSQL >                     POST_ACCUM @@avgAge += tgt.age;
GSQL >
GSQL >   PRINT SecondNeighbors;
GSQL >   PRINT @@avgAge;
GSQL > }
GSQL > END
Successfully created queries: [hello2].
GSQL > INSTALL QUERY hello2
Start installing queries, about 1 minute ...
hello2 query: curl -X GET 'https://127.0.0.1:9000/query/social1/hello2?p=VALUE'. Add -H "Authorization: Bearer TOKEN" if authentication is enabled.
Select 'm1' as compile server, now connecting ...
Node 'm1' is prepared as compile server.

[========================================================================================================] 100% (1/1)
Query installation finished.
```

**クエリの実行**

```
RUN QUERY hello2("Tom")
```

**実行結果**

```
GSQL > RUN QUERY hello2("Tom")
{
  "error": false,
  "message": "",
  "version": {
    "schema": 7,
    "edition": "enterprise",
    "api": "v2"
  },
  "results": [
    {"SecondNeighbors": [
      {
        "v_id": "Amily",
        "attributes": {
          "gender": "female",
          "@visited": false,
          "name": "Amily",
          "state": "ca",
          "age": 22
        },
        "v_type": "person1"
      },
      {
        "v_id": "Kevin",
        "attributes": {
          "gender": "male",
          "@visited": false,
          "name": "Kevin",
          "state": "az",
          "age": 28
        },
        "v_type": "person1"
      },
      {
        "v_id": "Nancy",
        "attributes": {
          "gender": "female",
          "@visited": false,
          "name": "Nancy",
          "state": "ky",
          "age": 20
        },
        "v_type": "person1"
      }
    ]},
    {"@@avgAge": 23.33333}
  ]
}
```

# おわり

今回はGSQLの基礎知識の紹介とハンズオンを通じてTigerGraphに対して様々な操作を実行しました。