Couchbase Server N1QL機能解説：ユーザー定義関数

## 紹介

Couchbase ServerのN1QLでは、ユーザー定義関数を作成し、組み込み関数と同じように、任意の式で呼び出すことができます。

ユーザー定義関数は、エンタープライズエディションでのみ、利用することができます。

### 種類

ユーザー定義関数には以下の次の2つの種類があります。

- **インライン関数**は、サブクエリを含むN1QL式を使用して定義されます。クエリを簡素化するために、複雑な式や、何度も用いられる式に名前を付けて定義し、再利用することができます。

- **外部関数**は、N1QLではなく、プログラミング言語を使用して定義されます。この機能により、N1QL式を使用して定義するのが困難または不可能な関数を作成することができます。（構文上は、他の言語の利用も想定される書式が用いられていますが、現時点で）サポートされている言語はJavaScriptのみです。

ユーザー定義関数には次の2つの呼び出し方法があります。

- **N1QL**の任意の式中で、組み込み関数と同じように、呼び出すことができます。ただし、Couchbase Serverのユーザー定義関数は、単純なインライン関数を超えた高い自由度を持ち、関数の中でドキュメントの変更などを行うことができる反面、このような「副作用」を持つ、ユーザー定義関数をN1QL式の中から呼び出した場合には、エラーが発生することに注意が必要です。
- **EXECUTE FUNCTIONステートメント**を使って、ユーザー定義関数を単体で直接実行することが可能です。ユーザー定義関数をテストする際に用いることができる他、`EXECUTE FUNCTION`ステートメントでは副作用のある関数を実行することが可能なため、RDBMSにおけるストアドプロシージャのような使い方ができます(なお、Couchbase Serverでは、RDBMSにおけるトリガに類似する機能としてEventingサービスを利用することができます)。

## 概要

ユーザー定義関数の構文は組み込み関数と基本的に同じですが、関数の引数の扱いなど独自の部分があります。

関数には任意の名前を付けることができますが、関数へのパスはクエリのコンテキストによって決定されることに注意が必要です。コンテクストの扱いの異なるグローバル関数とスコープ関数が存在します。

### 注意

- 組み込み関数の名前とは異なり、ユーザー定義関数の名前では大文字と小文字が区別されます。作成時に使用されたものと同じケースを使用して、ユーザー定義関数を呼び出す必要があります。
- 関数内部の実装にミューテーション（データ変更）の実行などのような「副作用」が存在する場合、式の中でユーザー定義関数を呼び出すことはできません。この場合、エラーが生成されます。

### 引数
ユーザー定義関数には、組み込み関数と同様に、コンマで区切られた引数があります。引数の数は、0個を含めた任意の個数であることができます。また、可変長個数の引数を用いることも可能です。各引数は、N1QL式をとることができます。

#### 名前付きパラメーター
関数が名前付きパラメーターを使用して作成された場合は、関数の作成時に指定されたすべての引数を指定する必要があります。

#### 可変個引数
関数が可変個引数使用して作成された場合は、引数は指定することも、指定しないこともできます。

### 戻り値
関数は、有効なN1QLタイプの値を１つ返します。結果（および結果のデータ型）は、関数定義に使用された式またはコードによって決まります。


### グローバル関数とスコープ関数

論理階層の異なる2つのレベルでユーザー定義関数を作成することができます。

#### グローバル関数
グローバル関数は、バケットと同じレベルのネームスペース内部に作成されます。

#### スコープ関数
スコープ関数は、スコープ内のコレクションと同じレベルで、スコープ内に作成されます。

ユーザー定義関数を作成する際、現在のクエリコンテキストによって、グローバル関数として作成されるかスコープ関数として作成されるかが決まります。現在のクエリコンテキストの外部でユーザー定義関数を作成する場合は、関数名を指定するときに関数へのフルパスを含める必要があります。

同様に、ユーザー定義関数を呼び出す際にも、現在のクエリコンテキストが関数へのパスを決定します。現在のクエリコンテキストの外部のユーザー定義関数を呼び出す場合は、関数名を指定するときに関数へのフルパスを含める必要があります。

最後に、グローバル関数は、バケットの`_default`スコープに格納されているスコープ関数と同じではないことに注意することが必要です。

## ユーザー定義関数の作成: CREATE FUNCTION



`CREATE FUNCTION`ステートメントを使用すると、ユーザー定義関数を作成できます。


### 構文
`CREATE FUNCTION`ステートメントは、作成する関数のタイプに応じて異なる構文を取ります。

```
create-function ::= create-function-inline | create-function-external
```

### 共通仕様

関数のタイプに寄らず、後述のそれぞれの構文内で使われる以下のキーワードは、同じ意味を持ちます。

#### name
関数の名前。関数は、現在のクエリコンテキストに応じて、グローバル関数またはスコープ関数として作成されます。

スコープが指定された関数名には、予約済みキーワードと同じ名前を利用することができます。

#### parameter
関数の引数の名前。関数に名前付きパラメーターを指定する場合は、実行時にまったく同じ数の引数を使用して関数を呼び出す必要があります。パラメータを指定しない場合は、引数なしで関数を呼び出す必要があります。可変個引数関数、つまり、任意の数の引数を使用して呼び出すことも、引数を指定せずに呼び出すこともできる関数を作成するには、パラメーターとして`...`を指定します。

