NeDB を使ってみたの続き

# はじめに

Electron を使って作るスタンドアロンで動作するアプリのために、データベースエンジンに NeDB を使っています。

[NeDB を使ってみた #JavaScript - Qiita](https://qiita.com/tinymouse/items/0731eef4aebf2779bd0b)

[開発が継続されなくなった]()と聞いて、使用するのに躊躇を感じたりしていましたが、フォークされた後継版を見つけました。これを使ってみたいと思います。

# NeDB とは

NeDB は、組込型の永続的あるいはインメモリのデータベースで、Node.js 、NW.js 、Electron およびブラウザで動作し、100 ％ JavaScript でバイナリ依存しません。API は MongoDB のサブセットで、非常に高速です。

フォークされた後継版を使用します。

公式 [seald/nedb: The JavaScript Database, for Node.js, nw.js, electron and the browser](https://github.com/seald/nedb)

# NeDB を使ってみた

* NeDB 4.1.2

## インストールする

Node.js アプリのワークスペースにインストールします。

```console:
npm install @seald-io/nedb --save 
```

## データベースを作成する

### インメモリのみ（データベース読込不要）

```js:
const Datastore = require('@seald-io/nedb');
const db = new Datastore();
```

### 永続的なデータストアに手動で読込

```js:
const Datastore = require('@seald-io/nedb');
const db = new Datastore({ 
    filename: '◇◇/◆◆.db'
});
await db.loadDatabase();
```

ファイル名の拡張子は任意です。
ファイルはなければ自動で作成されます。
あれば読込まれます。

### 永続的なデータストアに自動で読込

```js:
const Datastore = require('@seald-io/nedb');
const db = new Datastore({ 
    filename: '◇◇/◆◆.db',
    autoload: true
});
```

## メソッドの呼出の記述

従来の NeDB のメソッドは、実行の完了および返値をコールバックで受取します。

```js:
db.find({ ○○: ●● }, (err, docs) => {
    if (err) {
        ....
    }
    ....	
});
```

後継版の NeDB は、`Promise` を返すメソッドが追加されています。

```js:
db.findAsync({ ○○: ●● })
.then((docs) => {
    ....
})
.catch((err) => {
    ....
});
```

```js:
try {
    const docs = await db.findAsync({ ○○: ●● });
    ....
}
catch (err) {
    ....
}
```

以降の説明は `await` を使った記述します。

## ドキュメントを挿入する

MongoDB もそうですが、NoSQL データベースでは、データの構成単位は「レコード」でなく「ドキュメント」なんですね。

```js:
var doc = {
    ○○: ●●,
    ....
};
const newDoc = await db.insertAsync(doc);
```
```newDoc``` は新規挿入されたドキュメント。
```_id``` フィールドが追加されます。これは 16 文字の英数文字列。変更は不可です。

ドキュメントの配列も挿入できます。

```js:
var docs = [
    { .... },
    { .... },
];
const newDocs = await db.insertAsync(docs, function(err, newDocs);
```    
```newDocs``` は配列になります。

内容が同一のドキュメントは挿入できません。

## ドキュメントを検索する

### 基本的な問合せ

#### 指定した値と一致するドキュメントを検索
```js:
const docs = await db.findAsync({ ○○: ●● });
```
```docs``` はドキュメントの配列です。一致するドキュメントがなければ ```[]``` になります。 

#### 指定した文字列を「含む」ドキュメントを検索
```js:
const docs = await db.findAsync({ ○○: /●●/ });
```

#### 条件を AND 指定してドキュメントを検索
```js:
const docs = await db.findAsync({ ○○: ●●, □□: ■■ });
```

#### 条件を指定せず全てのドキュメントを検索
```js:
const docs = await db.findAsync({});
```

#### 指定した値と一致するドキュメントの一つを検索
```js:
const docs = await db.findOneAsync({ ○○: ●● });
```
```doc``` は一致するドキュメントがなければ ```null``` になります。

### 比較演算

#### 指定した値より小さい
```js:
const docs = await db.findAsync({ ○○: { $lt: ●● } });
```

#### 指定した値より小さいか等しい
```js:
const docs = await db.findAsync({ ○○: { $lte: ●● } });
```

#### 指定した値より大きい
```js:
const docs = await db.findAsync({ ○○: { $gt: ●● } });
```

#### 指定した値より大きいか等しい
```js:
const docs = await db.findAsync({ ○○: { $gte: ●● } });
```

#### 指定した値のいずれかと一致する
```js:
const docs = await db.findAsync({ ○○: { $in: [●●, ■■] } });
```

#### 指定した値と異なる
```js:
const docs = await db.findAsync({ ○○: { $ne: ●● } });
```

#### 指定した値のいずれでもない
```js:
const docs = await db.findAsync({ ○○: { $nin: [●●, ■■] } });
```

#### 指定したフィールドがある
```js:
const docs = await db.findAsync({ ○○: { $exists: true } });
```

#### 正規表現を他の条件指定と合わせて使う。例えば
```js:
const docs = await db.findAsync({ ○○: { $regex: /●●/, $nin: [●●, ■■] } });
```

### 条件を論理演算

#### 条件を OR 指定
```js:
const docs = await db.findAsync({ $or: [{ ○○: ●● }, { □□: ■■ }] });
```

#### 条件を NOT 指定
```js:
const docs = await db.findAsync({ $not: { ○○: ●● } });
```

#### 関数を使って条件指定
```js:
const docs = await db.findAsync({ $where: function(){ return this.○○ = ●●; } });
```

#### 指定を組合せる。例えば
```js:
const docs = await db.findAsync({ $or: [{ ○○: ●● }, { □□: ■■ }], ○○: ×× });
```

### 並替など

#### 並替
```js:
const docs = await db.findAsync({ .... }).sort({ ○○: 1 });
```

#### 逆順に並替
```js:
const docs = await db.findAsync({ .... }).sort({ ○○: -1 });
```

#### 複数フィールドを指定して並替
```js:
const docs = await db.findAsync({ .... }).sort({ ○○: 1, ◇◇: 1 });
```

#### 先頭から指定した数だけ
```js:
const docs = await db.findAsync({ .... }).sort({ .... }).limit(●);
```

### ドキュメントのフィールドを指定

#### 指定したフィールドだけ
```js:
const docs = await db.findAsync({ .... }, { ○○: 1, □□: 1 });
```

#### 指定したフィールドを除いて
```js:
const docs = await db.findAsync({ .... }, { ○○: 0, □□: 0 });
```

#### MongoDB と非互換の書き方
```js:
const docs = await db.findAsync({ .... }).projection({ ○○: 1, □□: 1 });
```

## ドキュメントをカウントする

#### 指定した条件に一致するドキュメントをカウント
```js:
const count = await db.countAsync({ .... });
```

#### 条件を指定せず全てのドキュメントをカウント
```js:
const count = await db.countAsync({});
```

## ドキュメントを更新する

#### ドキュメントの内容を別のに置換
```js:
const { numAffected } = await db.updateAsync({ .... }, { ○○: ●● }, { multi: .... });
```
指定されたフィールドの値が置換えられます。
指定されなかったフィールドは残らないので注意が必要です。
```_id``` は変更されずに保持されます。

#### 既存のフィールドの値を置換
```js:
const { numAffected } = await db.updateAsync({ .... }, { $set: { ○○: ●● } }, { multi: .... });
```
指定されたフィールドの値が置換えられます。
指定されなかったフィールドと値は残ります。

#### フィールドを削除
```js:
await db.updateAsync({ .... }, { $unset: { ○○: true } }, {});
```

#### ドキュメントあるいはフィールドがあれば更新、なければ挿入
```js:
const { numAffected, affectedDocs, upsert } = await db.updateAsync({ .... }, { ○○: ●● }, { upsert: true });
```

## ドキュメントを削除する

#### 指定した条件のドキュメント一つを削除
```js:
const { numRemoved } = await db.removeAsync({ .... }, {});
```

#### 指定した条件のドキュメント全てを削除
```js:
const { numRemoved } = await db.removeAsync({ ... }, { multi: true });
```

#### 全てのドキュメントを削除
```js:
const { numRemoved } = await db.removeAsync({}, { multi: true });
```