VaporアプリケーションでAPIを作る(SQLiteファイルのデータを更新削除する編)

はじめに

ようやく読み書きができるようになりました。仕上げに更新と削除です。今回も荒々しく進めていきます。猪突猛進。間違っていれば後で直そう。

更新と削除は意外とシンプルでした。flatMapの扱い以外は。

データを操作する

大雑把な手順

更新と削除を行う大まかな手順はよく似ていて、とてもシンプルです。

  • 対象となるデータを取得する
  • 取得したデータのプロパティを書き換える
  • データベースにアクションを返す

データを更新する

更新したいレコードをまずは特定し、プロパティの内容を書き換えてからupdateします。

レコード間で重複しないようにしたPrimary keyを使うのが一般的でしょうか。

router.post("api/book",Book.parameter) { req -> Future<Book> in
    return try req.parameters.next(Book.self).flatMap { book in
        var renewBook = Book(~) // 新しく作る
        renewBook.id = book.id // 更新対象を特定するプロパティを設定
        return renewBook.update(on: req) // 上書きする
    }
}

取得したものの値を変えることができませんでした(letだそうです)ので、取得したものが特定できるものを使って新しくデータを作り、更新をかけるイメージですね。ちょっと思ってたのと違う動きでした。

複数一気に更新することも可能です。その時はループを回してあげれば良いです。

return Book.query(on: req).filter( ~ ).all().flatMap(to: [Book].self) { books -> EventLoopFuture<[Book]> in
    for book in books {
      // 更新処理
    }
    // 何も返さないとエラーが出てしまうのでとりあえず全部返してみる
    return Book.query(on: req).all()
}

データを削除する

更新の時とほぼ同じです。消したいレコードをまずは特定してdeleteします。updateよりも単純。

router.delete("api/book",Book.parameter) { req -> Future<Book> in
    return try req.parameters.next(Book.self).delete(on: req)
}

あいまい検索などで取得した複数の結果を一括で削除、というのもできます。一括と言っておきながら何ですが、ループで回して1つずつ消してくださいね。

VaporアプリケーションでAPIを作る(SQLiteファイルからデータ取得する編)ではall()まででしたが、それにdelete(on: req)を引っ付ければ一気に消せるかと思ったのですが、ダメでした。横着せずに1つずつ消すしかなさそうです。

router.delete("api/books",String.parameter) { req -> Future<[Book]> in
    let publisher = try req.parameters.next(String.self).lowercased()
    for book in books {
      // 削除処理
    }
}

参考までに

データの更新でPrimary keyなidを変える

Primary keyなものを変えることはあまりないと思いますが、変えることもできるようです。

update(on: req, originalID: [オリジナルのid])

ちなみにoriginalIDは指定しなくてもいいです。指定しなかった場合はnilが渡されます。処理上はupdate対象のデータが持つidでupdateがかかるようになっているのでしょう。

値が重複してしまったり、重複しないように気を遣ったりなど、面倒事が多そうな気配がしますが、こういうのがあるということは、それなりの需要があるのでしょうか?

物理削除と論理削除

いわゆるデータベースのデータについて、存在を消してしまうか存在するけど無いものとして扱うかという件です。

全然気が付かなかったのですが、FluentSQLiteのデータモデルにはdeletedAtKeyというプロパティがあるようです。これが設定(タイムスタンプが入るようです)されていれば論理削除(データ自体は存在するが消したことになっている)になります。

リファレンスを見るとdelete(on: req)で削除したときは物理削除扱いです。ただしdeletedAtKeyに値が設定されているデータは無視します。ここで、delete(force: true, on: req)にすると、そんなことはお構いなしに物理削除します。このforce:も指定しなくてもいいです。指定しない場合はデフォルトでfalseが入り、論理削除されているデータを無視します。

使い所は、論理削除したものを一括削除したりするときでしょうか。

ちなみに、論理削除にはdeleteではなくsoftDeleteを使います。論理削除したものはrestoreで復元(論理削除の解除)ができます。おらナンタラさんとかを相手にする時のように、Nullが詰まった論理削除フラグをゴソゴソ取り回さなくても良さげで好印象。
Where句で論理削除フラグの指定を忘れていて、いざ動かしてみると不要なデータも出てきて慌てるのは良くあること。そして、鬼の首を獲ったかのように吊し上げの刑に処されるのは良くあること。

まとめ

まだまだVaporフレームワークの序の口で、リファレンスを見ると深淵が広がっています。気になることもたくさんありますが、今回はここで切りあげて、MongoDBを使ってみる本題に戻ろうと思います。冒頭でも言いましたが、間違いを見つけたら後で直そうと思います。

flatMapは取っつきにくいイメージがあって、とりあえずはmap一本槍で行こうと考えていましたが、ガッツリ使うことになりました。要は逃げるなと言うことですね。理解できたかはともかく、少し親しくなれた気がします。