#### OR REPLACE
`OR REPLACE`キーワードを使用すると、関数を再定義できます。この場合、同じ名前の関数がすでに存在する場合、既存の関数が置き換えられます。
`OR REPLACE`キーワードを使用せず、同じ名前の関数がすでに存在する場合、エラーが生成されます。

### インライン関数
インライン関数を定義するための2つの代替構文があります。`{}`中括弧を使用した構文と、`LANGUAGE`キーワードを使用した構文です。2つの構文は同義です。

```
create-function-inline ::= CREATE [ OR REPLACE ] FUNCTION name
                          '(' [ parameter ( ',' parameter )* ] ')'
                           ( '{' expression '}' | LANGUAGE INLINE AS expression )
```

#### expression
関数を定義するN1QL式には任意の有効な式を使用できます。関数に名前付きパラメーターを指定した場合は、これらを式で使用して、実行時に関数に渡される引数を表すことができます。関数が可変個引数であると指定した場合、実行時に関数に渡される引数はすべて、`args`という名前の配列に保持されます。

関数内の式にドキュメント内のフィールドと同じ名前のパラメータが含まれている場合、その式は常にパラメータを参照します。フィールドとパラメータを区別するには、フィールドの前にキースペース名を付けます（例：`landmark.activity`）。このあいまいさを回避するために、`vActivity`などのドキュメントのフィールド名と衝突しない一意のパラメータ名を使用することが考えられます。

関数は、有効なN1QLタイプの値を1つだけ返すことができます。インライン関数の場合、関数の結果とタイプは式の結果とタイプです。複数の値を返す必要がある場合は、配列を作成します。



### 外部関数

```
create-function-external ::= CREATE [ OR REPLACE ] FUNCTION name
                             '(' [ parameter ( ',' parameter )* ] ')'
                             LANGUAGE JAVASCRIPT AS object AT library
```


#### object
ユーザー定義関数に使用するJavaScript関数の名前。このパラメーターは文字列であり、引用符で囲む必要があります。N1QLの外部関数は、プレーンJavaScriptのみをサポートします（Eventingサービスの関数でサポートされている追加の言語機能はありません）。

#### library
使用する関数を含むJavaScriptライブラリの名前。このパラメーターは文字列であり、引用符で囲む必要があります。別途でJavaScript関数が定義されたファイルをライブラリとして作成しておく必要があります。



## ユーザー定義関数の実行: EXECUTION FUNCTION



ユーザー定義関数は、クエリの一部として利用するほか、`EXECUTE FUNCTION`ステートメントを使用して、直接実行することができます。

### 目的
`EXECUTE FUNCTION`は、でユーザー定義関数をテストする場合に役立ちます。また、N1QL式の中で呼び出す場合には不可能な、ミューテーション（データ変更）の実行などの副作用のある関数を実行することもできます。

`EXECUTE FUNCTION`ステートメントを使用して、組み込みのN1QL関数を実行することはできません。これを行うと、エラー`10101: Function not found`が生成されます。

### 構文

```
execute-function ::= EXECUTE FUNCTION name '(' [ expression ( ',' expression )* ] ')'
```

#### name
関数の名前。関数名の指定方法（関数へのパス）は現在のクエリコンテキストによって決定されます。

組み込み関数の名前とは異なり、ユーザー定義関数の名前では大文字と小文字が区別されます。ユーザー定義関数の実行には、作成時に使用されたのと同じケースを使用する必要があります。

#### expression

関数に引数として必要なN1QL式。関数が名前付きパラメーターを使用して作成された場合は、関数の作成時に指定されたすべての引数を指定する必要があります。名前付きパラメーターなしで関数が作成された場合、引数を指定することはできません。関数が可変個引数の場合、必要な数の引数を指定することも、指定しないこともできます。

### 戻り値
関数は、有効なN1QLタイプの1つの値を返します。結果（および結果のデータ型）は、関数の定義に使用された式またはコードによって異なります。

間違った数の引数、または間違ったデータ型の引数を指定すると、関数が可変個引数であるか、または一定数の引数が必要かによって、可能な結果が異なります。

#### 関数が一定数の引数を必要とする場合：

- 十分な引数を指定しないと、関数は`10104: Incorrect number of arguments`エラーを生成します。

- 指定する引数が多すぎると、関数は`10104: Incorrect number of arguments`エラーを生成します。

- 引数のいずれかが間違ったデータ型である場合、関数の式またはコードによっては、関数が予期しない結果を返す場合があります。

#### 関数が可変個引数の場合：

- 十分な引数を指定しないと、関数の式またはコードによっては、関数が予期しない結果を返す場合があります。

- 指定する引数が多すぎると、余分なパラメーターは無視されます。

- 引数のいずれかが間違ったデータ型である場合、関数の式またはコードによっては、関数が予期しない結果を返す場合があります。

#### 参考情報

[Couchbase公式ドキュメント User-Defined Functions](https://docs.couchbase.com/server/current/n1ql/n1ql-language-reference/userfun.html)

[Couchbase公式ドキュメント CREATE FUNCTION](https://docs.couchbase.com/server/current/n1ql/n1ql-language-reference/createfunction.html)

[Couchbase公式ドキュメント EXECUTE FUNCTION](https://docs.couchbase.com/server/current/n1ql/n1ql-language-reference/execfunction.html)