【Flutter】Isarを使ってみた

# Isarとは

https://isar.dev/ja/

> Flutterのための超高速クロスプラットフォームDatabase

とのこと。

簡単なCRUD操作を試してみたので記事にする。

# 事前準備

`pubspec.yaml` に以下を追加

```yaml
dependencies:
  path_provider: ^2.0.15
  isar: ^3.1.0+1
  isar_flutter_libs: ^3.1.0+1

dev_dependencies:
  isar_generator: ^3.1.0+1
  build_runner: ^2.4.4
```

[クイックスタート](https://isar.dev/ja/tutorials/quickstart.html) の最初に説明されている、
`idar` `isar_flutter_libs` `isar_generator` `build_runner` の4つと、
データベース保存パスを取得するための `path_provider` が必要。

# コレクションクラスの作成

コレクション、とはリレーショナルデータベースで言うテーブルのようなもの。

```dart
import 'package:isar/isar.dart';

part 'sample.g.dart';

@collection
class Sample {
  Id id = Isar.autoIncrement;
  String? name;
}
```

コレクション操作用のメソッド等はIsarが用意してくれるので、
`@collection` をつけて列の定義だけしてあげればよい。

用意ができたら、

```shell
flutter pub run build_runner build
```

をたたき、残りのコードを自動生成する。
(sample.g.dart が生成される)

# Isarインスタンスの生成

```dart
  Future<Isar> open() async {
    final dir = await getApplicationDocumentsDirectory();
    final isar = await Isar.open(
      [SampleSchema],
      directory: dir.path,
    );
    return isar;
  }
```
スキーマと格納するパスを指定する。
SampleSchema は 先程自動生成された sample.g.dart で定義されている。

# データ登録

```dart
  Future<List<Id>> insert(Isar isar) async {
    final newSample1 = Sample()..name = 'サンプルレコード1';
    final newSample2 = Sample()..name = 'サンプルレコード2';
    final newSample3 = Sample()..name = 'サンプルレコード3';
    final newSample4 = Sample()..name = 'サンプルレコード4';

    await isar.writeTxn(() async {
      // 1オブジェクトの登録
      await isar.samples.put(newSample1);
      // 複数オブジェクトの登録
      await isar.samples.putAll([newSample2, newSample3, newSample4]);
    });

    return [newSample1.id, newSample2.id, newSample3.id, newSample4.id];
  }
```

`put()` や `putAll)` を使用してデータを登録できる。

# データ取得と更新

```dart
  Future<void> update(Isar isar, List<Id> sampleIds) async {
    // getでid指定で取得できる
    final sample1 = await isar.samples.get(sampleIds[1]);
    // getAllで複数取得もできる
    final samples = await isar.samples.getAll([sampleIds[2], sampleIds[3]]);

    sample1?.name = '【更新】サンプルレコード2';
    samples[0]?.name = '【更新】サンプルレコード3';
    samples[1]?.name = '【更新】サンプルレコード4';

    await isar.writeTxn(() async {
      // 更新も登録と同じくputを使う
      await isar.samples.put(sample1!);
      await isar.samples.put(samples[0]!);
      await isar.samples.put(samples[1]!);
    });
  }
```

`get()` や `getAll()` を使用してデータ取得ができる。
更新に関しては、登録と同じく `put()` や `putAll()` で実施できる。

# データ削除

```dart
  Future<void> delete(Isar isar, List<Id> sampleIds) async {
    await isar.writeTxn(() async {
      // 削除もデータ取得と同じくidを指定して行う (割愛するがfilter()を使用して条件にマッチしたものを削除、というやり方もある)
      // また、putAll、getAll のように deleteAll で一気に指定することもできる
      await isar.samples.delete(sampleIds[2]);
      await isar.samples.delete(sampleIds[3]);
    });
  }
```

`delete()` や `deleteAll()` でデータ削除ができる。

# フィルターを使ったデータ取得

```dart
  Future<void> select(Isar isar, List<Id> sampleIds) async {
    // id指定だけではなく、条件指定でのデータ取得もできる
    final samples = await isar.samples.filter().nameContains('サンプルレコード').findAll();

    for(var sample in samples) {
      print('id : ${sample.id}, name : ${sample.name}');
    }
  }
```

`filter()` のあとに指定した条件にあったメソッドを使用することで条件指定ができる。
今回は `contains()` というのを使用しており、文字列の部分一致検索に対応している。
実際に使用するときは `nameContains()` のように、どの列に対して条件指定するかをメソッド名として指定して使用する。

フィルターに使用できる条件の種類などは [公式ドキュメント](https://isar.dev/ja/queries.html#%E3%83%95%E3%82%A3%E3%83%AB%E3%82%BF) を参照してください。